activerecord-jdbc-adapter 0.8 → 0.8.1

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 (38) hide show
  1. data/History.txt +21 -0
  2. data/Manifest.txt +12 -0
  3. data/README.txt +1 -0
  4. data/Rakefile +19 -13
  5. data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
  6. data/lib/active_record/connection_adapters/jdbc_adapter.rb +22 -5
  7. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +3 -0
  8. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
  9. data/lib/jdbc_adapter/jdbc.rake +32 -29
  10. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  11. data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
  12. data/lib/jdbc_adapter/jdbc_db2.rb +96 -7
  13. data/lib/jdbc_adapter/jdbc_derby.rb +35 -28
  14. data/lib/jdbc_adapter/jdbc_mssql.rb +62 -103
  15. data/lib/jdbc_adapter/jdbc_mysql.rb +34 -25
  16. data/lib/jdbc_adapter/jdbc_oracle.rb +7 -7
  17. data/lib/jdbc_adapter/jdbc_postgre.rb +1 -1
  18. data/lib/jdbc_adapter/jdbc_sqlite3.rb +188 -0
  19. data/lib/jdbc_adapter/jdbc_sybase.rb +39 -0
  20. data/lib/jdbc_adapter/tsql_helper.rb +59 -0
  21. data/lib/jdbc_adapter/version.rb +1 -1
  22. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +66 -40
  23. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +2 -2
  24. data/test/cachedb_simple_test.rb +6 -0
  25. data/test/db/cachedb.rb +9 -0
  26. data/test/db/mssql.rb +9 -0
  27. data/test/db/oracle.rb +22 -2
  28. data/test/db/sqlite3.rb +11 -0
  29. data/test/db2_simple_test.rb +4 -0
  30. data/test/jdbc_common.rb +3 -1
  31. data/test/manualTestDatabase.rb +1 -5
  32. data/test/models/entry.rb +1 -1
  33. data/test/mssql_simple_test.rb +6 -0
  34. data/test/mysql_simple_test.rb +9 -0
  35. data/test/oracle_simple_test.rb +23 -0
  36. data/test/simple.rb +3 -2
  37. data/test/sqlite3_simple_test.rb +6 -0
  38. metadata +14 -2
@@ -7,7 +7,7 @@ module ::JdbcSpec
7
7
  config[:driver] ||= "org.apache.derby.jdbc.EmbeddedDriver"
8
8
  embedded_driver(config)
9
9
  end
10
- end
10
+ end
11
11
 
12
12
  module Derby
13
13
  def self.column_selector
@@ -17,10 +17,10 @@ module ::JdbcSpec
17
17
  def self.adapter_selector
18
18
  [/derby/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::Derby)}]
19
19
  end
20
-
20
+
21
21
  def self.monkey_rails
22
22
  unless @already_monkeyd
23
- # Needed because Rails is broken wrt to quoting of
23
+ # Needed because Rails is broken wrt to quoting of
24
24
  # some values. Most databases are nice about it,
25
25
  # but not Derby. The real issue is that you can't
26
26
  # compare a CHAR value to a NUMBER column.
@@ -32,7 +32,7 @@ module ::JdbcSpec
32
32
  construct_finder_sql_for_association_limiting(options, join_dependency),
33
33
  "#{name} Load IDs For Limited Eager Loading"
34
34
  ).collect { |row| connection.quote(row[primary_key], columns_hash[primary_key]) }.join(", ")
35
- end
35
+ end
36
36
  end
37
37
 
38
38
  @already_monkeyd = true
@@ -46,12 +46,12 @@ module ::JdbcSpec
46
46
  def self.included(*args)
47
47
  monkey_rails
48
48
  end
49
-
49
+
50
50
  module Column
51
51
  def value_to_binary(value)
52
52
  value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
53
53
  end
54
-
54
+
55
55
  def cast_to_date_or_time(value)
56
56
  return value if value.is_a? Date
57
57
  return nil if value.blank?
@@ -71,14 +71,14 @@ module ::JdbcSpec
71
71
  end
72
72
 
73
73
  def simplified_type(field_type)
74
- return :boolean if field_type =~ /smallint/i
74
+ return :boolean if field_type =~ /smallint/i
75
75
  return :float if field_type =~ /real/i
76
76
  super
77
77
  end
