sequel 4.16.0 → 4.17.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 505bc666a3de9a394dd0bb31e07afff6feca86da
4
- data.tar.gz: 2082ac9b3731827b5c4e0c39859e8471e706a99d
3
+ metadata.gz: 1d6fa8ec8783c8834b984323d9e1c73bef006df5
4
+ data.tar.gz: 40e551bc46ec90be6a411ca2eec0e4114394b03d
5
5
  SHA512:
6
- metadata.gz: f728df92423ad2e207a2db67d89cc7c79819351973c348845978d7ba93826f33caf21b4251dec609c5fcc48c0c13b55460ac2800f7cd36b328d87463c804cc3a
7
- data.tar.gz: 836ab1fb7bc23c595070f15dff4cf300b80d4b4eaf3e024c305d073a84a3a84c19782bef11359e9e96c0cb739206995ac8a4828ece1a5057c66a8c50fee0f87b
6
+ metadata.gz: 06d6af6daccc3ae11f55e17d6b39472509c3ddd90af821e0e6e353dfab49b6b30ea4c213b4596f46f048a6ff3807bcfc9257a13d08d8ccdc9a52d18478e59e6a
7
+ data.tar.gz: 6c31bed38421bb9acf75235b01229fe777baeb311fef31554c4b6070454147d37b4a249650e4c6c788602d5550c0db373546af9066a398aa9d01b2882568b810
data/CHANGELOG CHANGED
@@ -1,3 +1,25 @@
1
+ === 4.17.0 (2014-12-01)
2
+
3
+ * Fix handling of Sequel::SQL::Blob instances in bound variables in the postgres adapter (jeremyevans) (#917)
4
+
5
+ * Add :preconnect Database option for immediately creating the maximum number of connections (jeremyevans)
6
+
7
+ * Support DB.pool.max_size for the single connection pools (jeremyevans)
8
+
9
+ * Work around regression in jdbc-sqlite3 3.8.7 where empty blobs are returned as nil (jeremyevans)
10
+
11
+ * Work around regression in jdbc-sqlite3 3.8.7 when using JDBC getDate method for date parsing (jeremyevans)
12
+
13
+ * Make Model#update_or_create return object if existing object exists but updates are not necessary (contentfree) (#916)
14
+
15
+ * Add Dataset#server? for conditionally setting a default server to use if no default is present (jeremyevans)
16
+
17
+ * Add Database#sharded? for determining if database uses sharding (jeremyevans)
18
+
19
+ * Fix server used by Dataset#insert_select on PostgreSQL (jeremyevans)
20
+
21
+ * Fix server used for deleting model instances when using sharding (jeremyevans)
22
+
1
23
  === 4.16.0 (2014-11-01)
2
24
 
3
25
  * Make Database#create_table? and #create_join_table? not use IF NOT EXISTS if indexes are being added (jeremyevans) (#904)
data/README.rdoc CHANGED
@@ -20,10 +20,15 @@ toolkit for Ruby.
20
20
 
21
21
  * {Website}[http://sequel.jeremyevans.net]
22
22
  * {Source code}[http://github.com/jeremyevans/sequel]
23
- * {Bug tracking}[http://github.com/jeremyevans/sequel/issues]
24
23
  * {Google group}[http://groups.google.com/group/sequel-talk]
24
+ * {IRC}[irc://irc.freenode.net/sequel]
25
+ * {Bug tracking}[http://github.com/jeremyevans/sequel/issues]
25
26
  * {RDoc}[http://sequel.jeremyevans.net/rdoc]
26
27
 
28
+ If you have questions about how to use Sequel, please ask on the
29
+ Google Group or IRC. Only use the the bug tracker to report
30
+ bugs in Sequel, not to ask for help on using Sequel.
31
+
27
32
  To check out the source code:
28
33
 
29
34
  git clone git://github.com/jeremyevans/sequel.git
@@ -756,7 +756,7 @@ ActiveRecord Method :: Notes, Workarounds
756
756
  +silence+ :: No equivalent. Because the logger is handled at the <tt>Sequel::Database</tt> level, there is no thread-safe way to turn it off for specific blocks.
757
757
  +scopes+ :: No equivalent
758
758
  +sti_name+ :: No equivalent.
759
- +update_counters+ :: <tt>Album.where(:id=>:id).update(:counter_name=>:counter_name + 1, :other_counter=>:other_counter - 1)</tt>
759
+ +update_counters+ :: <tt>Album.where(:id=>:id).update(:counter_name=>Sequel.+(:counter_name, 1), :other_counter=>Sequel.-(:other_counter, 1))</tt>
760
760
  +uncached+ :: No equivalent
761
761
 
762
762
  === Instance Methods with Significantly Different Behavior
data/doc/model_hooks.rdoc CHANGED
@@ -96,7 +96,7 @@ Sequel does not provide a simple way to turn off the running of save/create/upda
96
96
 
97
97
  However, you should note that there are plenty of ways to modify the database without saving a model object. One example is by using plain datasets, or one of the model's dataset methods:
98
98
 
99
- Album.where(:name=>'RF').update(:copies_sold=>:copies_sold + 1)
99
+ Album.where(:name=>'RF').update(:copies_sold=>Sequel.+(:copies_sold, 1))
100
100
  # UPDATE albums SET copies_sold = copies_sold + 1 WHERE name = 'RF'
101
101
 
102
102
  In this case, the +update+ method is called on the dataset returned by <tt>Album.where</tt>. Even if there is only a single object with the name RF, this will not call any hooks. If you want model hooks to be called, you need to make sure to operate on a model object:
data/doc/querying.rdoc CHANGED
@@ -292,7 +292,7 @@ in another place. This is what is meant by a functional style API.
292
292
  Let's say we only want to select the id and name columns, and that
293
293
  we want to order by name:
294
294
 
295
- ds3 = ds.order(:name).select(:id, :name)
295
+ ds3 = ds2.order(:name).select(:id, :name)
296
296
  # SELECT id, name FROM artists WHERE name LIKE 'A%' ESCAPE '\' ORDER BY name
297
297
 
298
298
  Note how you don't need to assign the returned value of order to a variable,
@@ -5,7 +5,7 @@
5
5
 
6
6
  * Dataset#distinct now accepts a virtual row block.
7
7
 
8
- * Database#drop_view with :foreign=>true option now drops foreign
8
+ * Database#drop_table with :foreign=>true option now drops foreign
9
9
  tables on PostgreSQL. Database#create_table with :foreign option
10
10
  is now reversible on PostgreSQL.
11
11
 
@@ -0,0 +1,38 @@
1
+ = New Features
2
+
3
+ * A :preconnect Database option has been added, for automatically
4
+ creating the maximum number of connections to the database on
5
+ instantiation. This is useful when there is high latency for
6
+ initial connection setup, where Sequel's usual approach of
7
+ connecting as needed can cause pauses at runtime.
8
+
9
+ * Database#sharded? has been added for checking whether the Database
10
+ object uses multiple servers.
11
+
12
+ * Dataset#server? has been added, for returning a cloned dataset
13
+ associated with the given server/shard if the dataset does not
14
+ already have a server set. This returns the receiver if the
15
+ server has already been set or the Database is not sharded.
16
+
17
+ = Other Improvements
18
+
19
+ * Sequel now uses the correct shard when deleting model instances.
20
+ Previously, the correct shard was only used in the unoptimized
21
+ case, not in the optimized case.
22
+
23
+ * Sequel now uses the correct shard when using Dataset#insert_select
24
+ on PostgreSQL. This was first broken in the 4.13.0 release.
25
+
26
+ * Sequel now correctly handles Sequel::SQL::Blob instances used in
27
+ bound variables in the postgres adapter. Previously this resulted
28
+ in duplicate apostrophes being used.
29
+
30
+ * When using the jdbc/sqlite3 adapter with jdbc-sqlite3 3.8.7, Sequel
31
+ now handles date objects and empty blobs correctly, working around
32
+ bugs in the driver.
33
+
34
+ = Backwards Compatibility
35
+
36
+ * In the update_or_create plugin, Model.update_or_create now always
37
+ returns the object. Previously it would not return the object if
38
+ the object already existed but no updates were necessary.
@@ -71,10 +71,23 @@ module Sequel
71
71
  end
72
72
 
73
73
  # Use getLong instead of getInt for converting integers on SQLite, since SQLite does not enforce a limit of 2**32.
74
+ # Work around regressions in jdbc-sqlite 3.8.7 for date and blob types.
74
75
  def setup_type_convertor_map
75
76
  super
76
77
  @type_convertor_map[Java::JavaSQL::Types::INTEGER] = @type_convertor_map[Java::JavaSQL::Types::BIGINT]
77
78
  @basic_type_convertor_map[Java::JavaSQL::Types::INTEGER] = @basic_type_convertor_map[Java::JavaSQL::Types::BIGINT]
79
+ @type_convertor_map[Java::JavaSQL::Types::DATE] = lambda do |r, i|
80
+ if v = r.getString(i)
81
+ Sequel.string_to_date(v)
82
+ end
83
+ end
84
+ @type_convertor_map[Java::JavaSQL::Types::BLOB] = lambda do |r, i|
85
+ if v = r.getBytes(i)
86
+ Sequel::SQL::Blob.new(String.from_java_bytes(v))
87
+ elsif !r.wasNull
88
+ Sequel::SQL::Blob.new('')
89
+ end
90
+ end
78
91
  end
79
92
  end
80
93
  end
@@ -192,7 +192,7 @@ module Sequel
192
192
  def bound_variable_arg(arg, conn)
193
193
  case arg
194
194
  when Sequel::SQL::Blob
195
- conn.escape_bytea(arg)
195
+ {:value=>arg, :type=>17, :format=>1}
196
196
  when Sequel::SQLTime
197
197
  literal(arg)
198
198
  when DateTime, Time
@@ -1329,7 +1329,7 @@ module Sequel
1329
1329
  # inserting a query if disable_insert_returning is used.
1330
1330
  def insert_select(*values)
1331
1331
  return unless supports_insert_select?
1332
- with_sql_first(insert_select_sql(*values))
1332
+ server?(:default).with_sql_first(insert_select_sql(*values))
1333
1333
  end
1334
1334
 
1335
1335
  # The SQL to use for an insert_select, adds a RETURNING clause to the insert
@@ -13,6 +13,8 @@
13
13
  # connection pool recognizes.
14
14
  # size :: an integer representing the total number of connections in the pool,
15
15
  # or for the given shard/server if sharding is supported.
16
+ # max_size :: an integer representing the maximum size of the connection pool,
17
+ # or the maximum size per shard/server if sharding is supported.
16
18
  #
17
19
  # For sharded connection pools, the sharded API adds the following methods:
18
20
  #
@@ -72,6 +74,9 @@ class Sequel::ConnectionPool
72
74
  # :after_connect :: A callable object called after each new connection is made, with the
73
75
  # connection object (and server argument if the callable accepts 2 arguments),
74
76
  # useful for customizations that you want to apply to all connections.
77
+ # :preconnect :: Automatically create the maximum number of connections, so that they don't
78
+ # need to be created as needed. This is useful when connecting takes a long time
79
+ # and you want to avoid possible latency during runtime.
75
80
  def initialize(db, opts=OPTS)
76
81
  @db = db
77
82
  @after_connect = opts[:after_connect]
@@ -54,6 +54,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
54
54
  end
55
55
  end
56
56
 
57
+ # The ShardedSingleConnectionPool always has a maximum size of 1.
58
+ def max_size
59
+ 1
60
+ end
61
+
57
62
  # Remove servers from the connection pool. Primarily used in conjunction with master/slave
58
63
  # or shard configurations. Similar to disconnecting from all given servers,
59
64
  # except that after it is used, future requests for the server will use the
@@ -94,5 +99,10 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
94
99
  @servers[server]
95
100
  end
96
101
 
102
+ # Make sure there is a valid connection for each server.
103
+ def preconnect
104
+ servers.each{|s| hold(s){}}
105
+ end
106
+
97
107
  CONNECTION_POOL_MAP[[true, true]] = self
98
108
  end
@@ -1,4 +1,4 @@
1
- Sequel.require 'connection_pool/threaded'
1
+ require 'sequel/connection_pool/threaded'
2
2
 
3
3
  # The slowest and most advanced connection, dealing with both multi-threaded
4
4
  # access and configurations with multiple shards/servers.
@@ -235,6 +235,11 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
235
235
  sync{@servers[server]}
236
236
  end
237
237
 
238
+ # Create the maximum number of connections to each server immediately.
239
+ def preconnect
240
+ servers.each{|s| (max_size - size(s)).times{checkin_connection(s, make_new(s))}}
241
+ end
242
+
238
243
  # Releases the connection assigned to the supplied thread and server. If the
239
244
  # server or connection given is scheduled for disconnection, remove the
240
245
  # connection instead of releasing it back to the pool.
@@ -2,17 +2,11 @@
2
2
  # It is just a wrapper around a single connection that uses the connection pool
3
3
  # API.
4
4
  class Sequel::SingleConnectionPool < Sequel::ConnectionPool
5
- # The SingleConnectionPool always has a size of 1 if connected
6
- # and 0 if not.
7
- def size
8
- @conn ? 1 : 0
9
- end
10
-
11
5
  # Yield the connection if one has been made.
12
6
  def all_connections
13
7
  yield @conn if @conn
14
8
  end
15
-
9
+
16
10
  # Disconnect the connection from the database.
17
11
  def disconnect(opts=nil)
18
12
  return unless @conn
@@ -30,9 +24,27 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
30
24
  end
31
25
  end
32
26
 
27
+ # The SingleConnectionPool always has a maximum size of 1.
28
+ def max_size
29
+ 1
30
+ end
31
+
33
32
  def pool_type
34
33
  :single
35
34
  end
36
35
 
36
+ # The SingleConnectionPool always has a size of 1 if connected
37
+ # and 0 if not.
38
+ def size
39
+ @conn ? 1 : 0
40
+ end
41
+
42
+ private
43
+
44
+ # Make sure there is a valid connection.
45
+ def preconnect
46
+ hold{}
47
+ end
48
+
37
49
  CONNECTION_POOL_MAP[[true, false]] = self
38
50
  end
@@ -34,12 +34,6 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
34
34
  @sleep_time = Float(opts[:pool_sleep_time] || 0.001)
35
35
  end
36
36
 
37
- # The total number of connections opened, either available or allocated.
38
- # This may not be completely accurate as it isn't protected by the mutex.
39
- def size
40
- @allocated.length + @available_connections.length
41
- end
42
-
43
37
  # Yield all of the available connections, and the one currently allocated to
44
38
  # this thread. This will not yield connections currently allocated to other
45
39
  # threads, as it is not safe to operate on them. This holds the mutex while
@@ -117,6 +111,12 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
117
111
  :threaded
118
112
  end
119
113
 
114
+ # The total number of connections opened, either available or allocated.
115
+ # This may not be completely accurate as it isn't protected by the mutex.
116
+ def size
117
+ @allocated.length + @available_connections.length
118
+ end
119
+
120
120
  private
121
121
 
122
122
  # Assigns a connection to the supplied thread, if one
@@ -144,8 +144,10 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
144
144
  conn
145
145
  end
146
146
 
147
- # Alias the default make_new method, so subclasses can call it directly.
148
- alias default_make_new make_new
147
+ unless method_defined?(:default_make_new)
148
+ # Alias the default make_new method, so subclasses can call it directly.
149
+ alias default_make_new make_new
150
+ end
149
151
 
150
152
  # Creates a new connection to the given server if the size of the pool for
151
153
  # the server is less than the maximum size of the pool. The calling code
@@ -176,6 +178,11 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
176
178
  sync{@allocated[thread]}
177
179
  end
178
180
 
181
+ # Create the maximum number of connections immediately.
182
+ def preconnect
183
+ (max_size - size).times{checkin_connection(make_new(nil))}
184
+ end
185
+
179
186
  # Releases the connection assigned to the supplied thread back to the pool.
180
187
  # The calling code should already have the mutex before calling this.
181
188
  def release(thread)
@@ -100,13 +100,14 @@ module Sequel
100
100
  #
101
101
  # Accepts the following options:
102
102
  # :default_string_column_size :: The default size of string columns, 255 by default.
103
- # :identifier_input_method :: A string method symbol to call on identifiers going into the database
104
- # :identifier_output_method :: A string method symbol to call on identifiers coming from the database
105
- # :logger :: A specific logger to use
106
- # :loggers :: An array of loggers to use
107
- # :quote_identifiers :: Whether to quote identifiers
108
- # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol
109
- # :single_threaded :: Whether to use a single-threaded connection pool
103
+ # :identifier_input_method :: A string method symbol to call on identifiers going into the database.
104
+ # :identifier_output_method :: A string method symbol to call on identifiers coming from the database.
105
+ # :logger :: A specific logger to use.
106
+ # :loggers :: An array of loggers to use.
107
+ # :preconnect :: Whether to automatically connect to the maximum number of servers.
108
+ # :quote_identifiers :: Whether to quote identifiers.
109
+ # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol .
110
+ # :single_threaded :: Whether to use a single-threaded connection pool.
110
111
  # :sql_log_level :: Method to use to log SQL to a logger, :info by default.
111
112
  #
112
113
  # All options given are also passed to the connection pool.
@@ -117,6 +118,7 @@ module Sequel
117
118
  self.log_warn_duration = @opts[:log_warn_duration]
118
119
  block ||= proc{|server| connect(server)}
119
120
  @opts[:servers] = {} if @opts[:servers].is_a?(String)
121
+ @sharded = !!@opts[:servers]
120
122
  @opts[:adapter_class] = self.class
121
123
 
122
124
  @opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, Database.single_threaded))
@@ -143,6 +145,7 @@ module Sequel
143
145
  Sequel.synchronize{::Sequel::DATABASES.push(self)}
144
146
  end
145
147
  Sequel::Database.run_after_initialize(self)
148
+ @pool.send(:preconnect) if typecast_value_boolean(@opts[:preconnect]) && @pool.respond_to?(:preconnect, true)
146
149
  end
147
150
 
148
151
  # If a transaction is not currently in process, yield to the block immediately.
@@ -276,6 +279,12 @@ module Sequel
276
279
  Sequel.synchronize{prepared_statements[name] = ps}
277
280
  end
278
281
 
282
+ # Whether this database instance uses multiple servers, either for sharding
283
+ # or for master/slave.
284
+ def sharded?
285
+ @sharded
286
+ end
287
+
279
288
  # The timezone to use for this database, defaulting to <tt>Sequel.database_timezone</tt>.
280
289
  def timezone
281
290
  @timezone || Sequel.database_timezone
@@ -794,6 +794,17 @@ module Sequel
794
794
  clone(:server=>servr)
795
795
  end
796
796
 
797
+ # If the database uses sharding and the current dataset has not had a
798
+ # server set, return a cloned dataset that uses the given server.
799
+ # Otherwise, return the receiver directly instead of returning a clone.
800
+ def server?(server)
801
+ if db.sharded? && !opts[:server]
802
+ server(server)
803
+ else
804
+ self
805
+ end
806
+ end
807
+
797
808
  # Unbind bound variables from this dataset's filter and return an array of two
798
809
  # objects. The first object is a modified dataset where the filter has been
799
810
  # replaced with one that uses bound variable placeholders. The second object
@@ -1119,7 +1130,7 @@ module Sequel
1119
1130
  # Return self if the dataset already has a server, or a cloned dataset with the
1120
1131
  # default server otherwise.
1121
1132
  def default_server
1122
- @opts[:server] ? self : clone(:server=>:default)
1133
+ server?(:default)
1123
1134
  end
1124
1135
 
1125
1136
  # Treat the +block+ as a virtual_row block if not +nil+ and
@@ -1752,7 +1752,8 @@ module Sequel
1752
1752
  def _delete_without_checking
1753
1753
  if sql = (m = model).fast_instance_delete_sql
1754
1754
  sql = sql.dup
1755
- (ds = m.dataset).literal_append(sql, pk)
1755
+ ds = use_server(m.dataset)
1756
+ ds.literal_append(sql, pk)
1756
1757
  ds.with_sql_delete(sql)
1757
1758
  else
1758
1759
  _delete_dataset.delete
@@ -1903,7 +1904,7 @@ module Sequel
1903
1904
  # default values of all columns. Separated from _save so it
1904
1905
  # can be overridden to avoid the refresh.
1905
1906
  def _save_refresh
1906
- _save_set_values(_refresh_get(this.opts[:server] ? this : this.server(:default)) || raise(Error, "Record not found"))
1907
+ _save_set_values(_refresh_get(this.server?(:default)) || raise(Error, "Record not found"))
1907
1908
  changed_columns.clear
1908
1909
  end
1909
1910
 
@@ -30,7 +30,7 @@ module Sequel
30
30
  eb = eo[:eager_block]
31
31
  set_server = proc do |ds|
32
32
  ds = eb.call(ds) if eb
33
- ds = ds.server(server) unless ds.opts[:server]
33
+ ds = ds.server?(server)
34
34
  ds
35
35
  end
36
36
  eo = eo.merge(:eager_block=>set_server)
@@ -53,7 +53,7 @@ module Sequel
53
53
  def eager_graph_dataset(opts, eager_options)
54
54
  ds = super
55
55
  if s = eager_options[:self].opts[:server]
56
- ds = ds.server(s) unless ds.opts[:server]
56
+ ds = ds.server?(s)
57
57
  end
58
58
  ds
59
59
  end
@@ -41,14 +41,16 @@ module Sequel
41
41
  # create a new record with the columns specified by both +attrs+ and
42
42
  # +set_attrs+, with the ones in +set_attrs+ taking priority. If
43
43
  # a block is given, the object is yielded to the block before the
44
- # object is saved.
44
+ # object is saved. Returns the new or updated object.
45
45
  def update_or_create(attrs, set_attrs=nil, &block)
46
- find_or_new(attrs, set_attrs, &block).save_changes
46
+ obj = find_or_new(attrs, set_attrs, &block)
47
+ obj.save_changes
48
+ obj
47
49
  end
48
50
 
49
51
  # Operates the same as +update_or_create+, but returns the objects
50
52
  # without persisting changes (no UPDATE/INSERT queries).
51
- def find_or_new(attrs, set_attrs=nil, &block)
53
+ def find_or_new(attrs, set_attrs=nil)
52
54
  obj = find(attrs) || new(attrs)
53
55
  obj.set(set_attrs) if set_attrs
54
56
  yield obj if block_given?
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 4
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 16
6
+ MINOR = 17
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -488,6 +488,13 @@ describe "A connection pool with multiple servers" do
488
488
  @pool = Sequel::ConnectionPool.get_pool(mock_db.call{|server| "#{server}#{ic[server] += 1}"}, CONNECTION_POOL_DEFAULTS.merge(:servers=>{:read_only=>{}}))
489
489
  end
490
490
 
491
+ specify "should support preconnect method that immediately creates the maximum number of connections" do
492
+ @pool.send(:preconnect)
493
+ i = 0
494
+ @pool.all_connections{|c1| i+=1}
495
+ i.should == @pool.max_size * 2
496
+ end
497
+
491
498
  specify "#all_connections should return connections for all servers" do
492
499
  @pool.hold{}
493
500
  @pool.all_connections{|c1| c1.should == "default1"}
@@ -755,6 +762,13 @@ describe "A single threaded pool with multiple servers" do
755
762
  @pool = Sequel::ConnectionPool.get_pool(mock_db.call(proc{|c| msp.call}){|c| c}, ST_CONNECTION_POOL_DEFAULTS.merge(:servers=>{:read_only=>{}}))
756
763
  end
757
764
 
765
+ specify "should support preconnect method that immediately creates the maximum number of connections" do
766
+ @pool.send(:preconnect)
767
+ i = 0
768
+ @pool.all_connections{|c1| i+=1}
769
+ i.should == 2
770
+ end
771
+
758
772
  specify "#all_connections should return connections for all servers" do
759
773
  @pool.hold{}
760
774
  @pool.all_connections{|c1| c1.should == :default}
@@ -879,6 +893,25 @@ shared_examples_for "All connection pools classes" do
879
893
  p.hold{|c| p.all_connections{|c1| c.should == c1}}
880
894
  end
881
895
 
896
+ specify "should have a size method that gives the current size of the pool" do
897
+ p = @class.new(mock_db.call{123}, {})
898
+ p.size.should == 0
899
+ p.hold{}
900
+ p.size.should == 1
901
+ end
902
+
903
+ specify "should have a max_size method that gives the maximum size of the pool" do
904
+ @class.new(mock_db.call{123}, {}).max_size.should >= 1
905
+ end
906
+
907
+ specify "should support preconnect method that immediately creates the maximum number of connections" do
908
+ p = @class.new(mock_db.call{123}, {})
909
+ p.send(:preconnect)
910
+ i = 0
911
+ p.all_connections{|c1| i+=1}
912
+ i.should == p.max_size
913
+ end
914
+
882
915
  specify "should be able to modify after_connect proc after the pool is created" do
883
916
  a = []
884
917
  p = @class.new(mock_db.call{123}, {})
@@ -23,6 +23,17 @@ describe "A new Database" do
23
23
  Sequel::Database.new(1 => 2, :logger => [4], :loggers => [3]).loggers.should == [4,3]
24
24
  end
25
25
 
26
+ specify "should support :preconnect option to preconnect to database" do
27
+ @db.pool.size.should == 0
28
+ c = Class.new(Sequel::Database) do
29
+ def connect(_)
30
+ :connect
31
+ end
32
+ end
33
+ db = c.new(1 => 2, :logger => 3, :preconnect=>true)
34
+ db.pool.size.should == db.pool.max_size
35
+ end
36
+
26
37
  specify "should handle the default string column size" do
27
38
  @db.default_string_column_size.should == 255
28
39
  db = Sequel::Database.new(:default_string_column_size=>50)
@@ -3723,7 +3723,7 @@ describe "Sequel::Dataset#qualify" do
3723
3723
  end
3724
3724
 
3725
3725
  specify "should handle SQL::DelayedEvaluations that take dataset arguments" do
3726
- ds = @ds.filter(Sequel.delay{|ds| ds.first_source}).qualify
3726
+ ds = @ds.filter(Sequel.delay{|x| x.first_source}).qualify
3727
3727
  ds.sql.should == 'SELECT t.* FROM t WHERE t.t'
3728
3728
  end
3729
3729
 
@@ -1117,7 +1117,7 @@ describe "Sequel.delay" do
1117
1117
  end
1118
1118
 
1119
1119
  specify "should call the block with the current dataset if it accepts one argument" do
1120
- ds = Sequel.mock[:b].where(Sequel.delay{|ds| ds.first_source})
1120
+ ds = Sequel.mock[:b].where(Sequel.delay{|x| x.first_source})
1121
1121
  ds.sql.should == "SELECT * FROM b WHERE b"
1122
1122
  ds.from(:c).sql.should == "SELECT * FROM c WHERE c"
1123
1123
  end
@@ -1132,7 +1132,7 @@ describe "Sequel.delay" do
1132
1132
  end
1133
1133
 
1134
1134
  specify "should have the condition specifier handling call block with the current dataset if it accepts one argument" do
1135
- ds = Sequel.mock[:b].where(:a=>Sequel.delay{|ds| ds.first_source})
1135
+ ds = Sequel.mock[:b].where(:a=>Sequel.delay{|x| x.first_source})
1136
1136
  ds.sql.should == "SELECT * FROM b WHERE (a = b)"
1137
1137
  ds.from(:c).sql.should == "SELECT * FROM c WHERE (a = c)"
1138
1138
  end
@@ -196,7 +196,7 @@ describe "Sequel::Plugins::DatasetAssociations" do
196
196
  end
197
197
 
198
198
  it "should deal correctly with :order option for one_through_many associations" do
199
- @Artist.one_through_many :otag, :clone=>:otag, :order=>:id, :order=>:tags__name
199
+ @Artist.one_through_many :otag, :clone=>:otag, :order=>:tags__name
200
200
  @Artist.otags.sql.should == 'SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM artists INNER JOIN albums ON (albums.artist_id = artists.id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id) WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND ((albums.artist_id, tags.id) IN (SELECT DISTINCT ON (albums.artist_id) albums.artist_id, tags.id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) ORDER BY albums.artist_id, tags.name))))) ORDER BY tags.name'
201
201
  end
202
202
 
@@ -367,7 +367,7 @@ describe Sequel::Model, "rcte_tree with composite keys" do
367
367
 
368
368
  it "should eagerly load descendants to a given level" do
369
369
  @c.plugin :rcte_tree, :key=>[:parent_id, :parent_id2]
370
- @ds._fetch = [[{:id=>2, :id2=>3, :parent_id=>1, :parent_id=>2, :name=>'AA'}, {:id=>6, :id2=>7, :parent_id=>2, :parent_id2=>3, :name=>'C'}, {:id=>7, :id2=>8, :parent_id=>1, :parent_id2=>2, :name=>'D'}],
370
+ @ds._fetch = [[{:id=>2, :id2=>3, :parent_id=>1, :parent_id2=>2, :name=>'AA'}, {:id=>6, :id2=>7, :parent_id=>2, :parent_id2=>3, :name=>'C'}, {:id=>7, :id2=>8, :parent_id=>1, :parent_id2=>2, :name=>'D'}],
371
371
  [{:id=>6, :id2=>7, :parent_id=>2, :parent_id2=>3, :name=>'C', :x_root_x_0=>2, :x_root_x_1=>3, :x_level_x=>0}, {:id=>9, :id2=>10, :parent_id=>2, :parent_id2=>3, :name=>'E', :x_root_x_0=>2, :x_root_x_1=>3, :x_level_x=>0},
372
372
  {:id=>3, :id2=>4, :name=>'00', :parent_id=>6, :parent_id2=>7, :x_root_x_0=>6, :x_root_x_1=>7, :x_level_x=>0}, {:id=>3, :id2=>4, :name=>'00', :parent_id=>6, :parent_id2=>7, :x_root_x_0=>2, :x_root_x_1=>3, :x_level_x=>1},
373
373
  {:id=>4, :id2=>5, :name=>'?', :parent_id=>7, :parent_id2=>8, :x_root_x_0=>7, :x_root_x_1=>8, :x_level_x=>0}, {:id=>5, :id2=>6, :name=>'?', :parent_id=>4, :parent_id2=>5, :x_root_x_0=>7, :x_root_x_1=>8, :x_level_x=>1}]]
@@ -375,7 +375,7 @@ describe Sequel::Model, "rcte_tree with composite keys" do
375
375
  sqls = @db.sqls
376
376
  sqls.first.should == "SELECT * FROM nodes"
377
377
  sqls.last.should =~ /WITH t AS \(SELECT parent_id AS x_root_x_0, parent_id2 AS x_root_x_1, nodes\.\*, CAST\(0 AS integer\) AS x_level_x FROM nodes WHERE \(\(parent_id, parent_id2\) IN \(\([267], [378]\), \([267], [378]\), \([267], [378]\)\)\) UNION ALL SELECT t\.x_root_x_0, t\.x_root_x_1, nodes\.\*, \(t\.x_level_x \+ 1\) AS x_level_x FROM nodes INNER JOIN t ON \(\(t\.id = nodes\.parent_id\) AND \(t\.id2 = nodes\.parent_id2\)\) WHERE \(t\.x_level_x < 1\)\) SELECT \* FROM t AS nodes/
378
- os.should == [@c.load(:id=>2, :id2=>3, :parent_id=>1, :parent_id=>2, :name=>'AA'), @c.load(:id=>6, :id2=>7, :parent_id=>2, :parent_id2=>3, :name=>'C'), @c.load(:id=>7, :id2=>8, :parent_id=>1, :parent_id2=>2, :name=>'D')]
378
+ os.should == [@c.load(:id=>2, :id2=>3, :parent_id=>1, :parent_id2=>2, :name=>'AA'), @c.load(:id=>6, :id2=>7, :parent_id=>2, :parent_id2=>3, :name=>'C'), @c.load(:id=>7, :id2=>8, :parent_id=>1, :parent_id2=>2, :name=>'D')]
379
379
  os.map{|o| o.descendants}.should == [[@c.load(:id=>6, :id2=>7, :parent_id=>2, :parent_id2=>3, :name=>'C'), @c.load(:id=>9, :id2=>10, :parent_id=>2, :parent_id2=>3, :name=>'E'), @c.load(:id=>3, :id2=>4, :name=>'00', :parent_id=>6, :parent_id2=>7)],
380
380
  [@c.load(:id=>3, :id2=>4, :name=>'00', :parent_id=>6, :parent_id2=>7)],
381
381
  [@c.load(:id=>4, :id2=>5, :name=>'?', :parent_id=>7, :parent_id2=>8), @c.load(:id=>5, :id2=>6, :name=>'?', :parent_id=>4, :parent_id2=>5)]]
@@ -48,6 +48,12 @@ describe "Sequel::Plugins::UpdateOrCreate" do
48
48
  sqls.shift.should == "SELECT * FROM test WHERE (id = 1) LIMIT 1"
49
49
  end
50
50
 
51
+ it ".update_or_create should return an existing record even if no changes necessary" do
52
+ @db.fetch = [[{:id=>1, :a=>2, :b=>3}]]
53
+ @c.update_or_create(:a=>2){|t| t.b = 3}.should == @c.load(:id=>1, :a=>2, :b=>3)
54
+ @db.sqls.should == ["SELECT * FROM test WHERE (a = 2) LIMIT 1"]
55
+ end
56
+
51
57
  it ".find_or_new should return an existing record" do
52
58
  @db.fetch = [[{:id=>1, :a=>2, :b=>3}]]
53
59
  @c.find_or_new(:a=>2){|t| t.b = 4}.should == @c.load(:id=>1, :a=>2, :b=>4)
@@ -327,6 +327,13 @@ describe "Bound Argument Types" do
327
327
  @ds.get(:file).should == @vs[:file]
328
328
  end
329
329
 
330
+ cspecify "should handle blob type with special characters", [:odbc], [:oracle] do
331
+ @ds.delete
332
+ blob = Sequel.blob("\"'[]`a0 ")
333
+ @ds.prepare(:insert, :ps_blob, {:file=>:$x}).call(:x=>blob)
334
+ @ds.get(:file).should == blob
335
+ end
336
+
330
337
  cspecify "should handle blob type with nil values", [:oracle], [:tinytds], [:jdbc, proc{|db| defined?(Sequel::JDBC::SQLServer::DatabaseMethods) && db.is_a?(Sequel::JDBC::SQLServer::DatabaseMethods)}] do
331
338
  @ds.delete
332
339
  @ds.prepare(:insert, :ps_blob, {:file=>:$x}).call(:x=>nil)
@@ -346,6 +346,12 @@ describe "Model#set_server" do
346
346
  @db.sqls.should == ["DELETE FROM items WHERE (id = 13) -- s1"]
347
347
  end
348
348
 
349
+ it "should set the server to use when deleting when using optimized delete" do
350
+ @c.set_primary_key :id
351
+ @c.load(:id=>13).set_server(:s1).delete
352
+ @db.sqls.should == ["DELETE FROM items WHERE id = 13 -- s1"]
353
+ end
354
+
349
355
  it "should set the server to use for transactions when destroying" do
350
356
  o = @c.load(:id=>13).set_server(:s1)
351
357
  o.use_transactions = true
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.16.0
4
+ version: 4.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-01 00:00:00.000000000 Z
11
+ date: 2014-12-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: The Database Toolkit for Ruby
14
14
  email: code@jeremyevans.net
@@ -133,6 +133,7 @@ extra_rdoc_files:
133
133
  - doc/release_notes/4.14.0.txt
134
134
  - doc/release_notes/4.15.0.txt
135
135
  - doc/release_notes/4.16.0.txt
136
+ - doc/release_notes/4.17.0.txt
136
137
  files:
137
138
  - CHANGELOG
138
139
  - MIT-LICENSE
@@ -236,6 +237,7 @@ files:
236
237
  - doc/release_notes/4.14.0.txt
237
238
  - doc/release_notes/4.15.0.txt
238
239
  - doc/release_notes/4.16.0.txt
240
+ - doc/release_notes/4.17.0.txt
239
241
  - doc/release_notes/4.2.0.txt
240
242
  - doc/release_notes/4.3.0.txt
241
243
  - doc/release_notes/4.4.0.txt
@@ -734,7 +736,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
734
736
  version: '0'
735
737
  requirements: []
736
738
  rubyforge_project:
737
- rubygems_version: 2.2.2
739
+ rubygems_version: 2.4.4
738
740
  signing_key:
739
741
  specification_version: 4
740
742
  summary: The Database Toolkit for Ruby