activerecord-jdbc-alt-adapter 70.1.0-java → 71.0.0.alpha1-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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +135 -21
  3. data/.github/workflows/ruby.yml +10 -10
  4. data/.gitignore +1 -0
  5. data/.solargraph.yml +15 -0
  6. data/Gemfile +17 -4
  7. data/README.md +7 -3
  8. data/RUNNING_TESTS.md +36 -0
  9. data/activerecord-jdbc-adapter.gemspec +2 -2
  10. data/activerecord-jdbc-alt-adapter.gemspec +1 -1
  11. data/lib/arel/visitors/sqlserver.rb +10 -0
  12. data/lib/arjdbc/abstract/connection_management.rb +23 -10
  13. data/lib/arjdbc/abstract/core.rb +5 -6
  14. data/lib/arjdbc/abstract/database_statements.rb +35 -25
  15. data/lib/arjdbc/abstract/statement_cache.rb +1 -6
  16. data/lib/arjdbc/abstract/transaction_support.rb +37 -9
  17. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  18. data/lib/arjdbc/jdbc/column.rb +0 -34
  19. data/lib/arjdbc/jdbc/connection_methods.rb +1 -1
  20. data/lib/arjdbc/mssql/adapter.rb +93 -80
  21. data/lib/arjdbc/mssql/column.rb +1 -0
  22. data/lib/arjdbc/mssql/connection_methods.rb +7 -55
  23. data/lib/arjdbc/mssql/database_statements.rb +182 -71
  24. data/lib/arjdbc/mssql/explain_support.rb +8 -5
  25. data/lib/arjdbc/mssql/schema_creation.rb +1 -1
  26. data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
  27. data/lib/arjdbc/mssql/schema_statements.rb +19 -11
  28. data/lib/arjdbc/mssql/server_version.rb +56 -0
  29. data/lib/arjdbc/mssql/utils.rb +23 -9
  30. data/lib/arjdbc/mysql/adapter.rb +64 -22
  31. data/lib/arjdbc/mysql/connection_methods.rb +43 -42
  32. data/lib/arjdbc/sqlite3/adapter.rb +218 -135
  33. data/lib/arjdbc/sqlite3/column.rb +103 -0
  34. data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
  35. data/lib/arjdbc/tasks/mssql_database_tasks.rb +9 -5
  36. data/lib/arjdbc/version.rb +1 -1
  37. data/rakelib/02-test.rake +1 -1
  38. data/rakelib/rails.rake +2 -0
  39. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4 -2
  40. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +2 -1
  41. metadata +11 -14
  42. data/lib/arel/visitors/sql_server/ng42.rb +0 -294
  43. data/lib/arel/visitors/sql_server.rb +0 -124
  44. data/lib/arjdbc/mssql/limit_helpers.rb +0 -231
  45. data/lib/arjdbc/mssql/lock_methods.rb +0 -77
  46. data/lib/arjdbc/mssql/old_adapter.rb +0 -804
  47. data/lib/arjdbc/mssql/old_column.rb +0 -200
