sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -26,6 +26,14 @@ module Sequel
26
26
  extend Metaprogramming
27
27
  include Metaprogramming
28
28
  include Enumerable
29
+ include SQL::AliasMethods
30
+ include SQL::BooleanMethods
31
+ include SQL::CastMethods
32
+ include SQL::ComplexExpressionMethods
33
+ include SQL::InequalityMethods
34
+ include SQL::NumericMethods
35
+ include SQL::OrderMethods
36
+ include SQL::StringMethods
29
37
  end
30
38
 
31
39
  require(%w"query actions features graph prepared_statements misc mutation sql", 'dataset')
@@ -44,6 +44,10 @@ module Sequel
44
44
  # and won't reraise it.
45
45
  class Rollback < Error ; end
46
46
 
47
+ # Exception that occurs when unbinding a dataset that has multiple different values
48
+ # for a given variable.
49
+ class UnbindDuplicate < Error; end
50
+
47
51
  class Error
48
52
  AdapterNotFound = Sequel::AdapterNotFound
49
53
  InvalidOperation = Sequel::InvalidOperation
@@ -0,0 +1,79 @@
1
+ # The pretty_table extension adds Sequel::Dataset#print and the
2
+ # Sequel::PrettyTable class for creating nice-looking plain-text
3
+ # tables.
4
+
5
+ module Sequel
6
+ module PrettyTable
7
+ # Prints nice-looking plain-text tables via puts
8
+ #
9
+ # +--+-------+
10
+ # |id|name |
11
+ # |--+-------|
12
+ # |1 |fasdfas|
13
+ # |2 |test |
14
+ # +--+-------+
15
+ def self.print(records, columns=nil)
16
+ puts string(records, columns)
17
+ end
18
+
19
+ # Return the string that #print will print via puts.
20
+ def self.string(records, columns = nil) # records is an array of hashes
21
+ columns ||= records.first.keys.sort_by{|x|x.to_s}
22
+ sizes = column_sizes(records, columns)
23
+ sep_line = separator_line(columns, sizes)
24
+
25
+ array = [sep_line, header_line(columns, sizes), sep_line]
26
+ records.each {|r| array << data_line(columns, sizes, r)}
27
+ array << sep_line
28
+ array.join("\n")
29
+ end
30
+
31
+ ### Private Module Methods ###
32
+
33
+ # Hash of the maximum size of the value for each column
34
+ def self.column_sizes(records, columns) # :nodoc:
35
+ sizes = Hash.new {0}
36
+ columns.each do |c|
37
+ s = c.to_s.size
38
+ sizes[c.to_sym] = s if s > sizes[c.to_sym]
39
+ end
40
+ records.each do |r|
41
+ columns.each do |c|
42
+ s = r[c].to_s.size
43
+ sizes[c.to_sym] = s if s > sizes[c.to_sym]
44
+ end
45
+ end
46
+ sizes
47
+ end
48
+
49
+ # String for each data line
50
+ def self.data_line(columns, sizes, record) # :nodoc:
51
+ '|' << columns.map {|c| format_cell(sizes[c], record[c])}.join('|') << '|'
52
+ end
53
+
54
+ # Format the value so it takes up exactly size characters
55
+ def self.format_cell(size, v) # :nodoc:
56
+ case v
57
+ when Bignum, Fixnum
58
+ "%#{size}d" % v
59
+ when Float
60
+ "%#{size}g" % v
61
+ else
62
+ "%-#{size}s" % v.to_s
63
+ end
64
+ end
65
+
66
+ # String for header line
67
+ def self.header_line(columns, sizes) # :nodoc:
68
+ '|' << columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') << '|'
69
+ end
70
+
71
+ # String for separtor line
72
+ def self.separator_line(columns, sizes) # :nodoc:
73
+ '+' << columns.map {|c| '-' * sizes[c]}.join('+') << '+'
74
+ end
75
+
76
+ private_class_method :column_sizes, :data_line, :format_cell, :header_line, :separator_line
77
+ end
78
+ end
79
+
@@ -0,0 +1,108 @@
1
+ # The arbitrary_servers extension allows you to connect to arbitrary
2
+ # servers/shards that were not defined when you created the database.
3
+ # To use it, you first extend the Database's connection pool with the
4
+ # Sequel::ArbitraryServers module:
5
+ #
6
+ # Sequel.extension :arbitrary_servers
7
+ # DB.pool.extend Sequel::ArbitraryServers
8
+ #
9
+ # Then you can pass arbitrary connection options for the server/shard
10
+ # to use as a hash:
11
+ #
12
+ # DB[:table].server(:host=>'...', :database=>'...').all
13
+ #
14
+ # Because Sequel can never be sure that the connection will be reused,
15
+ # arbitrary connections are disconnected as soon as the outermost block
16
+ # that uses them exits. So this example uses the same connection:
17
+ #
18
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c|
19
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c2|
20
+ # # c == c2
21
+ # end
22
+ # end
23
+ #
24
+ # But this example does not:
25
+ #
26
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c|
27
+ # end
28
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c2|
29
+ # # c != c2
30
+ # end
31
+ #
32
+ # You can use this extension in conjunction with the server_block
33
+ # extension:
34
+ #
35
+ # DB.with_server(:host=>'...', :database=>'...') do
36
+ # DB.synchronize do
37
+ # # All of these use the host/database given to with_server
38
+ # DB[:table].insert(...)
39
+ # DB[:table].update(...)
40
+ # DB.tables
41
+ # DB[:table].all
42
+ # end
43
+ # end
44
+ #
45
+ # Anyone using this extension in conjunction with the server_block
46
+ # extension may want to do the following to so that you don't need
47
+ # to call synchronize separately:
48
+ #
49
+ # def DB.with_server(*)
50
+ # super{synchronize{yield}}
51
+ # end
52
+ #
53
+ # Note that this extension only works with the sharded threaded connection
54
+ # pool. If you are using the sharded single connection pool, you need
55
+ # to switch to the sharded threaded connection pool before using this
56
+ # extension.
57
+
58
+ module Sequel
59
+ module ArbitraryServers
60
+ private
61
+
62
+ # If server is a hash, create a new connection for
63
+ # it, and cache it first by thread and then server.
64
+ def acquire(thread, server)
65
+ if server.is_a?(Hash)
66
+ sync{@allocated[thread] ||= {}}[server] = make_new(server)
67
+ else
68
+ super
69
+ end
70
+ end
71
+
72
+ # If server is a hash, the entry for it probably doesn't
73
+ # exist in the @allocated hash, so check for existence to
74
+ # avoid calling nil.[]
75
+ def owned_connection(thread, server)
76
+ if server.is_a?(Hash)
77
+ if a = sync{@allocated[thread]}
78
+ a[server]
79
+ end
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ # If server is a hash, return it directly.
86
+ def pick_server(server)
87
+ if server.is_a?(Hash)
88
+ server
89
+ else
90
+ super
91
+ end
92
+ end
93
+
94
+ # If server is a hash, delete the thread from the allocated
95
+ # connections for that server. Additionally, if this was the last thread
96
+ # using that server, delete the server from the @allocated hash.
97
+ def release(thread, conn, server)
98
+ if server.is_a?(Hash)
99
+ a = @allocated[thread]
100
+ a.delete(server)
101
+ @allocated.delete(thread) if a.empty?
102
+ @disconnection_proc.call(conn) if @disconnection_proc
103
+ else
104
+ super
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,61 @@
1
+ # The columns_introspection extension attempts to introspect the
2
+ # selected columns for a dataset before issuing a query. If it
3
+ # thinks it can guess correctly at the columns the query will use,
4
+ # it will return the columns without issuing a database query.
5
+ # This method is not fool-proof, it's possible that some databases
6
+ # will use column names that Sequel does not expect.
7
+ #
8
+ # To enable this for a single dataset, extend the dataset with
9
+ # Sequel::ColumnIntrospection. To enable this for all datasets, run:
10
+ #
11
+ # Sequel::Dataset.introspect_all_columns
12
+
13
+ module Sequel
14
+ module ColumnsIntrospection
15
+ # Attempt to guess the columns that will be returned
16
+ # if there are columns selected, in order to skip a database
17
+ # query to retrieve the columns. This should work with
18
+ # Symbols, SQL::Identifiers, SQL::QualifiedIdentifiers, and
19
+ # SQL::AliasedExpressions.
20
+ def columns
21
+ return @columns if @columns
22
+ return columns_without_introspection unless cols = opts[:select] and !cols.empty?
23
+ probable_columns = cols.map{|c| probable_column_name(c)}
24
+ if probable_columns.all?
25
+ @columns = probable_columns
26
+ else
27
+ columns_without_introspection
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # Return the probable name of the column, or nil if one
34
+ # cannot be determined.
35
+ def probable_column_name(c)
36
+ case c
37
+ when Symbol
38
+ _, c, a = split_symbol(c)
39
+ (a || c).to_sym
40
+ when SQL::Identifier
41
+ c.value.to_sym
42
+ when SQL::QualifiedIdentifier
43
+ col = c.column
44
+ col.is_a?(SQL::Identifier) ? col.value.to_sym : col.to_sym
45
+ when SQL::AliasedExpression
46
+ a = c.aliaz
47
+ a.is_a?(SQL::Identifier) ? a.value.to_sym : a.to_sym
48
+ end
49
+ end
50
+ end
51
+
52
+ class Dataset
53
+ alias columns_without_introspection columns
54
+
55
+ # Enable column introspection for every dataset.
56
+ def self.introspect_all_columns
57
+ include ColumnsIntrospection
58
+ remove_method(:columns) if instance_methods(false).map{|x| x.to_s}.include?('columns')
59
+ end
60
+ end
61
+ end
@@ -1,3 +1,13 @@
1
+ # These are extensions to core classes that Sequel enables by default.
2
+ # They make using Sequel's DSL easier by adding methods to Array,
3
+ # Hash, String, and Symbol to add methods that return Sequel
4
+ # expression objects.
5
+
6
+ # This extension loads the core extensions.
7
+ def Sequel.core_extensions?
8
+ true
9
+ end
10
+
1
11
  # Sequel extends +Array+ to add methods to implement the SQL DSL.
