kb-activerecord-jdbc-adapter 0.9.7.1-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 (135) hide show
  1. data/History.txt +296 -0
  2. data/LICENSE.txt +21 -0
  3. data/Manifest.txt +139 -0
  4. data/README.txt +219 -0
  5. data/Rakefile +10 -0
  6. data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
  7. data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
  8. data/lib/active_record/connection_adapters/h2_adapter.rb +13 -0
  9. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
  10. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  11. data/lib/active_record/connection_adapters/jdbc_adapter.rb +661 -0
  12. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +26 -0
  13. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  14. data/lib/active_record/connection_adapters/mssql_adapter.rb +13 -0
  15. data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
  16. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  18. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
  19. data/lib/activerecord-jdbc-adapter.rb +6 -0
  20. data/lib/arel/engines/sql/compilers/db2_compiler.rb +9 -0
  21. data/lib/arel/engines/sql/compilers/derby_compiler.rb +6 -0
  22. data/lib/arel/engines/sql/compilers/h2_compiler.rb +6 -0
  23. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +6 -0
  24. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
  25. data/lib/generators/jdbc/jdbc_generator.rb +9 -0
  26. data/lib/jdbc_adapter.rb +27 -0
  27. data/lib/jdbc_adapter/jdbc.rake +122 -0
  28. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  29. data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
  30. data/lib/jdbc_adapter/jdbc_db2.rb +222 -0
  31. data/lib/jdbc_adapter/jdbc_derby.rb +426 -0
  32. data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
  33. data/lib/jdbc_adapter/jdbc_hsqldb.rb +221 -0
  34. data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
  35. data/lib/jdbc_adapter/jdbc_mimer.rb +145 -0
  36. data/lib/jdbc_adapter/jdbc_mssql.rb +468 -0
  37. data/lib/jdbc_adapter/jdbc_mysql.rb +260 -0
  38. data/lib/jdbc_adapter/jdbc_oracle.rb +397 -0
  39. data/lib/jdbc_adapter/jdbc_postgre.rb +531 -0
  40. data/lib/jdbc_adapter/jdbc_sqlite3.rb +386 -0
  41. data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
  42. data/lib/jdbc_adapter/missing_functionality_helper.rb +87 -0
  43. data/lib/jdbc_adapter/railtie.rb +9 -0
  44. data/lib/jdbc_adapter/rake_tasks.rb +10 -0
  45. data/lib/jdbc_adapter/tsql_helper.rb +69 -0
  46. data/lib/jdbc_adapter/version.rb +5 -0
  47. data/lib/pg.rb +4 -0
  48. data/rails_generators/jdbc_generator.rb +15 -0
  49. data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
  50. data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
  51. data/rakelib/compile.rake +23 -0
  52. data/rakelib/package.rake +91 -0
  53. data/rakelib/rails.rake +41 -0
  54. data/rakelib/test.rake +78 -0
  55. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +53 -0
  56. data/src/java/jdbc_adapter/JdbcConnectionFactory.java +36 -0
  57. data/src/java/jdbc_adapter/JdbcDerbySpec.java +293 -0
  58. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +134 -0
  59. data/src/java/jdbc_adapter/MssqlRubyJdbcConnection.java +71 -0
  60. data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +55 -0
  61. data/src/java/jdbc_adapter/RubyJdbcConnection.java +1176 -0
  62. data/src/java/jdbc_adapter/SQLBlock.java +27 -0
  63. data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
  64. data/test/abstract_db_create.rb +107 -0
  65. data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
  66. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
  67. data/test/cachedb_simple_test.rb +6 -0
  68. data/test/db/cachedb.rb +9 -0
  69. data/test/db/db2.rb +9 -0
  70. data/test/db/derby.rb +14 -0
  71. data/test/db/h2.rb +11 -0
  72. data/test/db/hsqldb.rb +12 -0
  73. data/test/db/informix.rb +11 -0
  74. data/test/db/jdbc.rb +11 -0
  75. data/test/db/jndi_config.rb +30 -0
  76. data/test/db/logger.rb +3 -0
  77. data/test/db/mssql.rb +9 -0
  78. data/test/db/mysql.rb +10 -0
  79. data/test/db/oracle.rb +34 -0
  80. data/test/db/postgres.rb +9 -0
  81. data/test/db/sqlite3.rb +15 -0
  82. data/test/db2_simple_test.rb +10 -0
  83. data/test/derby_migration_test.rb +21 -0
  84. data/test/derby_multibyte_test.rb +12 -0
  85. data/test/derby_simple_test.rb +21 -0
  86. data/test/generic_jdbc_connection_test.rb +9 -0
  87. data/test/h2_simple_test.rb +6 -0
  88. data/test/has_many_through.rb +79 -0
  89. data/test/helper.rb +5 -0
  90. data/test/hsqldb_simple_test.rb +6 -0
  91. data/test/informix_simple_test.rb +48 -0
  92. data/test/jdbc_adapter/jdbc_db2_test.rb +26 -0
  93. data/test/jdbc_adapter/jdbc_sybase_test.rb +33 -0
  94. data/test/jdbc_common.rb +25 -0
  95. data/test/jndi_callbacks_test.rb +38 -0
  96. data/test/jndi_test.rb +35 -0
  97. data/test/manualTestDatabase.rb +191 -0
  98. data/test/minirunit.rb +109 -0
  99. data/test/minirunit/testConnect.rb +14 -0
  100. data/test/minirunit/testH2.rb +73 -0
  101. data/test/minirunit/testHsqldb.rb +73 -0
  102. data/test/minirunit/testLoadActiveRecord.rb +3 -0
  103. data/test/minirunit/testMysql.rb +83 -0
  104. data/test/minirunit/testRawSelect.rb +24 -0
  105. data/test/models/add_not_null_column_to_table.rb +12 -0
  106. data/test/models/auto_id.rb +18 -0
  107. data/test/models/data_types.rb +28 -0
  108. data/test/models/entry.rb +23 -0
  109. data/test/models/mixed_case.rb +20 -0
  110. data/test/models/reserved_word.rb +18 -0
  111. data/test/models/string_id.rb +18 -0
  112. data/test/models/validates_uniqueness_of_string.rb +19 -0
  113. data/test/mssql_db_create_test.rb +26 -0
  114. data/test/mssql_identity_insert_test.rb +19 -0
  115. data/test/mssql_legacy_types_test.rb +58 -0
  116. data/test/mssql_limit_offset_test.rb +108 -0
  117. data/test/mssql_multibyte_test.rb +18 -0
  118. data/test/mssql_simple_test.rb +49 -0
  119. data/test/mysql_db_create_test.rb +25 -0
  120. data/test/mysql_info_test.rb +62 -0
  121. data/test/mysql_multibyte_test.rb +10 -0
  122. data/test/mysql_nonstandard_primary_key_test.rb +42 -0
  123. data/test/mysql_simple_test.rb +32 -0
  124. data/test/oracle_simple_test.rb +54 -0
  125. data/test/pick_rails_version.rb +3 -0
  126. data/test/postgres_db_create_test.rb +21 -0
  127. data/test/postgres_mixed_case_test.rb +19 -0
  128. data/test/postgres_nonseq_pkey_test.rb +40 -0
  129. data/test/postgres_reserved_test.rb +22 -0
  130. data/test/postgres_schema_search_path_test.rb +46 -0
  131. data/test/postgres_simple_test.rb +13 -0
  132. data/test/simple.rb +494 -0
  133. data/test/sqlite3_simple_test.rb +233 -0
  134. data/test/sybase_jtds_simple_test.rb +6 -0
  135. metadata +230 -0