@@ -1,804 +0,0 @@
1
- # NOTE: file contains code adapted from **sqlserver** adapter, license follows
2
- =begin
3
- Copyright (c) 2008-2015
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
- =end
24
-
25
- ArJdbc.load_java_part :MSSQL
26
-
27
- require 'strscan'
28
-
29
- module ArJdbc
30
- module MSSQL
31
-
32
- require 'arjdbc/mssql/utils'
33
- require 'arjdbc/mssql/limit_helpers'
34
- require 'arjdbc/mssql/lock_methods'
35
- require 'arjdbc/mssql/column'
36
- require 'arjdbc/mssql/explain_support'
37
- require 'arjdbc/mssql/types' if AR42
38
- require 'arel/visitors/sql_server'
39
-
40
- include LimitHelpers
41
- include Utils
42
- include ExplainSupport
43
-
44
- # @private
45
- def self.extended(adapter)
46
- initialize!
47
-
48
- version = adapter.config[:sqlserver_version] ||= adapter.sqlserver_version
49
- adapter.send(:setup_limit_offset!, version)
50
- end
51
-
52
- # @private
53
- @@_initialized = nil
54
-
55
- # @private
56
- def self.initialize!
57
- return if @@_initialized; @@_initialized = true
58
-
59
- require 'arjdbc/util/serialized_attributes'
60
- Util::SerializedAttributes.setup /image/i, 'after_save_with_mssql_lob'
61
- end
62
-
63
- # @private
64
- @@update_lob_values = true
65
-
66
- # Updating records with LOB values (binary/text columns) in a separate
67
- # statement can be disabled using :
68
- #
69
- # ArJdbc::MSSQL.update_lob_values = false
70
- #
71
- # @note This only applies when prepared statements are not used.
72
- def self.update_lob_values?; @@update_lob_values; end
73
- # @see #update_lob_values?
74
- def self.update_lob_values=(update); @@update_lob_values = update; end
75
-
76
- # @private
77
- @@cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
78
-
79
- # Operator for sorting strings in SQLServer, setup as :
80
- #
81
- # ArJdbc::MSSQL.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
82
- #
83
- def self.cs_equality_operator; @@cs_equality_operator; end
84
- # @see #cs_equality_operator
85
- def self.cs_equality_operator=(operator); @@cs_equality_operator = operator; end
86
-
87
- # @see #quote
88
- # @private
89
- BLOB_VALUE_MARKER = "''"
90
-
91
- # @see #update_lob_values?
92
- # @see ArJdbc::Util::SerializedAttributes#update_lob_columns
93
- def update_lob_value?(value, column = nil)
94
- MSSQL.update_lob_values? && ! prepared_statements? # && value
95
- end
96
-
97
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
98
- def self.jdbc_connection_class
99
- ::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
100
- end
101
-
102
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
103
- def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::MSSQLColumn end
104
-
105
- def configure_connection
106
- use_database # config[:database]
107
- end
108
-
109
- def sqlserver_version
110
- @sqlserver_version ||= begin
111
- config_version = config[:sqlserver_version]
112
- config_version ? config_version.to_s :
113
- select_value("SELECT @@version")[/(Microsoft SQL Server\s+|Microsoft SQL Azure.+\n.+)(\d{4})/, 2]
114
- end
115
- end
116
-
117
- NATIVE_DATABASE_TYPES = {
118
- :primary_key => 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
119
- :integer => { :name => 'int', }, # :limit => 4
120
- :boolean => { :name => 'bit' },
121
- :decimal => { :name => 'decimal' },
122
- :float => { :name => 'float' },
123
- :bigint => { :name => 'bigint' },
124
- :real => { :name => 'real' },
125
- :date => { :name => 'date' },
126
- :time => { :name => 'time' },
127
- :datetime => { :name => 'datetime' },
128
- :timestamp => { :name => 'datetime' },
129
-
130
- :string => { :name => 'nvarchar', :limit => 4000 },
131
- #:varchar => { :name => 'varchar' }, # limit: 8000
132
- :text => { :name => 'nvarchar(max)' },
133
- :text_basic => { :name => 'text' },
134
- #:ntext => { :name => 'ntext' },
135
- :char => { :name => 'char' },
136
- #:nchar => { :name => 'nchar' },
137
- :binary => { :name => 'image' }, # NOTE: :name => 'varbinary(max)'
138
- :binary_basic => { :name => 'binary' },
139
- :uuid => { :name => 'uniqueidentifier' },
140
- :money => { :name => 'money' },
141
- #:smallmoney => { :name => 'smallmoney' },
142
- }
143
-
144
- def native_database_types
145
- # NOTE: due compatibility we're using the generic type resolution
146
- # ... NATIVE_DATABASE_TYPES won't be used at all on SQLServer 2K
147
- sqlserver_2000? ? super : super.merge(NATIVE_DATABASE_TYPES)
148
- end
149
-
150
- def modify_types(types)
151
- if sqlserver_2000?
152
- types[:primary_key] = NATIVE_DATABASE_TYPES[:primary_key]
153
- types[:string] = NATIVE_DATABASE_TYPES[:string]
154
- types[:boolean] = NATIVE_DATABASE_TYPES[:boolean]
155
- types[:text] = { :name => "ntext" }
156
- types[:integer][:limit] = nil
157
- types[:binary] = { :name => "image" }
158
- else
159
- # ~ private types for better "native" adapter compatibility
160
- types[:varchar_max] = { :name => 'varchar(max)' }
161
- types[:nvarchar_max] = { :name => 'nvarchar(max)' }
162
- types[:varbinary_max] = { :name => 'varbinary(max)' }
163
- end
164
- types[:string][:limit] = 255 unless AR40 # backwards compatibility
165
- types
166
- end
167
-
168
- # @private these cannot specify a limit
169
- NO_LIMIT_TYPES = %w( text binary boolean date datetime )
170
-
171
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
172
- type_s = type.to_s
173
- # MSSQL's NVARCHAR(n | max) column supports either a number between 1 and
174
- # 4000, or the word "MAX", which corresponds to 2**30-1 UCS-2 characters.
175
- #
176
- # It does not accept NVARCHAR(1073741823) here, so we have to change it
177
- # to NVARCHAR(MAX), even though they are logically equivalent.
178
- #
179
- # MSSQL Server 2000 is skipped here because I don't know how it will behave.
180
- #
181
- # See: http://msdn.microsoft.com/en-us/library/ms186939.aspx
182
- if type_s == 'string' && limit == 1073741823 && ! sqlserver_2000?
183
- 'NVARCHAR(MAX)'
184
- elsif NO_LIMIT_TYPES.include?(type_s)
185
- super(type)
186
- elsif type_s == 'integer' || type_s == 'int'
187
- if limit.nil? || limit == 4
188
- 'int'
189
- elsif limit == 2
190
- 'smallint'
191
- elsif limit == 1
192
- 'tinyint'
193
- else
194
- 'bigint'
195
- end
196
- elsif type_s == 'uniqueidentifier'
197
- type_s
198
- else
199
- super
200
- end
201
- end
202
-
203
- # @override
204
- def quote(value, column = nil)
205
- return value.quoted_id if value.respond_to?(:quoted_id)
206
- return value if sql_literal?(value)
207
-
208
- case value
209
- # SQL Server 2000 doesn't let you insert an integer into a NVARCHAR
210
- when String, ActiveSupport::Multibyte::Chars, Integer
211
- value = value.to_s
212
- column_type = column && column.type
213
- if column_type == :binary
214
- if update_lob_value?(value, column)
215
- BLOB_VALUE_MARKER
216
- else
217
- "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
218
- end
219
- elsif column_type == :integer
220
- value.to_i.to_s
221
- elsif column_type == :float
222
- value.to_f.to_s
223
- elsif ! column.respond_to?(:is_utf8?) || column.is_utf8?
224
- "N'#{quote_string(value)}'" # ' (for ruby-mode)
225
- else
226
- super
227
- end
228
- when Date, Time
229
- if column && column.type == :time
230
- "'#{quoted_time(value)}'"
231
- else
232
- "'#{quoted_date(value)}'"
233
- end
234
- when TrueClass then '1'
235
- when FalseClass then '0'
236
- else super
237
- end
238
- end
239
-
240
- # @override
241
- def quoted_date(value)
242
- if value.respond_to?(:usec)
243
- "#{super}.#{sprintf("%03d", value.usec / 1000)}"
244
- else
245
- super
246
- end
247
- end
248
-
249
- # @private
250
- def quoted_time(value)
251
- if value.acts_like?(:time)
252
- tz_value = get_time(value)
253
- usec = value.respond_to?(:usec) ? ( value.usec / 1000 ) : 0
254
- sprintf("%02d:%02d:%02d.%03d", tz_value.hour, tz_value.min, tz_value.sec, usec)
255
- else
256
- quoted_date(value)
257
- end
258
- end
259
-
260
- # @deprecated no longer used
261
- # @private
262
- def quoted_datetime(value)
263
- quoted_date(value)
264
- end
265
-
266
- # @deprecated no longer used
267
- # @private
268
- def quoted_full_iso8601(value)
269
- if value.acts_like?(:time)
270
- value.is_a?(Date) ?
271
- get_time(value).to_time.xmlschema.to(18) :
272
- get_time(value).iso8601(7).to(22)
273
- else
274
- quoted_date(value)
275
- end
276
- end
277
-
278
- def quote_table_name(name)
279
- quote_column_name(name)
280
- end
281
-
282
- def quote_column_name(name)
283
- name = name.to_s.split('.')
284
- name.map! { |n| quote_name_part(n) } # "[#{name}]"
285
- name.join('.')
286
- end
287
-
288
- def quote_database_name(name)
289
- quote_name_part(name.to_s)
290
- end
291
-
292
- # Does not quote function default values for UUID columns
293
- def quote_default_value(value, column)
294
- if column.type == :uuid && value =~ /\(\)/
295
- value
296
- else
297
- quote(value)
298
- end
299
- end
300
-
301
- ADAPTER_NAME = 'MSSQL'.freeze
302
-
303
- def adapter_name
304
- ADAPTER_NAME
305
- end
306
-
307
- def change_order_direction(order)
308
- asc, desc = /\bASC\b/i, /\bDESC\b/i
309
- order.split(",").collect do |fragment|
310
- case fragment
311
- when desc then fragment.gsub(desc, "ASC")
312
- when asc then fragment.gsub(asc, "DESC")
313
- else "#{fragment.split(',').join(' DESC,')} DESC"
314
- end
315
- end.join(",")
316
- end
317
-
318
- # @override
319
- def supports_ddl_transactions?; true end
320
-
321
- # @override
322
- def supports_views?; true end
323
-
324
- def tables(schema = current_schema)
325
- @connection.tables(nil, schema)
326
- end
327
-
328
- # NOTE: Dynamic Name Resolution - SQL Server 2000 vs. 2005
329
- #
330
- # A query such as "select * from table1" in SQL Server 2000 goes through
331
- # a set of steps to resolve and validate the object references before
332
- # execution.
333
- # The search first looks at the identity of the connection executing
334
- # the query.
335
- #
336
- # However SQL Server 2005 provides a mechanism to allow finer control over
337
- # name resolution to the administrators. By manipulating the value of the
338
- # default_schema_name columns in the sys.database_principals.
339
- #
340
- # http://blogs.msdn.com/b/mssqlisv/archive/2007/03/23/upgrading-to-sql-server-2005-and-default-schema-setting.aspx
341
-
342
- # Returns the default schema (to be used for table resolution) used for the {#current_user}.
343
- def default_schema
344
- return current_user if sqlserver_2000?
345
- @default_schema ||=
346
- @connection.execute_query_raw(
347
- "SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER"
348
- ).first['default_schema_name']
349
- end
350
- alias_method :current_schema, :default_schema
351
-
352
- # Allows for changing of the default schema (to be used during unqualified
353
- # table name resolution).
354
- # @note This is not supported on SQL Server 2000 !
355
- def default_schema=(default_schema) # :nodoc:
356
- raise "changing DEFAULT_SCHEMA only supported on SQLServer 2005+" if sqlserver_2000?
357
- execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
358
- @default_schema = nil if defined?(@default_schema)
359
- end
360
- alias_method :current_schema=, :default_schema=
361
-
362
- # `SELECT CURRENT_USER`
363
- def current_user
364
- @current_user ||= @connection.execute_query_raw("SELECT CURRENT_USER").first['']
365
- end
366
-
367
- def charset
368
- select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
369
- end
370
-
371
- def collation
372
- select_value "SELECT SERVERPROPERTY('Collation')"
373
- end
374
-
375
- def current_database
376
- select_value 'SELECT DB_NAME()'
377
- end
378
-
379
- def use_database(database = nil)
380
- database ||= config[:database]
381
- execute "USE #{quote_database_name(database)}" unless database.blank?
382
- end
383
-
384
- # @private
385
- def recreate_database(name, options = {})
386
- drop_database(name)
387
- create_database(name, options)
388
- end
389
-
390
- # @private
391
- def recreate_database!(database = nil)
392
- current_db = current_database
393
- database ||= current_db
394
- use_database('master') if this_db = ( database.to_s == current_db )
395
- drop_database(database)
396
- create_database(database)
397
- ensure
398
- use_database(current_db) if this_db
399
- end
400
-
401
- def drop_database(name)
402
- current_db = current_database
403
- use_database('master') if current_db.to_s == name
404
- execute "DROP DATABASE #{quote_database_name(name)}"
405
- end
406
-
407
- def create_database(name, options = {})
408
- execute "CREATE DATABASE #{quote_database_name(name)}"
409
- end
410
-
411
- def database_exists?(name)
412
- select_value "SELECT name FROM sys.databases WHERE name = '#{name}'"
413
- end
414
-
415
- # @override
416
- def rename_table(table_name, new_table_name)
417
- clear_cached_table(table_name)
418
- execute "EXEC sp_rename '#{table_name}', '#{new_table_name}'"
419
- end
420
-
421
- # Adds a new column to the named table.
422
- # @override
423
- def add_column(table_name, column_name, type, options = {})
424
- clear_cached_table(table_name)
425
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
426
- add_column_options!(add_column_sql, options)
427
- # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
428
- # add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date
429
- execute(add_column_sql)
430
- end
431
-
432
- # @override
433
- def rename_column(table_name, column_name, new_column_name)
434
- clear_cached_table(table_name)
435
- execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
436
- end
437
-
438
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
439
- # MSSQL requires the ORDER BY columns in the select list for distinct queries.
440
- def distinct(columns, order_by)
441
- "DISTINCT #{columns_for_distinct(columns, order_by)}"
442
- end
443
-
444
- def columns_for_distinct(columns, orders)
445
- return columns if orders.blank?
446
-
447
- # construct a clean list of column names from the ORDER BY clause,
448
- # removing any ASC/DESC modifiers
449
- order_columns = [ orders ]; order_columns.flatten! # AR 3.x vs 4.x
450
- order_columns.map! do |column|
451
- column = column.to_sql unless column.is_a?(String) # handle AREL node
452
- column.split(',').collect!{ |s| s.split.first }
453
- end.flatten!
454
- order_columns.reject!(&:blank?)
455
- order_columns = order_columns.zip(0...order_columns.size).to_a
456
- order_columns = order_columns.map{ |s, i| "#{s}" }
457
-
458
- columns = [ columns ]; columns.flatten!
459
- columns.push( *order_columns ).join(', ')
460
- # return a DISTINCT clause that's distinct on the columns we want but
461
- # includes all the required columns for the ORDER BY to work properly
462
- end
463
-
464
- # @override
465
- def change_column(table_name, column_name, type, options = {})
466
- column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
467
-
468
- indexes = EMPTY_ARRAY
469
- if options_include_default?(options) || (column && column.type != type.to_sym)
470
- remove_default_constraint(table_name, column_name)
471
- indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) }
472
- remove_indexes(table_name, column_name)
473
- end
474
-
475
- if ! options[:null].nil? && options[:null] == false && ! options[:default].nil?
476
- execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(options[:default], column)} WHERE #{quote_column_name(column_name)} IS NULL"
477
- clear_cached_table(table_name)
478
- end
479
- change_column_type(table_name, column_name, type, options)
480
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
481
-
482
- indexes.each do |index| # add any removed indexes back
483
- index_columns = index.columns.map { |c| quote_column_name(c) }.join(', ')
484
- execute "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index_columns})"
485
- end
486
- end
487
-
488
- def change_column_type(table_name, column_name, type, options = {})
489
- sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
490
- sql << (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
491
- result = execute(sql)
492
- clear_cached_table(table_name)
493
- result
494
- end
495
-
496
- def change_column_default(table_name, column_name, default)
497
- remove_default_constraint(table_name, column_name)
498
- unless default.nil?
499
- column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
500
- result = execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote_default_value(default, column)} FOR #{quote_column_name(column_name)}"
501
- clear_cached_table(table_name)
502
- result
503
- end
504
- end
505
-
506
- def remove_columns(table_name, *column_names)
507
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
508
- # remove_columns(:posts, :foo, :bar) old syntax : remove_columns(:posts, [:foo, :bar])
509
- clear_cached_table(table_name)
510
-
511
- column_names = column_names.flatten
512
- return do_remove_column(table_name, column_names.first) if column_names.size == 1
513
- column_names.each { |column_name| do_remove_column(table_name, column_name) }
514
- end
515
-
516
- def do_remove_column(table_name, column_name)
517
- remove_check_constraints(table_name, column_name)
518
- remove_default_constraint(table_name, column_name)
519
- remove_indexes(table_name, column_name) unless sqlserver_2000?
520
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
521
- end
522
- private :do_remove_column
523
-
524
- if ActiveRecord::VERSION::MAJOR >= 4
525
-
526
- # @override
527
- def remove_column(table_name, column_name, type = nil, options = {})
528
- remove_columns(table_name, column_name)
529
- end
530
-
531
- else
532
-
533
- def remove_column(table_name, *column_names); remove_columns(table_name, *column_names) end
534
-
535
- end
536
-
537
- def remove_default_constraint(table_name, column_name)
538
- clear_cached_table(table_name)
539
- if sqlserver_2000?
540
- # NOTE: since SQLServer 2005 these are provided as sys.sysobjects etc.
541
- # but only due backwards-compatibility views and should be avoided ...
542
- defaults = select_values "SELECT d.name" <<
543
- " FROM sysobjects d, syscolumns c, sysobjects t" <<
544
- " WHERE c.cdefault = d.id AND c.name = '#{column_name}'" <<
545
- " AND t.name = '#{table_name}' AND c.id = t.id"
546
- else
547
- defaults = select_values "SELECT d.name FROM sys.tables t" <<
548
- " JOIN sys.default_constraints d ON d.parent_object_id = t.object_id" <<
549
- " JOIN sys.columns c ON c.object_id = t.object_id AND c.column_id = d.parent_column_id" <<
550
- " WHERE t.name = '#{table_name}' AND c.name = '#{column_name}'"
551
- end
552
- defaults.each do |def_name|
553
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{def_name}"
554
- end
555
- end
556
-
557
- def remove_check_constraints(table_name, column_name)
558
- clear_cached_table(table_name)
559
- constraints = select_values "SELECT constraint_name" <<
560
- " FROM information_schema.constraint_column_usage" <<
561
- " WHERE table_name = '#{table_name}' AND column_name = '#{column_name}'"
562
- constraints.each do |constraint_name|
563
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint_name}"
564
- end
565
- end
566
-
567
- def remove_indexes(table_name, column_name)
568
- indexes = self.indexes(table_name)
569
- indexes.select{ |index| index.columns.include?(column_name.to_s) }.each do |index|
570
- remove_index(table_name, { :name => index.name })
571
- end
572
- end
573
-
574
- def remove_index(table_name, options = {})
575
- execute "DROP INDEX #{quote_table_name(table_name)}.#{index_name(table_name, options)}"
576
- end
577
-
578
- # @private
579
- SKIP_COLUMNS_TABLE_NAMES_RE = /^information_schema\./i
580
-
581
- # @private
582
- EMPTY_ARRAY = [].freeze
583
-
584
- def columns(table_name, name = nil, default = EMPTY_ARRAY)
585
- # It's possible for table_name to be an empty string, or nil, if something
586
- # attempts to issue SQL which doesn't involve a table.
587
- # IE. "SELECT 1" or "SELECT * FROM someFunction()".
588
- return default if table_name.blank?
589
-
590
- table_name = unquote_table_name(table_name)
591
-
592
- return default if table_name =~ SKIP_COLUMNS_TABLE_NAMES_RE
593
-
594
- unless columns = ( @table_columns ||= {} )[table_name]
595
- @table_columns[table_name] = columns = super(table_name, name)
596
- end
597
- columns
598
- end
599
-
600
- def clear_cached_table(table_name)
601
- ( @table_columns ||= {} ).delete(table_name.to_s)
602
- end
603
-
604
- def reset_column_information
605
- @table_columns = nil if defined? @table_columns
606
- end
607
-
608
- # Turns IDENTITY_INSERT ON for table during execution of the block
609
- # N.B. This sets the state of IDENTITY_INSERT to OFF after the
610
- # block has been executed without regard to its previous state
611
- def with_identity_insert_enabled(table_name)
612
- set_identity_insert(table_name, true)
613
- yield
614
- ensure
615
- set_identity_insert(table_name, false)
616
- end
617
-
618
- def set_identity_insert(table_name, enable = true)
619
- execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
620
- rescue Exception => e
621
- raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned" +
622
- " #{enable ? 'ON' : 'OFF'} for table #{table_name} due : #{e.inspect}"
623
- end
624
-
625
- def disable_referential_integrity
626
- execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'"
627
- yield
628
- ensure
629
- execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'"
630
- end
631
-
632
- # @private
633
- # @see ArJdbc::MSSQL::LimitHelpers
634
- def determine_order_clause(sql)
635
- return $1 if sql =~ /ORDER BY (.*)$/i
636
- columns = self.columns(table_name = get_table_name(sql))
637
- primary_column = columns.find { |column| column.primary? || column.identity? }
638
- unless primary_column # look for an id column and return it,
639
- # without changing case, to cover DBs with a case-sensitive collation :
640
- primary_column = columns.find { |column| column.name =~ /^id$/i }
641
- raise "no columns for table: #{table_name} (SQL query: ' #{sql} ')" if columns.empty?
642
- end
643
- # NOTE: if still no PK column simply get something for ORDER BY ...
644
- "#{quote_table_name(table_name)}.#{quote_column_name((primary_column || columns.first).name)}"
645
- end
646
-
647
- def truncate(table_name, name = nil)
648
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
649
- end
650
-
651
- # Support for executing a stored procedure.
652
- def exec_proc(proc_name, *variables)
653
- vars =
654
- if variables.any? && variables.first.is_a?(Hash)
655
- variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
656
- else
657
- variables.map { |v| quote(v) }
658
- end.join(', ')
659
- sql = "EXEC #{proc_name} #{vars}".strip
660
- log(sql, 'Execute Procedure') do
661
- result = @connection.execute_query_raw(sql)
662
- result.map! do |row|
663
- row = row.is_a?(Hash) ? row.with_indifferent_access : row
664
- yield(row) if block_given?
665
- row
666
- end
667
- result
668
- end
669
- end
670
- alias_method :execute_procedure, :exec_proc # AR-SQLServer-Adapter naming
671
-
672
- # @override
673
- def exec_query(sql, name = 'SQL', binds = [])
674
- # NOTE: we allow to execute SQL as requested returning a results.
675
- # e.g. this allows to use SQLServer's EXEC with a result set ...
676
- sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
677
-
678
- sql = repair_special_columns(sql)
679
- if prepared_statements?
680
- log(sql, name, binds) { @connection.execute_query(sql, binds) }
681
- else
682
- log(sql, name) { @connection.execute_query(sql) }
683
- end
684
- end
685
-
686
- # @override
687
- def exec_query_raw(sql, name = 'SQL', binds = [], &block)
688
- sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
689
-
690
- sql = repair_special_columns(sql)
691
- if prepared_statements?
692
- log(sql, name, binds) { @connection.execute_query_raw(sql, binds, &block) }
693
- else
694
- log(sql, name) { @connection.execute_query_raw(sql, &block) }
695
- end
696
- end
697
-
698
- # @override
699
- def release_savepoint(name = current_savepoint_name(false))
700
- if @connection.jtds_driver?
701
- @connection.release_savepoint(name)
702
- else # MS invented it's "own" way
703
- @connection.rollback_savepoint(name)
704
- end
705
- end
706
-
707
- private
708
-
709
- def _execute(sql, name = nil)
710
- # Match the start of the SQL to determine appropriate behavior.
711
- # Be aware of multi-line SQL which might begin with 'create stored_proc'
712
- # and contain 'insert into ...' lines.
713
- # NOTE: ignoring comment blocks prior to the first statement ?!
714
- if self.class.insert?(sql)
715
- if id_insert_table_name = identity_insert_table_name(sql)
716
- with_identity_insert_enabled(id_insert_table_name) do
717
- @connection.execute_insert(sql)
718
- end
719
- else
720
- @connection.execute_insert(sql)
721
- end
722
- elsif self.class.select?(sql)
723
- @connection.execute_query_raw repair_special_columns(sql)
724
- else # create | exec
725
- @connection.execute_update(sql)
726
- end
727
- end
728
-
729
- def identity_insert_table_name(sql)
730
- table_name = get_table_name(sql)
731
- id_column = identity_column_name(table_name)
732
- if id_column && sql.strip =~ /INSERT INTO [^ ]+ ?\((.+?)\)/i
733
- insert_columns = $1.split(/, */).map(&method(:unquote_column_name))
734
- return table_name if insert_columns.include?(id_column)
735
- end
736
- end
737
-
738
- def identity_column_name(table_name)
739
- for column in columns(table_name)
740
- return column.name if column.identity
741
- end
742
- nil
743
- end
744
-
745
- def repair_special_columns(sql)
746
- qualified_table_name = get_table_name(sql, true)
747
- if special_columns = special_column_names(qualified_table_name)
748
- return sql if special_columns.empty?
749
- special_columns = special_columns.sort { |n1, n2| n2.size <=> n1.size }
750
- for column in special_columns
751
- sql.gsub!(/\s?\[?#{column}\]?\s?=\s?/, " [#{column}] LIKE ")
752
- sql.gsub!(/ORDER BY \[?#{column}([^\.\w]|$)\]?/i, '') # NOTE: a bit stupid
753
- end
754
- end
755
- sql
756
- end
757
-
758
- def special_column_names(qualified_table_name)
759
- columns = self.columns(qualified_table_name, nil, nil)
760
- return columns if ! columns || columns.empty?
761
- special = []
762
- columns.each { |column| special << column.name if column.special? }
763
- special
764
- end
765
-
766
- def sqlserver_2000?
767
- sqlserver_version <= '2000'
768
- end
769
-
770
- end
771
- end
772
-
773
- require 'arjdbc/util/quoted_cache'
774
-
775
- module ActiveRecord::ConnectionAdapters
776
-
777
- class MSSQLAdapter < JdbcAdapter
778
- include ::ArJdbc::MSSQL
779
- include ::ArJdbc::Util::QuotedCache
780
-
781
- def initialize(*args)
782
- ::ArJdbc::MSSQL.initialize!
783
-
784
- super # configure_connection happens in super
785
-
786
- setup_limit_offset!
787
- end
788
-
789
- def arel_visitor # :nodoc:
790
- ( config && config[:sqlserver_version].to_s == '2000' ) ?
791
- ::Arel::Visitors::SQLServer2000.new(self) :
792
- ::Arel::Visitors::SQLServer.new(self)
793
- end
794
-
795
- def self.cs_equality_operator; ::ArJdbc::MSSQL.cs_equality_operator end
796
- def self.cs_equality_operator=(operator); ::ArJdbc::MSSQL.cs_equality_operator = operator end
797
-
798
- end
799
-
800
- class MSSQLColumn < JdbcColumn
801
- include ::ArJdbc::MSSQL::Column
802
- end
803
-
804
- end