activerecord-jdbc-adapter 0.8 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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 ||= {}