Skip to content
This repository
Browse code

SQLite adapters now support DDL transactions [#2080 state:resolved]

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
  • Loading branch information...
commit ac3848201dfd7400708d3ccae0acb9388318fb99 1 parent 5b025a1
Jason King authored March 14, 2009 lifo committed March 14, 2009
55  activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -72,6 +72,18 @@ def binary_to_string(value)
72 72
     #
73 73
     # * <tt>:database</tt> - Path to the database file.
74 74
     class SQLiteAdapter < AbstractAdapter
  75
+      class Version
  76
+        include Comparable
  77
+
  78
+        def initialize(version_string)
  79
+          @version = version_string.split('.').map(&:to_i)
  80
+        end
  81
+
  82
+        def <=>(version_string)
  83
+          @version <=> version_string.split('.').map(&:to_i)
  84
+        end
  85
+      end
  86
+
75 87
       def initialize(connection, logger, config)
76 88
         super(connection, logger)
77 89
         @config = config
@@ -81,6 +93,10 @@ def adapter_name #:nodoc:
81 93
         'SQLite'
82 94
       end
83 95
 
  96
+      def supports_ddl_transactions?
  97
+        sqlite_version >= '2.0.0'
  98
+      end
  99
+
84 100
       def supports_migrations? #:nodoc:
85 101
         true
86 102
       end
@@ -88,6 +104,10 @@ def supports_migrations? #:nodoc:
88 104
       def requires_reloading?
89 105
         true
90 106
       end
  107
+
  108
+      def supports_add_column?
  109
+        sqlite_version >= '3.1.6'
  110
+      end
91 111
  
92 112
       def disconnect!
93 113
         super
@@ -169,7 +189,6 @@ def rollback_db_transaction #:nodoc:
169 189
         catch_schema_changes { @connection.rollback }
170 190
       end
171 191
 
172  
-
173 192
       # SELECT ... FOR UPDATE is redundant since the table is locked.
174 193
       def add_lock!(sql, options) #:nodoc:
175 194
         sql
@@ -218,14 +237,20 @@ def rename_table(name, new_name)
218 237
         execute "ALTER TABLE #{name} RENAME TO #{new_name}"
219 238
       end
220 239
 
  240
+      # See: http://www.sqlite.org/lang_altertable.html
  241
+      # SQLite has an additional restriction on the ALTER TABLE statement
  242
+      def valid_alter_table_options( type, options)
  243
+        type.to_sym != :primary_key
  244
+      end
  245
+
221 246
       def add_column(table_name, column_name, type, options = {}) #:nodoc:
222  
-        if @connection.respond_to?(:transaction_active?) && @connection.transaction_active?
223  
-          raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction'
  247
+        if supports_add_column? && valid_alter_table_options( type, options )
  248
+          super(table_name, column_name, type, options)
  249
+        else
  250
+          alter_table(table_name) do |definition|
  251
+            definition.column(column_name, type, options)
  252
+          end
224 253
         end
225  
-        
226  
-        super(table_name, column_name, type, options)
227  
-        # See last paragraph on http://www.sqlite.org/lang_altertable.html
228  
-        execute "VACUUM"
229 254
       end
230 255
 
231 256
       def remove_column(table_name, *column_names) #:nodoc:
@@ -385,7 +410,7 @@ def catch_schema_changes
385 410
         end
386 411
 
387 412
         def sqlite_version
388  
-          @sqlite_version ||= select_value('select sqlite_version(*)')
  413
+          @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
389 414
         end
390 415
 
391 416
         def default_primary_key_type
@@ -398,23 +423,9 @@ def default_primary_key_type
398 423
     end
399 424
 
400 425
     class SQLite2Adapter < SQLiteAdapter # :nodoc:
401  
-      def supports_count_distinct? #:nodoc:
402  
-        false
403  
-      end
404  
-
405 426
       def rename_table(name, new_name)
406 427
         move_table(name, new_name)
407 428
       end
408  
-
409  
-      def add_column(table_name, column_name, type, options = {}) #:nodoc:
410  
-        if @connection.respond_to?(:transaction_active?) && @connection.transaction_active?
411  
-          raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction'
412  
-        end
413  
-
414  
-        alter_table(table_name) do |definition|
415  
-          definition.column(column_name, type, options)
416  
-        end
417  
-      end
418 429
     end
419 430
 
420 431
     class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc:
26  activerecord/test/cases/migration_test.rb
@@ -93,6 +93,30 @@ def test_add_index
93 93
       end
94 94
     end
95 95
 
  96
+    def testing_table_with_only_foo_attribute
  97
+      Person.connection.create_table :testings, :id => false do |t|
  98
+        t.column :foo, :string
  99
+      end
  100
+
  101
+      yield Person.connection
  102
+    ensure
  103
+      Person.connection.drop_table :testings rescue nil
  104
+    end
  105
+    protected :testing_table_with_only_foo_attribute
  106
+
  107
+    def test_create_table_without_id
  108
+      testing_table_with_only_foo_attribute do |connection|
  109
+        assert_equal connection.columns(:testings).size, 1
  110
+      end
  111
+    end
  112
+
  113
+    def test_add_column_with_primary_key_attribute
  114
+      testing_table_with_only_foo_attribute do |connection|
  115
+        assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
  116
+        assert_equal connection.columns(:testings).size, 2
  117
+      end
  118
+    end
  119
+
96 120
     def test_create_table_adds_id
97 121
       Person.connection.create_table :testings do |t|
98 122
         t.column :foo, :string
@@ -928,7 +952,7 @@ def test_migrator_double_down
928 952
       assert_equal(0, ActiveRecord::Migrator.current_version)
929 953
     end
930 954
 
931  
-    if current_adapter?(:PostgreSQLAdapter)
  955
+    if ActiveRecord::Base.connection.supports_ddl_transactions?
932 956
       def test_migrator_one_up_with_exception_and_rollback
933 957
         assert !Person.column_methods_hash.include?(:last_name)
934 958
 
15  activerecord/test/cases/transactions_test.rb
@@ -349,7 +349,7 @@ def test_open_transactions_count_is_reset_to_zero_if_no_transaction_active
349 349
     end
350 350
   end
351 351
 
352  
-  def test_sqlite_add_column_in_transaction_raises_statement_invalid
  352
+  def test_sqlite_add_column_in_transaction
353 353
     return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
354 354
 
355 355
     # Test first if column creation/deletion works correctly when no
@@ -368,10 +368,15 @@ def test_sqlite_add_column_in_transaction_raises_statement_invalid
368 368
       assert !Topic.column_names.include?('stuff')
369 369
     end
370 370
 
371  
-    # Test now inside a transaction: add_column should raise a StatementInvalid
372  
-    Topic.transaction do
373  
-      assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
374  
-      raise ActiveRecord::Rollback
  371
+    if Topic.connection.supports_ddl_transactions?
  372
+      assert_nothing_raised do
  373
+        Topic.transaction { Topic.connection.add_column('topics', 'stuff', :string) }
  374
+      end
  375
+    else
  376
+      Topic.transaction do
  377
+        assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
  378
+        raise ActiveRecord::Rollback
  379
+      end
375 380
     end
376 381
   end
377 382
 

0 notes on commit ac38482

Please sign in to comment.
Something went wrong with that request. Please try again.