sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -6,21 +6,27 @@ module Sequel
6
6
  class Database < Sequel::Database
7
7
  include Sequel::MSSQL::DatabaseMethods
8
8
  set_adapter_scheme :tinytds
9
+
10
+ # Choose whether to use unicode strings on initialization
11
+ def initialize(*)
12
+ super
13
+ set_mssql_unicode_strings
14
+ end
9
15
 
10
- # Transfer the :host and :user options to the
11
- # :dataserver and :username options.
16
+ # Transfer the :user option to the :username option.
12
17
  def connect(server)
13
18
  opts = server_opts(server)
14
- opts[:dataserver] = opts[:host]
15
19
  opts[:username] = opts[:user]
16
- TinyTds::Client.new(opts)
17
- end
20
+ c = TinyTds::Client.new(opts)
21
+
22
+ if (ts = opts[:textsize])
23
+ sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
24
+ log_yield(sql){c.execute(sql)}
25
+ end
18
26
 
19
- # Return instance of Sequel::TinyTDS::Dataset with the given options.
20
- def dataset(opts = nil)
21
- TinyTDS::Dataset.new(self, opts)
27
+ c
22
28
  end
23
-
29
+
24
30
  # Execute the given +sql+ on the server. If the :return option
25
31
  # is present, its value should be a method symbol that is called
26
32
  # on the TinyTds::Result object returned from executing the
@@ -32,13 +38,36 @@ module Sequel
32
38
  begin
33
39
  m = opts[:return]
34
40
  r = nil
35
- log_yield(sql) do
36
- r = c.execute(sql)
37
- return r.send(m) if m
41
+ if (args = opts[:arguments]) && !args.empty?
42
+ types = []
43
+ values = []
44
+ args.each_with_index do |(k, v), i|
45
+ v, type = ps_arg_type(v)
46
+ types << "@#{k} #{type}"
47
+ values << "@#{k} = #{v}"
48
+ end
49
+ case m
50
+ when :do
51
+ sql = "#{sql}; SELECT @@ROWCOUNT AS AffectedRows"
52
+ single_value = true
53
+ when :insert
54
+ sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
55
+ single_value = true
56
+ end
57
+ sql = "EXEC sp_executesql N'#{c.escape(sql)}', N'#{c.escape(types.join(', '))}', #{values.join(', ')}"
58
+ log_yield(sql) do
59
+ r = c.execute(sql)
60
+ r.each{|row| return row.values.first} if single_value
61
+ end
62
+ else
63
+ log_yield(sql) do
64
+ r = c.execute(sql)
65
+ return r.send(m) if m
66
+ end
38
67
  end
39
68
  yield(r) if block_given?
40
69
  rescue TinyTds::Error => e
41
- raise_error(e)
70
+ raise_error(e, :disconnect=>!c.active?)
42
71
  ensure
43
72
  r.cancel if r && c.sqlsent?
44
73
  end
@@ -74,51 +103,174 @@ module Sequel
74
103
  super
75
104
  end
76
105
 
106
+ # tiny_tds uses TinyTds::Error as the base error class.
107
+ def database_error_classes
108
+ [TinyTds::Error]
109
+ end
110
+
77
111
  # Close the TinyTds::Client object.
78
112
  def disconnect_connection(c)
79
113
  c.close
80
114
  end
115
+
116
+ # Return true if the :conn argument is present and not active.
117
+ def disconnect_error?(e, opts)
118
+ super || (opts[:conn] && !opts[:conn].active?)
119
+ end
120
+
121
+ # Return a 2 element array with the literal value and type to use
122
+ # in the prepared statement call for the given value and connection.
123
+ def ps_arg_type(v)
124
+ case v
125
+ when Fixnum
126
+ [v, 'int']
127
+ when Bignum
128
+ [v, 'bigint']
129
+ when Float
130
+ [v, 'double precision']
131
+ when Numeric
132
+ [v, 'numeric']
133
+ when Time
134
+ if v.is_a?(SQLTime)
135
+ [literal(v), 'time']
136
+ else
137
+ [literal(v), 'datetime']
138
+ end
139
+ when DateTime
140
+ [literal(v), 'datetime']
141
+ when Date
142
+ [literal(v), 'date']
143
+ when nil
144
+ ['NULL', 'nvarchar(max)']
145
+ when true
146
+ ['1', 'int']
147
+ when false
148
+ ['0', 'int']
149
+ when SQL::Blob
150
+ [literal(v), 'varbinary(max)']
151
+ else
152
+ [literal(v), 'nvarchar(max)']
153
+ end
154
+ end
81
155
  end