2
12
  # Most of these methods require that the array not be empty and that it
3
13
  # must consist solely of other arrays that have exactly two elements.
@@ -8,20 +18,7 @@ class Array
8
18
  # ~[[:a, true]] # SQL: a IS NOT TRUE
9
19
  # ~[[:a, 1], [:b, [2, 3]]] # SQL: a != 1 OR b NOT IN (2, 3)
10
20
  def ~
11
- sql_expr_if_all_two_pairs(:OR, true)
12
- end
13
-
14
- # +true+ if the array is not empty and all of its elements are
15
- # arrays of size 2, +false+ otherwise. This is used to determine if the array
16
- # could be a specifier of conditions, used similarly to a hash
17
- # but allowing for duplicate keys and a specific order.
18
- #
19
- # [].to_a.all_two_pairs? # => false
20
- # [:a].to_a.all_two_pairs? # => false
21
- # [[:b]].to_a.all_two_pairs? # => false
22
- # [[:a, 1]].to_a.all_two_pairs? # => true
23
- def all_two_pairs?
24
- !empty? && all?{|i| (Array === i) && (i.length == 2)}
21
+ Sequel.~(self)
25
22
  end
26
23
 
27
24
  # Return a <tt>Sequel::SQL::CaseExpression</tt> with this array as the conditions and the given
