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
@@ -68,6 +68,16 @@ module Sequel
68
68
 
69
69
  private
70
70
 
71
+ # If there are any instance filters, make sure not to use the
72
+ # instance delete optimization.
73
+ def _delete_without_checking
74
+ if @instance_filters && !@instance_filters.empty?
75
+ _delete_dataset.delete
76
+ else
77
+ super
78
+ end
79
+ end
80
+
71
81
  # Lazily initialize the instance filter array.
72
82
  def instance_filters
73
83
  @instance_filters ||= []
@@ -29,7 +29,7 @@ module Sequel
29
29
  BEFORE_HOOKS = Sequel::Model::BEFORE_HOOKS
30
30
  AFTER_HOOKS = Sequel::Model::AFTER_HOOKS - [:after_initialize]
31
31
  HOOKS = BEFORE_HOOKS + AFTER_HOOKS
32
- HOOKS.each{|h| class_eval("def #{h}_hook(&block); add_instance_hook(:#{h}, &block) end", __FILE__, __LINE__)}
32
+ HOOKS.each{|h| class_eval("def #{h}_hook(&block); add_instance_hook(:#{h}, &block); self end", __FILE__, __LINE__)}
33
33
 
34
34
  BEFORE_HOOKS.each{|h| class_eval("def #{h}; run_before_instance_hooks(:#{h}) == false ? false : super end", __FILE__, __LINE__)}
35
35
  AFTER_HOOKS.each{|h| class_eval(<<-END, __FILE__, __LINE__ + 1)}
@@ -56,6 +56,11 @@ module Sequel
56
56
  # Album.to_json
57
57
  # Album.filter(:artist_id=>1).to_json(:include=>:tags)
58
58
  #
59
+ # If you have an existing array of model instances you want to convert to
60
+ # JSON, you can call the class to_json method with the :array option:
61
+ #
62
+ # Album.to_json(:array=>[Album[1], Album[2]])
63
+ #
59
64
  # Usage:
60
65
  #
61
66
  # # Add JSON output capability to all model subclass instances (called before loading subclasses)
@@ -134,8 +139,13 @@ module Sequel
134
139
  module InstanceMethods
135
140
  # Parse the provided JSON, which should return a hash,
136
141
  # and call +set+ with that hash.
137
- def from_json(json)
138
- set(JSON.parse(json))
142
+ def from_json(json, opts={})
143
+ h = JSON.parse(json)
144
+ if fields = opts[:fields]
145
+ set_fields(h, fields, opts)
146
+ else
147
+ set(h)
148
+ end
139
149
  end
140
150
 
141
151
  # Return a string in JSON format. Accepts the following
@@ -149,7 +159,7 @@ module Sequel
149
159
  # to include in the JSON output. Using a nested
150
160
  # hash, you can pass options to associations
151
161
  # to affect the JSON used for associated objects.
152
- # :naked :: Not to add the JSON.create_id key to the JSON
162
+ # :naked :: Not to add the JSON.create_id (json_class) key to the JSON
153
163
  # output hash, so when the JSON is parsed, it
154
164
  # will yield a hash instead of a model object.
155
165
  # :only :: Symbol or Array of Symbols of columns to only
@@ -195,7 +205,21 @@ module Sequel
195
205
  module DatasetMethods
196
206
  # Return a JSON string representing an array of all objects in
197
207
  # this dataset. Takes the same options as the the instance
198
- # method, and passes them to every instance.
208
+ # method, and passes them to every instance. Additionally,
209
+ # respects the following options:
210
+ #
211
+ # :array :: An array of instances. If this is not provided,
212
+ # calls #all on the receiver to get the array.
213
+ # :root :: If set to :collection, only wraps the collection
214
+ # in a root object. If set to :instance, only wraps
215
+ # the instances in a root object. If set to :both,
216
+ # wraps both the collection and instances in a root
217
+ # object. Unfortunately, for backwards compatibility,
218
+ # if this option is true and doesn't match one of those
219
+ # symbols, it defaults to both. That may change in a
220
+ # future version, so for forwards compatibility, you
221
+ # should pick a specific symbol for your desired
222
+ # behavior.
199
223
  def to_json(*a)