82
156
 
83
157
  class Dataset < Sequel::Dataset
84
158
  include Sequel::MSSQL::DatasetMethods
159
+
160
+ Database::DatasetClass = self
161
+
162
+ # SQLite already supports named bind arguments, so use directly.
163
+ module ArgumentMapper
164
+ include Sequel::Dataset::ArgumentMapper
165
+
166
+ protected
167
+
168
+ # Return a hash with the same values as the given hash,
169
+ # but with the keys converted to strings.
170
+ def map_to_prepared_args(hash)
171
+ args = {}
172
+ hash.each{|k,v| args[k.to_s.gsub('.', '__')] = v}
173
+ args
174
+ end
175
+
176
+ private
177
+
178
+ # SQLite uses a : before the name of the argument for named
179
+ # arguments.
180
+ def prepared_arg(k)
181
+ LiteralString.new("@#{k.to_s.gsub('.', '__')}")
182
+ end
183
+
184
+ # Always assume a prepared argument.
185
+ def prepared_arg?(k)
186
+ true
187
+ end
188
+ end
189
+
190
+ # SQLite prepared statement uses a new prepared statement each time
191
+ # it is called, but it does use the bind arguments.
192
+ module PreparedStatementMethods
193
+ include ArgumentMapper
194
+
195
+ private
196
+
197
+ # Run execute_select on the database with the given SQL and the stored
198
+ # bind arguments.
199
+ def execute(sql, opts={}, &block)
200
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
201
+ end
202
+
203
+ # Same as execute, explicit due to intricacies of alias and super.
204
+ def execute_dui(sql, opts={}, &block)
205
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
206
+ end
207
+
208
+ # Same as execute, explicit due to intricacies of alias and super.
209
+ def execute_insert(sql, opts={}, &block)
210
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
211
+ end
212
+ end
85
213
 
86
214
  # Yield hashes with symbol keys, attempting to optimize for
87
215
  # various cases.
88
216
  def fetch_rows(sql)
89
217
  execute(sql) do |result|
90
218
  each_opts = {:cache_rows=>false}
91
- each_opts[:timezone] = :utc if Sequel.database_timezone == :utc
92
- offset = @opts[:offset]
93
- @columns = cols = result.fields.map{|c| output_identifier(c)}
94
- if identifier_output_method
219
+ each_opts[:timezone] = :utc if db.timezone == :utc
220
+ rn = row_number_column if offset = @opts[:offset]
221
+ columns = cols = result.fields.map{|c| output_identifier(c)}
222
+ if offset
223
+ rn = row_number_column
224
+ columns = columns.dup
225
+ columns.delete(rn)
226
+ end
227
+ @columns = columns
228
+ #if identifier_output_method
95
229
  each_opts[:as] = :array
96
230
  result.each(each_opts) do |r|
97
231
  h = {}
98
232
  cols.zip(r).each{|k, v| h[k] = v}
99
- h.delete(row_number_column) if offset
233
+ h.delete(rn) if rn
100
234
  yield h
101
235
  end
236
+ =begin
237
+ # Temporarily disable this optimization, as tiny_tds uses string keys
238
+ # if result.fields is called before result.each(:symbolize_keys=>true).
239
+ # See https://github.com/rails-sqlserver/tiny_tds/issues/57
102
240
  else
103
241
  each_opts[:symbolize_keys] = true
104
242
  if offset
105
243
  result.each(each_opts) do |r|
106
- r.delete(row_number_column) if offset
244
+ r.delete(rn) if rn
107
245
  yield r
108
246
  end
109
247
  else
110
- result.each(each_opts, &block)
248
+ result.each(each_opts, &Proc.new)
111
249
  end
112
250
  end
251
+ =end
113
252
  end
114
253
  self
115
254
  end
116
255
 