@@ -59,7 +56,7 @@ class Array
59
56
  # [[:a, true]].sql_expr # SQL: a IS TRUE
60
57
  # [[:a, 1], [:b, [2, 3]]].sql_expr # SQL: a = 1 AND b IN (2, 3)
61
58
  def sql_expr
62
- sql_expr_if_all_two_pairs
59
+ Sequel.expr(self)
63
60
  end
64
61
 
65
62
  # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching none
@@ -68,7 +65,7 @@ class Array
68
65
  # [[:a, true]].sql_negate # SQL: a IS NOT TRUE
69
66
  # [[:a, 1], [:b, [2, 3]]].sql_negate # SQL: a != 1 AND b NOT IN (2, 3)
70
67
  def sql_negate
71
- sql_expr_if_all_two_pairs(:AND, true)
68
+ Sequel.negate(self)
72
69
  end
73
70
 
74
71
  # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching any of the
@@ -77,10 +74,10 @@ class Array
77
74
  # [[:a, true]].sql_or # SQL: a IS TRUE
78
75
  # [[:a, 1], [:b, [2, 3]]].sql_or # SQL: a = 1 OR b IN (2, 3)
79
76
  def sql_or
80
- sql_expr_if_all_two_pairs(:OR)
77
+ Sequel.or(self)
81
78
  end