78
78
  end
79
79
 
80
80
  include JdbcSpec::MissingFunctionalityHelper
81
-
81
+
82
82
  def modify_types(tp)
83
83
  tp[:primary_key] = "int generated by default as identity NOT NULL PRIMARY KEY"
84
84
  tp[:integer][:limit] = nil
@@ -86,7 +86,14 @@ module ::JdbcSpec
86
86
  tp[:boolean] = {:name => "smallint"}
87
87
  tp
88
88
  end
89
-
89
+
90
+ # Override default -- fix case where ActiveRecord passes :default => nil, :null => true
91
+ def add_column_options!(sql, options)
92
+ options.delete(:default) if options.has_key?(:default) && options[:default].nil?
93
+ options.delete(:null) if options.has_key?(:null) && (options[:null].nil? || options[:null].true?)
94
+ super
95
+ end
96
+
90
97
  def classes_for_table_name(table)
91
98
  ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
92
99
  end
@@ -94,9 +101,9 @@ module ::JdbcSpec
94
101
  # Set the sequence to the max value of the table's column.
95
102
  def reset_sequence!(table, column, sequence = nil)
96
103
  mpk = select_value("SELECT MAX(#{quote_column_name column}) FROM #{table}")
97
- execute("ALTER TABLE #{table} ALTER COLUMN #{quote_column_name column} RESTART WITH #{mpk.to_i + 1}")
104
+ execute("ALTER TABLE #{table} ALTER COLUMN #{quote_column_name column} RESTART WITH #{mpk.to_i + 1}")
98
105
  end
99
-
106
+
100
107
  def reset_pk_sequence!(table, pk = nil, sequence = nil)
101
108
  klasses = classes_for_table_name(table)
102
109
  klass = klasses.nil? ? nil : klasses.first
@@ -105,7 +112,7 @@ module ::JdbcSpec
105
112
  reset_sequence!(klass.table_name, pk)
106
113
  end
107
114
  end
108
-
115
+
109
116
  def primary_key(table_name) #:nodoc:
110
117
  primary_keys(table_name).first
111
118
  end
@@ -117,7 +124,7 @@ module ::JdbcSpec
117
124
  def rename_table(name, new_name)
118
125
  execute "RENAME TABLE #{name} TO #{new_name}"
119
126
  end
120
-
127
+
121
128
  COLUMN_INFO_STMT = "SELECT C.COLUMNNAME, C.REFERENCEID, C.COLUMNNUMBER FROM SYS.SYSCOLUMNS C, SYS.SYSTABLES T WHERE T.TABLEID = '%s' AND T.TABLEID = C.REFERENCEID ORDER BY C.COLUMNNUMBER"
122
129
 
123
130
  COLUMN_TYPE_STMT = "SELECT COLUMNDATATYPE, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = '%s' AND COLUMNNAME = '%s'"
@@ -129,7 +136,7 @@ module ::JdbcSpec
129
136
  return name unless name
130
137
  %Q{"#{name}"}
131
138
  end
132
-
139
+
133
140
  def strip_quotes(str)
134
141
  return str unless str
