activerecord-jdbc-adapter 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/History.txt +33 -0
  2. data/Manifest.txt +17 -0
  3. data/README.txt +48 -20
  4. data/Rakefile +2 -169
  5. data/lib/active_record/connection_adapters/jdbc_adapter.rb +11 -5
  6. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +12 -0
  7. data/lib/jdbc_adapter.rb +1 -1
  8. data/lib/jdbc_adapter/jdbc.rake +43 -30
  9. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  10. data/lib/jdbc_adapter/jdbc_db2.rb +2 -2
  11. data/lib/jdbc_adapter/jdbc_derby.rb +11 -0
  12. data/lib/jdbc_adapter/jdbc_hsqldb.rb +6 -1
  13. data/lib/jdbc_adapter/jdbc_mimer.rb +14 -7
  14. data/lib/jdbc_adapter/jdbc_mssql.rb +18 -2
  15. data/lib/jdbc_adapter/jdbc_mysql.rb +3 -0
  16. data/lib/jdbc_adapter/jdbc_oracle.rb +24 -14
  17. data/lib/jdbc_adapter/jdbc_postgre.rb +38 -18
  18. data/lib/jdbc_adapter/jdbc_sqlite3.rb +96 -26
  19. data/lib/jdbc_adapter/missing_functionality_helper.rb +40 -34
  20. data/lib/jdbc_adapter/rake_tasks.rb +1 -1
  21. data/lib/jdbc_adapter/tsql_helper.rb +1 -0
  22. data/lib/jdbc_adapter/version.rb +1 -1
  23. data/lib/pg.rb +4 -0
  24. data/rails_generators/jdbc_generator.rb +15 -0
  25. data/rails_generators/templates/jdbc.rake +8 -0
  26. data/rails_generators/templates/jdbc.rb +7 -0
  27. data/rakelib/compile.rake +23 -0
  28. data/rakelib/package.rake +85 -0
  29. data/rakelib/rails.rake +41 -0
  30. data/rakelib/test.rake +71 -0
  31. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +1 -0
  32. data/src/java/jdbc_adapter/JdbcDerbySpec.java +11 -46
  33. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +3 -2
  34. data/src/java/jdbc_adapter/MssqlRubyJdbcConnection.java +71 -0
  35. data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +24 -4
  36. data/src/java/jdbc_adapter/RubyJdbcConnection.java +57 -44
  37. data/test/abstract_db_create.rb +45 -0
  38. data/test/db/mysql.rb +2 -2
  39. data/test/db/postgres.rb +2 -2
  40. data/test/helper.rb +5 -0
  41. data/test/jdbc_adapter/jdbc_db2_test.rb +5 -0
  42. data/test/jdbc_common.rb +2 -0
  43. data/test/models/entry.rb +3 -0
  44. data/test/models/validates_uniqueness_of_string.rb +19 -0
  45. data/test/mysql_db_create_test.rb +25 -0
  46. data/test/mysql_nonstandard_primary_key_test.rb +42 -0
  47. data/test/mysql_simple_test.rb +5 -0
  48. data/test/postgres_db_create_test.rb +21 -0
  49. data/test/postgres_nonseq_pkey_test.rb +40 -0
  50. data/test/simple.rb +62 -1
  51. data/test/sqlite3_simple_test.rb +153 -10
  52. metadata +26 -5
@@ -2,7 +2,7 @@ module JdbcSpec
2
2
  module DB2
3
3
  def self.adapter_matcher(name, config)
4
4
  if name =~ /db2/i
5
- return cfg[:url] =~ /^jdbc:derby:net:/ ? ::JdbcSpec::Derby : self
5
+ return config[:url] =~ /^jdbc:derby:net:/ ? ::JdbcSpec::Derby : self
6
6
  end
7
7
  false
8
8
  end
@@ -50,7 +50,7 @@ module JdbcSpec
50
50
  end
51
51
 
52
52
  def modify_types(tp)