82
79
 
83
- # Return a <tt>Sequel::SQL::BooleanExpression</tt> representing an SQL string made up of the
80
+ # Return a <tt>Sequel::SQL::StringExpression</tt> representing an SQL string made up of the
84
81
  # concatenation of this array's elements. If an argument is passed
85
82
  # it is used in between each element of the array in the SQL
86
83
  # concatenation.
@@ -90,22 +87,7 @@ class Array
90
87
  # [:a, 'b'].sql_string_join # SQL: a || 'b'
91
88
  # ['a', :b].sql_string_join(' ') # SQL: 'a' || ' ' || b
92
89
  def sql_string_join(joiner=nil)
93
- if joiner
94
- args = zip([joiner]*length).flatten
95
- args.pop
96
- else
97
- args = self
98
- end
99
- args = args.collect{|a| [Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass].any?{|c| a.is_a?(c)} ? a : a.to_s}
100
- ::Sequel::SQL::StringExpression.new(:'||', *args)
101
- end
102
-
103
- private
104
-
105
- # Raise an error if this array is not made up all two element arrays, otherwise create a <tt>Sequel::SQL::BooleanExpression</tt> from this array.
106
- def sql_expr_if_all_two_pairs(*args)
107
- raise(Sequel::Error, 'Not all elements of the array are arrays of size 2, so it cannot be converted to an SQL expression') unless all_two_pairs?
108
- ::Sequel::SQL::BooleanExpression.from_value_pairs(self, *args)
90
+ Sequel.join(self, joiner)
109
91
  end
110
92
  end
111
93
 
@@ -2,6 +2,7 @@
2
2
  # the user to easily group schema changes and migrate the database
3
3
  # to a newer version or revert to a previous version.
4
4
 
5
+ #
5
6
  module Sequel
6
7
  # Sequel's older migration class, available for backward compatibility.
7
8
  # Uses subclasses with up and down instance methods for each migration:
@@ -21,7 +22,7 @@ module Sequel
21
22
  #
22
23
  # Part of the +migration+ extension.
23
24
  class Migration
24
- # Creates a new instance of the migration and sets the @db attribute.
25
+ # Set the database associated with this migration.
25
26
  def initialize(db)
26
27
  @db = db
27
28
  end
@@ -42,6 +43,11 @@ module Sequel
42
43
  def self.inherited(base)
43
44
  descendants << base
44
45
  end
46
+
47
+ # Don't allow transaction overriding in old migrations.
48
+ def self.use_transactions
49
+ nil
50
+ end
45
51
 
46
52
  # The default down action does nothing
47
53
  def down
@@ -68,6 +74,15 @@ module Sequel
68
74
  # Proc used for the up action
69
75
  attr_accessor :up
70
76
 
