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
@@ -8,32 +8,69 @@ describe Sequel::Model, "many_through_many" do
8
8
  plugin :many_through_many
9
9
  end
10
10
  class ::Tag < Sequel::Model
11
+ columns :id, :h1, :h2
11
12
  end
12
- MODEL_DB.reset
13
13
  @c1 = Artist
14
14
  @c2 = Tag
15
15
  @dataset = @c2.dataset
16
- def @dataset.fetch_rows(sql)
17
- @db << sql
18
- yield({:id=>1})
19
- end
16
+ @dataset._fetch = {:id=>1}
17
+ MODEL_DB.reset
20
18
  end
21
19
  after do
22
20
  Object.send(:remove_const, :Artist)
23
21
  Object.send(:remove_const, :Tag)
24
22
  end
25
23
 
24
+ it "should populate :key_hash and :id_map option correctly for custom eager loaders" do
25
+ khs = []
26
+ pr = proc{|h| khs << [h[:key_hash], h[:id_map]]}
27
+ @c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_loader=>pr
28
+ @c1.eager(:tags).all
29
+ khs.should == [[{:id=>{1=>[Artist.load(:x=>1, :id=>1)]}}, {1=>[Artist.load(:x=>1, :id=>1)]}]]
30
+
31
+ khs.clear
32
+ @c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :left_primary_key=>:id, :left_primary_key_column=>:i, :eager_loader=>pr
33
+ @c1.eager(:tags).all
34
+ khs.should == [[{:id=>{1=>[Artist.load(:x=>1, :id=>1)]}}, {1=>[Artist.load(:x=>1, :id=>1)]}]]
35
+ end
36
+
37
+ it "should support using a custom :left_primary_key option when eager loading many_to_many associations" do
38
+ @c1.send(:define_method, :id3){id*3}
39
+ @c1.dataset._fetch = {:id=>1}
40
+ @c2.dataset._fetch = {:id=>4, :x_foreign_key_x=>3}
41
+ @c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :left_primary_key=>:id3
42
+ a = @c1.eager(:tags).all
43
+ a.should == [@c1.load(:id => 1)]
44
+ MODEL_DB.sqls.should == ['SELECT * FROM artists', "SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (3)))"]
45
+ a.first.tags.should == [@c2.load(:id=>4)]
46
+ MODEL_DB.sqls.should == []
47
+ end
48
+
49
+ it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup" do
50
+ @c1.dataset._fetch = {:id=>1}
51
+ @c2.dataset._fetch = {:id=>4, :x_foreign_key_x=>1}
52
+ @c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_loading_predicate_key=>Sequel./(:albums_artists__artist_id, 3)
53
+ a = @c1.eager(:tags).all
54
+ a.should == [@c1.load(:id => 1)]
55
+ MODEL_DB.sqls.should == ['SELECT * FROM artists', "SELECT tags.*, (albums_artists.artist_id / 3) AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND ((albums_artists.artist_id / 3) IN (1)))"]
56
+ a.first.tags.should == [@c2.load(:id=>4)]
57
+ end
58
+
26
59
  it "should default to associating to other models in the same scope" do
27
- class ::AssociationModuleTest
28
- class Artist < Sequel::Model
29
- plugin :many_through_many
30
- many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
60
+ begin
61
+ class ::AssociationModuleTest
62
+ class Artist < Sequel::Model
63
+ plugin :many_through_many
64
+ many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
65
+ end
66
+ class Tag < Sequel::Model
67
+ end
31
68
  end
32
- class Tag < Sequel::Model
33
- end
34
- end
35
-
36
- ::AssociationModuleTest::Artist.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
69
+
70
+ ::AssociationModuleTest::Artist.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
71
+ ensure
72
+ Object.send(:remove_const, :AssociationModuleTest)
73
+ end
37
74
  end
38
75
 
39
76
  it "should raise an error if in invalid form of through is used" do
@@ -45,9 +82,7 @@ describe Sequel::Model, "many_through_many" do
45
82
  it "should allow only two arguments with the :through option" do
46
83
  @c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
47
84
  n = @c1.load(:id => 1234)
48
- a = n.tags_dataset
49
- a.should be_a_kind_of(Sequel::Dataset)
50
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
85
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
51
86
  n.tags.should == [@c2.load(:id=>1)]
52
87
  end
53
88
 
@@ -55,18 +90,14 @@ describe Sequel::Model, "many_through_many" do
55
90
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
56
91
  @c1.many_through_many :other_tags, :clone=>:tags
57
92
  n = @c1.load(:id => 1234)
58
- a = n.other_tags_dataset
59
- a.should be_a_kind_of(Sequel::Dataset)
60
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
93
+ n.other_tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
61
94
  n.tags.should == [@c2.load(:id=>1)]
62
95
  end
63
96
 
64
97
  it "should use join tables given" do
65
98
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
66
99
  n = @c1.load(:id => 1234)
67
- a = n.tags_dataset
68
- a.should be_a_kind_of(Sequel::Dataset)
69
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
100
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
70
101
  n.tags.should == [@c2.load(:id=>1)]
71
102
  end
72
103
 
@@ -75,9 +106,7 @@ describe Sequel::Model, "many_through_many" do
75
106
  end
76
107
  @c1.many_through_many :albums, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_artists, :album_id, :artist_id], [:artists, :id, :id], [:albums_artists, :artist_id, :album_id]]
77
108
  n = @c1.load(:id => 1234)
78
- a = n.albums_dataset
79
- a.should be_a_kind_of(Sequel::Dataset)
80
- a.sql.should == 'SELECT albums.* FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) INNER JOIN artists ON (artists.id = albums_artists.artist_id) INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) INNER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id) INNER JOIN albums_artists AS albums_artists_1 ON ((albums_artists_1.album_id = albums_0.id) AND (albums_artists_1.artist_id = 1234))'
109
+ n.albums_dataset.sql.should == 'SELECT albums.* FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) INNER JOIN artists ON (artists.id = albums_artists.artist_id) INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) INNER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id) INNER JOIN albums_artists AS albums_artists_1 ON ((albums_artists_1.album_id = albums_0.id) AND (albums_artists_1.artist_id = 1234))'
81
110
  n.albums.should == [Album.load(:id=>1, :x=>1)]
82
111
  Object.send(:remove_const, :Album)
83
112
  end
@@ -85,9 +114,7 @@ describe Sequel::Model, "many_through_many" do
85
114
  it "should use explicit class if given" do
86
115
  @c1.many_through_many :albums_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag
87
116
  n = @c1.load(:id => 1234)
88
- a = n.albums_tags_dataset
89
- a.should be_a_kind_of(Sequel::Dataset)
90
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
117
+ n.albums_tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
91
118
  n.albums_tags.should == [@c2.load(:id=>1)]
92
119
  end
93
120
 
@@ -95,9 +122,7 @@ describe Sequel::Model, "many_through_many" do
95
122
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :right_primary_key=>:tag_id, :left_primary_key=>:yyy
96
123
  n = @c1.load(:id => 1234)
97
124
  n.yyy = 85