53
- tp[:primary_key] = 'int generated by default as identity (start with 42) primary key'
53
+ tp[:primary_key] = 'int not null generated by default as identity (start with 1) primary key'
54
54
  tp[:string][:limit] = 255
55
55
  tp[:integer][:limit] = nil
56
56
  tp[:boolean][:limit] = nil
@@ -3,6 +3,7 @@ require 'jdbc_adapter/missing_functionality_helper'
3
3
  module ::JdbcSpec
4
4
  module ActiveRecordExtensions
5
5
  def derby_connection(config)
6
+ require File.dirname(__FILE__) + "/../active_record/connection_adapters/derby_adapter"
6
7
  config[:url] ||= "jdbc:derby:#{config[:database]};create=true"
7
8
  config[:driver] ||= "org.apache.derby.jdbc.EmbeddedDriver"
8
9
  embedded_driver(config)
@@ -400,6 +401,16 @@ module ::JdbcSpec
400
401
  '0'
401
402
  end
402
403
 
404
+ def add_limit_offset!(sql, options) #:nodoc:
405
+ if options[:offset]
406
+ sql << " OFFSET #{options[:offset]} ROWS"
407
+ end
408
+ if options[:limit]
409
+ #ROWS/ROW and FIRST/NEXT mean the same
410
+ sql << " FETCH FIRST #{options[:limit]} ROWS ONLY"
411
+ end
412
+ end
413
+
403
414
  private
404
415
  # Derby appears to define schemas using the username
405
416
  def derby_schema
@@ -1,12 +1,14 @@
1
1
  module ::JdbcSpec
2
2
  module ActiveRecordExtensions
3
3
  def hsqldb_connection(config)
4
+ require File.dirname(__FILE__) + "/../active_record/connection_adapters/hsqldb_adapter"
4
5
  config[:url] ||= "jdbc:hsqldb:#{config[:database]}"
5
6
  config[:driver] ||= "org.hsqldb.jdbcDriver"
6
7
  embedded_driver(config)
7
8
  end
8
9
 
9
10
  def h2_connection(config)
11
+ require File.dirname(__FILE__) + "/../active_record/connection_adapters/h2_adapter"
10
12
  config[:url] ||= "jdbc:h2:#{config[:database]}"
11
13
  config[:driver] ||= "org.h2.Driver"
12
14
  embedded_driver(config)
@@ -82,10 +84,13 @@ module ::JdbcSpec
82
84
  "''"
83
85
  elsif column && column.type == :binary
84
86
  "'#{value.unpack("H*")}'"
87
+ elsif column.respond_to?(:primary) && column.primary
88
+ value.to_i.to_s
85
89
  else
86
90
  "'#{quote_string(value)}'"
87
91
  end
88
- else super
92
+ else
93
+ super
89
94
  end
90
95
  end
91
96
 
@@ -73,13 +73,20 @@ module JdbcSpec
73
73
  return "X'#{quote_string(value.unpack("C*").collect {|v| v.to_s(16)}.join)}'"
74
74
  end
75
75
  case value
