viking-sequel 3.10.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.
Files changed (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,84 @@
1
+ module Sequel
2
+ class Dataset
3
+ module StoredProcedureMethods
4
+ SQL_QUERY_TYPE = Hash.new{|h,k| h[k] = k}
5
+ SQL_QUERY_TYPE[:first] = SQL_QUERY_TYPE[:all] = :select
6
+
7
+ # The name of the stored procedure to call
8
+ attr_accessor :sproc_name
9
+
10
+ # The name of the stored procedure to call
11
+ attr_writer :sproc_args
12
+
13
+ # Call the stored procedure with the given args
14
+ def call(*args, &block)
15
+ sp = clone
16
+ sp.sproc_args = args
17
+ sp.run(&block)
18
+ end
19
+
20
+ # Programmer friendly string showing this is a stored procedure,
21
+ # showing the name of the procedure.
22
+ def inspect
23
+ "<#{self.class.name}/StoredProcedure name=#{@sproc_name}>"
24
+ end
25
+
26
+ # Run the stored procedure with the current args on the database
27
+ def run(&block)
28
+ case @sproc_type
29
+ when :select, :all
30
+ all(&block)
31
+ when :first
32
+ first
33
+ when :insert
34
+ insert
35
+ when :update
36
+ update
37
+ when :delete
38
+ delete
39
+ end
40
+ end
41
+
42
+ # Set the type of the stored procedure and override the corresponding _sql
43
+ # method to return the empty string (since the result will be
44
+ # ignored anyway).
45
+ def sproc_type=(type)
46
+ @sproc_type = type
47
+ meta_def("#{sql_query_type}_sql"){|*a| ''}
48
+ end
49
+
50
+ private
51
+
52
+ # The type of query (:select, :insert, :delete, :update).
53
+ def sql_query_type
54
+ SQL_QUERY_TYPE[@sproc_type]
55
+ end
56
+ end
57
+
58
+ module StoredProcedures
59
+ # For the given type (:select, :first, :insert, :update, or :delete),
60
+ # run the database stored procedure with the given name with the given
61
+ # arguments.
62
+ def call_sproc(type, name, *args)
63
+ prepare_sproc(type, name).call(*args)
64
+ end
65
+
66
+ # Transform this dataset into a stored procedure that you can call
67
+ # multiple times with new arguments.
68
+ def prepare_sproc(type, name)
69
+ sp = clone
70
+ prepare_extend_sproc(sp)
71
+ sp.sproc_type = type
72
+ sp.sproc_name = name
73
+ sp
74
+ end
75
+
76
+ private
77
+
78
+ # Extend the dataset with the stored procedure methods.
79
+ def prepare_extend_sproc(ds)
80
+ ds.extend(StoredProcedureMethods)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,99 @@
1
+ # The base connection pool class, which all other connection pools are built
2
+ # on. This class is not instantiated directly, but subclasses should at
3
+ # the very least implement the following API:
4
+ # * initialize(Hash, &block) - The block is used as the connection proc,
5
+ # which should accept a single symbol argument.
6
+ # * hold(Symbol, &block) - yield a connection object (obtained from calling
7
+ # the block passed to initialize) to the current block. For sharded
8
+ # connection pools, the Symbol passed is the shard/server to use.
9
+ # * disconnect(Symbol, &block) - disconnect the connection object. If a
10
+ # block is given, pass the connection option to it, otherwise use the
11
+ # :disconnection_proc option in the hash passed to initialize. For sharded
12
+ # connection pools, the Symbol passed is the shard/server to use.
13
+ # * servers - an array of shard/server symbols for all shards/servers that this
14
+ # connection pool recognizes.
15
+ # * size - an integer representing the total number of connections in the pool,
16
+ # or for the given shard/server if sharding is supported.
17
+ #
18
+ # For sharded connection pools, the sharded API:
19
+ # * add_servers(Array of Symbols) - start recognizing all shards/servers specified
20
+ # by the array of symbols.
21
+ # * remove_servers(Array of Symbols) - no longer recognize all shards/servers
22
+ # specified by the array of symbols.
23
+ class Sequel::ConnectionPool
24
+ # The default server to use
25
+ DEFAULT_SERVER = :default
26
+
27
+ # A map of [single threaded, sharded] values to files (indicating strings to
28
+ # be required) ConnectionPool subclasses.
29
+ CONNECTION_POOL_MAP = {[true, false] => :single,
30
+ [true, true] => :sharded_single,
31
+ [false, false] => :threaded,
32
+ [false, true] => :sharded_threaded}
33
+
34
+ # Class methods used to return an appropriate pool subclass, separated
35
+ # into a module for easier overridding by extensions.
36
+ module ClassMethods
37
+ # Return a pool subclass instance based on the given options. If a :pool_class
38
+ # option is provided is provided, use that pool class, otherwise
39
+ # use a new instance of an appropriate pool subclass based on the
40
+ # :single_threaded and :servers options.
41
+ def get_pool(opts = {}, &block)
42
+ case v = connection_pool_class(opts)
43
+ when Class
44
+ v.new(opts, &block)
45
+ when Symbol
46
+ Sequel.ts_require("connection_pool/#{v}")
47
+ connection_pool_class(opts).new(opts, &block) || raise(Sequel::Error, "No connection pool class found")
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # Return a connection pool class based on the given options.
54
+ def connection_pool_class(opts)
55
+ opts[:pool_class] || CONNECTION_POOL_MAP[[!!opts[:single_threaded], !!opts[:servers]]]
56
+ end
57
+ end
58
+ extend ClassMethods
59
+
60
+ # Instantiates a connection pool with the given options. The block is called
61
+ # with a single symbol (specifying the server/shard to use) every time a new
62
+ # connection is needed. The following options are respected for all connection
63
+ # pools:
64
+ # * :after_connect - The proc called after each new connection is made, with the
65
+ # connection object, useful for customizations that you want to apply to all
66
+ # connections.
67
+ # * :disconnection_proc - The proc called when removing connections from the pool,
68
+ # which is passed the connection to disconnect.
69
+ def initialize(opts={}, &block)
70
+ raise(Sequel::Error, "No connection proc specified") unless @connection_proc = block
71
+ @disconnection_proc = opts[:disconnection_proc]
72
+ @after_connect = opts[:after_connect]
73
+ end
74
+
75
+ # Alias for size, not aliased directly for ease of subclass implementation
76
+ def created_count(*args)
77
+ size(*args)
78
+ end
79
+
80
+ # An array of symbols for all shards/servers, which is a single :default by default.
81
+ def servers
82
+ [:default]
83
+ end
84
+
85
+ private
86
+
87
+ # Return a new connection by calling the connection proc with the given server name,
88
+ # and checking for connection errors.
89
+ def make_new(server)
90
+ begin
91
+ conn = @connection_proc.call(server)
92
+ @after_connect.call(conn) if @after_connect
93
+ rescue Exception=>exception
94
+ raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
95
+ end
96
+ raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
97
+ conn
98
+ end
99
+ end
@@ -0,0 +1,84 @@
1
+ # A ShardedSingleConnectionPool is a single threaded connection pool that
2
+ # works with multiple shards/servers.
3
+ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
4
+ # Initializes the instance with the supplied block as the connection_proc.
5
+ #
6
+ # The single threaded pool takes the following options:
7
+ #
8
+ # * :servers - A hash of servers to use. Keys should be symbols. If not
9
+ # present, will use a single :default server. The server name symbol will
10
+ # be passed to the connection_proc.
11
+ def initialize(opts={}, &block)
12
+ super
13
+ @conns = {}
14
+ @servers = Hash.new(:default)
15
+ add_servers([:default])
16
+ add_servers(opts[:servers].keys) if opts[:servers]
17
+ end
18
+
19
+ # Adds new servers to the connection pool. Primarily used in conjunction with master/slave
20
+ # or shard configurations. Allows for dynamic expansion of the potential slaves/shards
21
+ # at runtime. servers argument should be an array of symbols.
22
+ def add_servers(servers)
23
+ servers.each{|s| @servers[s] = s}
24
+ end
25
+
26
+ # The connection for the given server.
27
+ def conn(server=:default)
28
+ @conns[@servers[server]]
29
+ end
30
+
31
+ # Disconnects from the database. Once a connection is requested using
32
+ # #hold, the connection is reestablished. Options:
33
+ # * :server - Should be a symbol specifing the server to disconnect from,
34
+ # or an array of symbols to specify multiple servers.
35
+ def disconnect(opts={}, &block)
36
+ block ||= @disconnection_proc
37
+ (opts[:server] ? Array(opts[:server]) : servers).each{|s| disconnect_server(s, &block)}
38
+ end
39
+
40
+ # Yields the connection to the supplied block for the given server.
41
+ # This method simulates the ConnectionPool#hold API.
42
+ def hold(server=:default)
43
+ begin
44
+ server = @servers[server]
45
+ yield(@conns[server] ||= make_new(server))
46
+ rescue Sequel::DatabaseDisconnectError
47
+ disconnect_server(server, &@disconnection_proc)
48
+ raise
49
+ end
50
+ end
51
+
52
+ # Remove servers from the connection pool. Primarily used in conjunction with master/slave
53
+ # or shard configurations. Similar to disconnecting from all given servers,
54
+ # except that after it is used, future requests for the server will use the
55
+ # :default server instead.
56
+ def remove_servers(servers)
57
+ raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
58
+ servers.each do |server|
59
+ disconnect_server(server, &@disconnection_proc)
60
+ @servers.delete(server)
61
+ end
62
+ end
63
+
64
+ # Return an array of symbols for servers in the connection pool.
65
+ def servers
66
+ @servers.keys
67
+ end
68
+
69
+ # The number of different shards/servers this pool is connected to.
70
+ def size
71
+ @conns.length
72
+ end
73
+
74
+ private
75
+
76
+ # Disconnect from the given server, if connected.
77
+ def disconnect_server(server, &block)
78
+ if conn = @conns.delete(server)
79
+ block.call(conn) if block
80
+ end
81
+ end
82
+
83
+ CONNECTION_POOL_MAP[[true, true]] = self
84
+ end
@@ -0,0 +1,211 @@
1
+ Sequel.require 'connection_pool/threaded'
2
+
3
+ # The slowest and most advanced connection, dealing with both multi-threaded
4
+ # access and configurations with multiple shards/servers.
5
+ #
6
+ # In addition, this pool subclass also handles scheduling in-use connections
7
+ # to be removed from the pool when they are returned to it.
8
+ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
9
+ # The following additional options are respected:
10
+ # * :servers - A hash of servers to use. Keys should be symbols. If not
11
+ # present, will use a single :default server. The server name symbol will
12
+ # be passed to the connection_proc.
13
+ def initialize(opts = {}, &block)
14
+ super
15
+ @available_connections = {}
16
+ @connections_to_remove = []
17
+ @servers = Hash.new(:default)
18
+ add_servers([:default])
19
+ add_servers(opts[:servers].keys) if opts[:servers]
20
+ end
21
+
22
+ # Adds new servers to the connection pool. Primarily used in conjunction with master/slave
23
+ # or shard configurations. Allows for dynamic expansion of the potential slaves/shards
24
+ # at runtime. servers argument should be an array of symbols.
25
+ def add_servers(servers)
26
+ sync do
27
+ servers.each do |server|
28
+ unless @servers.has_key?(server)
29
+ @servers[server] = server
30
+ @available_connections[server] = []
31
+ @allocated[server] = {}
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # A hash of connections currently being used for the given server, key is the
38
+ # Thread, value is the connection. Nonexistent servers will return nil. Treat
39
+ # this as read only, do not modify the resulting object.
40
+ def allocated(server=:default)
41
+ @allocated[server]
42
+ end
43
+
44
+ # An array of connections opened but not currently used, for the given
45
+ # server. Nonexistent servers will return nil. Treat this as read only, do
46
+ # not modify the resulting object.
47
+ def available_connections(server=:default)
48
+ @available_connections[server]
49
+ end
50
+
51
+ # The total number of connections opened for the given server, should
52
+ # be equal to available_connections.length + allocated.length. Nonexistent
53
+ # servers will return the created count of the default server.
54
+ def size(server=:default)
55
+ server = @servers[server]
56
+ @allocated[server].length + @available_connections[server].length
57
+ end
58
+
59
+ # Removes all connection currently available on all servers, optionally
60
+ # yielding each connection to the given block. This method has the effect of
61
+ # disconnecting from the database, assuming that no connections are currently
62
+ # being used. If connections are being used, they are scheduled to be
63
+ # disconnected as soon as they are returned to the pool.
64
+ #
65
+ # Once a connection is requested using #hold, the connection pool
66
+ # creates new connections to the database. Options:
67
+ # * :server - Should be a symbol specifing the server to disconnect from,
68
+ # or an array of symbols to specify multiple servers.
69
+ def disconnect(opts={}, &block)
70
+ block ||= @disconnection_proc
71
+ sync do
72
+ (opts[:server] ? Array(opts[:server]) : @servers.keys).each do |s|
73
+ disconnect_server(s, &block)
74
+ end
75
+ end
76
+ end
77
+
78
+ # Chooses the first available connection to the given server, or if none are
79
+ # available, creates a new connection. Passes the connection to the supplied
80
+ # block:
81
+ #
82
+ # pool.hold {|conn| conn.execute('DROP TABLE posts')}
83
+ #
84
+ # Pool#hold is re-entrant, meaning it can be called recursively in
85
+ # the same thread without blocking.
86
+ #
87
+ # If no connection is immediately available and the pool is already using the maximum
88
+ # number of connections, Pool#hold will block until a connection
89
+ # is available or the timeout expires. If the timeout expires before a
90
+ # connection can be acquired, a Sequel::PoolTimeout is
91
+ # raised.
92
+ def hold(server=:default)
93
+ sync{server = @servers[server]}
94
+ t = Thread.current
95
+ if conn = owned_connection(t, server)
96
+ return yield(conn)
97
+ end
98
+ begin
99
+ unless conn = acquire(t, server)
100
+ time = Time.now
101
+ timeout = time + @timeout
102
+ sleep_time = @sleep_time
103
+ sleep sleep_time
104
+ until conn = acquire(t, server)
105
+ raise(::Sequel::PoolTimeout) if Time.now > timeout
106
+ sleep sleep_time
107
+ end
108
+ end
109
+ yield conn
110
+ rescue Sequel::DatabaseDisconnectError
111
+ sync{@connections_to_remove << conn} if conn
112
+ raise
113
+ ensure
114
+ sync{release(t, conn, server)} if conn
115
+ end
116
+ end
117
+
118
+ # Remove servers from the connection pool. Primarily used in conjunction with master/slave
119
+ # or shard configurations. Similar to disconnecting from all given servers,
120
+ # except that after it is used, future requests for the server will use the
121
+ # :default server instead.
122
+ def remove_servers(servers)
123
+ sync do
124
+ raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
125
+ servers.each do |server|
126
+ if @servers.include?(server)
127
+ disconnect_server(server, &@disconnection_proc)
128
+ @available_connections.delete(server)
129
+ @allocated.delete(server)
130
+ @servers.delete(server)
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ # Return an array of symbols for servers in the connection pool.
137
+ def servers
138
+ sync{@servers.keys}
139
+ end
140
+
141
+ private
142
+
143
+ # Assigns a connection to the supplied thread for the given server, if one
144
+ # is available. The calling code should NOT already have the mutex when
145
+ # calling this.
146
+ def acquire(thread, server)
147
+ sync do
148
+ if conn = available(server)
149
+ allocated(server)[thread] = conn
150
+ end
151
+ end
152
+ end
153
+
154
+ # Returns an available connection to the given server. If no connection is
155
+ # available, tries to create a new connection. The calling code should already
156
+ # have the mutex before calling this.
157
+ def available(server)
158
+ available_connections(server).pop || make_new(server)
159
+ end
160
+
161
+ # Disconnect from the given server. Disconnects available connections
162
+ # immediately, and schedules currently allocated connections for disconnection
163
+ # as soon as they are returned to the pool. The calling code should already
164
+ # have the mutex before calling this.
165
+ def disconnect_server(server, &block)
166
+ if conns = available_connections(server)
167
+ conns.each{|conn| block.call(conn)} if block
168
+ conns.clear
169
+ end
170
+ @connections_to_remove.concat(allocated(server).values)
171
+ end
172
+
173
+ # Creates a new connection to the given server if the size of the pool for
174
+ # the server is less than the maximum size of the pool. The calling code
175
+ # should already have the mutex before calling this.
176
+ def make_new(server)
177
+ if (n = size(server)) >= @max_size
178
+ allocated(server).to_a.each{|t, c| release(t, c, server) unless t.alive?}
179
+ n = nil
180
+ end
181
+ default_make_new(server) if (n || size(server)) < @max_size
182
+ end
183
+
184
+ # Returns the connection owned by the supplied thread for the given server,
185
+ # if any. The calling code should NOT already have the mutex before calling this.
186
+ def owned_connection(thread, server)
187
+ sync{@allocated[server][thread]}
188
+ end
189
+
190
+ # Releases the connection assigned to the supplied thread and server. If the
191
+ # server or connection given is scheduled for disconnection, remove the
192
+ # connection instead of releasing it back to the pool.
193
+ # The calling code should already have the mutex before calling this.
194
+ def release(thread, conn, server)
195
+ if @connections_to_remove.include?(conn)
196
+ remove(thread, conn, server)
197
+ else
198
+ available_connections(server) << allocated(server).delete(thread)
199
+ end
200
+ end
201
+
202
+ # Removes the currently allocated connection from the connection pool. The
203
+ # calling code should already have the mutex before calling this.
204
+ def remove(thread, conn, server)
205
+ @connections_to_remove.delete(conn)
206
+ allocated(server).delete(thread) if @servers.include?(server)
207
+ @disconnection_proc.call(conn) if @disconnection_proc
208
+ end
209
+
210
+ CONNECTION_POOL_MAP[[false, true]] = self
211
+ end