98
- a = n.tags_dataset
99
- a.should be_a_kind_of(Sequel::Dataset)
100
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.tag_id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 85))'
125
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.tag_id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 85))'
101
126
  n.tags.should == [@c2.load(:id=>1)]
102
127
  end
103
128
 
@@ -105,61 +130,123 @@ describe Sequel::Model, "many_through_many" do
105
130
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
106
131
  n = @c1.load(:id => 1234)
107
132
  n.yyy = 85
108
- a = n.tags_dataset
109
- a.should be_a_kind_of(Sequel::Dataset)
110
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2) AND (albums_artists.b1 = 1234) AND (albums_artists.b2 = 85))'
133
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2) AND (albums_artists.b1 = 1234) AND (albums_artists.b2 = 85))'
111
134
  n.tags.should == [@c2.load(:id=>1)]
112
135
  end
113
136
 
137
+ it "should allowing filtering by many_through_many associations" do
138
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
139
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
140
+ end
141
+
142
+ it "should allowing filtering by many_through_many associations with a single through table" do
143
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id]]
144
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists WHERE ((albums_artists.album_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
145
+ end
146
+
147
+ it "should allowing filtering by many_through_many associations with aliased tables" do
148
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums_artists, :id, :id], [:albums_artists, :album_id, :tag_id]]
149
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.id = albums_artists.album_id) INNER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.album_id = albums_artists_0.id) WHERE ((albums_artists_1.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
150
+ end
151
+
152
+ it "should allowing filtering by many_through_many associations with composite keys" do
153
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
154
+ @c1.filter(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
155
+ end
156
+
157
+ it "should allowing excluding by many_through_many associations" do
158
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
159
+ @c1.exclude(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
160
+ end
161
+
162
+ it "should allowing excluding by many_through_many associations with composite keys" do
163
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
164
+ @c1.exclude(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
165
+ end
166
+
167
+ it "should allowing filtering by multiple many_through_many associations" do
168
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
169
+ @c1.filter(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL))))'
170
+ end
171
+
172
+ it "should allowing filtering by multiple many_through_many associations with composite keys" do
173
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
174
+ @c1.filter(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
175
+ end
176
+
177
+ it "should allowing excluding by multiple many_through_many associations" do
178
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
179
+ @c1.exclude(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
180
+ end
181
+
182
+ it "should allowing excluding by multiple many_through_many associations with composite keys" do
183
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
184
+ @c1.exclude(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
185
+ end
186
+
187
+ it "should allowing filtering/excluding many_through_many associations with NULL values" do
188
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
189
+ @c1.filter(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'f\''
190
+ @c1.exclude(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'t\''
191
+ end
192
+
193
+ it "should allowing filtering by many_through_many association datasets" do
194
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
195
+ @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL))))'
196
+ end
197
+
198
+ it "should allowing filtering by many_through_many association datasets with composite keys" do
199
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
200
+ @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
201
+ end
202
+
203
+ it "should allowing excluding by many_through_many association datasets" do
204
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
205
+ @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
206
+ end
207
+
208
+ it "should allowing excluding by many_through_many association datasets with composite keys" do
209
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
210
+ @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
211
+ end
212
+
114
213
  it "should support a :conditions option" do
115
214
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:a=>32}
116
215
  n = @c1.load(:id => 1234)
117
- a = n.tags_dataset
118
- a.should be_a_kind_of(Sequel::Dataset)
119
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (a = 32)'
216
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (a = 32)'
120
217
  n.tags.should == [@c2.load(:id=>1)]
121
218
 
122
219
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>['a = ?', 42]
123
220
  n = @c1.load(:id => 1234)
124
- a = n.tags_dataset
125
- a.should be_a_kind_of(Sequel::Dataset)
126
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (a = 42)'
221
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (a = 42)'
127
222
  n.tags.should == [@c2.load(:id=>1)]
128
223
  end
129
224
 
130
225
  it "should support an :order option" do
131
226
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :order=>:blah
132
227
  n = @c1.load(:id => 1234)
133
- a = n.tags_dataset
134
- a.should be_a_kind_of(Sequel::Dataset)
135
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) ORDER BY blah'
228
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) ORDER BY blah'
136
229
  n.tags.should == [@c2.load(:id=>1)]
137
230
  end
138
231
 
139
232
  it "should support an array for the :order option" do
140
233
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :order=>[:blah1, :blah2]
141
234
  n = @c1.load(:id => 1234)
142
- a = n.tags_dataset
143
- a.should be_a_kind_of(Sequel::Dataset)
144
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) ORDER BY blah1, blah2'
235
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) ORDER BY blah1, blah2'
145
236
  n.tags.should == [@c2.load(:id=>1)]
146
237
  end
147
238
 
148
239
  it "should support a select option" do
149
240
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :select=>:blah
150
241
  n = @c1.load(:id => 1234)
151
- a = n.tags_dataset
152
- a.should be_a_kind_of(Sequel::Dataset)
153
- a.sql.should == 'SELECT blah FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
242
+ n.tags_dataset.sql.should == 'SELECT blah FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
154
243
  n.tags.should == [@c2.load(:id=>1)]
155
244
  end
156
245
 
157
246
  it "should support an array for the select option" do
158
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :select=>[:tags.*, :albums__name]
247
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :select=>[Sequel::SQL::ColumnAll.new(:tags), :albums__name]
159
248
  n = @c1.load(:id => 1234)
160
- a = n.tags_dataset
161
- a.should be_a_kind_of(Sequel::Dataset)
162
- a.sql.should == 'SELECT tags.*, albums.name FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
249
+ n.tags_dataset.sql.should == 'SELECT tags.*, albums.name FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
163
250
  n.tags.should == [@c2.load(:id=>1)]
164
251
  end
165
252
 
@@ -167,9 +254,7 @@ describe Sequel::Model, "many_through_many" do
167
254
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]] do |ds| ds.filter(:yyy=>@yyy) end
168
255
  n = @c1.load(:id => 1234)
169
256
  n.yyy = 85
170
- a = n.tags_dataset
171
- a.should be_a_kind_of(Sequel::Dataset)
172
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (yyy = 85)'
257
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (yyy = 85)'
173
258
  n.tags.should == [@c2.load(:id=>1)]
174
259
  end
175
260
 
@@ -177,34 +262,26 @@ describe Sequel::Model, "many_through_many" do
177
262
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :order=>:blah do |ds| ds.filter(:yyy=>@yyy) end
178
263
  n = @c1.load(:id => 1234)
179
264
  n.yyy = 85
180
- a = n.tags_dataset
181
- a.should be_a_kind_of(Sequel::Dataset)
182
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (yyy = 85) ORDER BY blah'
265
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (yyy = 85) ORDER BY blah'
183
266
  n.tags.should == [@c2.load(:id=>1)]
184
267
  end
185
268
 
186
269
  it "should support a :dataset option that is used instead of the default" do
187
270
  @c1.many_through_many :tags, [[:a, :b, :c]], :dataset=>proc{Tag.join(:albums_tags, [:tag_id]).join(:albums, [:album_id]).join(:albums_artists, [:album_id]).filter(:albums_artists__artist_id=>id)}