200
224
  if opts = a.first.is_a?(Hash)
201
225
  opts = model.json_serializer_opts.merge(a.first)
@@ -203,8 +227,36 @@ module Sequel
203
227
  else
204
228
  opts = model.json_serializer_opts
205
229
  end
206
- res = row_proc ? all.map{|obj| Literal.new(obj.to_json(opts))} : all
207
- opts[:root] ? {model.send(:pluralize, model.send(:underscore, model.to_s)) => res}.to_json(*a) : res.to_json(*a)
230
+
231
+ collection_root = case opts[:root]
232
+ when nil, false, :instance
233
+ false
234
+ when :collection
235
+ opts = opts.dup
236
+ opts.delete(:root)
237
+ opts[:naked] = true unless opts.has_key?(:naked)
238
+ true
239
+ else
240
+ true
241
+ end
242
+
243
+ res = if row_proc
244
+ array = if opts[:array]
245
+ opts = opts.dup
246
+ opts.delete(:array)
247
+ else
248
+ all
249
+ end
250
+ array.map{|obj| Literal.new(obj.to_json(opts))}
251
+ else
252
+ all
253
+ end
254
+
255
+ if collection_root
256
+ {model.send(:pluralize, model.send(:underscore, model.to_s)) => res}.to_json(*a)
257
+ else
258
+ res.to_json(*a)
259
+ end
208
260
  end
209
261
  end
210
262
  end
@@ -39,9 +39,11 @@ module Sequel
39
39
  # Item.plugin :list, :scope=>:user_id
40
40
  #
41
41
  # Note that using this plugin modifies the order of the model's dataset to
42
- # sort by the position and scope fields.
42
+ # sort by the position and scope fields. Also note that this plugin is subject to
43
+ # race conditions, and is not safe when concurrent modifications are made
44
+ # to the same list.
43
45
  #
44
- # Also note that unlike ruby arrays, the list plugin assumes that the
46
+ # Additionally, note that unlike ruby arrays, the list plugin assumes that the
45
47
  # first entry in the list has position 1, not position 0.
46
48
  #
47
49
  # Copyright (c) 2007-2010 Sharon Rosner, Wayne E. Seguin, Aman Gupta, Adrian Madrid, Jeremy Evans
@@ -90,6 +92,15 @@ module Sequel
90
92
  list_dataset.first(position_field => p)
91
93
  end
92
94
 
95
+ # Set the value of the position_field to the maximum value plus 1 unless the
96
+ # position field already has a value.
97
+ def before_create
98
+ unless send(position_field)
99
+ send("#{position_field}=", list_dataset.max(position_field).to_i+1)
100
+ end
101
+ super
102
+ end
103
+
93
104
  # Find the last position in the list containing this instance.
94
105
  def last_position
95
106
  list_dataset.max(position_field).to_i
@@ -47,20 +47,18 @@ module Sequel
47
47
  class ManyThroughManyAssociationReflection < Sequel::Model::Associations::ManyToManyAssociationReflection
48
48
  Sequel::Model::Associations::ASSOCIATION_TYPES[:many_through_many] = self
49
49
 
50
- # The table containing the column to use for the associated key when eagerly loading
51
- def associated_key_table
52
- self[:associated_key_table] = self[:final_reverse_edge][:alias]
53
- end
54
-
55
50
  # The default associated key alias(es) to use when eager loading
56
51
  # associations via eager.
57
52
  def default_associated_key_alias
58
53
  self[:uses_left_composite_keys] ? (0...self[:through].first[:left].length).map{|i| :"x_foreign_key_#{i}_x"} : :x_foreign_key_x
59
54
  end
60
55
 