@@ -0,0 +1,468 @@
1
+ require 'jdbc_adapter/tsql_helper'
2
+
3
+ module ::JdbcSpec
4
+
5
+ module ActiveRecordExtensions
6
+
7
+ def mssql_connection(config)
8
+ require "active_record/connection_adapters/mssql_adapter"
9
+ config[:host] ||= "localhost"
10
+ config[:port] ||= 1433
11
+ config[:url] ||= "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
12
+ config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
13
+ embedded_driver(config)
14
+ end
15
+
16
+ end
17
+
18
+ module MsSQL
19
+
20
+ include TSqlMethods
21
+
22
+ def self.extended(mod)
23
+ unless @lob_callback_added
24
+ ActiveRecord::Base.class_eval do
25
+ def after_save_with_mssql_lob
26
+ self.class.columns.select { |c| c.sql_type =~ /image/i }.each do |c|
27
+ value = self[c.name]
28
+ value = value.to_yaml if unserializable_attribute?(c.name, c)
29
+ next if value.nil? || (value == '')
30
+
31
+ connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
32
+ end
33
+ end
34
+ end
35
+
36
+ ActiveRecord::Base.after_save :after_save_with_mssql_lob
37
+ @lob_callback_added = true
38
+ end
39
+ mod.add_version_specific_add_limit_offset
40
+ end
41
+
42
+ def self.adapter_matcher(name, *)
43
+ name =~ /sqlserver|tds/i ? self : false
44
+ end
45
+
46
+ def self.column_selector
47
+ [/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
48
+ end
49
+
50
+ def self.jdbc_connection_class
51
+ ::ActiveRecord::ConnectionAdapters::MssqlJdbcConnection
52
+ end
53
+
54
+ def sqlserver_version
55
+ @sqlserver_version ||= select_value("select @@version")[/Microsoft SQL Server\s+(\d{4})/, 1]
56
+ end
57
+
58
+ def add_version_specific_add_limit_offset
59
+ if sqlserver_version == "2000"
60
+ extend SqlServer2000LimitOffset
61
+ else
62
+ extend SqlServerLimitOffset
63
+ end
64
+ end
65
+
66
+ def modify_types(tp) #:nodoc:
67
+ super(tp)
68
+ tp[:string] = {:name => "NVARCHAR", :limit => 255}
69
+ if sqlserver_version == "2000"
70
+ tp[:text] = {:name => "NTEXT"}
71
+ else
72
+ tp[:text] = {:name => "NVARCHAR(MAX)"}
73
+ end
74
+ tp
75
+ end
76
+
77
+ module Column
78
+ attr_accessor :identity, :is_special
79
+
80
+ def simplified_type(field_type)
81
+ case field_type
82
+ when /int|bigint|smallint|tinyint/i then :integer
83
+ when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
84
+ when /float|double|decimal|money|real|smallmoney/i then :decimal
85
+ when /datetime|smalldatetime/i then :datetime
86
+ when /timestamp/i then :timestamp
87
+ when /time/i then :time
88
+ when /date/i then :date
89
+ when /text|ntext/i then :text
90
+ when /binary|image|varbinary/i then :binary
91
+ when /char|nchar|nvarchar|string|varchar/i then :string
92
+ when /bit/i then :boolean
93
+ when /uniqueidentifier/i then :string
94
+ end
95
+ end
96
+
97
+ def default_value(value)
98
+ return $1 if value =~ /^\(N?'(.*)'\)$/
99
+ value
100
+ end
101
+
102
+ def type_cast(value)
103
+ return nil if value.nil? || value == "(null)" || value == "(NULL)"
104
+ case type
105
+ when :integer then unquote(value).to_i rescue value ? 1 : 0
106
+ when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
107
+ when :decimal then self.class.value_to_decimal(unquote(value))
108
+ when :datetime then cast_to_datetime(value)
109
+ when :timestamp then cast_to_time(value)
110
+ when :time then cast_to_time(value)
111
+ when :date then cast_to_date(value)
112
+ when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or unquote(value)=="1"
113
+ when :binary then unquote value
114
+ else value
115
+ end
116
+
117
+ end
118
+
119
+ def is_utf8?
120
+ sql_type =~ /nvarchar|ntext|nchar/i
121
+ end
122
+
123
+ def unquote(value)
124
+ value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
125
+ end
126
+
127
+ def cast_to_time(value)
128
+ return value if value.is_a?(Time)
129
+ time_array = ParseDate.parsedate(value)
130
+ time_array[0] ||= 2000
131
+ time_array[1] ||= 1
132
+ time_array[2] ||= 1
133
+ Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
134
+ end
135
+
136
+ def cast_to_date(value)
137
+ return value if value.is_a?(Date)
138
+ return Date.parse(value) rescue nil
139
+ end
140
+
141
+ def cast_to_datetime(value)
142
+ if value.is_a?(Time)
143
+ if value.year != 0 and value.month != 0 and value.day != 0
144
+ return value
145
+ else
146
+ return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
147
+ end
148
+ end
149
+ return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
150
+ value
151
+ end
152
+
153
+ # These methods will only allow the adapter to insert binary data with a length of 7K or less
154
+ # because of a SQL Server statement length policy.
155
+ def self.string_to_binary(value)
156
+ ''
157
+ end
158
+
159
+ end
160
+
161
+ def quote(value, column = nil)
162
+ return value.quoted_id if value.respond_to?(:quoted_id)
163
+
164
+ case value
165
+ when String, ActiveSupport::Multibyte::Chars
166
+ value = value.to_s
167
+ if column && column.type == :binary
168
+ "'#{quote_string(JdbcSpec::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
169
+ elsif column && [:integer, :float].include?(column.type)
170
+ value = column.type == :integer ? value.to_i : value.to_f
171
+ value.to_s
172
+ elsif !column.respond_to?(:is_utf8?) || column.is_utf8?
173
+ "N'#{quote_string(value)}'" # ' (for ruby-mode)
174
+ else
175
+ super
176
+ end
177
+ when TrueClass then '1'
178
+ when FalseClass then '0'
179
+ else super
180
+ end
181
+ end
182
+
183
+ def quote_string(string)
184
+ string.gsub(/\'/, "''")
185
+ end
186
+
187
+ def quote_table_name(name)
188
+ name
189
+ end
190
+
191
+ def quote_column_name(name)
192
+ "[#{name}]"
193
+ end
194
+
195
+ def quoted_true
196
+ quote true
197
+ end
198
+
199
+ def quoted_false
200
+ quote false
201
+ end
202
+
203
+ module SqlServer2000LimitOffset
204
+ def add_limit_offset!(sql, options)
205
+ limit = options[:limit]
206
+ if limit
207
+ offset = (options[:offset] || 0).to_i
208
+ start_row = offset + 1
209
+ end_row = offset + limit.to_i
210
+ order = (options[:order] || determine_order_clause(sql))
211
+ sql.sub!(/ ORDER BY.*$/i, '')
212
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/i
213
+ whole, select, rest_of_query = find_select.match(sql).to_a
214
+ if (start_row == 1) && (end_row ==1)
215
+ new_sql = "#{select} TOP 1 #{rest_of_query}"
216
+ sql.replace(new_sql)
217
+ else
218
+ #UGLY
219
+ #KLUDGY?
220
+ #removing out stuff before the FROM...
221
+ rest = rest_of_query[/FROM/i=~ rest_of_query.. -1]
222
+ #need the table name for avoiding amiguity
223
+ table_name = get_table_name(sql)
224
+ #I am not sure this will cover all bases. but all the tests pass
225
+ new_order = "#{order}, #{table_name}.id" if order.index("#{table_name}.id").nil?
226
+ new_order ||= order
227
+ new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} ORDER BY #{new_order}) ORDER BY #{order} "
228
+ sql.replace(new_sql)
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ module SqlServerLimitOffset
235
+ def add_limit_offset!(sql, options)
236
+ limit = options[:limit]
237
+ if limit
238
+ offset = (options[:offset] || 0).to_i
239
+ start_row = offset + 1
240
+ end_row = offset + limit.to_i
241
+ order = (options[:order] || determine_order_clause(sql))
242
+ sql.sub!(/ ORDER BY.*$/i, '')
243
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/i
244
+ whole, select, rest_of_query = find_select.match(sql).to_a
245
+ new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(ORDER BY #{order}) AS row_num, #{rest_of_query}"
246
+ new_sql << ") AS t WHERE t.row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
247
+ sql.replace(new_sql)
248
+ end
249
+ end
250
+ end
251
+
252
+ def change_order_direction(order)
253
+ order.split(",").collect do |fragment|
254
+ case fragment
255
+ when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
256
+ when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
257
+ else String.new(fragment).split(',').join(' DESC,') + ' DESC'
258
+ end
259
+ end.join(",")
260
+ end
261
+
262
+ def supports_ddl_transactions?
263
+ true
264
+ end
265
+
266
+ def recreate_database(name)
267
+ drop_database(name)
268
+ create_database(name)
269
+ end
270
+
271
+ def drop_database(name)
272
+ execute "USE master"
273
+ execute "DROP DATABASE #{name}"
274
+ end
275
+
276
+ def create_database(name)
277
+ execute "CREATE DATABASE #{name}"
278
+ execute "USE #{name}"
279
+ end
280
+
281
+ def rename_table(name, new_name)
282
+ execute "EXEC sp_rename '#{name}', '#{new_name}'"
283
+ end
284
+
285
+ # Adds a new column to the named table.
286
+ # See TableDefinition#column for details of the options you can use.
287
+ def add_column(table_name, column_name, type, options = {})
288
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
289
+ add_column_options!(add_column_sql, options)
290
+ # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
291
+ # 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
292
+ execute(add_column_sql)
293
+ end
294
+
295
+ def rename_column(table, column, new_column_name)
296
+ execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
297
+ end
298
+
299
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
300
+ change_column_type(table_name, column_name, type, options)
301
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
302
+ end
303
+
304
+ def change_column_type(table_name, column_name, type, options = {}) #:nodoc:
305
+ sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
306
+ if options.has_key?(:null)
307
+ sql += (options[:null] ? " NULL" : " NOT NULL")
308
+ end
309
+ execute(sql)
310
+ end
311
+
312
+ def change_column_default(table_name, column_name, default) #:nodoc:
313
+ remove_default_constraint(table_name, column_name)
314
+ unless default.nil?
315
+ execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
316
+ end
317
+ end
318
+
319
+ def remove_column(table_name, column_name)
320
+ remove_check_constraints(table_name, column_name)
321
+ remove_default_constraint(table_name, column_name)
322
+ execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
323
+ end
324
+
325
+ def remove_default_constraint(table_name, column_name)
326
+ defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
327
+ defaults.each {|constraint|
328
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
329
+ }
330
+ end
331
+
332
+ def remove_check_constraints(table_name, column_name)
333
+ # TODO remove all constraints in single method
334
+ constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
335
+ constraints.each do |constraint|
336
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
337
+ end
338
+ end
339
+
340
+ def remove_index(table_name, options = {})
341
+ execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
342
+ end
343
+
344
+ def columns(table_name, name = nil)
345
+ return [] if table_name =~ /^information_schema\./i
346
+ cc = super
347
+ cc.each do |col|
348
+ col.identity = true if col.sql_type =~ /identity/i
349
+ col.is_special = true if col.sql_type =~ /text|ntext|image/i
350
+ end
351
+ cc
352
+ end
353
+
354
+ def _execute(sql, name = nil)
355
+ if sql.lstrip =~ /^insert/i
356
+ if query_requires_identity_insert?(sql)
357
+ table_name = get_table_name(sql)
358
+ with_identity_insert_enabled(table_name) do
359
+ id = @connection.execute_insert(sql)
360
+ end
361
+ else
362
+ @connection.execute_insert(sql)
363
+ end
364
+ elsif sql.lstrip =~ /^(create|exec)/i
365
+ @connection.execute_update(sql)
366
+ elsif sql.lstrip =~ /^\(?\s*(select|show)/i
367
+ repair_special_columns(sql)
368
+ @connection.execute_query(sql)
369
+ else
370
+ @connection.execute_update(sql)
371
+ end
372
+ end
373
+
374
+ #SELECT .. FOR UPDATE is not supported on Microsoft SQL Server
375
+ def add_lock!(sql, options)
376
+ sql
377
+ end
378
+
379
+ private
380
+
381
+ # Turns IDENTITY_INSERT ON for table during execution of the block
382
+ # N.B. This sets the state of IDENTITY_INSERT to OFF after the
383
+ # block has been executed without regard to its previous state
384
+ def with_identity_insert_enabled(table_name, &block)
385
+ set_identity_insert(table_name, true)
386
+ yield
387
+ ensure
388
+ set_identity_insert(table_name, false)
389
+ end
390
+
391
+ def set_identity_insert(table_name, enable = true)
392
+ execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
393
+ rescue Exception => e
394
+ raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
395
+ end
396
+
397
+ def get_table_name(sql)
398
+ if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
399
+ $1
400
+ elsif sql =~ /from\s+([^\(\s,]+)\s*/i
401
+ $1
402
+ else
403
+ nil
404
+ end
405
+ end
406
+
407
+ def identity_column(table_name)
408
+ @table_columns = {} unless @table_columns
409
+ @table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
410
+ @table_columns[table_name].each do |col|
411
+ return col.name if col.identity
412
+ end
413
+
414
+ return nil
415
+ end
416
+
417
+ def query_requires_identity_insert?(sql)
418
+ table_name = get_table_name(sql)
419
+ id_column = identity_column(table_name)
420
+ if sql.strip =~ /insert into [^ ]+ ?\((.+?)\)/i
421
+ insert_columns = $1.split(/, */).map(&method(:unquote_column_name))
422
+ return table_name if insert_columns.include?(id_column)
423
+ end
424
+ end
425
+
426
+ def unquote_column_name(name)
427
+ if name =~ /^\[.*\]$/
428
+ name[1..-2]
429
+ else
430
+ name
431
+ end
432
+ end
433
+
434
+ def get_special_columns(table_name)
435
+ special = []
436
+ @table_columns ||= {}
437
+ @table_columns[table_name] ||= columns(table_name)
438
+ @table_columns[table_name].each do |col|
439
+ special << col.name if col.is_special
440
+ end
441
+ special
442
+ end
443
+
444
+ def repair_special_columns(sql)
445
+ special_cols = get_special_columns(get_table_name(sql))
446
+ for col in special_cols.to_a
447
+ sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
448
+ sql.gsub!(/ORDER BY #{col.to_s}/i, '')
449
+ end
450
+ sql
451
+ end
452
+
453
+ def determine_order_clause(sql)
454
+ return $1 if sql =~ /ORDER BY (.*)$/
455
+ sql =~ /FROM +(\w+?)\b/ || raise("can't determine table name")
456
+ table_name = $1
457
+ "#{table_name}.#{determine_primary_key(table_name)}"
458
+ end
459
+
460
+ def determine_primary_key(table_name)
461
+ primary_key = columns(table_name).detect { |column| column.primary || column.identity }
462
+ primary_key ? primary_key.name : "id"
463
+ end
464
+
465
+ end
466
+
467
+ end
468
+