188
271
  n = @c1.load(:id => 1234)
189
- a = n.tags_dataset
190
- a.should be_a_kind_of(Sequel::Dataset)
191
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags USING (tag_id) INNER JOIN albums USING (album_id) INNER JOIN albums_artists USING (album_id) WHERE (albums_artists.artist_id = 1234)'
272
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags USING (tag_id) INNER JOIN albums USING (album_id) INNER JOIN albums_artists USING (album_id) WHERE (albums_artists.artist_id = 1234)'
192
273
  n.tags.should == [@c2.load(:id=>1)]
193
274
  end
194
275
 
195
276
  it "should support a :limit option" do
196
277
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :limit=>10
197
278
  n = @c1.load(:id => 1234)
198
- a = n.tags_dataset
199
- a.should be_a_kind_of(Sequel::Dataset)
200
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) LIMIT 10'
279
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) LIMIT 10'
201
280
  n.tags.should == [@c2.load(:id=>1)]
202
281
 
203
282
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :limit=>[10, 10]
204
283
  n = @c1.load(:id => 1234)
205
- a = n.tags_dataset
206
- a.should be_a_kind_of(Sequel::Dataset)
207
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) LIMIT 10 OFFSET 10'
284
+ n.tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) LIMIT 10 OFFSET 10'
208
285
  n.tags.should == [@c2.load(:id=>1)]
209
286
  end
210
287
 
@@ -228,7 +305,7 @@ describe Sequel::Model, "many_through_many" do
228
305
  n.tags.should == [@c2.load(:id=>1)]
229
306
  MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))']
230
307
  n.associations[:tags].should == n.tags
231
- MODEL_DB.sqls.length.should == 1
308
+ MODEL_DB.sqls.length.should == 0
232
309
  end
233
310
 
234
311
  it "should use cached instance variable if available" do
@@ -247,7 +324,7 @@ describe Sequel::Model, "many_through_many" do
247
324
  n.tags(true).should == [@c2.load(:id=>1)]
248
325
  MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))']
249
326
  n.associations[:tags].should == n.tags
250
- MODEL_DB.sqls.length.should == 1
327
+ MODEL_DB.sqls.length.should == 0
251
328
  end
252
329
 
253
330
  it "should not add associations methods directly to class" do
@@ -264,18 +341,13 @@ describe Sequel::Model, "many_through_many" do
264
341
  h = []
265
342
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :after_load=>:al
266
343
  @c1.class_eval do
267
- class_variable_set(:@@blah, h)
344
+ self::Foo = h
268
345
  def al(v)
269
- v.each{|x| self.class.send(:class_variable_get, :@@blah) << x.pk * 20}
270
- end
271
- end
272
- @c2.class_eval do
273
- def @dataset.fetch_rows(sql)
274
- yield({:id=>20})
275
- yield({:id=>30})
346
+ v.each{|x| model::Foo << x.pk * 20}
276
347
  end
277
348
  end
278
- p = @c1.load(:id=>10, :parent_id=>20)
349
+ @c2.dataset._fetch = [{:id=>20}, {:id=>30}]
350
+ p = @c1.load(:id=>10, :parent_id=>20)
279
351
  p.tags
280
352
  h.should == [400, 600]
281
353
  p.tags.collect{|a| a.pk}.should == [20, 30]
@@ -284,14 +356,7 @@ describe Sequel::Model, "many_through_many" do
284
356
  it "should support a :uniq option that removes duplicates from the association" do
285
357
  h = []
286
358
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :uniq=>true
287
- @c2.class_eval do
288
- def @dataset.fetch_rows(sql)
289
- yield({:id=>20})
290
- yield({:id=>30})
291
- yield({:id=>20})
292
- yield({:id=>30})
293
- end
294
- end
359
+ @c2.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]
295
360
  @c1.load(:id=>10).tags.should == [@c2.load(:id=>20), @c2.load(:id=>30)]
296
361
  end
297
362
  end
@@ -352,57 +417,40 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
352
417
  end
353
418
  class ::Track < Sequel::Model
354
419
  end
355
-
356
- Artist.dataset.extend(Module.new {
357
- def columns
358
- [:id]
359
- end
360
-
361
- def fetch_rows(sql)
362
- @db << sql
363
- h = {:id => 1}
364
- if sql =~ /FROM artists LEFT OUTER JOIN albums_artists/
365
- h[:tags_id] = 2
366
- h[:albums_0_id] = 3 if sql =~ /LEFT OUTER JOIN albums AS albums_0/
367
- h[:tracks_id] = 4 if sql =~ /LEFT OUTER JOIN tracks/
368
- h[:other_tags_id] = 9 if sql =~ /other_tags\.id AS other_tags_id/
369
- h[:artists_0_id] = 10 if sql =~ /artists_0\.id AS artists_0_id/
370
- end
371
- yield h
420
+ Artist.dataset.columns(:id)._fetch = proc do |sql|
421
+ h = {:id => 1}
422
+ if sql =~ /FROM artists LEFT OUTER JOIN albums_artists/
423
+ h[:tags_id] = 2
424
+ h[:albums_0_id] = 3 if sql =~ /LEFT OUTER JOIN albums AS albums_0/
425
+ h[:tracks_id] = 4 if sql =~ /LEFT OUTER JOIN tracks/
426
+ h[:other_tags_id] = 9 if sql =~ /other_tags\.id AS other_tags_id/
427
+ h[:artists_0_id] = 10 if sql =~ /artists_0\.id AS artists_0_id/
372
428
  end
373
- })
374
-
375
- Tag.dataset.extend(Module.new {
376
- def fetch_rows(sql)
377
- @db << sql
378
- h = {:id => 2}
379
- if sql =~ /albums_artists.artist_id IN \(([18])\)/
380
- h.merge!(:x_foreign_key_x=>$1.to_i)
381
- elsif sql =~ /\(\(albums_artists.b1, albums_artists.b2\) IN \(\(1, 8\)\)\)/
382
- h.merge!(:x_foreign_key_0_x=>1, :x_foreign_key_1_x=>8)
383
- end
384
- h[:tag_id] = h.delete(:id) if sql =~ /albums_artists.artist_id IN \(8\)/
385
- yield h
386
- end
387
- })
429
+ h
430
+ end
388
431
 
389
- Album.dataset.extend(Module.new {
390
- def fetch_rows(sql)
391
- @db << sql
392
- h = {:id => 3}
393
- h.merge!(:x_foreign_key_x=>1) if sql =~ /albums_artists.artist_id IN \(1\)/
394
- yield h
432
+ Tag.dataset._fetch = proc do |sql|
433
+ h = {:id => 2}
434
+ if sql =~ /albums_artists.artist_id IN \(([18])\)/
435
+ h[:x_foreign_key_x] = $1.to_i
436
+ elsif sql =~ /\(\(albums_artists.b1, albums_artists.b2\) IN \(\(1, 8\)\)\)/
437
+ h.merge!(:x_foreign_key_0_x=>1, :x_foreign_key_1_x=>8)
395
438
  end
396
- })
439
+ h[:tag_id] = h.delete(:id) if sql =~ /albums_artists.artist_id IN \(8\)/
440
+ h
441
+ end
397
442
 