256
+ # Create a named prepared statement that is stored in the
257
+ # database (and connection) for reuse.
258
+ def prepare(type, name=nil, *values)
259
+ ps = to_prepared_statement(type, values)
260
+ ps.extend(PreparedStatementMethods)
261
+ if name
262
+ ps.prepared_statement_name = name
263
+ db.set_prepared_statement(name, ps)
264
+ end
265
+ ps
266
+ end
267
+
117
268
  private
118
269
 
119
270
  # Properly escape the given string +v+.
120
- def literal_string(v)
121
- db.synchronize{|c| "N'#{c.escape(v)}'"}
271
+ def literal_string_append(sql, v)
272
+ sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
273
+ sql << db.synchronize{|c| c.escape(v)}.gsub(BACKSLASH_CRLF_RE, BACKSLASH_CRLF_REPLACE) << APOS
122
274
  end
123
275
  end
124
276
  end
@@ -0,0 +1,77 @@
1
+ module Sequel
2
+ module EmulateOffsetWithRowNumber
3
+ # When a subselect that uses :offset is used in IN or NOT IN,
4
+ # use a nested subselect that only includes the first column
5
+ # instead of the ROW_NUMBER column added by the emulated offset support.
6
+ def complex_expression_sql_append(sql, op, args)
7
+ case op
8
+ when :IN, :"NOT IN"
9
+ ds = args.at(1)
10
+ if ds.is_a?(Sequel::Dataset) && ds.opts[:offset]
11
+ c = ds.opts[:select].first
12
+ case c
13
+ when Symbol
14
+ t, cl, a = split_symbol(c)
15
+ if a
16
+ c = SQL::Identifier.new(a)
17
+ elsif t
18
+ c = SQL::Identifier.new(cl)
19
+ end
20
+ when SQL::AliasedExpression
21
+ c = SQL::Identifier.new(c.aliaz)
22
+ when SQL::QualifiedIdentifier
23
+ c = SQL::Identifier.new(c.column)
24
+ end
25
+ super(sql, op, [args.at(0), ds.from_self.select(c)])
26
+ else
27
+ super
28
+ end
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ # Emulate OFFSET support with the ROW_NUMBER window function
35
+ #
36
+ # The implementation is ugly, cloning the current dataset and modifying
37
+ # the clone to add a ROW_NUMBER window function (and some other things),
38
+ # then using the modified clone in a subselect which is selected from.
39
+ #
40
+ # If offset is used, an order must be provided, because the use of ROW_NUMBER
41
+ # requires an order.
42
+ def select_sql
43
+ return super unless o = @opts[:offset]
44
+
45
+ order = @opts[:order] || default_offset_order
46
+ if order.nil? || order.empty?
47
+ raise(Error, "#{db.database_type} requires an order be provided if using an offset")
48
+ end
49
+
50
+ dsa1 = dataset_alias(1)
51
+ rn = row_number_column
52
+ sql = @opts[:append_sql] || ''
53
+ subselect_sql_append(sql, unlimited.
54
+ unordered.
55
+ select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
56
+ from_self(:alias=>dsa1).
57
+ limit(@opts[:limit]).
58
+ where(SQL::Identifier.new(rn) > o).
59
+ order(rn))
60
+ sql
61
+ end
62
+
63
+ private
64
+
65
+ # The default order to use for datasets with offsets, if no order is defined.
66
+ # By default, orders by all of the columns in the dataset.
67
+ def default_offset_order
68
+ clone(:append_sql=>'').columns
69
+ end
70
+
71
+ # This emulation adds an extra row number column that should be
72
+ # eliminated.
73
+ def offset_returns_row_number_column?
74
+ true
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,81 @@
1
+ module Sequel
2
+ module Postgres
3
+ NAN = 0.0/0.0
4
+ PLUS_INFINITY = 1.0/0.0
5
+ MINUS_INFINITY = -1.0/0.0
6
+ NAN_STR = 'NaN'.freeze
7
+ PLUS_INFINITY_STR = 'Infinity'.freeze
8
+ MINUS_INFINITY_STR = '-Infinity'.freeze
9
+ TRUE_STR = 't'.freeze
10
+ DASH_STR = '-'.freeze
11
+
12
+ TYPE_TRANSLATOR = tt = Class.new do
13
+ def boolean(s) s == TRUE_STR end
14
+ def integer(s) s.to_i end
15
+ def float(s)
16
+ case s
17
+ when NAN_STR
18
+ NAN
19
+ when PLUS_INFINITY_STR
20
+ PLUS_INFINITY
21
+ when MINUS_INFINITY_STR
22
+ MINUS_INFINITY
23
+ else
24
+ s.to_f
25
+ end
26
+ end
27
+ def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
28
+ def bytea(str)
29
+ str = if str =~ /\A\\x/
30
+ # PostgreSQL 9.0+ bytea hex format
31
+ str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
32
+ else
33
+ # Historical PostgreSQL bytea escape format
34
+ str.gsub(/\\(\\|'|[0-3][0-7][0-7])/) {|s|
35
+ if s.size == 2 then s[1,1] else s[1,3].oct.chr end
36
+ }
37
+ end
38
+ ::Sequel::SQL::Blob.new(str)
39
+ end
40
+ end.new
41
+
42
+ # Type OIDs for string types used by PostgreSQL. These types don't
43
+ # have conversion procs associated with them (since the data is
44
+ # already in the form of a string).
45
+ STRING_TYPES = [18, 19, 25, 1042, 1043]
46
+
47
+ # Hash with type name strings/symbols and callable values for converting PostgreSQL types.
48
+ # Non-builtin types that don't have fixed numbers should use this to register
49
+ # conversion procs.
50
+ PG_NAMED_TYPES = {} unless defined?(PG_NAMED_TYPES)
51
+
52
+ # Hash with integer keys and callable values for converting PostgreSQL types.
53
+ PG_TYPES = {} unless defined?(PG_TYPES)
54
+
55
+ {
56
+ [16] => tt.method(:boolean),
57
+ [17] => tt.method(:bytea),
58
+ [20, 21, 23, 26] => tt.method(:integer),
59
+ [700, 701] => tt.method(:float),
60
+ [1700] => ::BigDecimal.method(:new),
61
+ [1083, 1266] => ::Sequel.method(:string_to_time),
62
+ [1184, 1114] => ::Sequel.method(:database_to_application_timestamp),
63
+ }.each do |k,v|
64
+ k.each{|n| PG_TYPES[n] = v}
65
+ end
66
+
67
+ class << self
68
+ # As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
69
+ # the date in a known format that Sequel can parse faster. This can be turned off
70
+ # if you require a date style other than ISO.
71
+ attr_reader :use_iso_date_format
72
+ end
73
+
74
+ # Modify the type translator for the date type depending on the value given.
75
+ def self.use_iso_date_format=(v)
76
+ PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
77
+ @use_iso_date_format = v
78
+ end
79
+ self.use_iso_date_format = true
80
+ end
81
+ end
@@ -1,9 +1,6 @@
1
1
  module Sequel
2
2
  class Dataset
3
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
4
  # The name of the stored procedure to call
8
5
  attr_accessor :sproc_name
9
6
 
@@ -44,14 +41,7 @@ module Sequel
44
41
  # ignored anyway).
45
42
  def sproc_type=(type)
46
43
  @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]