61
- # The list of joins to use when eager graphing
62
- def edges
63
- self[:edges] || calculate_edges || self[:edges]
56
+ %w'associated_key_table predicate_key edges final_edge final_reverse_edge reverse_edges'.each do |meth|
57
+ class_eval(<<-END, __FILE__, __LINE__+1)
58
+ def #{meth}
59
+ cached_fetch(:#{meth}){calculate_edges[:#{meth}]}
60
+ end
61
+ END
64
62
  end
65
63
 
66
64
  # Many through many associations don't have a reciprocal
@@ -68,11 +66,6 @@ module Sequel
68
66
  nil
69
67
  end
70
68
 
71
- # The list of joins to use when lazy loading or eager loading
72
- def reverse_edges
73
- self[:reverse_edges] || calculate_edges || self[:reverse_edges]
74
- end
75
-
76
69
  private
77
70
 
78
71
  # Make sure to use unique table aliases when lazy loading or eager loading
@@ -94,7 +87,7 @@ module Sequel
94
87
 
95
88
  # Transform the :through option into a list of edges and reverse edges to use to join tables when loading the association.
96
89
  def calculate_edges
97
- es = [{:left_table=>self[:model].table_name, :left_key=>self[:left_primary_key]}]
90
+ es = [{:left_table=>self[:model].table_name, :left_key=>self[:left_primary_key_column]}]
98
91
  self[:through].each do |t|
99
92
  es.last.merge!(:right_key=>t[:left], :right_table=>t[:table], :join_type=>t[:join_type]||self[:graph_join_type], :conditions=>(t[:conditions]||[]).to_a, :block=>t[:block])
100
93
  es.last[:only_conditions] = t[:only_conditions] if t.include?(:only_conditions)
@@ -109,37 +102,38 @@ module Sequel
109
102
  reverse_edges = es.reverse.map{|e| {:table=>e[:left_table], :left=>e[:left_key], :right=>e[:right_key]}}
110
103
  reverse_edges.pop
111
104
  calculate_reverse_edge_aliases(reverse_edges)
112
- self[:final_edge] = edges.pop
113
- self[:final_reverse_edge] = reverse_edges.pop
114
- self[:edges] = edges
115
- self[:reverse_edges] = reverse_edges
116
- nil
105
+ final_reverse_edge = reverse_edges.pop
106
+ final_reverse_alias = final_reverse_edge[:alias]
107
+
108
+ h = {:final_edge=>edges.pop,
109
+ :final_reverse_edge=>final_reverse_edge,
110
+ :edges=>edges,
111
+ :reverse_edges=>reverse_edges,
112
+ :predicate_key=>qualify(final_reverse_alias, edges.first[:right]),
113
+ :associated_key_table=>final_reverse_edge[:alias],
114
+ }
115
+ h.each{|k, v| cached_set(k, v)}
116
+ h
117
117
  end
118
118
  end
119
+
119
120
  module ClassMethods
120
121
  # Create a many_through_many association. Arguments:
121
122
  # * name - Same as associate, the name of the association.
122
123
  # * through - The tables and keys to join between the current table and the associated table.
123
124
  # Must be an array, with elements that are either 3 element arrays, or hashes with keys :table, :left, and :right.
124
125
  # The required entries in the array/hash are:
125
- # * :table (first array element) - The name of the table to join.
126
- # * :left (middle array element) - The key joining the table to the previous table. Can use an
127
- # array of symbols for a composite key association.
128
- # * :right (last array element) - The key joining the table to the next table. Can use an
129
- # array of symbols for a composite key association.
126
+ # :table (first array element) :: The name of the table to join.
127
+ # :left (middle array element) :: The key joining the table to the previous table. Can use an
128
+ # array of symbols for a composite key association.
129
+ # :right (last array element) :: The key joining the table to the next table. Can use an
130
+ # array of symbols for a composite key association.
130
131
  # If a hash is provided, the following keys are respected when using eager_graph:
131
- # * :block - A proc to use as the block argument to join.
132
- # * :conditions - Extra conditions to add to the JOIN ON clause. Must be a hash or array of two pairs.
133
- # * :join_type - The join type to use for the join, defaults to :left_outer.
134
- # * :only_conditions - Conditions to use for the join instead of the ones specified by the keys.
135
- # * opts - The options for the associaion. Takes the same options as associate, and supports these additional options:
136
- # * :left_primary_key - column in current table that the first :left option in
137
- # through points to, as a symbol. Defaults to primary key of current table. Can use an
138
- # array of symbols for a composite key association.
139
- # * :right_primary_key - column in associated table that the final :right option in
140
- # through points to, as a symbol. Defaults to primary key of the associated table. Can use an
141
- # array of symbols for a composite key association.
142
- # * :uniq - Adds a after_load callback that makes the array of objects unique.
132
+ # :block :: A proc to use as the block argument to join.
133
+ # :conditions :: Extra conditions to add to the JOIN ON clause. Must be a hash or array of two pairs.
134
+ # :join_type :: The join type to use for the join, defaults to :left_outer.
135
+ # :only_conditions :: Conditions to use for the join instead of the ones specified by the keys.
136
+ # * opts - The options for the associaion. Takes the same options as many_to_many.
143
137
  def many_through_many(name, through, opts={}, &block)
144
138
  associate(:many_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block)
145
139
  end
@@ -170,24 +164,41 @@ module Sequel
170
164
  uses_lcks = opts[:uses_left_composite_keys] = left_key.is_a?(Array)
171
165
  left_keys = Array(left_key)
172
166
  left_pk = (opts[:left_primary_key] ||= self.primary_key)
167
+ opts[:eager_loader_key] = left_pk unless opts.has_key?(:eager_loader_key)
173
168
  left_pks = opts[:left_primary_keys] = Array(left_pk)
169
+ lpkc = opts[:left_primary_key_column] ||= left_pk
170
+ lpkcs = opts[:left_primary_key_columns] ||= Array(lpkc)
174
171
  opts[:dataset] ||= lambda do
175
172
  ds = opts.associated_class
176
- opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
177
- ft = opts[:final_reverse_edge]
178
- ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + left_keys.zip(left_pks.map{|k| send(k)}), :table_alias=>ft[:alias])
173
+ opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias], :qualify=>:deep)}
174
+ ft = opts.final_reverse_edge
175
+ ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + opts.predicate_keys.zip(left_pks.map{|k| send(k)}), :table_alias=>ft[:alias], :qualify=>:deep)
179
176
  end
