Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Make Database#transaction on PostgreSQL recognize :read_only and :def…

…errable options

In addition to setting the transaction isolation level, PostgreSQL
also supports these additional options.  READ ONLY ensures that
most data changing commands are not allowed.  DEFERRABLE is only
important for transactions that are both SERIALIZABLE and READ
ONLY, in which case it has less overhead than a standard
SERIALIZABLE transaction.
  • Loading branch information...
commit 3710b1d5997eb7454c49599cf5bd2a6806e22713 1 parent 229ea97
Jeremy Evans authored May 30, 2012
4  CHANGELOG
... ...
@@ -1,5 +1,7 @@
1 1
 === HEAD
2 2
 
  3
+* Add document explaining Sequel's object model (jeremyevans)
  4
+
3 5
 * Attempt to detect more disconnect errors in the mysql2 adapter (jeremyevans)
4 6
 
5 7
 * Add is_current? and check_current to the migrators, for checking/raising if there are unapplied migrations (pvh, jeremyevans) (#487)
@@ -38,7 +40,7 @@
38 40
 
39 41
 * Support :concurrently option when adding and dropping indexes on PostgreSQL (jeremyevans)
40 42
 
41  
-* Make Database#transaction on PostgreSQL accept a :synchronous option to change synchronous_commit setting for that transaction (jeremyevans)
  43
+* Make Database#transaction on PostgreSQL recognize :synchronous, :read_only, and :deferrable options (jeremyevans)
42 44
 
43 45
 * Support :sql_mode option when connecting to MySQL (jeremyevans)
44 46
 
16  lib/sequel/adapters/shared/postgres.rb
@@ -462,7 +462,7 @@ def begin_new_transaction(conn, opts)
462 462
           log_connection_execute(conn, "SET LOCAL synchronous_commit = #{sync}")
463 463
         end
464 464
       end
465  
-
  465
+      
466 466
       # If the :prepare option is given and we aren't in a savepoint,
467 467
       # prepare the transaction for a two-phase commit.
468 468
       def commit_transaction(conn, opts={})
@@ -666,6 +666,20 @@ def schema_parse_table(table_name, opts)
666 666
         end
667 667
       end
668 668
 
  669
+      # Set the transaction isolation level on the given connection
  670
+      def set_transaction_isolation(conn, opts)
  671
+        level = opts.fetch(:isolation, transaction_isolation_level)
  672
+        set_read_mode = opts.has_key?(:read_only)
  673
+        set_deferrable_mode = opts.has_key?(:deferrable)
  674
+        if level || set_read_mode || set_deferrable_mode
  675
+          sql = "SET TRANSACTION"
  676
+          sql << " ISOLATION LEVEL #{Sequel::Database::TRANSACTION_ISOLATION_LEVELS[level]}" if level
  677
+          sql << " READ #{opts[:read_only] ? 'ONLY' : 'WRITE'}" if set_read_mode
  678
+          sql << " #{'NOT ' unless opts[:deferrable]}DEFERRABLE" if set_deferrable_mode
  679
+          log_connection_execute(conn, sql)
  680
+        end
  681
+      end
  682
+     
669 683
       # Turns an array of argument specifiers into an SQL fragment used for function arguments.  See create_function_sql.
670 684
       def sql_function_args(args)
671 685
         "(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
9  lib/sequel/database/query.rb
@@ -232,7 +232,7 @@ def tables(opts={})
232 232
     # either all statements are successful or none of the statements are
233 233
     # successful.  Note that MySQL MyISAM tabels do not support transactions.
234 234
     #
235  
-    # The following options are respected:
  235
+    # The following general options are respected:
236 236
     #
237 237
     # :isolation :: The transaction isolation level to use for this transaction,
238 238
     #               should be :uncommitted, :committed, :repeatable, or :serializable,
@@ -249,7 +249,12 @@ def tables(opts={})
249 249
     #               only respected if the database/adapter supports savepoints.  By
250 250
     #               default Sequel will reuse an existing transaction, so if you want to
251 251
     #               use a savepoint you must use this option.
252  
-    # :synchronous :: (PostgreSQL only) if non-nil, set synchronous_commit
  252
+    #
  253
+    # PostgreSQL specific options:
  254
+    #
  255
+    # :deferrable :: (9.1+) If present, set to DEFERRABLE if true or NOT DEFERRABLE if false.
  256
+    # :read_only :: If present, set to READ ONLY if true or READ WRITE if false.
  257
+    # :synchronous :: if non-nil, set synchronous_commit
253 258
     #                 appropriately.  Valid values true, :on, false, :off, :local (9.1+),
254 259
     #                 and :remote_write (9.2+).
255 260
     def transaction(opts={}, &block)
20  spec/adapters/postgres_spec.rb
@@ -183,6 +183,26 @@ def logger.method_missing(m, msg)
183 183
     end
184 184
   end
185 185
 
  186
+  specify "should have #transaction support read only transactions" do
  187
+    @db = POSTGRES_DB
  188
+    @db.transaction(:read_only=>true){}
  189
+    @db.transaction(:read_only=>false){}
  190
+    @db.transaction(:isolation=>:serializable, :read_only=>true){}
  191
+    @db.transaction(:isolation=>:serializable, :read_only=>false){}
  192
+    @db.sqls.grep(/READ/).should == ["SET TRANSACTION READ ONLY", "SET TRANSACTION READ WRITE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE"]
  193
+  end
  194
+
  195
+  specify "should have #transaction support deferrable transactions" do
  196
+    @db = POSTGRES_DB
  197
+    @db.transaction(:deferrable=>true){}
  198
+    @db.transaction(:deferrable=>false){}
  199
+    @db.transaction(:deferrable=>true, :read_only=>true){}
  200
+    @db.transaction(:deferrable=>false, :read_only=>false){}
  201
+    @db.transaction(:isolation=>:serializable, :deferrable=>true, :read_only=>true){}
  202
+    @db.transaction(:isolation=>:serializable, :deferrable=>false, :read_only=>false){}
  203
+    @db.sqls.grep(/DEF/).should == ["SET TRANSACTION DEFERRABLE", "SET TRANSACTION NOT DEFERRABLE", "SET TRANSACTION READ ONLY DEFERRABLE", "SET TRANSACTION READ WRITE NOT DEFERRABLE",  "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE NOT DEFERRABLE"]
  204
+  end if POSTGRES_DB.server_version >= 90100
  205
+
186 206
   specify "should support creating indexes concurrently" do
187 207
     POSTGRES_DB.sqls.clear
188 208
     POSTGRES_DB.add_index :test, [:name, :value], :concurrently=>true

0 notes on commit 3710b1d

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