44
+ @opts[:sql] = ''
55
45
  end
56
46
  end
57
47
 
@@ -0,0 +1,194 @@
1
+ module Sequel
2
+ # The +ASTTransformer+ class is designed to handle the abstract syntax trees
3
+ # that Sequel uses internally and produce modified copies of them. By itself
4
+ # it only produces a straight copy. It's designed to be subclassed and have
5
+ # subclasses returned modified copies of the specific nodes that need to
6
+ # be modified.
7
+ class ASTTransformer
8
+ # Return +obj+ or a potentially transformed version of it.
9
+ def transform(obj)
10
+ v(obj)
11
+ end
12
+
13
+ private
14
+
15
+ # Recursive version that handles all of Sequel's internal object types
16
+ # and produces copies of them.
17
+ def v(o)
18
+ case o
19
+ when Symbol, Numeric, String, Class, TrueClass, FalseClass, NilClass
20
+ o
21
+ when Array
22
+ o.map{|x| v(x)}
23
+ when Hash
24
+ h = {}
25
+ o.each{|k, val| h[v(k)] = v(val)}
26
+ h
27
+ when SQL::ComplexExpression
28
+ SQL::ComplexExpression.new(o.op, *v(o.args))
29
+ when SQL::Identifier
30
+ SQL::Identifier.new(v(o.value))
31
+ when SQL::QualifiedIdentifier
32
+ SQL::QualifiedIdentifier.new(v(o.table), v(o.column))
33
+ when SQL::OrderedExpression
34
+ SQL::OrderedExpression.new(v(o.expression), o.descending, :nulls=>o.nulls)
35
+ when SQL::AliasedExpression
36
+ SQL::AliasedExpression.new(v(o.expression), o.aliaz)
37
+ when SQL::CaseExpression
38
+ args = [v(o.conditions), v(o.default)]
39
+ args << v(o.expression) if o.expression?
40
+ SQL::CaseExpression.new(*args)
41
+ when SQL::Cast
42
+ SQL::Cast.new(v(o.expr), o.type)
43
+ when SQL::Function
44
+ SQL::Function.new(o.f, *v(o.args))
45
+ when SQL::Subscript
46
+ SQL::Subscript.new(v(o.f), v(o.sub))
47
+ when SQL::WindowFunction
48
+ SQL::WindowFunction.new(v(o.function), v(o.window))
49
+ when SQL::Window
50
+ opts = o.opts.dup
51
+ opts[:partition] = v(opts[:partition]) if opts[:partition]
52
+ opts[:order] = v(opts[:order]) if opts[:order]
53
+ SQL::Window.new(opts)
54
+ when SQL::PlaceholderLiteralString
55
+ args = if o.args.is_a?(Hash)
56
+ h = {}
57
+ o.args.each{|k,val| h[k] = v(val)}
58
+ h
59
+ else
60
+ v(o.args)
61
+ end
62
+ SQL::PlaceholderLiteralString.new(o.str, args, o.parens)
63
+ when SQL::JoinOnClause
64
+ SQL::JoinOnClause.new(v(o.on), o.join_type, v(o.table), v(o.table_alias))
65
+ when SQL::JoinUsingClause
66
+ SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table), v(o.table_alias))
67
+ when SQL::JoinClause
68
+ SQL::JoinClause.new(o.join_type, v(o.table), v(o.table_alias))
69
+ when SQL::Wrapper
70
+ SQL::Wrapper.new(v(o.value))
71
+ else
72
+ o
73
+ end
74
+ end
75
+ end
76
+
77
+ # Handles qualifying existing datasets, so that unqualified columns
78
+ # in the dataset are qualified with a given table name.
79
+ class Qualifier < ASTTransformer
80
+ # Store the dataset to use as the basis for qualification,
81
+ # and the table used to qualify unqualified columns.
82
+ def initialize(ds, table)
83
+ @ds = ds
84
+ @table = table
85
+ end
86
+
87
+ private
88
+
89
+ # Turn <tt>SQL::Identifier</tt>s and symbols that aren't implicitly
90
+ # qualified into <tt>SQL::QualifiedIdentifier</tt>s. For symbols that
91
+ # are not implicitly qualified by are implicitly aliased, return an
92
+ # <tt>SQL::AliasedExpression</tt>s with a qualified version of the symbol.
93
+ def v(o)
94
+ case o
95
+ when Symbol
96
+ t, column, aliaz = @ds.send(:split_symbol, o)
97
+ if t
98
+ o
99
+ elsif aliaz
100
+ SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(@table, SQL::Identifier.new(column)), aliaz)
101
+ else
102
+ SQL::QualifiedIdentifier.new(@table, o)
103
+ end
104
+ when SQL::Identifier
105
+ SQL::QualifiedIdentifier.new(@table, o)
106
+ when SQL::QualifiedIdentifier, SQL::JoinClause
107
+ # Return these directly, so we don't accidentally qualify symbols in them.
108
+ o
109
+ else
110
+ super
111
+ end
112
+ end
113
+ end
114
+
115
+ # +Unbinder+ is used to take a dataset filter and return a modified version
116
+ # that unbinds already bound values and returns a dataset with bound value
117
+ # placeholders and a hash of bind values. You can then prepare the dataset
118
+ # and use the bound variables to execute it with the same values.
119
+ #
120
+ # This class only does a limited form of unbinding where the variable names
121
+ # and values can be associated unambiguously. The only cases it handles
122
+ # are <tt>SQL::ComplexExpression<tt> with an operator in +UNBIND_OPS+, a
123
+ # first argument that's an instance of a member of +UNBIND_KEY_CLASSES+, and
124
+ # a second argument that's an instance of a member of +UNBIND_VALUE_CLASSES+.
125
+ #
126
+ # So it can handle cases like:
127
+ #
128
+ # DB.filter(:a=>1).exclude(:b=>2).where{c > 3}
129
+ #
130
+ # But it cannot handle cases like:
131
+ #
132
+ # DB.filter(:a + 1 < 0)
133
+ class Unbinder < ASTTransformer
134
+ # The <tt>SQL::ComplexExpression<tt> operates that will be considered
135
+ # for transformation.
136
+ UNBIND_OPS = [:'=', :'!=', :<, :>, :<=, :>=]
137
+
138
+ # The key classes (first argument of the ComplexExpression) that will
139
+ # considered for transformation.
140
+ UNBIND_KEY_CLASSES = [Symbol, SQL::Identifier, SQL::QualifiedIdentifier]
141
+
142
+ # The value classes (second argument of the ComplexExpression) that
143
+ # will be considered for transformation.
144
+ UNBIND_VALUE_CLASSES = [Numeric, String, Date, Time]
145
+
146
+ # The hash of bind variables that were extracted from the dataset filter.
147
+ attr_reader :binds
148
+
149
+ # Intialize an empty +binds+ hash.
150
+ def initialize
151
+ @binds = {}
152
+ end
153
+
154
+ private
155
+
156
+ # Create a suitable bound variable key for the object, which should be
157
+ # an instance of one of the +UNBIND_KEY_CLASSES+.
158
+ def bind_key(obj)
159
+ case obj
160
+ when Symbol, String
161
+ obj
162
+ when SQL::Identifier
163
+ bind_key(obj.value)
164
+ when SQL::QualifiedIdentifier
165
+ :"#{bind_key(obj.table)}.#{bind_key(obj.column)}"
166
+ else
167
+ raise Error, "unhandled object in Sequel::Unbinder#bind_key: #{obj}"
168
+ end
169
+ end
170
+
171
+ # Handle <tt>SQL::ComplexExpression</tt> instances with suitable ops
172
+ # and arguments, substituting the value with a bound variable placeholder
173
+ # and assigning it an entry in the +binds+ hash with a matching key.
174
+ def v(o)
175
+ if o.is_a?(SQL::ComplexExpression) && UNBIND_OPS.include?(o.op)
176
+ l, r = o.args
177
+ l = l.value if l.is_a?(Sequel::SQL::Wrapper)
178
+ r = r.value if r.is_a?(Sequel::SQL::Wrapper)
179
+ if UNBIND_KEY_CLASSES.any?{|c| l.is_a?(c)} && UNBIND_VALUE_CLASSES.any?{|c| r.is_a?(c)} && !r.is_a?(LiteralString)
180
+ key = bind_key(l)
181
+ if (old = binds[key]) && old != r
182
+ raise UnbindDuplicate, "two different values for #{key.inspect}: #{[r, old].inspect}"
183
+ end
184
+ binds[key] = r
185
+ SQL::ComplexExpression.new(o.op, l, :"$#{key}")
186
+ else
187
+ super
188
+ end
189
+ else
190
+ super
191
+ end
192
+ end
193
+ end
194
+ end
@@ -26,6 +26,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
26
26
  def add_servers(servers)
27
27
  servers.each{|s| @servers[s] = s}
28
28
  end
29
+
30
+ # Yield all of the currently established connections
31
+ def all_connections
32
+ @conns.values.each{|c| yield c}
33
+ end
29
34
 
30
35
  # The connection for the given server.
31
36
  def conn(server=:default)
@@ -45,7 +50,7 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
45
50
  # This method simulates the ConnectionPool#hold API.
46
51
  def hold(server=:default)
47
52
  begin
48
- server = @servers[server]
53
+ server = pick_server(server)
49
54
  yield(@conns[server] ||= make_new(server))
50
55
  rescue Sequel::DatabaseDisconnectError
51
56
  disconnect_server(server, &@disconnection_proc)
@@ -83,6 +88,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
83
88
  block.call(conn) if block
84
89
  end
85
90
  end
91
+
92
+ # If the server given is in the hash, return it, otherwise, return the default server.
93
+ def pick_server(server)
94
+ @servers[server]
95
+ end
86
96
 
87
97
  CONNECTION_POOL_MAP[[true, true]] = self
88
98
  end