180
177
 
181
178
  left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
182
179
  opts[:eager_loader] ||= lambda do |eo|
183
- h = eo[:key_hash][left_pk]
184
- eo[:rows].each{|object| object.associations[name] = []}
180
+ h = eo[:id_map]
181
+ rows = eo[:rows]
182
+ rows.each{|object| object.associations[name] = []}
185
183
  ds = opts.associated_class
186
- opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
187
- ft = opts[:final_reverse_edge]
188
- conds = uses_lcks ? [[left_keys.map{|k| SQL::QualifiedIdentifier.new(ft[:table], k)}, h.keys]] : [[left_key, h.keys]]
189
- ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + conds, :table_alias=>ft[:alias])
190
- model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo).all do |assoc_record|
184
+ opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias], :qualify=>:deep)}
185
+ ft = opts.final_reverse_edge
186
+ ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + [[opts.predicate_key, h.keys]], :table_alias=>ft[:alias], :qualify=>:deep)
187
+ ds = model.eager_loading_dataset(opts, ds, nil, eo[:associations], eo)
188
+ case opts.eager_limit_strategy
189
+ when :window_function
190
+ delete_rn = true
191
+ rn = ds.row_number_column
192
+ ds = apply_window_function_eager_limit_strategy(ds, opts)
193
+ when :correlated_subquery
194
+ ds = apply_correlated_subquery_eager_limit_strategy(ds, opts) do |xds|
195
+ dsa = ds.send(:dataset_alias, 2)
196
+ opts.reverse_edges.each{|t| xds = xds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
197
+ xds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + left_keys.map{|k| [k, SQL::QualifiedIdentifier.new(ft[:table], k)]}, :table_alias=>dsa, :qualify=>:deep)
198
+ end
199
+ end
200
+ ds.all do |assoc_record|
201
+ assoc_record.values.delete(rn) if delete_rn
191
202
  hash_key = if uses_lcks