398
- Track.dataset.extend(Module.new {
399
- def fetch_rows(sql)
400
- @db << sql
401
- h = {:id => 4}
402
- h.merge!(:x_foreign_key_x=>2) if sql =~ /albums_tags.tag_id IN \(2\)/
403
- yield h
404
- end
405
- })
443
+ Album.dataset._fetch = proc do |sql|
444
+ h = {:id => 3}
445
+ h[:x_foreign_key_x] = 1 if sql =~ /albums_artists.artist_id IN \(1\)/
446
+ h
447
+ end
448
+
449
+ Track.dataset._fetch = proc do |sql|
450
+ h = {:id => 4}
451
+ h[:x_foreign_key_x] = 2 if sql =~ /albums_tags.tag_id IN \(2\)/
452
+ h
453
+ end
406
454
 
407
455
  @c1 = Artist
408
456
  MODEL_DB.reset
@@ -416,33 +464,35 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
416
464
  a.should == [@c1.load(:id=>1)]
417
465
  MODEL_DB.sqls.should == ['SELECT * FROM artists', 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
418
466
  a.first.tags.should == [Tag.load(:id=>2)]
419
- MODEL_DB.sqls.length.should == 2
467
+ MODEL_DB.sqls.length.should == 0
420
468
  end
421
469
 
422
470
  it "should eagerly load multiple associations in a single call" do
423
471
  a = @c1.eager(:tags, :albums).all
424
472
  a.should == [@c1.load(:id=>1)]
425
- MODEL_DB.sqls.length.should == 3
426
- MODEL_DB.sqls[0].should == 'SELECT * FROM artists'
427
- MODEL_DB.sqls[1..-1].should(include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
428
- MODEL_DB.sqls[1..-1].should(include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
473
+ sqls = MODEL_DB.sqls
474
+ sqls.length.should == 3
475
+ sqls[0].should == 'SELECT * FROM artists'
476
+ sqls[1..-1].should(include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
477
+ sqls[1..-1].should(include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
429
478
  a = a.first
430
479
  a.tags.should == [Tag.load(:id=>2)]
431
480
  a.albums.should == [Album.load(:id=>3)]
432
- MODEL_DB.sqls.length.should == 3
481
+ MODEL_DB.sqls.length.should == 0
433
482
  end
434
483
 
435
484
  it "should eagerly load multiple associations in separate" do
436
485
  a = @c1.eager(:tags).eager(:albums).all
437
486
  a.should == [@c1.load(:id=>1)]
438
- MODEL_DB.sqls.length.should == 3
439
- MODEL_DB.sqls[0].should == 'SELECT * FROM artists'
440
- MODEL_DB.sqls[1..-1].should(include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
441
- MODEL_DB.sqls[1..-1].should(include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
487
+ sqls = MODEL_DB.sqls
488
+ sqls.length.should == 3
489
+ sqls[0].should == 'SELECT * FROM artists'
490
+ sqls[1..-1].should(include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
491
+ sqls[1..-1].should(include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
442
492
  a = a.first
443
493
  a.tags.should == [Tag.load(:id=>2)]
444
494
  a.albums.should == [Album.load(:id=>3)]
445
- MODEL_DB.sqls.length.should == 3
495
+ MODEL_DB.sqls.length.should == 0
446
496
  end
447
497
 
448
498
  it "should allow cascading of eager loading for associations of associated models" do
@@ -454,7 +504,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
454
504
  a = a.first
455
505
  a.tags.should == [Tag.load(:id=>2)]
456
506
  a.tags.first.tracks.should == [Track.load(:id=>4)]
457
- MODEL_DB.sqls.length.should == 3
507
+ MODEL_DB.sqls.length.should == 0
458
508
  end
459
509
 
460
510
  it "should cascade eagerly loading when the :eager association option is used" do
@@ -467,7 +517,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
467
517
  a = a.first
468
518
  a.tags.should == [Tag.load(:id=>2)]
469
519
  a.tags.first.tracks.should == [Track.load(:id=>4)]
470
- MODEL_DB.sqls.length.should == 3
520
+ MODEL_DB.sqls.length.should == 0
471
521
  end
472
522
 
473
523
  it "should respect :eager when lazily loading an association" do
@@ -477,39 +527,20 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
477
527
  MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1))',
478
528
  'SELECT tracks.*, albums_tags.tag_id AS x_foreign_key_x FROM tracks INNER JOIN albums ON (albums.id = tracks.album_id) INNER JOIN albums_tags ON ((albums_tags.album_id = albums.id) AND (albums_tags.tag_id IN (2)))']
479
529
  a.tags.first.tracks.should == [Track.load(:id=>4)]
480
- MODEL_DB.sqls.length.should == 2
530
+ MODEL_DB.sqls.length.should == 0
481
531
  end
482
532
 
483
533
  it "should cascade eagerly loading when the :eager_graph association option is used" do
484
- Tag.dataset.extend(Module.new {
485
- def columns
486
- [:id]
487
- end
488
- def fetch_rows(sql)
489
- @db << sql
490
- yield({:id=>2, :tracks_id=>4, :x_foreign_key_x=>1})
491
- end
492
- })
493
534
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_graph=>:tracks
494
- a = @c1.eager(:tags).all
495
- a.should == [@c1.load(:id=>1)]
496
- MODEL_DB.sqls.should == ['SELECT * FROM artists',
497
- 'SELECT tags.id, tracks.id AS tracks_id, albums_artists.artist_id AS x_foreign_key_x FROM (SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))) AS tags LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums ON (albums.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)']
498
- a = a.first
499
- a.tags.should == [Tag.load(:id=>2)]
500
- a.tags.first.tracks.should == [Track.load(:id=>4)]
501
- MODEL_DB.sqls.length.should == 2
535
+ proc{@c1.eager(:tags).all}.should raise_error(Sequel::Error)
502
536
  end
503
537
 
504
538
  it "should respect :eager_graph when lazily loading an association" do
539
+ Tag.dataset._fetch = {:id=>2, :tracks_id=>4}
505
540
  Tag.dataset.extend(Module.new {
506
541
  def columns
507
542
  [:id]
508
543
  end
509
- def fetch_rows(sql)
510
- @db << sql
511
- yield({:id=>2, :tracks_id=>4})
512
- end
513
544
  })
514
545
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_graph=>:tracks
515
546
  a = @c1.load(:id=>1)
@@ -517,7 +548,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
517
548
  MODEL_DB.sqls.should == [ 'SELECT tags.id, tracks.id AS tracks_id FROM (SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1))) AS tags LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums ON (albums.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)']
518
549
  a.tags.should == [Tag.load(:id=>2)]
519
550
  a.tags.first.tracks.should == [Track.load(:id=>4)]
520
- MODEL_DB.sqls.length.should == 1
551
+ MODEL_DB.sqls.length.should == 0
521
552
  end
522
553
 
523
554
  it "should respect :conditions when eagerly loading" do
@@ -527,7 +558,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
527
558
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
528
559
  'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE (a = 32)']
529
560
  a.first.tags.should == [Tag.load(:id=>2)]
530
- MODEL_DB.sqls.length.should == 2
561
+ MODEL_DB.sqls.length.should == 0
531
562
  end
532
563
 
533
564
  it "should respect :order when eagerly loading" do
@@ -537,7 +568,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
537
568
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
538
569
  'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) ORDER BY blah']
539
570
  a.first.tags.should == [Tag.load(:id=>2)]
540
- MODEL_DB.sqls.length.should == 2
571
+ MODEL_DB.sqls.length.should == 0
541
572
  end
542
573
 
543
574
  it "should use the association's block when eager loading by default" do
@@ -547,7 +578,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
547
578
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
548
579
  'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE a']
549
580
  a.first.tags.should == [Tag.load(:id=>2)]
550
- MODEL_DB.sqls.length.should == 2
581
+ MODEL_DB.sqls.length.should == 0
551
582
  end
552
583
 
553
584
  it "should use the :eager_block option when eager loading if given" do
@@ -557,7 +588,88 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
557
588
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
558
589
  'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE b']
559
590
  a.first.tags.should == [Tag.load(:id=>2)]
560
- MODEL_DB.sqls.length.should == 2
591
+ MODEL_DB.sqls.length.should == 0
592
+ end
593
+
594
+ it "should respect the :limit option on a many_through_many association" do
595
+ @c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2
596
+ Tag.dataset._fetch = [{:x_foreign_key_x=>1, :id=>5},{:x_foreign_key_x=>1, :id=>6}, {:x_foreign_key_x=>1, :id=>7}]
597
+ a = @c1.eager(:first_two_tags).all
598
+ a.should == [@c1.load(:id=>1)]
599
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
600
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
601
+ a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
602
+ MODEL_DB.sqls.length.should == 0
603
+
604
+ @c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1]
605
+ a = @c1.eager(:first_two_tags).all
606
+ a.should == [@c1.load(:id=>1)]
607
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
608
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
609
+ a.first.first_two_tags.should == [Tag.load(:id=>6), Tag.load(:id=>7)]
610
+ MODEL_DB.sqls.length.should == 0
611
+ end
612
+
613
+ it "should respect the :limit option on a many_through_many association using a :window_function strategy" do
614
+ Tag.dataset.meta_def(:supports_window_functions?){true}
615
+ @c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2, :eager_limit_strategy=>true, :order=>:name
616
+ Tag.dataset._fetch = [{:x_foreign_key_x=>1, :id=>5},{:x_foreign_key_x=>1, :id=>6}]
617
+ a = @c1.eager(:first_two_tags).all
618
+ a.should == [@c1.load(:id=>1)]
619
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
620
+ 'SELECT * FROM (SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x, row_number() OVER (PARTITION BY albums_artists.artist_id ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))) AS t1 WHERE (x_sequel_row_number_x <= 2)']
621
+ a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
622
+ MODEL_DB.sqls.length.should == 0
623
+
624
+ @c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1], :eager_limit_strategy=>true, :order=>:name
625
+ a = @c1.eager(:first_two_tags).all
626
+ a.should == [@c1.load(:id=>1)]
627
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
628
+ 'SELECT * FROM (SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x, row_number() OVER (PARTITION BY albums_artists.artist_id ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))']
629
+ a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
630
+ MODEL_DB.sqls.length.should == 0
631
+ end
632
+
633
+ it "should respect the :limit option on a many_through_many association with composite primary keys on the main table using a :window_function strategy" do
634
+ Tag.dataset.meta_def(:supports_window_functions?){true}
635
+ @c1.set_primary_key([:id1, :id2])
636
+ @c1.columns :id1, :id2
637
+ @c1.many_through_many :first_two_tags, [[:albums_artists, [:artist_id1, :artist_id2], :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2, :eager_limit_strategy=>true, :order=>:name
638
+ @c1.dataset._fetch = [{:id1=>1, :id2=>2}]
639
+ Tag.dataset._fetch = [{:x_foreign_key_0_x=>1, :x_foreign_key_1_x=>2, :id=>5}, {:x_foreign_key_0_x=>1, :x_foreign_key_1_x=>2, :id=>6}]
640
+ a = @c1.eager(:first_two_tags).all
641
+ a.should == [@c1.load(:id1=>1, :id2=>2)]
642
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
643
+ 'SELECT * FROM (SELECT tags.*, albums_artists.artist_id1 AS x_foreign_key_0_x, albums_artists.artist_id2 AS x_foreign_key_1_x, row_number() OVER (PARTITION BY albums_artists.artist_id1, albums_artists.artist_id2 ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND ((albums_artists.artist_id1, albums_artists.artist_id2) IN ((1, 2))))) AS t1 WHERE (x_sequel_row_number_x <= 2)']
644
+ a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
645
+ MODEL_DB.sqls.length.should == 0
646
+
647
+ @c1.many_through_many :first_two_tags, [[:albums_artists, [:artist_id1, :artist_id2], :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1], :eager_limit_strategy=>true, :order=>:name
648
+ a = @c1.eager(:first_two_tags).all
649
+ a.should == [@c1.load(:id1=>1, :id2=>2)]
650
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
651
+ 'SELECT * FROM (SELECT tags.*, albums_artists.artist_id1 AS x_foreign_key_0_x, albums_artists.artist_id2 AS x_foreign_key_1_x, row_number() OVER (PARTITION BY albums_artists.artist_id1, albums_artists.artist_id2 ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND ((albums_artists.artist_id1, albums_artists.artist_id2) IN ((1, 2))))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))']
652
+ a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
653
+ MODEL_DB.sqls.length.should == 0
654
+ end
655
+
656
+ it "should respect the :limit option on a many_through_many association using a :correlated_subquery strategy" do
657
+ @c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2, :eager_limit_strategy=>:correlated_subquery, :order=>:name
658
+ Tag.dataset._fetch = [{:x_foreign_key_x=>1, :id=>5},{:x_foreign_key_x=>1, :id=>6}]
659
+ a = @c1.eager(:first_two_tags).all
660
+ a.should == [@c1.load(:id=>1)]
661
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
662
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE (tags.id IN (SELECT t1.id FROM tags AS t1 INNER JOIN albums_tags ON (albums_tags.tag_id = t1.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists AS t2 ON ((t2.album_id = albums.id) AND (t2.artist_id = albums_artists.artist_id)) ORDER BY name LIMIT 2)) ORDER BY name']
663
+ a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
664
+ MODEL_DB.sqls.length.should == 0
665
+
666
+ @c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1], :eager_limit_strategy=>:correlated_subquery, :order=>:name
667
+ a = @c1.eager(:first_two_tags).all
668
+ a.should == [@c1.load(:id=>1)]
669
+ MODEL_DB.sqls.should == ['SELECT * FROM artists',
670
+ 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE (tags.id IN (SELECT t1.id FROM tags AS t1 INNER JOIN albums_tags ON (albums_tags.tag_id = t1.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists AS t2 ON ((t2.album_id = albums.id) AND (t2.artist_id = albums_artists.artist_id)) ORDER BY name LIMIT 2 OFFSET 1)) ORDER BY name']
671
+ a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
672
+ MODEL_DB.sqls.length.should == 0
561
673
  end
562
674
 
563
675
  it "should raise an error when attempting to eagerly load an association with the :allow_eager option set to false" do
@@ -573,47 +685,33 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
573
685
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
574
686
  'SELECT tags.name, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
575
687
  a.first.tags.should == [Tag.load(:id=>2)]
576
- MODEL_DB.sqls.length.should == 2
688
+ MODEL_DB.sqls.length.should == 0
577
689
  end
578
690
 
579
- it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
691
+ it "should respect many_through_many association's :left_primary_key and :right_primary_key options" do
580
692
  @c1.send(:define_method, :yyy){values[:yyy]}
581
- @c1.dataset.extend(Module.new {
582
- def columns
583
- [:id, :yyy]
584
- end
585
- def fetch_rows(sql)
586
- @db << sql
587
- yield({:id=>1, :yyy=>8})
588
- end
589
- })
693
+ @c1.dataset._fetch = {:id=>1, :yyy=>8}
694
+ @c1.dataset.meta_def(:columns){[:id, :yyy]}
590
695
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :left_primary_key=>:yyy, :right_primary_key=>:tag_id
591
696
  a = @c1.eager(:tags).all
592
697
  a.should == [@c1.load(:id=>1, :yyy=>8)]
593
698
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
594
699
  'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.tag_id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (8)))']
595
700
  a.first.tags.should == [Tag.load(:tag_id=>2)]
596
- MODEL_DB.sqls.length.should == 2
701
+ MODEL_DB.sqls.length.should == 0
597
702
  end
598
703
 
599
704
  it "should handle composite keys" do
600
705
  @c1.send(:define_method, :yyy){values[:yyy]}
601
- @c1.dataset.extend(Module.new {
602
- def columns
603
- [:id, :yyy]
604
- end
605
- def fetch_rows(sql)
606
- @db << sql
607
- yield({:id=>1, :yyy=>8})
608
- end
609
- })
706
+ @c1.dataset._fetch = {:id=>1, :yyy=>8}
707
+ @c1.dataset.meta_def(:columns){[:id, :yyy]}
610
708
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
611
709
  a = @c1.eager(:tags).all
612
710
  a.should == [@c1.load(:id=>1, :yyy=>8)]
613
711
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
614
712
  'SELECT tags.*, albums_artists.b1 AS x_foreign_key_0_x, albums_artists.b2 AS x_foreign_key_1_x FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2) AND ((albums_artists.b1, albums_artists.b2) IN ((1, 8))))']
615
713
  a.first.tags.should == [Tag.load(:id=>2)]
616
- MODEL_DB.sqls.length.should == 2
714
+ MODEL_DB.sqls.length.should == 0
617
715
  end
618
716
 
619
717
  it "should respect :after_load callbacks on associations when eager loading" do
@@ -623,7 +721,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
623
721
  MODEL_DB.sqls.should == ['SELECT * FROM artists',
624
722
  'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
625
723
  a.first.tags.should == [Tag.load(:id=>6)]
626
- MODEL_DB.sqls.length.should == 2
724
+ MODEL_DB.sqls.length.should == 0
627
725
  end
628
726
 
629
727
  it "should raise an error if called without a symbol or hash" do
@@ -635,7 +733,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
635
733
  a.should == [@c1.load(:id=>1)]
636
734
  MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
637
735
  a.first.tags.should == [Tag.load(:id=>2)]
638
- MODEL_DB.sqls.length.should == 1
736
+ MODEL_DB.sqls.length.should == 0
639
737
  end
640
738
 
641
739
  it "should eagerly graph multiple associations in a single call" do
@@ -645,7 +743,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
645
743
  a = a.first
646
744
  a.tags.should == [Tag.load(:id=>2)]
647
745
  a.albums.should == [Album.load(:id=>3)]
648
- MODEL_DB.sqls.length.should == 1
746
+ MODEL_DB.sqls.length.should == 0
649
747
  end
650
748
 
651
749
  it "should eagerly graph multiple associations in separate calls" do
@@ -655,7 +753,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
655
753
  a = a.first
656
754
  a.tags.should == [Tag.load(:id=>2)]
657
755
  a.albums.should == [Album.load(:id=>3)]
658
- MODEL_DB.sqls.length.should == 1
756
+ MODEL_DB.sqls.length.should == 0
659
757
  end
660
758
 
661
759
  it "should allow cascading of eager graphing for associations of associated models" do
@@ -665,24 +763,18 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
665
763
  a = a.first
666
764
  a.tags.should == [Tag.load(:id=>2)]
667
765
  a.tags.first.tracks.should == [Track.load(:id=>4)]
668
- MODEL_DB.sqls.length.should == 1
766
+ MODEL_DB.sqls.length.should == 0
669
767
  end
670
768
 
671
769
  it "eager graphing should eliminate duplicates caused by cartesian products" do
672
770
  ds = @c1.eager_graph(:tags)
673
- def ds.fetch_rows(sql, &block)
674
- @db << sql
675
- # Assume artist has 2 albums each with 2 tags
676
- yield({:id=>1, :tags_id=>2})
677
- yield({:id=>1, :tags_id=>3})
678
- yield({:id=>1, :tags_id=>2})
679
- yield({:id=>1, :tags_id=>3})
680
- end
771
+ # Assume artist has 2 albums each with 2 tags
772
+ ds._fetch = [{:id=>1, :tags_id=>2}, {:id=>1, :tags_id=>3}, {:id=>1, :tags_id=>2}, {:id=>1, :tags_id=>3}]
681
773
  a = ds.all
682
774
  a.should == [@c1.load(:id=>1)]
683
775
  MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
684
776
  a.first.tags.should == [Tag.load(:id=>2), Tag.load(:id=>3)]
685
- MODEL_DB.sqls.length.should == 1
777
+ MODEL_DB.sqls.length.should == 0
686
778
  end
687
779
 
688
780
  it "should eager graph multiple associations from the same table" do
@@ -692,7 +784,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
692
784
  a = a.first
693
785
  a.tags.should == [Tag.load(:id=>2)]
694
786
  a.other_tags.should == [Tag.load(:id=>9)]
695
- MODEL_DB.sqls.length.should == 1
787
+ MODEL_DB.sqls.length.should == 0
696
788
  end
697
789
 
698
790
  it "should eager graph a self_referential association" do
@@ -702,11 +794,11 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
702
794
  a = a.first
703
795
  a.tags.should == [Tag.load(:id=>2)]
704
796
  a.artists.should == [@c1.load(:id=>10)]
705
- MODEL_DB.sqls.length.should == 1
797
+ MODEL_DB.sqls.length.should == 0
706
798
  end
707
799
 
708
- it "eager graphing should give you a graph of tables when called without .all" do
709
- @c1.eager_graph(:tags, :artists).first.should == {:artists=>@c1.load(:id=>1), :artists_0=>@c1.load(:id=>10), :tags=>Tag.load(:id=>2)}
800
+ it "eager graphing should give you a plain hash when called without .all" do
801
+ @c1.eager_graph(:tags, :artists).first.should == {:albums_0_id=>3, :artists_0_id=>10, :id=>1, :tags_id=>2}
710
802
  end
711
803
 
712
804
  it "should be able to use eager and eager_graph together" do
@@ -717,31 +809,22 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
717
809
  a = a.first
718
810
  a.tags.should == [Tag.load(:id=>2)]
719
811
  a.albums.should == [Album.load(:id=>3)]
720
- MODEL_DB.sqls.length.should == 2
812
+ MODEL_DB.sqls.length.should == 0
721
813
  end
722
814
 
723
815
  it "should handle no associated records when eagerly graphing a single many_through_many association" do
724
816
  ds = @c1.eager_graph(:tags)
725
- def ds.fetch_rows(sql)
726
- @db << sql
727
- yield({:id=>1, :tags_id=>nil})
728
- end
817
+ ds._fetch = {:id=>1, :tags_id=>nil}
729
818
  a = ds.all
730
819
  a.should == [@c1.load(:id=>1)]
731
820
  MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
732
821
  a.first.tags.should == []
733
- MODEL_DB.sqls.length.should == 1
822
+ MODEL_DB.sqls.length.should == 0
734
823
  end
735
824
 
736
825
  it "should handle no associated records when eagerly graphing multiple many_through_many associations" do
737
826
  ds = @c1.eager_graph(:tags, :albums)
738
- def ds.fetch_rows(sql)
739
- @db << sql
740
- yield({:id=>1, :tags_id=>nil, :albums_0_id=>3})
741
- yield({:id=>1, :tags_id=>2, :albums_0_id=>nil})
742
- yield({:id=>1, :tags_id=>5, :albums_0_id=>6})
743
- yield({:id=>7, :tags_id=>nil, :albums_0_id=>nil})
744
- end
827
+ ds._fetch = [{:id=>1, :tags_id=>nil, :albums_0_id=>3}, {:id=>1, :tags_id=>2, :albums_0_id=>nil}, {:id=>1, :tags_id=>5, :albums_0_id=>6}, {:id=>7, :tags_id=>nil, :albums_0_id=>nil}]
745
828
  a = ds.all
746
829
  a.should == [@c1.load(:id=>1), @c1.load(:id=>7)]
747
830
  MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, albums_0.id AS albums_0_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id)']
@@ -749,17 +832,12 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
749
832
  a.first.albums.should == [Album.load(:id=>3), Album.load(:id=>6)]
750
833
  a.last.tags.should == []
751
834
  a.last.albums.should == []
752
- MODEL_DB.sqls.length.should == 1
835
+ MODEL_DB.sqls.length.should == 0
753
836
  end
754
837
 
755
838
  it "should handle missing associated records when cascading eager graphing for associations of associated models" do
756
839
  ds = @c1.eager_graph(:tags=>:tracks)
757
- def ds.fetch_rows(sql)
758
- @db << sql
759
- yield({:id=>1, :tags_id=>2, :tracks_id=>4})
760
- yield({:id=>1, :tags_id=>3, :tracks_id=>nil})
761
- yield({:id=>2, :tags_id=>nil, :tracks_id=>nil})
762
- end
840
+ ds._fetch = [{:id=>1, :tags_id=>2, :tracks_id=>4}, {:id=>1, :tags_id=>3, :tracks_id=>nil}, {:id=>2, :tags_id=>nil, :tracks_id=>nil}]
763
841
  a = ds.all
764
842
  a.should == [@c1.load(:id=>1), @c1.load(:id=>2)]
765
843
  MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, tracks.id AS tracks_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums_0.id)']