77
+ # Whether to use transactions for this migration, default depends on the
78
+ # database.
79
+ attr_accessor :use_transactions
80
+
81
+ # Don't set transaction use by default.
82
+ def initialize
83
+ @use_transactions = nil
84
+ end
85
+
71
86
  # Apply the appropriate block on the +Database+
72
87
  # instance using instance_eval.
73
88
  def apply(db, direction)
@@ -99,6 +114,16 @@ module Sequel
99
114
  migration.down = block
100
115
  end
101
116
 
117
+ # Disable the use of transactions for the related migration
118
+ def no_transaction
119
+ migration.use_transactions = false
120
+ end
121
+
122
+ # Enable the use of transactions for the related migration
123
+ def transaction
124
+ migration.use_transactions = true
125
+ end
126
+
102
127
  # Defines the migration's up action.
103
128
  def up(&block)
104
129
  migration.up = block
@@ -165,6 +190,10 @@ module Sequel
165
190
  @actions << [:alter_table, table, MigrationAlterTableReverser.new.reverse(&block)]
166
191
  end
167
192
 
193
+ def create_join_table(*args)
194
+ @actions << [:drop_join_table, *args]
195
+ end
196
+
168
197
  def create_table(*args)
169
198
  @actions << [:drop_table, args.first]
170
199
  end
@@ -302,7 +331,7 @@ module Sequel
302
331
  #
303
332
  # Part of the +migration+ extension.
304
333
  class Migrator
305
- MIGRATION_FILE_PATTERN = /\A\d+_.+\.rb\z/i.freeze
334
+ MIGRATION_FILE_PATTERN = /\A(\d+)_.+\.rb\z/i.freeze
306
335
  MIGRATION_SPLITTER = '_'.freeze
307
336
  MINIMUM_TIMESTAMP = 20000101
308
337
 
@@ -311,11 +340,28 @@ module Sequel
311
340
  class Error < Sequel::Error
312
341
  end
313
342
 
343
+ # Exception class raised when Migrator.check_current signals that it is
344
+ # not current.
345
+ class NotCurrentError < Error
346
+ end
347
+
314
348
  # Wrapper for +run+, maintaining backwards API compatibility
315
349
  def self.apply(db, directory, target = nil, current = nil)
316
350
  run(db, directory, :target => target, :current => current)
317
351
  end
318
352
 
353
+ # Raise a NotCurrentError unless the migrator is current, takes the same
354
+ # arguments as #run.
355
+ def self.check_current(*args)
356
+ raise(NotCurrentError, 'migrator is not current') unless is_current?(*args)
357
+ end
358
+
359
+ # Return whether the migrator is current (i.e. it does not need to make
360
+ # any changes). Takes the same arguments as #run.
361
+ def self.is_current?(db, directory, opts={})
362
+ migrator_class(directory).new(db, directory, opts).is_current?
363
+ end
364
+
319
365
  # Migrates the supplied database using the migration files in the the specified directory. Options:
320
366
  # * :column :: The column in the :table argument storing the migration version (default: :version).
321
367
  # * :current :: The current version of the database. If not given, it is retrieved from the database
@@ -336,11 +382,15 @@ module Sequel
336
382
  # if the version number appears to be a unix time integer for a year
337
383
  # after 2005, otherwise uses the IntegerMigrator.
338
384
  def self.migrator_class(directory)
339
- Dir.new(directory).each do |file|
340
- next unless MIGRATION_FILE_PATTERN.match(file)
341
- return TimestampMigrator if file.split(MIGRATION_SPLITTER, 2).first.to_i > MINIMUM_TIMESTAMP
385
+ if self.equal?(Migrator)
386
+ Dir.new(directory).each do |file|
387
+ next unless MIGRATION_FILE_PATTERN.match(file)
388
+ return TimestampMigrator if file.split(MIGRATION_SPLITTER, 2).first.to_i > MINIMUM_TIMESTAMP
389
+ end
390
+ IntegerMigrator
391
+ else
392
+ self
342
393
  end
343
- IntegerMigrator
344
394
  end