192
203
  left_key_alias.map{|k| assoc_record.values.delete(k)}
193
204
  else
@@ -196,6 +207,10 @@ module Sequel
196
207
  next unless objects = h[hash_key]
197
208
  objects.each{|object| object.associations[name].push(assoc_record)}
198
209
  end
210
+ if opts.eager_limit_strategy == :ruby
211
+ limit, offset = opts.limit_and_offset
212
+ rows.each{|o| o.associations[name] = o.associations[name].slice(offset||0, limit) || []}
213
+ end
199
214
  end
200
215
 
201
216
  join_type = opts[:graph_join_type]
@@ -204,19 +219,56 @@ module Sequel
204
219
  only_conditions = opts[:graph_only_conditions]
205
220
  use_only_conditions = opts.include?(:graph_only_conditions)
206
221
  conditions = opts[:graph_conditions]
207
- opts[:eager_grapher] ||= proc do |ds, assoc_alias, table_alias|
208
- iq = table_alias
222
+ opts[:eager_grapher] ||= proc do |eo|
223
+ ds = eo[:self]
224
+ iq = eo[:implicit_qualifier]
209
225
  opts.edges.each do |t|
210
- ds = ds.graph(t[:table], t.fetch(:only_conditions, (Array(t[:right]).zip(Array(t[:left])) + t[:conditions])), :select=>false, :table_alias=>ds.unused_table_alias(t[:table]), :join_type=>t[:join_type], :implicit_qualifier=>iq, &t[:block])
226
+ ds = ds.graph(t[:table], t.fetch(:only_conditions, (Array(t[:right]).zip(Array(t[:left])) + t[:conditions])), :select=>false, :table_alias=>ds.unused_table_alias(t[:table]), :join_type=>t[:join_type], :qualify=>:deep, :implicit_qualifier=>iq, &t[:block])
211
227
  iq = nil
212
228
  end
213
- fe = opts[:final_edge]
214
- ds.graph(opts.associated_class, use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>assoc_alias, :join_type=>join_type, &graph_block)
229
+ fe = opts.final_edge
230
+ ds.graph(opts.associated_class, use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :qualify=>:deep, :join_type=>join_type, &graph_block)
215
231
  end
216
232
 
217
233
  def_association_dataset_methods(opts)
218
234
  end
219
235
  end
236
+
237
+ module DatasetMethods
238
+ private
239
+
240
+ # Use a subquery to filter rows to those related to the given associated object
241
+ def many_through_many_association_filter_expression(op, ref, obj)
242
+ lpks = ref[:left_primary_key_columns]
243
+ lpks = lpks.first if lpks.length == 1
244
+ lpks = ref.qualify(model.table_name, lpks)
245
+ edges = ref.edges
246
+ first, rest = edges.first, edges[1..-1]
247
+ last = edges.last
248
+ ds = model.db[first[:table]].select(*Array(ref.qualify(first[:table], first[:right])))
249
+ rest.each{|e| ds = ds.join(e[:table], e.fetch(:only_conditions, (Array(e[:right]).zip(Array(e[:left])) + e[:conditions])), :table_alias=>ds.unused_table_alias(e[:table]), :qualify=>:deep, &e[:block])}
250
+ last_alias = if rest.empty?
251
+ first[:table]
252
+ else
253
+ last_join = ds.opts[:join].last
254
+ last_join.table_alias || last_join.table
255
+ end
256
+
257
+ meths = if obj.is_a?(Sequel::Dataset)
258
+ ref.qualify(obj.model.table_name, ref.right_primary_keys)
259
+ else
260
+ ref.right_primary_key_methods
261
+ end
262
+
263
+ exp = association_filter_key_expression(ref.qualify(last_alias, Array(ref.final_edge[:left])), meths, obj)
264
+ if exp == SQL::Constants::FALSE
265
+ association_filter_handle_inversion(op, exp, Array(lpks))
266
+ else
267
+ ds = ds.where(exp).exclude(SQL::BooleanExpression.from_value_pairs(ds.opts[:select].zip([]), :OR))
268
+ association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>ds), Array(lpks))
269
+ end
270
+ end
271
+ end
220
272
  end