@@ -768,7 +846,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
768
846
  a.tags.should == [Tag.load(:id=>2), Tag.load(:id=>3)]
769
847
  a.tags.first.tracks.should == [Track.load(:id=>4)]
770
848
  a.tags.last.tracks.should == []
771
- MODEL_DB.sqls.length.should == 1
849
+ MODEL_DB.sqls.length.should == 0
772
850
  end
773
851
 
774
852
  it "eager graphing should respect :left_primary_key and :right_primary_key options" do
@@ -776,15 +854,12 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
776
854
  @c1.dataset.meta_def(:columns){[:id, :yyy]}
777
855
  Tag.dataset.meta_def(:columns){[:id, :tag_id]}
778
856
  ds = @c1.eager_graph(:tags)
779
- def ds.fetch_rows(sql)
780
- @db << sql
781
- yield({:id=>1, :yyy=>8, :tags_id=>2, :tag_id=>4})
782
- end
857
+ ds._fetch = {:id=>1, :yyy=>8, :tags_id=>2, :tag_id=>4}
783
858
  a = ds.all
784
859
  a.should == [@c1.load(:id=>1, :yyy=>8)]
785
860
  MODEL_DB.sqls.should == ['SELECT artists.id, artists.yyy, tags.id AS tags_id, tags.tag_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.yyy) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.tag_id = albums_tags.tag_id)']