345
395
  private_class_method :migrator_class
346
396
 
@@ -375,13 +425,35 @@ module Sequel
375
425
  @db = db
376
426
  @directory = directory
377
427
  @files = get_migration_files
378
- @table = opts[:table] || self.class.const_get(:DEFAULT_SCHEMA_TABLE)
428
+ schema, table = @db.send(:schema_and_table, opts[:table] || self.class.const_get(:DEFAULT_SCHEMA_TABLE))
429
+ @table = schema ? Sequel::SQL::QualifiedIdentifier.new(schema, table) : table
379
430
  @column = opts[:column] || self.class.const_get(:DEFAULT_SCHEMA_COLUMN)
380
431
  @ds = schema_dataset
432
+ @use_transactions = opts[:use_transactions]
381
433
  end
382
434
 
383
435
  private
384
436
 
437
+ # If transactions should be used for the migration, yield to the block
438
+ # inside a transaction. Otherwise, just yield to the block.
439
+ def checked_transaction(migration, &block)
440
+ use_trans = if @use_transactions.nil?
441
+ if migration.use_transactions.nil?
442
+ @db.supports_transactional_ddl?
443
+ else
444
+ migration.use_transactions
445
+ end
446
+ else
447
+ @use_transactions
448
+ end
449
+
450
+ if use_trans
451
+ db.transaction(&block)
452
+ else
453
+ yield
454
+ end
455
+ end
456
+
385
457
  # Remove all migration classes. Done by the migrator to ensure that
386
458
  # the correct migration classes are picked up.
387
459
  def remove_migration_classes
@@ -423,19 +495,24 @@ module Sequel
423
495
  @current = opts[:current] || current_migration_version
424
496
 
425
497
  raise(Error, "No current version available") unless current
426
- raise(Error, "No target version available") unless target
498
+ raise(Error, "No target version available, probably because no migration files found or filenames don't follow the migration filename convention") unless target
427
499
 
428
500
  @direction = current < target ? :up : :down
429
501
  @migrations = get_migrations
430
502
  end
431
503
 
504
+ # The integer migrator is current if the current version is the same as the target version.
505
+ def is_current?
506
+ current_migration_version == target
507
+ end
508
+
432
509
  # Apply all migrations on the database
433
510
  def run
434
511
  migrations.zip(version_numbers).each do |m, v|
435
512
  t = Time.now
436
513
  lv = up? ? v : v + 1
437
514
  db.log_info("Begin applying migration version #{lv}, direction: #{direction}")
438
- db.transaction do
515
+ checked_transaction(m) do
439
516
  m.apply(db, direction)
440
517
  set_migration_version(v)
441
518
  end
@@ -459,6 +536,9 @@ module Sequel
459
536
  Dir.new(directory).each do |file|
460
537
  next unless MIGRATION_FILE_PATTERN.match(file)
461
538
  version = migration_version_from_file(file)
539
+ if version >= 20000101
540
+ raise Migrator::Error, "Migration number too large, must use TimestampMigrator: #{file}"
541
+ end
462
542
  raise(Error, "Duplicate migration version: #{version}") if files[version]
463
543
  files[version] = File.join(directory, file)
464
544
  end
@@ -543,12 +623,18 @@ module Sequel
543
623
  @migration_tuples = get_migration_tuples
544
624
  end
545
625
 
626
+ # The timestamp migrator is current if there are no migrations to apply
627
+ # in either direction.
628
+ def is_current?
629
+ migration_tuples.empty?
630
+ end
631
+
546
632
  # Apply all migration tuples on the database
547
633
  def run
548
634
  migration_tuples.each do |m, f, direction|
549
635
  t = Time.now
550
636
  db.log_info("Begin applying migration #{f}, direction: #{direction}")
551
- db.transaction do
637
+ checked_transaction(m) do
552
638
  m.apply(db, direction)
553
639
  fi = f.downcase
554
640
  direction == :up ? ds.insert(column=>fi) : ds.filter(column=>fi).delete