135
142
  return str unless /^(["']).*\1$/ =~ str
@@ -140,7 +147,7 @@ module ::JdbcSpec
140
147
  return name unless name && name['"']
141
148
  name.gsub(/"/,'""')
142
149
  end
143
-
150
+
144
151
  def reinstate_auto_increment(name, refid, coldef)
145
152
  stmt = AUTO_INC_STMT % [refid, strip_quotes(name)]
146
153
  data = execute(stmt).first
@@ -224,9 +231,9 @@ module ::JdbcSpec
224
231
  end
225
232
  coldef
226
233
  end
227
-
234
+
228
235
  SIZEABLE = %w(VARCHAR CLOB BLOB)
229
-
236
+
230
237
  def structure_dump #:nodoc:
231
238
  definition=""
232
239
  rs = @connection.connection.meta_data.getTables(nil,nil,nil,["TABLE"].to_java(:string))
@@ -244,14 +251,14 @@ module ::JdbcSpec
244
251
  elsif d1
245
252
  default = " DEFAULT #{d1}"
246
253
  end
247
-
254
+
248
255
  type = rs2.getString(6)
249
256
  col_size = rs2.getString(7)
250
257
  nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
251
- create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
252
- " " +
258
+ create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
259
+ " " +
253
260
  type +
254
- (SIZEABLE.include?(type) ? "(#{col_size})" : "") +
261
+ (SIZEABLE.include?(type) ? "(#{col_size})" : "") +
255
262
  nulling +
256
263
  default
257
264
  if !first_col
@@ -259,23 +266,23 @@ module ::JdbcSpec
259
266
  else
260
267
  create_col_string = " #{create_col_string}"
261
268
  end
262
-
269
+
263
270
  definition << create_col_string
264
-
271
+
265
272
  first_col = false
266
273
  end
267
274
  definition << ");\n\n"
268
275
  end
269
276
  definition
270
277
  end
271
-
278
+
272
279
  # Support for removing columns added via derby bug issue:
273
280
  # https://issues.apache.org/jira/browse/DERBY-1489
274
281
  #
275
282
  # This feature has not made it into a formal release and is not in Java 6.
276
283
  # If the normal strategy fails we fall back on a strategy by creating a new
277
284
  # table without the new column and there after moving the data to the new
278
- #
285
+ #
279
286
  def remove_column(table_name, column_name)
280
287
  begin
281
288
  execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name} RESTRICT"
@@ -285,7 +292,7 @@ module ::JdbcSpec
285
292
  end
286
293
  end
287
294
  end
288
-
295
+
289
296
  # Notes about changing in Derby:
290
297
  # http://db.apache.org/derby/docs/10.2/ref/rrefsqlj81859.html#rrefsqlj81859__rrefsqlj37860)
291
298
  #
@@ -338,7 +345,7 @@ module ::JdbcSpec
338
345
  alter_table(table_name, :rename => {column_name => new_column_name})
339
346
  end
340
347
  end
341
-
348
+
342
349
  def primary_keys(table_name)
343
350
  @connection.primary_keys table_name.to_s.upcase
344
351
  end
@@ -364,7 +371,7 @@ module ::JdbcSpec
364
371
  name
365
372
  end
366
373
  end
367
-
374
+
368
375
  def quoted_true
369
376
  '1'
370
377
  end
@@ -1,3 +1,5 @@
1
+ require 'jdbc_adapter/tsql_helper'
2
+
1
3
  module ::ActiveRecord
2
4
  class Base
3
5
  # After setting large objects to empty, write data back with a helper method
@@ -19,6 +21,8 @@ end
19
21
 
20
22
  module JdbcSpec
21
23
  module MsSQL
24
+ include TSqlMethods
25
+
22
26
  def self.column_selector
23
27
  [/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
24
28
  end
@@ -26,14 +30,15 @@ module JdbcSpec
26
30
  def self.adapter_selector
27
31
  [/sqlserver|tds/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::MsSQL)}]
28
32
  end
29
-
33
+
30
34
  module Column
31
35
  attr_accessor :identity, :is_special
32
-
36
+
33
37
  def simplified_type(field_type)
34
38
  case field_type
35
39
  when /int|bigint|smallint|tinyint/i then :integer
36
- when /float|double|decimal|money|numeric|real|smallmoney/i then @scale == 0 ? :integer : :decimal
40
+ when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
41
+ when /float|double|decimal|money|real|smallmoney/i then :decimal
37
42
  when /datetime|smalldatetime/i then :datetime
38
43
  when /timestamp/i then :timestamp
39
44
  when /time/i then :time
@@ -46,9 +51,9 @@ module JdbcSpec
46
51
  end
47
52
 
48
53
  def type_cast(value)
49
- return nil if value.nil? || value == "(NULL)"
54
+ return nil if value.nil? || value == "(null)" || value == "(NULL)"
50
55
  case type
51
- when :string then unquote value
56
+ when :string then unquote_string value
52
57
  when :integer then unquote(value).to_i rescue value ? 1 : 0
53
58
  when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
54
59
  when :decimal then self.class.value_to_decimal(unquote(value))
@@ -61,11 +66,16 @@ module JdbcSpec
61
66
  else value
62
67
  end
63
68
  end
64
-
69
+
70
+ # JRUBY-2011: Match balanced quotes and parenthesis - 'text',('text') or (text)
71
+ def unquote_string(value)
72
+ value.sub(/^\((.*)\)$/,'\1').sub(/^'(.*)'$/,'\1')
73
+ end
74
+
65
75
  def unquote(value)
66
76
  value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
67
77
  end
68
-
78
+
69
79
  def cast_to_time(value)
70
80
  return value if value.is_a?(Time)
71
81
  time_array = ParseDate.parsedate(value)
@@ -93,15 +103,7 @@ module JdbcSpec
93
103
  ''
94
104
  end
95
105
  end
96
-
97
- def modify_types(tp)
98
- tp[:primary_key] = "int NOT NULL IDENTITY(1, 1) PRIMARY KEY"
99
- tp[:integer][:limit] = nil
100
- tp[:boolean] = {:name => "bit"}
101
- tp[:binary] = { :name => "image"}
102
- tp
103
- end
104
-
106
+
105
107
  def quote(value, column = nil)
106
108
  return value.quoted_id if value.respond_to?(:quoted_id)
107
109
 
@@ -116,72 +118,41 @@ module JdbcSpec
116
118
  else
117
119
  "'#{quote_string(value)}'" # ' (for ruby-mode)
118
120
  end
119
- when TrueClass then '1'
120
- when FalseClass then '0'
121
- when Time, DateTime then "'#{value.strftime("%Y%m%d %H:%M:%S")}'"
122
- when Date then "'#{value.strftime("%Y%m%d")}'"
123
- else super
121
+ when TrueClass then '1'
122
+ when FalseClass then '0'
123
+ when Time, DateTime then "'#{value.strftime("%Y%m%d %H:%M:%S")}'"
124
+ when Date then "'#{value.strftime("%Y%m%d")}'"
125
+ else super
124
126
  end
125
127
  end
126
128
 
127
- def quote_string(string)
128
- string.gsub(/\'/, "''")
129
- end
129
+ def quote_string(string)
130
+ string.gsub(/\'/, "''")
131
+ end
130
132
 
131
- def quote_column_name(name)
132
- "[#{name}]"
133
- end
134
-
135
- def add_limit_offset!(sql, options)
136
- if options[:limit] and options[:offset]
137
- total_rows = select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT\\1 TOP 1000000000")}) tally")[0]["TotalRows"].to_i
138
- if (options[:limit] + options[:offset]) >= total_rows
139
- options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
140
- end
141
- sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT\\1 TOP #{options[:limit] + options[:offset]} ")
142
- sql << ") AS tmp1"
143
- if options[:order]
144
- options[:order] = options[:order].split(',').map do |field|
145
- parts = field.split(" ")
146
- tc = parts[0]
147
- if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
148
- tc.gsub!(/\./, '\\.\\[')
149
- tc << '\\]'
150
- end
151
- if sql =~ /#{tc} AS (t\d_r\d\d?)/
152
- parts[0] = $1
153
- elsif parts[0] =~ /\w+\.(\w+)/
154
- parts[0] = $1
155
- end
156
- parts.join(' ')
157
- end.join(', ')
158
- sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
159
- else
160
- sql << " ) AS tmp2"
161
- end
162
- elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
163
- sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
164
- "SELECT#{$1} TOP #{options[:limit]}"
165
- end unless options[:limit].nil?
166
- end
133
+ def quote_table_name(name)
134
+ name
135
+ end
136
+
137
+ def quote_column_name(name)
138
+ "[#{name}]"
139
+ end
140
+
141
+ def change_order_direction(order)
142
+ order.split(",").collect {|fragment|
143
+ case fragment
144
+ when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
145
+ when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
146
+ else String.new(fragment).split(',').join(' DESC,') + ' DESC'
167
147
  end
168
-
169
-
170
- def change_order_direction(order)
171
- order.split(",").collect {|fragment|
172
- case fragment
173
- when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
174
- when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
175
- else String.new(fragment).split(',').join(' DESC,') + ' DESC'
176
- end
177
- }.join(",")
178
- end
179
-
148
+ }.join(",")
149
+ end
150
+
180
151
  def recreate_database(name)
181
152
  drop_database(name)
182
153
  create_database(name)
183
154
  end
184
-
155
+
185
156
  def drop_database(name)
186
157
  execute "DROP DATABASE #{name}"
187
158
  end
@@ -193,35 +164,21 @@ module JdbcSpec
193
164
  def rename_table(name, new_name)
194
165
  execute "EXEC sp_rename '#{name}', '#{new_name}'"
195
166
  end
196
-
167
+
197
168
  # Adds a new column to the named table.
198
169
  # See TableDefinition#column for details of the options you can use.
199
170
  def add_column(table_name, column_name, type, options = {})
200
171
  add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
201
172
  add_column_options!(add_column_sql, options)
202
173
  # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
203
- # 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
174
+ # 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
204
175
  execute(add_column_sql)
205
176
  end
206
177
 
207
178
  def rename_column(table, column, new_column_name)
208
179
  execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
209
180
  end
210
-
211
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
212
- return super unless type.to_s == 'integer'
213
-
214
- if limit.nil? || limit == 4
215
- 'int'
216
- elsif limit == 2
217
- 'smallint'
218
- elsif limit ==1
219
- 'tinyint'
220
- else
221
- 'bigint'
222
- end
223
- end
224
-
181
+
225
182
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
226
183
  sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"]
227
184
  if options_include_default?(options)
@@ -233,6 +190,7 @@ module JdbcSpec
233
190
  }
234
191
  end
235
192
  def change_column_default(table_name, column_name, default) #:nodoc:
193
+ remove_default_constraint(table_name, column_name)
236
194
  execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default, column_name)} FOR #{column_name}"