786
861
  a.first.tags.should == [Tag.load(:id=>2, :tag_id=>4)]
787
- MODEL_DB.sqls.length.should == 1
862
+ MODEL_DB.sqls.length.should == 0
788
863
  end
789
864
 
790
865
  it "eager graphing should respect composite keys" do
@@ -792,29 +867,23 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
792
867
  @c1.dataset.meta_def(:columns){[:id, :yyy]}
793
868
  Tag.dataset.meta_def(:columns){[:id, :tag_id]}
794
869
  ds = @c1.eager_graph(:tags)
795
- def ds.fetch_rows(sql)
796
- @db << sql
797
- yield({:id=>1, :yyy=>8, :tags_id=>2, :tag_id=>4})
798
- end
870
+ ds._fetch = {:id=>1, :yyy=>8, :tags_id=>2, :tag_id=>4}
799
871
  a = ds.all
800
872
  a.should == [@c1.load(:id=>1, :yyy=>8)]
801
873
  MODEL_DB.sqls.should == ['SELECT artists.id, artists.yyy, tags.id AS tags_id, tags.tag_id FROM artists LEFT OUTER JOIN albums_artists ON ((albums_artists.b1 = artists.id) AND (albums_artists.b2 = artists.yyy)) LEFT OUTER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) LEFT OUTER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) LEFT OUTER JOIN tags ON ((tags.id = albums_tags.g1) AND (tags.tag_id = albums_tags.g2))']