@@ -588,7 +674,7 @@ module Sequel
588
674
  next unless MIGRATION_FILE_PATTERN.match(file)
589
675
  files << File.join(directory, file)
590
676
  end
591
- files.sort
677
+ files.sort_by{|f| MIGRATION_FILE_PATTERN.match(File.basename(f))[1].to_i}
592
678
  end
593
679
 
594
680
  # Returns tuples of migration, filename, and direction
@@ -21,6 +21,11 @@
21
21
  # Then, before data is stored in the database, it is converted to New
22
22
  # York time. When data is retrieved from the database, it is
23
23
  # converted to Los Angeles time.
24
+ #
25
+ # Note that typecasting from the database timezone to the application
26
+ # timezone when fetching rows is dependent on the database adapter,
27
+ # and only works on adapters where Sequel itself does the conversion.
28
+ # It should work on mysql, postgres, sqlite, ibmdb, and jdbc.
24
29
 
25
30
  require 'tzinfo'
26
31
 
@@ -0,0 +1,90 @@
1
+ # The null_dataset extension adds the Dataset#nullify method, which
2
+ # returns a cloned dataset that will never issue a query to the
3
+ # database. It implements the null object pattern for datasets.
4
+ #
5
+ # The most common usage is probably in a method that must return
6
+ # a dataset, where the method knows the dataset shouldn't return
7
+ # anything. With standard Sequel, you'd probably just add a
8
+ # WHERE condition that is always false, but that still results
9
+ # in a query being sent to the database, and can be overridden
10
+ # using #unfiltered, the OR operator, or a UNION.
11
+ #
12
+ # Usage:
13
+ #
14
+ # ds = DB[:items].nullify.where(:a=>:b).select(:c)
15
+ # ds.sql # => "SELECT c FROM items WHERE (a = b)"
16
+ # ds.all # => [] # no query sent to the database
17
+ #
18
+ # Note that there is one case where a null dataset will sent
19
+ # a query to the database. If you call #columns on a nulled
20
+ # dataset and the dataset doesn't have an already cached
21
+ # version of the columns, it will create a new dataset with
22
+ # the same options to get the columns.
23
+
24
+ module Sequel
25
+ class Dataset
26
+ module NullDataset
27
+ # Create a new dataset from the dataset (which won't
28
+ # be nulled) to get the columns if they aren't already cached.
29
+ def columns
30
+ @columns ||= db.dataset(@opts).columns
31
+ end
32
+
33
+ # Return 0 without sending a database query.
34
+ def delete
35
+ 0
36
+ end
37
+
38
+ # Return self without sending a database query, never yielding.
39
+ def each
40
+ self
41
+ end
42
+
43
+ # Return nil without sending a database query, never yielding.
44
+ def fetch_rows(sql)
45
+ nil
46
+ end
47
+
48
+ # Return nil without sending a database query.
49
+ def insert(*)
50
+ nil
51
+ end
52
+
53
+ # Return nil without sending a database query.
54
+ def truncate
55
+ nil
56
+ end
57
+
58
+ # Return 0 without sending a database query.
59
+ def update(v={})
60
+ 0
61
+ end
62
+
63
+ protected
64
+
65
+ # Return nil without sending a database query.
66
+ def _import(columns, values, opts)
67
+ nil
68
+ end
69
+
70
+ private
71
+
72
+ # Just in case these are called directly by some internal code,
73
+ # make them noops. There's nothing we can do if the db
74
+ # is accessed directly to make a change, though.
75
+ (%w'_ddl _dui _insert' << '').each do |m|
76
+ class_eval("private; def execute#{m}(sql, opts={}) end", __FILE__, __LINE__)
77
+ end
78
+ end
79
+
80
+ # Return a cloned nullified dataset.
81
+ def nullify
82
+ clone.nullify!
83
+ end
84
+
85
+ # Nullify the current dataset
86
+ def nullify!
87
+ extend NullDataset
88
+ end
89
+ end
90
+ end