237
195
  end
238
196
  def remove_column(table_name, column_name)
@@ -240,7 +198,7 @@ module JdbcSpec
240
198
  remove_default_constraint(table_name, column_name)
241
199
  execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
242
200
  end
243
-
201
+
244
202
  def remove_default_constraint(table_name, column_name)
245
203
  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"
246
204
  defaults.each {|constraint|
@@ -255,13 +213,14 @@ module JdbcSpec
255
213
  execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
256
214
  end
257
215
  end
258
-
216
+
259
217
  def remove_index(table_name, options = {})
260
218
  execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
261
219
  end
262
-
220
+
263
221
 
264
222
  def columns(table_name, name = nil)
223
+ return [] if table_name =~ /^information_schema\./i
265
224
  cc = super
266
225
  cc.each do |col|
267
226
  col.identity = true if col.sql_type =~ /identity/i
@@ -269,12 +228,12 @@ module JdbcSpec
269
228
  end
270
229
  cc
271
230
  end
272
-
231
+
273
232
  def _execute(sql, name = nil)
274
233
  if sql.lstrip =~ /^insert/i
275
234
  if query_requires_identity_insert?(sql)
276
235
  table_name = get_table_name(sql)
277
- with_identity_insert_enabled(table_name) do
236
+ with_identity_insert_enabled(table_name) do
278
237
  id = @connection.execute_insert(sql)
279
238
  end
280
239
  else
@@ -287,8 +246,8 @@ module JdbcSpec
287
246
  @connection.execute_update(sql)
288
247
  end
289
248
  end
290
-
291
-
249
+
250
+
292
251
  private
293
252
  # Turns IDENTITY_INSERT ON for table during execution of the block
294
253
  # N.B. This sets the state of IDENTITY_INSERT to OFF after the
@@ -298,19 +257,19 @@ module JdbcSpec
298
257
  set_identity_insert(table_name, true)
299
258
  yield
300
259
  ensure
301
- set_identity_insert(table_name, false)
260
+ set_identity_insert(table_name, false)
302
261
  end
303
-
262
+
304
263
  def set_identity_insert(table_name, enable = true)
305
264
  execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
306
265
  rescue Exception => e
307
- raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
266
+ raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
308
267
  end
309
268
 
310
269
  def get_table_name(sql)
311
- if sql =~ /^\s*insert\s+into\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
270
+ if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
312
271
  $1
313
- elsif sql =~ /from\s+([^\(\s]+)\s*/i
272
+ elsif sql =~ /from\s+([^\(\s,]+)\s*/i
314
273
  $1
315
274
  else
316
275
  nil
@@ -332,7 +291,7 @@ module JdbcSpec
332
291
  id_column = identity_column(table_name)
333
292
  sql =~ /\[#{id_column}\]/ ? table_name : nil
334
293
  end
335
-
294
+
336
295
  def get_special_columns(table_name)
337
296
  special = []
338
297
  @table_columns ||= {}