802
874
  a.first.tags.should == [Tag.load(:id=>2, :tag_id=>4)]
803
- MODEL_DB.sqls.length.should == 1
875
+ MODEL_DB.sqls.length.should == 0
804
876
  end
805
877
 
806
878
  it "should respect the association's :graph_select option" do
807
879
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :graph_select=>:b
808
880
  ds = @c1.eager_graph(:tags)
809
- def ds.fetch_rows(sql)
810
- @db << sql
811
- yield({:id=>1, :b=>2})
812
- end
881
+ ds._fetch = {:id=>1, :b=>2}
813
882
  a = ds.all
814
883
  a.should == [@c1.load(:id=>1)]
815
884
  MODEL_DB.sqls.should == ['SELECT artists.id, tags.b FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
816
885
  a.first.tags.should == [Tag.load(:b=>2)]
817
- MODEL_DB.sqls.length.should == 1
886
+ MODEL_DB.sqls.length.should == 0
818
887
  end
819
888
 
820
889
  it "should respect the association's :graph_join_type option" do
@@ -845,12 +914,12 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
845
914
  end
846
915
 
847
916
  it "should respect the association's :graph_block option" do
848
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :graph_block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}
917
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :graph_block=>proc{|ja,lja,js| {Sequel.qualify(ja, :active)=>true}}
849
918
  @c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON ((tags.id = albums_tags.tag_id) AND (tags.active IS TRUE))'