76
- when String : %Q{'#{quote_string(value)}'}
77
- when NilClass : 'NULL'
78
- when TrueClass : '1'
79
- when FalseClass : '0'
80
- when Numeric : value.to_s
81
- when Date, Time : %Q{TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
82
- else %Q{'#{quote_string(value.to_yaml)}'}
76
+ when String
77
+ %Q{'#{quote_string(value)}'}
78
+ when NilClass
79
+ 'NULL'
80
+ when TrueClass
81
+ '1'
82
+ when FalseClass
83
+ '0'
84
+ when Numeric
85
+ value.to_s
86
+ when Date, Time
87
+ %Q{TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
88
+ else
89
+ %Q{'#{quote_string(value.to_yaml)}'}
83
90
  end
84
91
  end
85
92
 
@@ -31,6 +31,10 @@ module ::JdbcSpec
31
31
  [/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
32
32
  end
33
33
 
34
+ def self.jdbc_connection_class
35
+ ::ActiveRecord::ConnectionAdapters::MssqlJdbcConnection
36
+ end
37
+
34
38
  module Column
35
39
  attr_accessor :identity, :is_special
36
40
 
@@ -69,7 +73,7 @@ module ::JdbcSpec
69
73
 
70
74
  # JRUBY-2011: Match balanced quotes and parenthesis - 'text',('text') or (text)
71
75
  def unquote_string(value)
72
- value.sub(/^\((.*)\)$/,'\1').sub(/^'(.*)'$/,'\1')
76
+ value.to_s.sub(/^\((.*)\)$/,'\1').sub(/^'(.*)'$/,'\1')
73
77
  end
74
78
 
75
79
  def unquote(value)
@@ -116,7 +120,7 @@ module ::JdbcSpec
116
120
  value = column.type == :integer ? value.to_i : value.to_f
117
121
  value.to_s
118
122
  else
119
- "'#{quote_string(value)}'" # ' (for ruby-mode)
123
+ "N'#{quote_string(value)}'" # ' (for ruby-mode)
120
124
  end
121
125
  when TrueClass then '1'
122
126
  when FalseClass then '0'
@@ -138,6 +142,14 @@ module ::JdbcSpec
138
142
  "[#{name}]"
139
143
  end
140
144
 
145
+ def quoted_true
146
+ quote true
147
+ end
148
+
149
+ def quoted_false
150
+ quote false
151
+ end
152
+
141
153
  def change_order_direction(order)
142
154
  order.split(",").collect {|fragment|
143
155
  case fragment
@@ -249,6 +261,10 @@ module ::JdbcSpec
249
261
  end
250
262
  end
251
263
 
264
+ #SELECT .. FOR UPDATE is not supported on Microsoft SQL Server
265
+ def add_lock!(sql, options)
266
+ sql
267
+ end
252
268
 
253
269
  private
254
270
  # Turns IDENTITY_INSERT ON for table during execution of the block
@@ -5,7 +5,10 @@ module ::JdbcSpec
5
5
  $LOADED_FEATURES << "active_record/connection_adapters/mysql_adapter.rb"
6
6
 
7
7
  module ActiveRecordExtensions
8
+ add_method_to_remove_from_ar_base(:mysql_connection)
9
+
8
10
  def mysql_connection(config)
11
+ require File.dirname(__FILE__) + "/../active_record/connection_adapters/mysql_adapter"
9
12
  config[:port] ||= 3306
10
13
  url_options = "zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding="
11
14
  url_options << (config[:encoding] || 'utf8')
@@ -26,6 +26,9 @@ module ::JdbcSpec
26
26
  ActiveRecord::Base.after_save :after_save_with_oracle_lob
27
27
  @lob_callback_added = true
28
28
  end
29
+ mod.class_eval do
30
+ alias_chained_method :insert, :query_dirty, :jdbc_oracle_insert
31
+ end
29
32
  end
30
33
 
31
34
  def self.adapter_matcher(name, *)
@@ -67,14 +70,14 @@ module ::JdbcSpec
67
70
  private
68
71
  def simplified_type(field_type)
69
72
  case field_type
70
- when /^number\(1\)$/i : :boolean
71
- when /char/i : :string
72
- when /float|double/i : :float
73
- when /int/i : :integer
74
- when /num|dec|real/i : @scale == 0 ? :integer : :decimal
75
- when /date|time/i : :datetime
76
- when /clob/i : :text
77
- when /blob/i : :binary
73
+ when /^number\(1\)$/i then :boolean
74
+ when /char/i then :string
75
+ when /float|double/i then :float
76
+ when /int/i then :integer
77
+ when /num|dec|real/i then @scale == 0 ? :integer : :decimal
78
+ when /date|time/i then :datetime
79
+ when /clob/i then :text
80
+ when /blob/i then :binary
78
81
  end
79
82
  end
80
83
 
@@ -87,6 +90,9 @@ module ::JdbcSpec
87
90
 
88
91
  return nil if value == "null"
89
92
 
93
+ # sysdate default should be treated like a null value
94
+ return nil if value.downcase == "sysdate"
95
+
90
96
  # jdbc returns column default strings with actual single quotes around the value.
91
97
  return $1 if value =~ /^'(.*)'$/
92
98
 
@@ -132,8 +138,8 @@ module ::JdbcSpec
132
138
  recreate_database(name)
133
139
  end
134
140
 
135
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
136
- if id_value # Pre-assigned id
141
+ def jdbc_oracle_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
142
+ if id_value || pk.nil? # Pre-assigned id or table without a primary key
137
143
  execute sql, name
138
144
  else # Assume the sql contains a bind-variable for the id
139
145
  # Extract the table from the insert sql. Yuck.
@@ -153,7 +159,7 @@ module ::JdbcSpec
153
159
 
154
160
  def _execute(sql, name = nil)
155
161
  case sql.strip
156
- when /\A\(?\s*(select|show)/i:
162
+ when /\A\(?\s*(select|show)/i then
157
163
  @connection.execute_query(sql)
158
164
  else
159
165
  @connection.execute_update(sql)
@@ -308,13 +314,13 @@ module ::JdbcSpec
308
314
  # see: abstract/quoting.rb
309
315
 
310
316
  # Camelcase column names need to be quoted.
311
- # Nonquoted identifiers can contain only alphanumeric characters from your
317
+ # Nonquoted identifiers can contain only alphanumeric characters from your
312
318
  # database character set and the underscore (_), dollar sign ($), and pound sign (#).
313
319
  # Database links can also contain periods (.) and "at" signs (@).
314
320
  # Oracle strongly discourages you from using $ and # in nonquoted identifiers.
315
321
  # Source: http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/sql_elements008.htm
316
322
  def quote_column_name(name) #:nodoc:
317
- name.to_s =~ /^[a-z_$#]+$/ ? name.to_s : "\"#{name}\""
323
+ name.to_s =~ /^[a-z0-9_$#]+$/ ? name.to_s : "\"#{name}\""
318
324
  end
319
325
 
320
326
  def quote_string(string) #:nodoc:
@@ -329,6 +335,9 @@ module ::JdbcSpec
329
335
  %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
330
336
  end
331
337
  else
338
+ if column.respond_to?(:primary) && column.primary
339
+ return value.to_i.to_s
340
+ end
332
341
  quoted = super
333
342
  if value.acts_like?(:date) || value.acts_like?(:time)
334
343
  quoted = "#{quoted_date(value)}"
@@ -353,7 +362,7 @@ module ::JdbcSpec
353
362
  # In Oracle, schemas are created under your username:
354
363
  # http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
355
364
  def oracle_schema
356
- @config[:username].to_s
365
+ @config[:username].to_s if @config[:username]
357
366
  end
358
367
 
359
368
  def select(sql, name=nil)
@@ -365,3 +374,4 @@ module ::JdbcSpec
365
374
  end
366
375
  end
367
376
  end
377
+
@@ -4,7 +4,10 @@ module ::JdbcSpec
4
4
  $LOADED_FEATURES << "active_record/connection_adapters/postgresql_adapter.rb"
5
5
 
6
6
  module ActiveRecordExtensions
7
+ add_method_to_remove_from_ar_base(:postgresql_connection)
8
+
7
9
  def postgresql_connection(config)
10
+ require File.dirname(__FILE__) + "/../active_record/connection_adapters/postgresql_adapter"
8
11
  config[:host] ||= "localhost"
9
12
  config[:port] ||= 5432
10
13
  config[:url] ||= "jdbc:postgresql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
@@ -87,10 +90,12 @@ module ::JdbcSpec
87
90
  def postgresql_version
88
91
  @postgresql_version ||=
89
92
  begin
90
- select('SELECT version()').to_a[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
91
- ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
92
- rescue
93
- 0
93
+ value = select_value('SELECT version()')
94
+ if value =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
95
+ ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
96
+ else
97
+ 0
98
+ end
94
99
  end
95
100
  end
96
101
 
@@ -217,28 +222,36 @@ module ::JdbcSpec
217
222
  table = sql.split(" ", 4)[2].gsub('"', '')
218
223
 
219
224
  # Try an insert with 'returning id' if available (PG >= 8.2)
220
- if supports_insert_with_returning?
225
+ if supports_insert_with_returning? && id_value.nil? && false # FIXME:
226
+ # Disabled, as it causes:
227
+ # ActiveRecord::ActiveRecordError: A result was returned when none was expected
228
+ # This was previously disabled because postgresql_version returned 0
221
229
  pk, sequence_name = *pk_and_sequence_for(table) unless pk
222
230
  if pk
223
- id = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
224
- clear_query_cache
225
- return id
231
+ id_value = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
232
+ clear_query_cache #FIXME: Why now?
233
+ return id_value
226
234
  end
227
235
  end
228
236
 
229
- # Otherwise, insert then grab last_insert_id.
237
+ # Otherwise, plain insert
230
238
  execute(sql, name)
231
239
 
232
- # If neither pk nor sequence name is given, look them up.
233
- unless pk || sequence_name
234
- pk, sequence_name = *pk_and_sequence_for(table)
235
- end
240
+ # Don't need to look up id_value if we already have it.
241
+ # (and can't in case of non-sequence PK)
242
+ unless id_value
243
+ # If neither pk nor sequence name is given, look them up.
244
+ unless pk || sequence_name
245
+ pk, sequence_name = *pk_and_sequence_for(table)
246
+ end
236
247
 
237
- # If a pk is given, fallback to default sequence name.
238
- # Don't fetch last insert id for a table without a pk.
239
- if pk && sequence_name ||= default_sequence_name(table, pk)
240
- last_insert_id(table, sequence_name)
248
+ # If a pk is given, fallback to default sequence name.
249
+ # Don't fetch last insert id for a table without a pk.
250
+ if pk && sequence_name ||= default_sequence_name(table, pk)
251
+ id_value = last_insert_id(table, sequence_name)
252
+ end
241
253
  end
254
+ id_value
242
255
  end
243
256
 
244
257
  def columns(table_name, name=nil)
@@ -332,7 +345,7 @@ module ::JdbcSpec
332
345
 
333
346
  def _execute(sql, name = nil)
334
347
  case sql.strip
335
- when /\A\(?\s*(select|show)/i:
348
+ when /\A\(?\s*(select|show)/i then
336
349
  @connection.execute_query(sql)
337
350
  else
338
351
  @connection.execute_update(sql)
@@ -445,6 +458,13 @@ module ::JdbcSpec
445
458
  execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT '#{default}'"
446
459
  end
447
460
 
461
+ def change_column_null(table_name, column_name, null, default = nil)
462
+ unless null || default.nil?
463
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
464
+ end
465
+ execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
466
+ end
467
+
448
468
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
449
469
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
450
470
  end
@@ -1,6 +1,13 @@
1
1
  module ::JdbcSpec
2
+ # Don't need to load native postgres adapter
3
+ $LOADED_FEATURES << "active_record/connection_adapters/sqlite3_adapter.rb"
4
+
2
5
  module ActiveRecordExtensions
6
+ add_method_to_remove_from_ar_base(:sqlite3_connection)
7
+
3
8
  def sqlite3_connection(config)
9
+ require File.dirname(__FILE__) + "/../active_record/connection_adapters/sqlite3_adapter"
10
+
4
11
  parse_sqlite3_config!(config)
5
12
 
6
13
  config[:url] ||= "jdbc:sqlite:#{config[:database]}"
@@ -34,12 +41,15 @@ module ::JdbcSpec
34
41
  end
35
42
 
36
43
  module Column
44
+ def init_column(name, default, *args)
45
+ @default = '' if default =~ /NULL/
46
+ end
37
47
 
38
48
  def type_cast(value)
39
49
  return nil if value.nil?
40
50
  case type
41
51
  when :string then value
42
- when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
52
+ when :integer then JdbcSpec::SQLite3::Column.cast_to_integer(value)
43
53
  when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
44
54
  when :float then value.to_f
45
55
  when :datetime then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
@@ -51,11 +61,23 @@ module ::JdbcSpec
51
61
  end
52
62
  end
53
63
 
64
+ def type_cast_code(var_name)
65
+ case type
66
+ when :integer then "JdbcSpec::SQLite3::Column.cast_to_integer(#{var_name})"
67
+ when :datetime then "JdbcSpec::SQLite3::Column.cast_to_date_or_time(#{var_name})"
68
+ when :date then "JdbcSpec::SQLite3::Column.cast_to_date_or_time(#{var_name})"
69
+ when :time then "JdbcSpec::SQLite3::Column.cast_to_time(#{var_name})"
70
+ else
71
+ super
72
+ end
73
+ end
74
+
54
75
  private
55
76
  def simplified_type(field_type)
56
77
  case field_type
57
78
  when /boolean/i then :boolean
58
- when /text/i then :string
79
+ when /text/i then :text
80
+ when /varchar/i then :string
59
81
  when /int/i then :integer
60
82
  when /float/i then :float
61
83
  when /real/i then @scale == 0 ? :integer : :decimal
@@ -89,6 +111,11 @@ module ::JdbcSpec
89
111
  value
90
112
  end
91
113
 
114
+ def self.cast_to_integer(value)
115
+ return nil if value =~ /NULL/ or value.to_s.empty? or value.nil?
116
+ return (value.to_i) ? value.to_i : (value ? 1 : 0)
117
+ end
118
+
92
119
  def self.cast_to_date_or_time(value)
93
120
  return value if value.is_a? Date
94
121
  return nil if value.blank?
@@ -125,7 +152,8 @@ module ::JdbcSpec
125
152
  end
126
153
 
127
154
  def modify_types(tp)
128
- tp[:primary_key] = "INTEGER PRIMARY KEY AUTOINCREMENT"
155
+ tp[:primary_key] = "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"
156
+ tp[:string] = { :name => "VARCHAR", :limit => 255 }
129
157
  tp[:float] = { :name => "REAL" }
130
158
  tp[:decimal] = { :name => "REAL" }
131
159
  tp[:datetime] = { :name => "DATETIME" }
@@ -142,6 +170,10 @@ module ::JdbcSpec
142
170
  when String
143
171
  if column && column.type == :binary
144
172
  "'#{quote_string(column.class.string_to_binary(value))}'"
173
+ elsif column && [:integer, :float].include?(column.type)
174
+ (column.type == :integer ? value.to_i : value.to_f).to_s
175
+ elsif column && column.respond_to?(:primary) && column.primary
176
+ value.to_i.to_s
145
177
  else
146
178
  "'#{quote_string(value)}'"
147
179
  end
@@ -184,34 +216,38 @@ module ::JdbcSpec
184
216
  end
185
217
  end
186
218
 
187
- def remove_column(table_name, column_name) #:nodoc:
188
- cols = columns(table_name).collect {|col| col.name}
189
- cols.delete(column_name)
190
- cols = cols.join(', ')
191
- table_backup = table_name + "_backup"
192
-
193
- @connection.begin
194
-
195
- execute "CREATE TEMPORARY TABLE #{table_backup}(#{cols})"
196
- insert "INSERT INTO #{table_backup} SELECT #{cols} FROM #{table_name}"
197
- execute "DROP TABLE #{table_name}"
198
- execute "CREATE TABLE #{table_name}(#{cols})"
199
- insert "INSERT INTO #{table_name} SELECT #{cols} FROM #{table_backup}"
200
- execute "DROP TABLE #{table_backup}"
201
-
202
- @connection.commit
219
+ def remove_column(table_name, *column_names) #:nodoc:
220
+ column_names.flatten.each do |column_name|
221
+ alter_table(table_name) do |definition|
222
+ definition.columns.delete(definition[column_name])
223
+ end
224
+ end
203
225
  end
226
+ alias :remove_columns :remove_column
204
227
 
205
228
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
206
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
229
+ alter_table(table_name) do |definition|
230
+ include_default = options_include_default?(options)
231
+ definition[column_name].instance_eval do
232
+ self.type = type
233
+ self.limit = options[:limit] if options.include?(:limit)
234
+ self.default = options[:default] if include_default
235
+ self.null = options[:null] if options.include?(:null)
236
+ end
237
+ end
207
238
  end
208
239
 
209
240
  def change_column_default(table_name, column_name, default) #:nodoc:
210
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
241
+ alter_table(table_name) do |definition|
242
+ definition[column_name].default = default
243
+ end
211
244
  end
212
245
 
213
246
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
214
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
247
+ unless columns(table_name).detect{|c| c.name == column_name.to_s }
248
+ raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
249
+ end
250
+ alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
215
251
  end
216
252
 
217
253
  def rename_table(name, new_name)
@@ -221,6 +257,7 @@ module ::JdbcSpec
221
257
  def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
222
258
  log(sql,name) do
223
259
  @connection.execute_update(sql)
260
+ clear_query_cache
224
261
  end
225
262
  table = sql.split(" ", 4)[2]
226
263
  id_value || last_insert_id(table, nil)
@@ -237,8 +274,16 @@ module ::JdbcSpec
237
274
  end
238
275
  end
239
276
 
240
- def tables
241
- @connection.tables.select {|row| row.to_s !~ /^sqlite_/i }
277
+ def tables(name = nil) #:nodoc:
278
+ sql = <<-SQL
279
+ SELECT name
280
+ FROM sqlite_master
281
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
282
+ SQL
283
+
284
+ select_rows(sql, name).map do |row|
285
+ row[0]
286
+ end
242
287
  end
243
288
 
244
289
  def remove_index(table_name, options = {})
@@ -256,6 +301,11 @@ module ::JdbcSpec
256
301
  ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, name, unique, cols)
257
302
  end
258
303
  end
304
+
305
+ def primary_key(table_name) #:nodoc:
306
+ column = table_structure(table_name).find {|field| field['pk'].to_i == 1}
307
+ column ? column['name'] : nil
308
+ end
259
309
 
260
310
  def recreate_database(name)
261
311
  tables.each{ |table| drop_table(table) }
@@ -269,7 +319,19 @@ module ::JdbcSpec
269
319
  ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql) ? last_insert_id(sql.split(" ", 4)[2], nil) : affected_rows
270
320
  end
271
321
  end
272
-
322
+
323
+ def select(sql, name=nil)
324
+ execute(sql, name).map do |row|
325
+ record = {}
326
+ row.each_key do |key|
327
+ if key.is_a?(String)
328
+ record[key.sub(/^"?\w+"?\./, '')] = row[key]
329
+ end
330
+ end
331
+ record
332
+ end
333
+ end
334
+
273
335
  def table_structure(table_name)
274
336
  returning structure = @connection.execute_query("PRAGMA table_info(#{quote_table_name(table_name)})") do
275
337
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
@@ -278,9 +340,17 @@ module ::JdbcSpec
278
340
 
279
341
  def columns(table_name, name = nil) #:nodoc:
280
342
  table_structure(table_name).map do |field|
281
- ::ActiveRecord::ConnectionAdapters::JdbcColumn.new(@config, field['name'], field['dflt_value'], field['type'], field['notnull'] == "0")
343
+ ::ActiveRecord::ConnectionAdapters::JdbcColumn.new(@config, field['name'], field['dflt_value'], field['type'], field['notnull'] == 0)
282
344
  end
283
345
  end
346
+
347
+ # SELECT ... FOR UPDATE is redundant since the table is locked.
348
+ def add_lock!(sql, options) #:nodoc:
349
+ sql
350
+ end
351
+
352
+ protected
353
+ include JdbcSpec::MissingFunctionalityHelper
284
354
  end
285
355
  end
286
356