221
273
  end
222
274
  end
@@ -0,0 +1,71 @@
1
+ module Sequel
2
+ module Plugins
3
+ # This is a fairly simple plugin that modifies the internal association loading logic
4
+ # for many_to_one associations to use a simple primary key lookup on the associated
5
+ # class, which is generally faster as it uses mostly static SQL. Additional, if the
6
+ # associated class is caching primary key lookups, you get the benefit of a cached
7
+ # lookup.
8
+ #
9
+ # This plugin is generally not as fast as the prepared_statements_associations plugin
10
+ # in the case where the model is not caching primary key lookups, however, it is
11
+ # probably significantly faster if the model is caching primary key lookups. If
12
+ # the prepared_statements_associations plugin has been loaded first, this
13
+ # plugin will only use the primary key lookup code if the associated model is
14
+ # caching primary key lookups.
15
+ #
16
+ # This plugin attempts to determine cases where the primary key lookup would have
17
+ # different results than the regular lookup, and use the regular lookup in that case,
18
+ # but it cannot handle all situations correctly, which is why it is not Sequel's
19
+ # default behavior.
20
+ #
21
+ # You can disable primary key lookups on a per association basis with this
22
+ # plugin using the :many_to_one_pk_lookup=>false association option.
23
+ #
24
+ # Usage:
25
+ #
26
+ # # Make all model subclass instances use primary key lookups for many_to_one
27
+ # # association loading
28
+ # Sequel::Model.plugin :many_to_one_pk_lookup
29
+ #
30
+ # # Do so for just the album class.
31
+ # Album.plugin :many_to_one_pk_lookup
32
+ module ManyToOnePkLookup
33
+ module InstanceMethods
34
+ private
35
+
36
+ # If the current association is a fairly simple many_to_one association, use
37
+ # a simple primary key lookup on the associated model, which can benefit from
38
+ # caching if the associated model is using caching.
39
+ def _load_associated_object(opts, dynamic_opts)
40
+ klass = opts.associated_class
41
+ cache_lookup = opts.send(:cached_fetch, :many_to_one_pk_lookup) do
42
+ opts[:type] == :many_to_one &&
43
+ opts[:key] &&
44
+ opts.primary_key == klass.primary_key
45
+ end
46
+ if cache_lookup &&
47
+ !dynamic_opts[:callback] &&
48
+ (o = klass.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| send(c)} : send(fk))))
49
+ o
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ # Deal with the situation where the prepared_statements_associations plugin is
56
+ # loaded first, by using a primary key lookup for many_to_one associations if
57
+ # the associated class is using caching, and using the default code otherwise.
58
+ # This is done because the prepared_statements_associations code is probably faster
59
+ # than the primary key lookup this plugin uses if the model is not caching lookups,
60
+ # but probably slower if the model is caching lookups.
61
+ def _load_associated_objects(opts, dynamic_opts={})
62
+ if opts.can_have_associated_objects?(self) && opts[:type] == :many_to_one && opts.associated_class.respond_to?(:cache_get_pk)
63
+ _load_associated_object(opts, dynamic_opts)
64
+ else
65
+ super
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end