850
919
  end
851
920
 
852
921
  it "should respect the association's :block option on through" do
853
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id, :block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}}, [:albums_tags, :album_id, :tag_id]]
922
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id, :block=>proc{|ja,lja,js| {Sequel.qualify(ja, :active)=>true}}}, [:albums_tags, :album_id, :tag_id]]
854
923
  @c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON ((albums.id = albums_artists.album_id) AND (albums.active IS TRUE)) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)'
855
924
  end
856
925
 
@@ -874,7 +943,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
874
943
  end
875
944
 
876
945
  it "should only qualify unqualified symbols, identifiers, or ordered versions in association's :order" do
877
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :order=>[:blah__id.identifier, :blah__id.identifier.desc, :blah__id.desc, :blah__id, :album_id, :album_id.desc, 1, 'RANDOM()'.lit, :a.qualify(:b)]
946
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :order=>[Sequel.identifier(:blah__id), Sequel.identifier(:blah__id).desc, Sequel.desc(:blah__id), :blah__id, :album_id, Sequel.desc(:album_id), 1, Sequel.lit('RANDOM()'), Sequel.qualify(:b, :a)]
878
947
  @c1.order(:artists__blah2, :artists__blah3).eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) ORDER BY artists.blah2, artists.blah3, tags.blah__id, tags.blah__id DESC, blah.id DESC, blah.id, tags.album_id, tags.album_id DESC, 1, RANDOM(), b.a'
879
948
  end
880
949
 
@@ -901,3 +970,48 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
901
970
  @c1.eager_graph(:tags, :albums).sql.should == 'SELECT artists.id, tags.id AS tags_id, albums_0.id AS albums_0_id FROM artists LEFT OUTER JOIN albums_artists ON ((albums_artists.artist_id = artists.id) AND (albums_artists.a = artists.b)) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON ((albums_artists_0.artist_id = artists.id) AND (albums_artists_0.c = artists.d)) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id)'
902
971
  end
903
972
  end
973
+
974
+ describe "many_through_many associations with non-column expression keys" do
975
+ before do
976
+ @db = Sequel.mock(:fetch=>{:id=>1, :object_ids=>[2]})
977
+ @Foo = Class.new(Sequel::Model(@db[:foos]))
978
+ @Foo.columns :id, :object_ids
979
+ @Foo.plugin :many_through_many
980
+ m = Module.new{def obj_id; object_ids[0]; end}
981
+ @Foo.include m
982
+
983
+ @Foo.many_through_many :foos, [
984
+ [:f, Sequel.subscript(:l, 0), Sequel.subscript(:r, 0)],
985
+ [:f, Sequel.subscript(:l, 1), Sequel.subscript(:r, 1)]
986
+ ], :class=>@Foo, :left_primary_key=>:obj_id, :left_primary_key_column=>Sequel.subscript(:object_ids, 0), :right_primary_key=>Sequel.subscript(:object_ids, 0), :right_primary_key_method=>:obj_id
987
+ @foo = @Foo.load(:id=>1, :object_ids=>[2])
988
+ @db.sqls
989
+ end
990
+
991
+ it "should have working regular association methods" do
992
+ @Foo.first.foos.should == [@foo]
993
+ @db.sqls.should == ["SELECT * FROM foos LIMIT 1", "SELECT foos.* FROM foos INNER JOIN f ON (f.r[1] = foos.object_ids[0]) INNER JOIN f AS f_0 ON ((f_0.r[0] = f.l[1]) AND (f_0.l[0] = 2))"]
994
+ end
995
+
996
+ it "should have working eager loading methods" do
997
+ @db.fetch = [[{:id=>1, :object_ids=>[2]}], [{:id=>1, :object_ids=>[2], :x_foreign_key_x=>2}]]
998
+ @Foo.eager(:foos).all.map{|o| [o, o.foos]}.should == [[@foo, [@foo]]]
999
+ @db.sqls.should == ["SELECT * FROM foos", "SELECT foos.*, f_0.l[0] AS x_foreign_key_x FROM foos INNER JOIN f ON (f.r[1] = foos.object_ids[0]) INNER JOIN f AS f_0 ON ((f_0.r[0] = f.l[1]) AND (f_0.l[0] IN (2)))"]
1000
+ end
1001
+
1002
+ it "should have working eager graphing methods" do
1003
+ @db.fetch = {:id=>1, :object_ids=>[2], :foos_0_id=>1, :foos_0_object_ids=>[2]}
1004
+ @Foo.eager_graph(:foos).all.map{|o| [o, o.foos]}.should == [[@foo, [@foo]]]
1005
+ @db.sqls.should == ["SELECT foos.id, foos.object_ids, foos_0.id AS foos_0_id, foos_0.object_ids AS foos_0_object_ids FROM foos LEFT OUTER JOIN f ON (f.l[0] = foos.object_ids[0]) LEFT OUTER JOIN f AS f_0 ON (f_0.l[1] = f.r[0]) LEFT OUTER JOIN foos AS foos_0 ON (foos_0.object_ids[0] = f_0.r[1])"]
1006
+ end
1007
+
1008
+ it "should have working filter by associations with model instances" do
1009
+ @Foo.first(:foos=>@foo).should == @foo
1010
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT f.l[0] FROM f INNER JOIN f AS f_0 ON (f_0.l[1] = f.r[0]) WHERE ((f_0.r[1] = 2) AND (f.l[0] IS NOT NULL)))) LIMIT 1"]
1011
+ end
1012
+
1013
+ it "should have working filter by associations with model datasets" do
1014
+ @Foo.first(:foos=>@Foo.where(:id=>@foo.id)).should == @foo
1015
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT f.l[0] FROM f INNER JOIN f AS f_0 ON (f_0.l[1] = f.r[0]) WHERE ((f_0.r[1] IN (SELECT foos.object_ids[0] FROM foos WHERE ((id = 1) AND (foos.object_ids[0] IS NOT NULL)))) AND (f.l[0] IS NOT NULL)))) LIMIT 1"]
1016
+ end
1017
+ end