sequel 4.45.0 → 4.46.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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +108 -0
  3. data/doc/release_notes/4.46.0.txt +404 -0
  4. data/doc/security.rdoc +9 -0
  5. data/doc/sql.rdoc +2 -2
  6. data/doc/testing.rdoc +1 -1
  7. data/doc/validations.rdoc +1 -2
  8. data/lib/sequel/adapters/ado.rb +8 -3
  9. data/lib/sequel/adapters/ado/access.rb +8 -4
  10. data/lib/sequel/adapters/ado/mssql.rb +3 -1
  11. data/lib/sequel/adapters/amalgalite.rb +5 -0
  12. data/lib/sequel/adapters/cubrid.rb +16 -7
  13. data/lib/sequel/adapters/do.rb +7 -1
  14. data/lib/sequel/adapters/do/mysql.rb +8 -4
  15. data/lib/sequel/adapters/ibmdb.rb +10 -5
  16. data/lib/sequel/adapters/jdbc.rb +8 -2
  17. data/lib/sequel/adapters/jdbc/as400.rb +10 -3
  18. data/lib/sequel/adapters/jdbc/db2.rb +27 -16
  19. data/lib/sequel/adapters/jdbc/derby.rb +47 -20
  20. data/lib/sequel/adapters/jdbc/h2.rb +13 -7
  21. data/lib/sequel/adapters/jdbc/hsqldb.rb +18 -9
  22. data/lib/sequel/adapters/jdbc/mssql.rb +5 -2
  23. data/lib/sequel/adapters/jdbc/mysql.rb +3 -2
  24. data/lib/sequel/adapters/jdbc/oracle.rb +3 -2
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -3
  26. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +2 -1
  27. data/lib/sequel/adapters/jdbc/sqlite.rb +10 -3
  28. data/lib/sequel/adapters/jdbc/sqlserver.rb +23 -0
  29. data/lib/sequel/adapters/jdbc/transactions.rb +16 -10
  30. data/lib/sequel/adapters/mock.rb +5 -0
  31. data/lib/sequel/adapters/mysql.rb +8 -1
  32. data/lib/sequel/adapters/mysql2.rb +6 -1
  33. data/lib/sequel/adapters/odbc.rb +20 -8
  34. data/lib/sequel/adapters/odbc/mssql.rb +6 -3
  35. data/lib/sequel/adapters/oracle.rb +12 -6
  36. data/lib/sequel/adapters/postgres.rb +20 -8
  37. data/lib/sequel/adapters/shared/access.rb +76 -47
  38. data/lib/sequel/adapters/shared/cubrid.rb +16 -11
  39. data/lib/sequel/adapters/shared/db2.rb +46 -19
  40. data/lib/sequel/adapters/shared/firebird.rb +20 -8
  41. data/lib/sequel/adapters/shared/informix.rb +6 -3
  42. data/lib/sequel/adapters/shared/mssql.rb +132 -72
  43. data/lib/sequel/adapters/shared/mysql.rb +112 -65
  44. data/lib/sequel/adapters/shared/oracle.rb +36 -21
  45. data/lib/sequel/adapters/shared/postgres.rb +91 -56
  46. data/lib/sequel/adapters/shared/sqlanywhere.rb +65 -37
  47. data/lib/sequel/adapters/shared/sqlite.rb +67 -32
  48. data/lib/sequel/adapters/sqlanywhere.rb +9 -1
  49. data/lib/sequel/adapters/sqlite.rb +8 -1
  50. data/lib/sequel/adapters/swift.rb +5 -0
  51. data/lib/sequel/adapters/swift/mysql.rb +4 -2
  52. data/lib/sequel/adapters/swift/sqlite.rb +1 -1
  53. data/lib/sequel/adapters/tinytds.rb +10 -3
  54. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
  55. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +1 -1
  56. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
  57. data/lib/sequel/adapters/utils/pg_types.rb +14 -6
  58. data/lib/sequel/adapters/utils/replace.rb +4 -2
  59. data/lib/sequel/connection_pool/single.rb +2 -2
  60. data/lib/sequel/core.rb +24 -11
  61. data/lib/sequel/database/connecting.rb +9 -3
  62. data/lib/sequel/database/dataset_defaults.rb +7 -1
  63. data/lib/sequel/database/logging.rb +1 -0
  64. data/lib/sequel/database/misc.rb +5 -2
  65. data/lib/sequel/database/query.rb +7 -5
  66. data/lib/sequel/database/schema_generator.rb +1 -0
  67. data/lib/sequel/database/schema_methods.rb +50 -27
  68. data/lib/sequel/database/transactions.rb +19 -9
  69. data/lib/sequel/dataset/actions.rb +15 -6
  70. data/lib/sequel/dataset/graph.rb +15 -5
  71. data/lib/sequel/dataset/misc.rb +12 -4
  72. data/lib/sequel/dataset/mutation.rb +17 -8
  73. data/lib/sequel/dataset/prepared_statements.rb +3 -2
  74. data/lib/sequel/dataset/query.rb +84 -38
  75. data/lib/sequel/dataset/sql.rb +302 -191
  76. data/lib/sequel/deprecated.rb +26 -17
  77. data/lib/sequel/extensions/_deprecated_identifier_mangling.rb +2 -2
  78. data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
  79. data/lib/sequel/extensions/from_block.rb +1 -0
  80. data/lib/sequel/extensions/graph_each.rb +1 -1
  81. data/lib/sequel/extensions/identifier_mangling.rb +2 -2
  82. data/lib/sequel/extensions/migration.rb +28 -4
  83. data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -0
  84. data/lib/sequel/extensions/schema_dumper.rb +4 -4
  85. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +5 -3
  86. data/lib/sequel/extensions/set_overrides.rb +2 -0
  87. data/lib/sequel/extensions/split_array_nil.rb +2 -2
  88. data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
  89. data/lib/sequel/model.rb +11 -7
  90. data/lib/sequel/model/associations.rb +5 -7
  91. data/lib/sequel/model/base.rb +47 -45
  92. data/lib/sequel/model/dataset_module.rb +9 -14
  93. data/lib/sequel/model/plugins.rb +3 -0
  94. data/lib/sequel/no_core_ext.rb +1 -0
  95. data/lib/sequel/plugins/blacklist_security.rb +1 -1
  96. data/lib/sequel/plugins/boolean_subsets.rb +7 -5
  97. data/lib/sequel/plugins/class_table_inheritance.rb +47 -10
  98. data/lib/sequel/plugins/dataset_associations.rb +1 -1
  99. data/lib/sequel/plugins/def_dataset_method.rb +90 -0
  100. data/lib/sequel/plugins/finder.rb +240 -0
  101. data/lib/sequel/plugins/inverted_subsets.rb +19 -12
  102. data/lib/sequel/plugins/many_through_many.rb +1 -1
  103. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  104. data/lib/sequel/plugins/schema.rb +1 -1
  105. data/lib/sequel/plugins/single_table_inheritance.rb +7 -1
  106. data/lib/sequel/plugins/subset_conditions.rb +11 -3
  107. data/lib/sequel/plugins/whitelist_security.rb +118 -0
  108. data/lib/sequel/sql.rb +80 -36
  109. data/lib/sequel/timezones.rb +2 -0
  110. data/lib/sequel/version.rb +1 -1
  111. data/spec/adapters/mssql_spec.rb +20 -0
  112. data/spec/adapters/mysql_spec.rb +1 -1
  113. data/spec/adapters/oracle_spec.rb +12 -8
  114. data/spec/adapters/postgres_spec.rb +1 -1
  115. data/spec/adapters/spec_helper.rb +1 -1
  116. data/spec/adapters/sqlite_spec.rb +36 -34
  117. data/spec/core/connection_pool_spec.rb +2 -1
  118. data/spec/core/database_spec.rb +87 -9
  119. data/spec/core/dataset_spec.rb +501 -129
  120. data/spec/core/deprecated_spec.rb +1 -1
  121. data/spec/core/expression_filters_spec.rb +146 -60
  122. data/spec/core/mock_adapter_spec.rb +1 -1
  123. data/spec/core/object_graph_spec.rb +61 -9
  124. data/spec/core/placeholder_literalizer_spec.rb +20 -2
  125. data/spec/core/schema_generator_spec.rb +6 -6
  126. data/spec/core/schema_spec.rb +54 -5
  127. data/spec/core_extensions_spec.rb +122 -18
  128. data/spec/deprecation_helper.rb +27 -2
  129. data/spec/extensions/_deprecated_identifier_mangling_spec.rb +6 -6
  130. data/spec/extensions/association_proxies_spec.rb +2 -2
  131. data/spec/extensions/auto_literal_strings_spec.rb +212 -0
  132. data/spec/extensions/blacklist_security_spec.rb +1 -0
  133. data/spec/extensions/class_table_inheritance_spec.rb +1037 -39
  134. data/spec/extensions/column_select_spec.rb +20 -8
  135. data/spec/extensions/columns_introspection_spec.rb +3 -3
  136. data/spec/extensions/core_refinements_spec.rb +29 -12
  137. data/spec/extensions/dataset_associations_spec.rb +12 -12
  138. data/spec/extensions/def_dataset_method_spec.rb +100 -0
  139. data/spec/extensions/error_sql_spec.rb +1 -1
  140. data/spec/extensions/finder_spec.rb +260 -0
  141. data/spec/extensions/graph_each_spec.rb +2 -2
  142. data/spec/extensions/identifier_mangling_spec.rb +14 -8
  143. data/spec/extensions/inverted_subsets_spec.rb +4 -4
  144. data/spec/extensions/lazy_attributes_spec.rb +7 -0
  145. data/spec/extensions/many_through_many_spec.rb +38 -14
  146. data/spec/extensions/nested_attributes_spec.rb +18 -6
  147. data/spec/extensions/no_auto_literal_strings_spec.rb +1 -1
  148. data/spec/extensions/pg_enum_spec.rb +16 -1
  149. data/spec/extensions/pg_interval_spec.rb +11 -2
  150. data/spec/extensions/pg_loose_count_spec.rb +5 -0
  151. data/spec/extensions/pg_row_spec.rb +25 -0
  152. data/spec/extensions/prepared_statements_spec.rb +10 -1
  153. data/spec/extensions/query_spec.rb +2 -2
  154. data/spec/extensions/schema_dumper_spec.rb +2 -2
  155. data/spec/extensions/schema_spec.rb +2 -2
  156. data/spec/extensions/set_overrides_spec.rb +7 -3
  157. data/spec/extensions/sql_expr_spec.rb +0 -1
  158. data/spec/extensions/subset_conditions_spec.rb +6 -6
  159. data/spec/extensions/table_select_spec.rb +24 -12
  160. data/spec/extensions/to_dot_spec.rb +4 -4
  161. data/spec/extensions/whitelist_security_spec.rb +131 -0
  162. data/spec/integration/dataset_test.rb +9 -5
  163. data/spec/integration/model_test.rb +2 -0
  164. data/spec/integration/plugin_test.rb +2 -2
  165. data/spec/integration/spec_helper.rb +1 -1
  166. data/spec/model/associations_spec.rb +39 -11
  167. data/spec/model/base_spec.rb +44 -24
  168. data/spec/model/class_dataset_methods_spec.rb +18 -16
  169. data/spec/model/dataset_methods_spec.rb +4 -4
  170. data/spec/model/eager_loading_spec.rb +84 -24
  171. data/spec/model/model_spec.rb +97 -63
  172. data/spec/model/record_spec.rb +21 -13
  173. metadata +13 -2
@@ -5,7 +5,7 @@ module Sequel
5
5
  MAJOR = 4
6
6
  # The minor version of Sequel. Bumped for every non-patch level
7
7
  # release, generally around once a month.
8
- MINOR = 45
8
+ MINOR = 46
9
9
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
10
10
  # releases that fix regressions from previous versions.
11
11
  TINY = 0
@@ -68,6 +68,26 @@ describe "MSSQL" do
68
68
  it "should should support OUTER APPLY" do
69
69
  @db[:test3].outer_apply(@db[:test4].where(Sequel[:test3][:v3]=>Sequel[:test4][:v4])).select_order_map([:v3, :v4]).must_equal [[1,1], [2, nil]]
70
70
  end
71
+
72
+ cspecify "should handle time values with fractional seconds", [:ado] do
73
+ # ado: Returns nil values
74
+ t = Sequel::SQLTime.create(10, 20, 30, 999900)
75
+ v = @db.get(Sequel.cast(t, 'time'))
76
+ v = Sequel.string_to_time(v) if v.is_a?(String)
77
+ pr = lambda{|x| [:hour, :min, :sec, :usec].map{|m| x.send(m)}}
78
+ pr[v].must_equal(pr[t])
79
+ end
80
+
81
+ cspecify "should get datetimeoffset values as Time with fractional seconds", [:odbc], [:ado], [:tinytds, proc{|db| TinyTds::VERSION < '0.9'}] do
82
+ # odbc: Returns string rounded to nearest second
83
+ # ado: Returns nil values
84
+ # tiny_tds < 0.9: Returns wrong value for hour
85
+ t = Time.local(2010, 11, 12, 10, 20, 30, 999000)
86
+ v = @db.get(Sequel.cast(t, 'datetimeoffset'))
87
+ v = Sequel.string_to_datetime(v) if v.is_a?(String)
88
+ pr = lambda{|x| [:year, :month, :day, :hour, :min, :sec, :usec].map{|m| x.send(m)}}
89
+ pr[v].must_equal(pr[t])
90
+ end
71
91
  end
72
92
 
73
93
  # This spec is currently disabled as the SQL Server 2008 R2 Express doesn't support
@@ -354,7 +354,7 @@ describe "A MySQL database" do
354
354
 
355
355
  it "should handle qualified tables in #indexes" do
356
356
  DB.create_table!(:test_innodb){primary_key :id; String :name; index :name, :unique=>true, :name=>:test_innodb_name_idx}
357
- DB.indexes(Sequel.qualify(DB.get{database{}}, :test_innodb)).must_equal(:test_innodb_name_idx=>{:unique=>true, :columns=>[:name]})
357
+ DB.indexes(Sequel.qualify(DB.get{database.function}, :test_innodb)).must_equal(:test_innodb_name_idx=>{:unique=>true, :columns=>[:name]})
358
358
  end
359
359
  end
360
360
 
@@ -63,15 +63,19 @@ describe "An Oracle database" do
63
63
  DB.view_exists?(:cats).must_equal false
64
64
  DB.create_view(:cats, DB[:categories])
65
65
  DB.view_exists?(:cats).must_equal true
66
- om = DB.identifier_output_method
67
- im = DB.identifier_input_method
68
- DB.identifier_output_method = :reverse
69
- DB.identifier_input_method = :reverse
70
- DB.view_exists?(:STAC).must_equal true
71
- DB.view_exists?(:cats).must_equal false
66
+ if IDENTIFIER_MANGLING && !DB.frozen?
67
+ om = DB.identifier_output_method
68
+ im = DB.identifier_input_method
69
+ DB.identifier_output_method = :reverse
70
+ DB.identifier_input_method = :reverse
71
+ DB.view_exists?(:STAC).must_equal true
72
+ DB.view_exists?(:cats).must_equal false
73
+ end
72
74
  ensure
73
- DB.identifier_output_method = om
74
- DB.identifier_input_method = im
75
+ if IDENTIFIER_MANGLING && !DB.frozen?
76
+ DB.identifier_output_method = om
77
+ DB.identifier_input_method = im
78
+ end
75
79
  DB.drop_view(:cats)
76
80
  end
77
81
  end
@@ -463,7 +463,7 @@ describe "A PostgreSQL dataset" do
463
463
  end if DB.server_version >= 90300
464
464
 
465
465
  it "should support ordered-set and hypothetical-set aggregate functions" do
466
- @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{mode{}.within_group(:a)}.must_equal 1
466
+ @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{mode.function.within_group(:a)}.must_equal 1
467
467
  end if DB.server_version >= 90400
468
468
 
469
469
  it "should support filtered aggregate functions" do
@@ -40,7 +40,7 @@ class Minitest::HooksSpec
40
40
  end
41
41
  end
42
42
 
43
- IDENTIFIER_MANGLING = !ENV['SEQUEL_NO_MANGLE'] unless defined?(IDENTIFIER_MANGLING)
43
+ IDENTIFIER_MANGLING = !!ENV['SEQUEL_IDENTIFIER_MANGLING'] unless defined?(IDENTIFIER_MANGLING)
44
44
 
45
45
  unless defined?(DB)
46
46
  env_var = "SEQUEL_#{SEQUEL_ADAPTER_TEST.to_s.upcase}_URL"
@@ -5,19 +5,23 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
5
5
  describe "An SQLite database" do
6
6
  before do
7
7
  @db = DB
8
+ deprecated do
8
9
  @fk = @db.foreign_keys
10
+ end
9
11
  end
10
12
  after do
11
13
  @db.drop_table?(:fk)
12
- @db.auto_vacuum = :none
13
- @db.run 'VACUUM'
14
- @db.foreign_keys = @fk
15
- @db.case_sensitive_like = true
14
+ deprecated do
15
+ @db.auto_vacuum = :none
16
+ @db.run 'VACUUM'
17
+ @db.foreign_keys = @fk
18
+ @db.case_sensitive_like = true
19
+ end
16
20
  @db.use_timestamp_timezones = false
17
21
  Sequel.datetime_class = Time
18
22
  end
19
23
 
20
- it "should support getting setting pragma values" do
24
+ deprecated "should support getting setting pragma values" do
21
25
  @db.pragma_set(:auto_vacuum, '0')
22
26
  @db.run 'VACUUM'
23
27
  @db.pragma_get(:auto_vacuum).to_s.must_equal '0'
@@ -29,7 +33,7 @@ describe "An SQLite database" do
29
33
  @db.pragma_get(:auto_vacuum).to_s.must_equal '2'
30
34
  end
31
35
 
32
- it "should support getting and setting the auto_vacuum pragma" do
36
+ deprecated "should support getting and setting the auto_vacuum pragma" do
33
37
  @db.auto_vacuum = :full
34
38
  @db.run 'VACUUM'
35
39
  @db.auto_vacuum.must_equal :full
@@ -40,12 +44,12 @@ describe "An SQLite database" do
40
44
  proc {@db.auto_vacuum = :invalid}.must_raise(Sequel::Error)
41
45
  end
42
46
 
43
- it "should respect case sensitive like false" do
47
+ deprecated "should respect case sensitive like false" do
44
48
  @db.case_sensitive_like = false
45
49
  @db.get(Sequel.like('a', 'A')).to_s.must_equal '1'
46
50
  end
47
51
 
48
- it "should respect case sensitive like true" do
52
+ deprecated "should respect case sensitive like true" do
49
53
  @db.case_sensitive_like = true
50
54
  @db.get(Sequel.like('a', 'A')).to_s.must_equal '0'
51
55
  end
@@ -63,13 +67,13 @@ describe "An SQLite database" do
63
67
  @db.sqlite_version.must_be_kind_of(Integer)
64
68
  end
65
69
 
66
- it "should support setting and getting the foreign_keys pragma" do
70
+ deprecated "should support setting and getting the foreign_keys pragma" do
67
71
  (@db.sqlite_version >= 30619 ? [true, false] : [nil]).must_include(@db.foreign_keys)
68
72
  @db.foreign_keys = true
69
73
  @db.foreign_keys = false
70
74
  end
71
75
 
72
- it "should enforce foreign key integrity if foreign_keys pragma is set" do
76
+ deprecated "should enforce foreign key integrity if foreign_keys pragma is set" do
73
77
  @db.foreign_keys = true
74
78
  @db.create_table!(:fk){primary_key :id; foreign_key :parent_id, :fk}
75
79
  @db[:fk].insert(1, nil)
@@ -78,7 +82,7 @@ describe "An SQLite database" do
78
82
  proc{@db[:fk].insert(4, 5)}.must_raise(Sequel::ForeignKeyConstraintViolation, Sequel::ConstraintViolation, Sequel::DatabaseError)
79
83
  end if DB.sqlite_version >= 30619
80
84
 
81
- it "should not enforce foreign key integrity if foreign_keys pragma is unset" do
85
+ deprecated "should not enforce foreign key integrity if foreign_keys pragma is unset" do
82
86
  @db.foreign_keys = false
83
87
  @db.create_table!(:fk){primary_key :id; foreign_key :parent_id, :fk}
84
88
  @db[:fk].insert(1, 2)
@@ -104,7 +108,7 @@ describe "An SQLite database" do
104
108
  @db.tables.must_include(:fk)
105
109
  end
106
110
 
107
- it "should support getting and setting the synchronous pragma" do
111
+ deprecated "should support getting and setting the synchronous pragma" do
108
112
  @db.synchronous = :off
109
113
  @db.synchronous.must_equal :off
110
114
  @db.synchronous = :normal
@@ -115,7 +119,7 @@ describe "An SQLite database" do
115
119
  proc {@db.synchronous = :invalid}.must_raise(Sequel::Error)
116
120
  end
117
121
 
118
- it "should support getting and setting the temp_store pragma" do
122
+ deprecated "should support getting and setting the temp_store pragma" do
119
123
  @db.temp_store = :default
120
124
  @db.temp_store.must_equal :default
121
125
  @db.temp_store = :file
@@ -527,30 +531,28 @@ describe "A SQLite database" do
527
531
  @db[:test3].select(:id).all.must_equal [{:id => 1}, {:id => 3}]
528
532
  end
529
533
 
530
- if DB.foreign_keys
531
- it "should keep foreign keys when dropping a column" do
532
- @db.create_table! :test do
533
- primary_key :id
534
- String :name
535
- Integer :value
536
- end
537
- @db.create_table! :test3 do
538
- String :name
539
- Integer :value
540
- foreign_key :test_id, :test, :on_delete => :set_null, :on_update => :cascade
541
- end
534
+ it "should keep foreign keys when dropping a column" do
535
+ @db.create_table! :test do
536
+ primary_key :id
537
+ String :name
538
+ Integer :value
539
+ end
540
+ @db.create_table! :test3 do
541
+ String :name
542
+ Integer :value
543
+ foreign_key :test_id, :test, :on_delete => :set_null, :on_update => :cascade
544
+ end
542
545
 
543
- @db[:test3].insert(:name => "abc", :test_id => @db[:test].insert(:name => "foo", :value => 3))
544
- @db[:test3].insert(:name => "def", :test_id => @db[:test].insert(:name => "bar", :value => 4))
546
+ @db[:test3].insert(:name => "abc", :test_id => @db[:test].insert(:name => "foo", :value => 3))
547
+ @db[:test3].insert(:name => "def", :test_id => @db[:test].insert(:name => "bar", :value => 4))
545
548
 
546
- @db.drop_column :test3, :value
549
+ @db.drop_column :test3, :value
547
550
 
548
- @db[:test].filter(:name => 'bar').delete
549
- @db[:test3][:name => 'def'][:test_id].must_be_nil
551
+ @db[:test].filter(:name => 'bar').delete
552
+ @db[:test3][:name => 'def'][:test_id].must_be_nil
550
553
 
551
- @db[:test].filter(:name => 'foo').update(:id=>100)
552
- @db[:test3][:name => 'abc'][:test_id].must_equal 100
553
- end
554
+ @db[:test].filter(:name => 'foo').update(:id=>100)
555
+ @db[:test3][:name => 'abc'][:test_id].must_equal 100
554
556
  end
555
557
 
556
558
  it "should support rename_column operations" do
@@ -690,7 +692,7 @@ describe "A SQLite database" do
690
692
  sqls.last.must_equal "BEGIN DEFERRED TRANSACTION"
691
693
  end
692
694
  @db.transaction do
693
- sqls.last.must_equal Sequel::Database::SQL_BEGIN
695
+ sqls.last.must_equal 'BEGIN'
694
696
  end
695
697
 
696
698
  @db.transaction_mode.must_be_nil
@@ -761,11 +761,12 @@ describe "SingleConnectionPool" do
761
761
  it "should provide a #disconnect method" do
762
762
  conn = nil
763
763
  x = nil
764
- pool = Sequel::ConnectionPool.get_pool(mock_db.call(proc{|c| conn = c}){1234}, ST_CONNECTION_POOL_DEFAULTS)
764
+ pool = Sequel::ConnectionPool.get_pool(mock_db.call(proc{|c| conn = c; c.must_be_kind_of(Integer)}){1234}, ST_CONNECTION_POOL_DEFAULTS)
765
765
  pool.hold{|c| x = c}
766
766
  x.must_equal 1234
767
767
  pool.disconnect
768
768
  conn.must_equal 1234
769
+ pool.disconnect
769
770
  end
770
771
  end
771
772
 
@@ -38,6 +38,7 @@ describe "A new Database" do
38
38
  it "should support :preconnect option to preconnect to database" do
39
39
  @db.pool.size.must_equal 0
40
40
  c = Class.new(Sequel::Database) do
41
+ def dataset_class_default; Sequel::Dataset end
41
42
  def connect(_)
42
43
  :connect
43
44
  end
@@ -191,17 +192,17 @@ describe "Database#log_yield" do
191
192
  @db = Sequel::Database.new(:logger=>@o)
192
193
  end
193
194
 
194
- it "should yield to the passed block" do
195
+ deprecated "should yield to the passed block" do
195
196
  a = nil
196
197
  @db.log_yield('blah'){a = 1}
197
198
  a.must_equal 1
198
199
  end
199
200
 
200
- it "should raise an exception if a block is not passed" do
201
+ deprecated "should raise an exception if a block is not passed" do
201
202
  proc{@db.log_yield('blah')}.must_raise LocalJumpError
202
203
  end
203
204
 
204
- it "should log message with duration at info level to all loggers" do
205
+ deprecated "should log message with duration at info level to all loggers" do
205
206
  @db.log_yield('blah'){}
206
207
  @o.logs.length.must_equal 1
207
208
  @o.logs.first.length.must_equal 2
@@ -209,7 +210,7 @@ describe "Database#log_yield" do
209
210
  @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
210
211
  end
211
212
 
212
- it "should respect sql_log_level setting" do
213
+ deprecated "should respect sql_log_level setting" do
213
214
  @db.sql_log_level = :debug
214
215
  @db.log_yield('blah'){}
215
216
  @o.logs.length.must_equal 1
@@ -218,7 +219,7 @@ describe "Database#log_yield" do
218
219
  @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
219
220
  end
220
221
 
221
- it "should log message with duration at warn level if duration greater than log_warn_duration" do
222
+ deprecated "should log message with duration at warn level if duration greater than log_warn_duration" do
222
223
  @db.log_warn_duration = 0
223
224
  @db.log_yield('blah'){}
224
225
  @o.logs.length.must_equal 1
@@ -227,7 +228,7 @@ describe "Database#log_yield" do
227
228
  @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
228
229
  end
229
230
 
230
- it "should log message with duration at info level if duration less than log_warn_duration" do
231
+ deprecated "should log message with duration at info level if duration less than log_warn_duration" do
231
232
  @db.log_warn_duration = 1000
232
233
  @db.log_yield('blah'){}
233
234
  @o.logs.length.must_equal 1
@@ -236,7 +237,7 @@ describe "Database#log_yield" do
236
237
  @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
237
238
  end
238
239
 
239
- it "should log message at error level if block raises an error" do
240
+ deprecated "should log message at error level if block raises an error" do
240
241
  @db.log_warn_duration = 0
241
242
  proc{@db.log_yield('blah'){raise Sequel::Error, 'adsf'}}.must_raise Sequel::Error
242
243
  @o.logs.length.must_equal 1
@@ -245,7 +246,7 @@ describe "Database#log_yield" do
245
246
  @o.logs.first.last.must_match(/\ASequel::Error: adsf: blah\z/)
246
247
  end
247
248
 
248
- it "should include args with message if args passed" do
249
+ deprecated "should include args with message if args passed" do
249
250
  @db.log_yield('blah', [1, 2]){}
250
251
  @o.logs.length.must_equal 1
251
252
  @o.logs.first.length.must_equal 2
@@ -259,6 +260,7 @@ describe "Database#log_connection_yield" do
259
260
  @o = Object.new
260
261
  def @o.logs; @logs || []; end
261
262
  def @o.to_ary; [self]; end
263
+ def @o.warn(*args); (@logs ||= []) << [:warn] + args; end
262
264
  def @o.method_missing(*args); (@logs ||= []) << args; end
263
265
  @conn = Object.new
264
266
  @db = Sequel::Database.new(:logger=>@o)
@@ -281,11 +283,74 @@ describe "Database#log_connection_yield" do
281
283
  @o.logs.first.first.must_equal :info
282
284
  @o.logs.first.last.must_match(/\(conn: -?\d+\) some SQL\z/)
283
285
  end
286
+
287
+ it "should yield to the passed block" do
288
+ a = nil
289
+ @db.log_connection_yield('blah', @conn){a = 1}
290
+ a.must_equal 1
291
+ end
292
+
293
+ it "should raise an exception if a block is not passed" do
294
+ proc{@db.log_connection_yield('blah', @conn)}.must_raise LocalJumpError
295
+ end
296
+
297
+ it "should log message with duration at info level to all loggers" do
298
+ @db.log_connection_yield('blah', @conn){}
299
+ @o.logs.length.must_equal 1
300
+ @o.logs.first.length.must_equal 2
301
+ @o.logs.first.first.must_equal :info
302
+ @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
303
+ end
304
+
305
+ it "should respect sql_log_level setting" do
306
+ @db.sql_log_level = :debug
307
+ @db.log_connection_yield('blah', @conn){}
308
+ @o.logs.length.must_equal 1
309
+ @o.logs.first.length.must_equal 2
310
+ @o.logs.first.first.must_equal :debug
311
+ @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
312
+ end
313
+
314
+ it "should log message with duration at warn level if duration greater than log_warn_duration" do
315
+ @db.log_warn_duration = 0
316
+ @db.log_connection_yield('blah', @conn){}
317
+ @o.logs.length.must_equal 1
318
+ @o.logs.first.length.must_equal 2
319
+ @o.logs.first.first.must_equal :warn
320
+ @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
321
+ end
322
+
323
+ it "should log message with duration at info level if duration less than log_warn_duration" do
324
+ @db.log_warn_duration = 1000
325
+ @db.log_connection_yield('blah', @conn){}
326
+ @o.logs.length.must_equal 1
327
+ @o.logs.first.length.must_equal 2
328
+ @o.logs.first.first.must_equal :info
329
+ @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah\z/)
330
+ end
331
+
332
+ it "should log message at error level if block raises an error" do
333
+ @db.log_warn_duration = 0
334
+ proc{@db.log_connection_yield('blah', @conn){raise Sequel::Error, 'adsf'}}.must_raise Sequel::Error
335
+ @o.logs.length.must_equal 1
336
+ @o.logs.first.length.must_equal 2
337
+ @o.logs.first.first.must_equal :error
338
+ @o.logs.first.last.must_match(/\ASequel::Error: adsf: blah\z/)
339
+ end
340
+
341
+ it "should include args with message if args passed" do
342
+ @db.log_connection_yield('blah', @conn, [1, 2]){}
343
+ @o.logs.length.must_equal 1
344
+ @o.logs.first.length.must_equal 2
345
+ @o.logs.first.first.must_equal :info
346
+ @o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah; \[1, 2\]\z/)
347
+ end
284
348
  end
285
349
 
286
350
  describe "Database#uri" do
287
351
  before do
288
352
  @c = Class.new(Sequel::Database) do
353
+ def dataset_class_default; Sequel::Dataset end
289
354
  set_adapter_scheme :mau
290
355
  end
291
356
 
@@ -310,6 +375,7 @@ describe "Database.adapter_scheme and #adapter_scheme" do
310
375
  Sequel::Database.adapter_scheme.must_be_nil
311
376
 
312
377
  @c = Class.new(Sequel::Database) do
378
+ def dataset_class_default; Sequel::Dataset end
313
379
  set_adapter_scheme :mau
314
380
  end
315
381
 
@@ -723,6 +789,18 @@ DatabaseTransactionSpecs = shared_description do
723
789
  @db.sqls.must_equal ['BEGIN']
724
790
  end
725
791
 
792
+ it "should raise original exception if there is an exception raised when rolling back when using :rollback=>:always" do
793
+ ec = Class.new(StandardError)
794
+ meta_def(@db, :database_error_classes){[ec]}
795
+ meta_def(@db, :log_connection_execute){|c, sql| sql =~ /ROLLBACK/ ? raise(ec, 'bad') : super(c, sql)}
796
+ begin
797
+ @db.transaction(:rollback=>:always){}
798
+ rescue => e
799
+ end
800
+ e.must_be_kind_of(ec)
801
+ @db.sqls.must_equal ['BEGIN']
802
+ end
803
+
726
804
  it "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
727
805
  @db.transaction do
728
806
  @db.drop_table(:a)
@@ -834,7 +912,7 @@ DatabaseTransactionSpecs = shared_description do
834
912
  tr.must_be :empty?
835
913
  end
836
914
 
837
- it "should correctly handle nested transacation use with separate shards" do
915
+ it "should correctly handle nested transaction use with separate shards" do
838
916
  @db.transaction do |c1|
839
917
  @db.transaction(:server=>:test) do |c2|
840
918
  c1.wont_equal c2
@@ -310,10 +310,13 @@ describe "Dataset#unused_table_alias" do
310
310
  @ds.from(:test, :test_0).cross_join(:test_1).unused_table_alias(:test).must_equal :test_2
311
311
  end
312
312
 
313
- it "should return an appropriate symbol if given other forms of identifiers" do
314
- @ds.unused_table_alias('test').must_equal :test_0
313
+ with_symbol_splitting "should return an appropriate symbol if given splittable symbol" do
315
314
  @ds.unused_table_alias(:b__t___test).must_equal :test_0
316
315
  @ds.unused_table_alias(:b__test).must_equal :test_0
316
+ end
317
+
318
+ it "should return an appropriate symbol if given other forms of identifiers" do
319
+ @ds.unused_table_alias('test').must_equal :test_0
317
320
  @ds.unused_table_alias(Sequel.qualify(:b, :test)).must_equal :test_0
318
321
  @ds.unused_table_alias(Sequel.expr(:b).as(:test)).must_equal :test_0
319
322
  @ds.unused_table_alias(Sequel.expr(:b).as(Sequel.identifier(:test))).must_equal :test_0
@@ -346,65 +349,116 @@ describe "Dataset#where" do
346
349
  before do
347
350
  @dataset = Sequel.mock[:test]
348
351
  @d1 = @dataset.where(:region => 'Asia')
349
- @d2 = @dataset.where('region = ?', 'Asia')
350
- @d3 = @dataset.where("a = 1")
351
352
  end
352
353
 
353
- it "should just clone if given an empty argument" do
354
+ if false # SEQUEL5: remove if
355
+ it "should just clone if given no arguments or block" do
356
+ proc{@dataset.where}.must_raise Sequel::Error
357
+ end
358
+
359
+ it "should handle nil argument if block is given" do
360
+ @d1.where(nil){a}.sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND NULL AND a)"
361
+ end
362
+
363
+ it "should handle nil argument if block has no existing filter" do
364
+ @dataset.where(nil).sql.must_equal "SELECT * FROM test WHERE NULL"
365
+ end
366
+ end
367
+
368
+ it "should just clone if given an empty array or hash argument" do
354
369
  @dataset.where({}).sql.must_equal @dataset.sql
355
370
  @dataset.where([]).sql.must_equal @dataset.sql
356
- @dataset.where('').sql.must_equal @dataset.sql
357
371
 
358
372
  @dataset.filter({}).sql.must_equal @dataset.sql
359
373
  @dataset.filter([]).sql.must_equal @dataset.sql
374
+ end
375
+
376
+ deprecated "should just clone if given an empty string argument" do
377
+ @dataset.where('').sql.must_equal @dataset.sql
360
378
  @dataset.filter('').sql.must_equal @dataset.sql
361
379
  end
362
380
 
381
+ deprecated "should just clone if given no arguments or block" do
382
+ @dataset.where.sql.must_equal @dataset.sql
383
+ @dataset.filter.sql.must_equal @dataset.sql
384
+ end
385
+
386
+ deprecated "should ignore nil argument if block is given" do
387
+ @d1.where(nil){a}.sql.must_equal @d1.where(:a).sql
388
+ end
389
+
390
+ deprecated "should ignore nil argument if block has no existing filter" do
391
+ @dataset.where(nil).sql.must_equal @dataset.sql
392
+ end
393
+
363
394
  it "should work with hashes" do
364
395
  @dataset.where(:name => 'xyz', :price => 342).select_sql.
365
396
  must_match(/WHERE \(\(name = 'xyz'\) AND \(price = 342\)\)|WHERE \(\(price = 342\) AND \(name = 'xyz'\)\)/)
366
397
  end
367
398
 
368
- it "should work with a string with placeholders and arguments for those placeholders" do
399
+ deprecated "should handle literal strings in arrays in filter methods" do
400
+ @dataset.where([Sequel.lit("a")]).sql.must_equal 'SELECT * FROM test WHERE (a)'
401
+ end
402
+
403
+ deprecated "should work with a string with placeholders and arguments for those placeholders" do
369
404
  @dataset.where('price < ? AND id in ?', 100, [1, 2, 3]).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
370
405
  end
371
406
 
372
- it "should not modify passed array with placeholders" do
407
+ it "should work with a placeholder literal string" do
408
+ @dataset.where(Sequel.lit('price < ? AND id in ?', 100, [1, 2, 3])).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
409
+ end
410
+
411
+ deprecated "should not modify passed array with placeholders" do
373
412
  a = ['price < ? AND id in ?', 100, 1, 2, 3]
374
413
  b = a.dup
375
414
  @dataset.where(a)
376
415
  b.must_equal a
377
416
  end
378
417
 
379
- it "should work with strings (custom SQL expressions)" do
380
- @dataset.where('(a = 1 AND b = 2)').select_sql.
381
- must_equal "SELECT * FROM test WHERE ((a = 1 AND b = 2))"
418
+ deprecated "should work with strings (custom SQL expressions)" do
419
+ @dataset.where('(a = 1 AND b = 2)').select_sql.must_equal "SELECT * FROM test WHERE ((a = 1 AND b = 2))"
420
+ end
421
+
422
+ it "should work with literal strings" do
423
+ @dataset.where(Sequel.lit('(a = 1 AND b = 2)')).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1 AND b = 2))"
424
+ end
425
+
426
+ deprecated "should work with a string with named placeholders and a hash of placeholder value arguments" do
427
+ @dataset.where('price < :price AND id in :ids', :price=>100, :ids=>[1, 2, 3]).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
382
428
  end
383
429
 
384
- it "should work with a string with named placeholders and a hash of placeholder value arguments" do
385
- @dataset.where('price < :price AND id in :ids', :price=>100, :ids=>[1, 2, 3]).select_sql.
386
- must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
430
+ it "should work with named placeholder strings" do
431
+ @dataset.where(Sequel.lit('price < :price AND id in :ids', :price=>100, :ids=>[1, 2, 3])).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
387
432
  end
388
433
 
389
- it "should not modify passed array with named placeholders" do
434
+ deprecated "should not modify passed array with named placeholders" do
390
435
  a = ['price < :price AND id in :ids', {:price=>100}]
391
436
  b = a.dup
392
437
  @dataset.where(a)
393
438
  b.must_equal a
394
439
  end
395
440
 
396
- it "should not replace named placeholders that don't exist in the hash" do
441
+ deprecated "should not replace named placeholders that don't exist in the hash" do
397
442
  @dataset.where('price < :price AND id in :ids', :price=>100).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in :ids)"
398
443
  end
399
444
 
400
- it "should raise an error for a mismatched number of placeholders" do
445
+ it "should not replace named placeholders that don't exist in the hash when using placeholder strings" do
446
+ @dataset.where(Sequel.lit('price < :price AND id in :ids', :price=>100)).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in :ids)"
447
+ end
448
+
449
+ deprecated "should raise an error for a mismatched number of placeholders" do
401
450
  proc{@dataset.where('price < ? AND id in ?', 100).select_sql}.must_raise(Sequel::Error)
402
451
  proc{@dataset.where('price < ? AND id in ?', 100, [1, 2, 3], 4).select_sql}.must_raise(Sequel::Error)
403
452
  end
404
453
 
454
+ it "should raise an error for a mismatched number of placeholders in placeholder literal strings" do
455
+ proc{@dataset.where(Sequel.lit('price < ? AND id in ?', 100)).select_sql}.must_raise(Sequel::Error)
456
+ proc{@dataset.where(Sequel.lit('price < ? AND id in ?', 100, [1, 2, 3], 4)).select_sql}.must_raise(Sequel::Error)
457
+ end
458
+
405
459
  it "should handle placeholders when using an array" do
406
- @dataset.where(Sequel.lit(['price < ', ' AND id in '], 100, [1, 2, 3])).select_sql.must_equal "SELECT * FROM test WHERE price < 100 AND id in (1, 2, 3)"
407
- @dataset.where(Sequel.lit(['price < ', ' AND id in '], 100)).select_sql.must_equal "SELECT * FROM test WHERE price < 100 AND id in "
460
+ @dataset.where(Sequel.lit(['price < ', ' AND id in '], 100, [1, 2, 3])).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
461
+ @dataset.where(Sequel.lit(['price < ', ' AND id in '], 100)).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in )"
408
462
  end
409
463
 
410
464
  it "should handle a mismatched number of placeholders when using an array" do
@@ -412,24 +466,49 @@ describe "Dataset#where" do
412
466
  proc{@dataset.where(Sequel.lit(['price < ', ' AND id in '], 100, [1, 2, 3], 4)).select_sql}.must_raise(Sequel::Error)
413
467
  end
414
468
 
415
- it "should handle partial names" do
469
+ deprecated "should handle partial names" do
416
470
  @dataset.where('price < :price AND id = :p', :p=>2, :price=>100).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id = 2)"
417
471
  end
418
472
 
419
- it "should handle ::cast syntax when no parameters are supplied" do
473
+ it "should handle partial names when using placeholder literal strings" do
474
+ @dataset.where(Sequel.lit('price < :price AND id = :p', :p=>2, :price=>100)).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id = 2)"
475
+ end
476
+
477
+ deprecated "should handle ::cast syntax when no parameters are supplied" do
420
478
  @dataset.where('price::float = 10', {}).select_sql.must_equal "SELECT * FROM test WHERE (price::float = 10)"
421
479
  @dataset.where('price::float ? 10', {}).select_sql.must_equal "SELECT * FROM test WHERE (price::float ? 10)"
422
480
  end
423
481
 
482
+ it "should handle ::cast syntax when no parameters are supplied when using placeholder strings" do
483
+ @dataset.where(Sequel.lit('price::float = 10', {})).select_sql.must_equal "SELECT * FROM test WHERE (price::float = 10)"
484
+ @dataset.where(Sequel.lit('price::float ? 10', {})).select_sql.must_equal "SELECT * FROM test WHERE (price::float ? 10)"
485
+ end
486
+
424
487
  it "should affect select, delete and update statements" do
425
488
  @d1.select_sql.must_equal "SELECT * FROM test WHERE (region = 'Asia')"
426
489
  @d1.delete_sql.must_equal "DELETE FROM test WHERE (region = 'Asia')"
427
490
  @d1.update_sql(:GDP => 0).must_equal "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
491
+ end
428
492
 
493
+ deprecated "should affect select, delete and update statements when using strings" do
494
+ @d2 = @dataset.where('region = ?', 'Asia')
495
+ @d2.select_sql.must_equal "SELECT * FROM test WHERE (region = 'Asia')"
496
+ @d2.delete_sql.must_equal "DELETE FROM test WHERE (region = 'Asia')"
497
+ @d2.update_sql(:GDP => 0).must_equal "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
498
+
499
+ @d3 = @dataset.where("a = 1")
500
+ @d3.select_sql.must_equal "SELECT * FROM test WHERE (a = 1)"
501
+ @d3.delete_sql.must_equal "DELETE FROM test WHERE (a = 1)"
502
+ @d3.update_sql(:GDP => 0).must_equal "UPDATE test SET GDP = 0 WHERE (a = 1)"
503
+ end
504
+
505
+ it "should affect select, delete and update statements when using literal strings" do
506
+ @d2 = @dataset.where(Sequel.lit('region = ?', 'Asia'))
429
507
  @d2.select_sql.must_equal "SELECT * FROM test WHERE (region = 'Asia')"
430
508
  @d2.delete_sql.must_equal "DELETE FROM test WHERE (region = 'Asia')"
431
509
  @d2.update_sql(:GDP => 0).must_equal "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
432
510
 
511
+ @d3 = @dataset.where(Sequel.lit("a = 1"))
433
512
  @d3.select_sql.must_equal "SELECT * FROM test WHERE (a = 1)"
434
513
  @d3.delete_sql.must_equal "DELETE FROM test WHERE (a = 1)"
435
514
  @d3.update_sql(:GDP => 0).must_equal "UPDATE test SET GDP = 0 WHERE (a = 1)"
@@ -437,27 +516,48 @@ describe "Dataset#where" do
437
516
 
438
517
  it "should be composable using AND operator (for scoping)" do
439
518
  @d1.where(:size => 'big').select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (size = 'big'))"
440
- @d1.where('population > 1000').select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (population > 1000))"
441
- @d1.where('(a > 1) OR (b < 2)').select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND ((a > 1) OR (b < 2)))"
442
- @d1.where('GDP > ?', 1000).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
519
+ @d1.where{population > 1000}.select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (population > 1000))"
520
+ @d1.where{(a > 1) | (b < 2)}.select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND ((a > 1) OR (b < 2)))"
521
+ @d1.where{GDP() > 1000}.select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
522
+ end
523
+
524
+ deprecated "should be composable using AND operator (for scoping) when using strings" do
525
+ @d2 = @dataset.where('region = ?', 'Asia')
443
526
  @d2.where('GDP > ?', 1000).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
444
527
  @d2.where(:name => ['Japan', 'China']).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
445
528
  @d2.where('GDP > ?').select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
529
+
530
+ @d3 = @dataset.where("a = 1")
446
531
  @d3.where('b = 2').select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
447
532
  @d3.where(:c => 3).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
448
533
  @d3.where('d = ?', 4).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
449
534
  end
450
535
 
451
- it "should be composable using AND operator (for scoping) with block" do
452
- @d3.where{e < 5}.select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
536
+ it "should be composable using AND operator (for scoping) when using literal strings" do
537
+ @d2 = @dataset.where(Sequel.lit('region = ?', 'Asia'))
538
+ @d2.where(Sequel.lit('GDP > ?', 1000)).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
539
+ @d2.where(:name => ['Japan', 'China']).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
540
+ @d2.where(Sequel.lit('GDP > ?')).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
541
+
542
+ @d3 = @dataset.where(Sequel.lit("a = 1"))
543
+ @d3.where(Sequel.lit('b = 2')).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
544
+ @d3.where(:c => 3).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
545
+ @d3.where(Sequel.lit('d = ?', 4)).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
546
+ end
547
+
548
+ deprecated "should be composable using AND operator (for scoping) with block and string" do
549
+ @dataset.where("a = 1").where{e < 5}.select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
550
+ end
551
+
552
+ it "should be composable using AND operator (for scoping) with block and literal string" do
553
+ @dataset.where(Sequel.lit("a = 1")).where{e < 5}.select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
453
554
  end
454
555
 
455
556
  it "should accept ranges" do
456
557
  @dataset.filter(:id => 4..7).sql.must_equal 'SELECT * FROM test WHERE ((id >= 4) AND (id <= 7))'
457
558
  @dataset.filter(:id => 4...7).sql.must_equal 'SELECT * FROM test WHERE ((id >= 4) AND (id < 7))'
458
-
459
- @dataset.filter(:table__id => 4..7).sql.must_equal 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id <= 7))'
460
- @dataset.filter(:table__id => 4...7).sql.must_equal 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id < 7))'
559
+ @dataset.filter(:id => 4..7).sql.must_equal 'SELECT * FROM test WHERE ((id >= 4) AND (id <= 7))'
560
+ @dataset.filter(:id => 4...7).sql.must_equal 'SELECT * FROM test WHERE ((id >= 4) AND (id < 7))'
461
561
  end
462
562
 
463
563
  it "should accept nil" do
@@ -465,7 +565,7 @@ describe "Dataset#where" do
465
565
  end
466
566
 
467
567
  it "should accept a subquery" do
468
- @dataset.filter('gdp > ?', @d1.select(Sequel.function(:avg, :gdp))).sql.must_equal "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
568
+ @dataset.filter{|o| o.gdp > @d1.select(Sequel.function(:avg, :gdp))}.sql.must_equal "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
469
569
  end
470
570
 
471
571
  it "should handle all types of IN/NOT IN queries with empty arrays" do
@@ -578,16 +678,16 @@ describe "Dataset#where" do
578
678
  x = nil
579
679
  @dataset.filter{|r| x = r; false}
580
680
  x.must_be_kind_of(Sequel::SQL::VirtualRow)
581
- @dataset.filter{|r| ((r.name < 'b') & {r.table__id => 1}) | r.is_active(r.blah, r.xx, r.x__y_z)}.sql.
582
- must_equal "SELECT * FROM test WHERE (((name < 'b') AND (table.id = 1)) OR is_active(blah, xx, x.y_z))"
681
+ @dataset.filter{|r| ((r.name < 'b') & {r.table_id => 1}) | r.is_active(r.blah, r.xx, r.x_y_z)}.sql.
682
+ must_equal "SELECT * FROM test WHERE (((name < 'b') AND (table_id = 1)) OR is_active(blah, xx, x_y_z))"
583
683
  end
584
684
 
585
685
  it "should instance_eval the block in the context of a VirtualRow if the block doesn't request an argument" do
586
686
  x = nil
587
687
  @dataset.filter{x = self; false}
588
688
  x.must_be_kind_of(Sequel::SQL::VirtualRow)
589
- @dataset.filter{((name < 'b') & {table__id => 1}) | is_active(blah, xx, x__y_z)}.sql.
590
- must_equal "SELECT * FROM test WHERE (((name < 'b') AND (table.id = 1)) OR is_active(blah, xx, x.y_z))"
689
+ @dataset.filter{((name < 'b') & {table_id => 1}) | is_active(blah, xx, x_y_z)}.sql.
690
+ must_equal "SELECT * FROM test WHERE (((name < 'b') AND (table_id = 1)) OR is_active(blah, xx, x_y_z))"
591
691
  end
592
692
 
593
693
  it "should handle arbitrary objects" do
@@ -620,9 +720,12 @@ describe "Dataset#or" do
620
720
  @dataset.or(:a => 1).sql.must_equal 'SELECT * FROM test'
621
721
  end
622
722
 
623
- it "should just clone if given an empty argument" do
723
+ it "should just clone if given an empty array or hash argument" do
624
724
  @d1.or({}).sql.must_equal @d1.sql
625
725
  @d1.or([]).sql.must_equal @d1.sql
726
+ end
727
+
728
+ deprecated "should just clone if given an empty string argument" do
626
729
  @d1.or('').sql.must_equal @d1.sql
627
730
  end
628
731
 
@@ -630,8 +733,15 @@ describe "Dataset#or" do
630
733
  @d1.or(:y => 2).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) OR (y = 2))'
631
734
  end
632
735
 
633
- it "should accept all forms of filters" do
736
+ deprecated "should accept string filters" do
634
737
  @d1.or('y > ?', 2).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
738
+ end
739
+
740
+ it "should accept literal string filters" do
741
+ @d1.or(Sequel.lit('y > ?', 2)).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
742
+ end
743
+
744
+ it "should accept expression filters" do
635
745
  @d1.or(Sequel.expr(:yy) > 3).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
636
746
  end
637
747
 
@@ -663,8 +773,15 @@ describe "Dataset#and" do
663
773
  @d1.and(:y => 2).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) AND (y = 2))'
664
774
  end
665
775
 
666
- it "should accept different types of filters" do
776
+ deprecated "should accept string filters with placeholders" do
667
777
  @d1.and('y > ?', 2).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
778
+ end
779
+
780
+ it "should accept placeholder literal string filters" do
781
+ @d1.and(Sequel.lit('y > ?', 2)).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
782
+ end
783
+
784
+ it "should accept expression filters" do
668
785
  @d1.and(Sequel.expr(:yy) > 3).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
669
786
  end
670
787
 
@@ -693,14 +810,22 @@ describe "Dataset#exclude" do
693
810
  /WHERE \(\(name != 'Japan'\) OR \(region != 'Asia'\)\)/))
694
811
  end
695
812
 
696
- it "should parenthesize a single string condition correctly" do
813
+ deprecated "should parenthesize a single string condition correctly" do
697
814
  @dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.must_equal "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
698
815
  end
699
816
 
700
- it "should parenthesize an array condition correctly" do
817
+ deprecated "should parenthesize an array condition correctly" do
701
818
  @dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.must_equal "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
702
819
  end
703
820
 
821
+ it "should parenthesize a single literal string condition correctly" do
822
+ @dataset.exclude(Sequel.lit("region = 'Asia' AND name = 'Japan'")).select_sql.must_equal "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
823
+ end
824
+
825
+ it "should parenthesize a placeholder literal string condition correctly" do
826
+ @dataset.exclude(Sequel.lit('region = ? AND name = ?', 'Asia', 'Japan')).select_sql.must_equal "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
827
+ end
828
+
704
829
  it "should correctly parenthesize when it is used twice" do
705
830
  @dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.must_equal "SELECT * FROM test WHERE ((region != 'Asia') AND (name != 'Japan'))"
706
831
  end
@@ -763,16 +888,23 @@ describe "Dataset#having" do
763
888
  @grouped = @dataset.group(:region).select(:region, Sequel.function(:sum, :population), Sequel.function(:avg, :gdp))
764
889
  end
765
890
 
766
- it "should just clone if given an empty argument" do
891
+ it "should just clone if given an empty array or hash argument" do
767
892
  @dataset.having({}).sql.must_equal @dataset.sql
768
893
  @dataset.having([]).sql.must_equal @dataset.sql
894
+ end
895
+
896
+ deprecated "should just clone if given an empty string argument" do
769
897
  @dataset.having('').sql.must_equal @dataset.sql
770
898
  end
771
899
 
772
- it "should affect select statements" do
900
+ deprecated "should handle string arguments" do
773
901
  @grouped.having('sum(population) > 10').select_sql.must_equal "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
774
902
  end
775
903
 
904
+ it "should handle literal string arguments" do
905
+ @grouped.having(Sequel.lit('sum(population) > 10')).select_sql.must_equal "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
906
+ end
907
+
776
908
  it "should support proc expressions" do
777
909
  @grouped.having{Sequel.function(:sum, :population) > 10}.sql.must_equal "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
778
910
  end
@@ -908,21 +1040,57 @@ describe "Dataset#as" do
908
1040
  end
909
1041
  end
910
1042
 
911
- describe "Dataset#literal" do
1043
+ describe "Dataset#literal with expressions" do
1044
+ before do
1045
+ @ds = Sequel.mock.dataset
1046
+ end
1047
+
1048
+ it "should convert qualified identifiers into dot notation" do
1049
+ @ds.literal(Sequel[:abc][:def]).must_equal 'abc.def'
1050
+ end
1051
+
1052
+ it "should convert aliased expressions into SQL AS notation" do
1053
+ @ds.literal(Sequel[:xyz].as(:x)).must_equal 'xyz AS x'
1054
+ @ds.literal(Sequel[:abc][:def].as(:x)).must_equal 'abc.def AS x'
1055
+ end
1056
+
1057
+ it "should support names with digits" do
1058
+ @ds.literal(:abc2).must_equal 'abc2'
1059
+ @ds.literal(Sequel[:xx][:yy3]).must_equal 'xx.yy3'
1060
+ @ds.literal(Sequel[:ab34][:temp3_4ax]).must_equal 'ab34.temp3_4ax'
1061
+ @ds.literal(Sequel[:x1].as(:y2)).must_equal 'x1 AS y2'
1062
+ @ds.literal(Sequel[:abc2][:def3].as(:ggg4)).must_equal 'abc2.def3 AS ggg4'
1063
+ end
1064
+
1065
+ it "should support upper case and lower case" do
1066
+ @ds.literal(:ABC).must_equal 'ABC'
1067
+ @ds.literal(Sequel[:Zvashtoy][:aBcD]).must_equal 'Zvashtoy.aBcD'
1068
+ end
1069
+
1070
+ it "should support spaces inside column names" do
1071
+ @ds = @ds.with_quote_identifiers(true)
1072
+ @ds.literal(:"AB C").must_equal '"AB C"'
1073
+ @ds.literal(Sequel[:"Zvas htoy"][:"aB cD"]).must_equal '"Zvas htoy"."aB cD"'
1074
+ @ds.literal(Sequel[:"aB cD"].as(:"XX XX")).must_equal '"aB cD" AS "XX XX"'
1075
+ @ds.literal(Sequel[:"Zva shtoy"][:"aB cD"].as("XX XX")).must_equal '"Zva shtoy"."aB cD" AS "XX XX"'
1076
+ end
1077
+ end
1078
+
1079
+ describe "Dataset#literal with splittable symbols" do
912
1080
  before do
913
1081
  @ds = Sequel.mock.dataset
914
1082
  end
915
1083
 
916
- it "should convert qualified symbol notation into dot notation" do
1084
+ with_symbol_splitting "should convert qualified symbol notation into dot notation" do
917
1085
  @ds.literal(:abc__def).must_equal 'abc.def'
918
1086
  end
919
1087
 
920
- it "should convert AS symbol notation into SQL AS notation" do
1088
+ with_symbol_splitting "should convert AS symbol notation into SQL AS notation" do
921
1089
  @ds.literal(:xyz___x).must_equal 'xyz AS x'
922
1090
  @ds.literal(:abc__def___x).must_equal 'abc.def AS x'
923
1091
  end
924
1092
 
925
- it "should support names with digits" do
1093
+ with_symbol_splitting "should support names with digits" do
926
1094
  @ds.literal(:abc2).must_equal 'abc2'
927
1095
  @ds.literal(:xx__yy3).must_equal 'xx.yy3'
928
1096
  @ds.literal(:ab34__temp3_4ax).must_equal 'ab34.temp3_4ax'
@@ -930,12 +1098,12 @@ describe "Dataset#literal" do
930
1098
  @ds.literal(:abc2__def3___ggg4).must_equal 'abc2.def3 AS ggg4'
931
1099
  end
932
1100
 
933
- it "should support upper case and lower case" do
1101
+ with_symbol_splitting "should support upper case and lower case" do
934
1102
  @ds.literal(:ABC).must_equal 'ABC'
935
1103
  @ds.literal(:Zvashtoy__aBcD).must_equal 'Zvashtoy.aBcD'
936
1104
  end
937
1105
 
938
- it "should support spaces inside column names" do
1106
+ with_symbol_splitting "should support spaces inside column names" do
939
1107
  @ds = @ds.with_quote_identifiers(true)
940
1108
  @ds.literal(:"AB C").must_equal '"AB C"'
941
1109
  @ds.literal(:"Zvas htoy__aB cD").must_equal '"Zvas htoy"."aB cD"'
@@ -980,6 +1148,9 @@ describe "Dataset#literal" do
980
1148
 
981
1149
  it "should literalize symbols as column references" do
982
1150
  @dataset.literal(:name).must_equal "name"
1151
+ end
1152
+
1153
+ with_symbol_splitting "should literalize symbols with embedded qualifiers as column references" do
983
1154
  @dataset.literal(:items__name).must_equal "items.name"
984
1155
  @dataset.literal(:"items__na#m$e").must_equal "items.na#m$e"
985
1156
  end
@@ -1023,6 +1194,13 @@ describe "Dataset#literal" do
1023
1194
  @dataset.literal(DateTime.new(2010, 1, 2, 3, 4, Rational(55, 10))).must_equal "'2010-01-02 03:04:05.500'"
1024
1195
  end
1025
1196
 
1197
+ it "should literalize times properly for databases with different time and timestamp precision" do
1198
+ @dataset = @dataset.with_extend{def timestamp_precision; 3 end; def sqltime_precision; 6 end}
1199
+ @dataset.literal(Sequel::SQLTime.create(1, 2, 3, 500000)).must_equal "'01:02:03.500000'"
1200
+ @dataset.literal(Time.local(2010, 1, 2, 3, 4, 5, 500000)).must_equal "'2010-01-02 03:04:05.500'"
1201
+ @dataset.literal(DateTime.new(2010, 1, 2, 3, 4, Rational(55, 10))).must_equal "'2010-01-02 03:04:05.500'"
1202
+ end
1203
+
1026
1204
  it "should literalize Date properly" do
1027
1205
  d = Date.today
1028
1206
  s = d.strftime("'%Y-%m-%d'")
@@ -1143,7 +1321,7 @@ describe "Dataset#from" do
1143
1321
  @dataset.select_sql.must_equal "SELECT * FROM DEFFROM"
1144
1322
  end
1145
1323
 
1146
- it "should accept :schema__table___alias symbol format" do
1324
+ with_symbol_splitting "should accept :schema__table___alias symbol format" do
1147
1325
  @dataset.from(:abc__def).select_sql.must_equal "SELECT * FROM abc.def"
1148
1326
  @dataset.from(:a_b__c).select_sql.must_equal "SELECT * FROM a_b.c"
1149
1327
  @dataset.from(:'#__#').select_sql.must_equal 'SELECT * FROM #.#'
@@ -1155,7 +1333,7 @@ describe "Dataset#from" do
1155
1333
  @dataset.from(:'#___#').select_sql.must_equal 'SELECT * FROM # AS #'
1156
1334
  end
1157
1335
 
1158
- it "should not handle :foo__schema__table___alias specially" do
1336
+ with_symbol_splitting "should not handle multi level qualification in embedded symbols specially" do
1159
1337
  @dataset.from(:foo__schema__table___alias).select_sql.must_equal "SELECT * FROM foo.schema__table AS alias"
1160
1338
  end
1161
1339
 
@@ -1176,15 +1354,20 @@ describe "Dataset#select" do
1176
1354
 
1177
1355
  it "should accept variable arity" do
1178
1356
  @d.select(:name).sql.must_equal 'SELECT name FROM test'
1179
- @d.select(:a, :b, :test__c).sql.must_equal 'SELECT a, b, test.c FROM test'
1357
+ @d.select(:a, :b, Sequel[:test][:c]).sql.must_equal 'SELECT a, b, test.c FROM test'
1358
+ end
1359
+
1360
+ with_symbol_splitting "should accept symbols with embedded qualification and aliasing" do
1361
+ @d.select(:test__cc).sql.must_equal 'SELECT test.cc FROM test'
1362
+ @d.select(:test___cc).sql.must_equal 'SELECT test AS cc FROM test'
1363
+ @d.select(:test__name___n).sql.must_equal 'SELECT test.name AS n FROM test'
1180
1364
  end
1181
1365
 
1182
1366
  it "should accept symbols and literal strings" do
1183
1367
  @d.select(Sequel.lit('aaa')).sql.must_equal 'SELECT aaa FROM test'
1184
1368
  @d.select(:a, Sequel.lit('b')).sql.must_equal 'SELECT a, b FROM test'
1185
- @d.select(:test__cc, Sequel.lit('test.d AS e')).sql.must_equal 'SELECT test.cc, test.d AS e FROM test'
1186
- @d.select(Sequel.lit('test.d AS e'), :test__cc).sql.must_equal 'SELECT test.d AS e, test.cc FROM test'
1187
- @d.select(:test__name___n).sql.must_equal 'SELECT test.name AS n FROM test'
1369
+ @d.select(:test, Sequel.lit('test.d AS e')).sql.must_equal 'SELECT test, test.d AS e FROM test'
1370
+ @d.select(Sequel.lit('test.d AS e'), :test).sql.must_equal 'SELECT test.d AS e, test FROM test'
1188
1371
  end
1189
1372
 
1190
1373
  it "should accept ColumnAlls" do
@@ -1192,6 +1375,10 @@ describe "Dataset#select" do
1192
1375
  end
1193
1376
 
1194
1377
  it "should accept QualifiedIdentifiers" do
1378
+ @d.select(Sequel.expr(Sequel[:test][:name]).as(:n)).sql.must_equal 'SELECT test.name AS n FROM test'
1379
+ end
1380
+
1381
+ with_symbol_splitting "should accept qualified identifiers in symbols in expressions" do
1195
1382
  @d.select(Sequel.expr(:test__name).as(:n)).sql.must_equal 'SELECT test.name AS n FROM test'
1196
1383
  end
1197
1384
 
@@ -1214,7 +1401,7 @@ describe "Dataset#select" do
1214
1401
 
1215
1402
  it "should accept arbitrary objects and literalize them correctly" do
1216
1403
  @d.select(1, :a, 't').sql.must_equal "SELECT 1, a, 't' FROM test"
1217
- @d.select(nil, Sequel.function(:sum, :t), :x___y).sql.must_equal "SELECT NULL, sum(t), x AS y FROM test"
1404
+ @d.select(nil, Sequel.function(:sum, :t), Sequel[:x].as(:y)).sql.must_equal "SELECT NULL, sum(t), x AS y FROM test"
1218
1405
  @d.select(nil, 1, Sequel.as(:x, :y)).sql.must_equal "SELECT NULL, 1, x AS y FROM test"
1219
1406
  end
1220
1407
 
@@ -1240,6 +1427,10 @@ describe "Dataset#select_group" do
1240
1427
 
1241
1428
  it "should set both SELECT and GROUP" do
1242
1429
  @d.select_group(:name).sql.must_equal 'SELECT name FROM test GROUP BY name'
1430
+ @d.select_group(:a, Sequel[:b][:c], Sequel[:d].as(:e)).sql.must_equal 'SELECT a, b.c, d AS e FROM test GROUP BY a, b.c, d'
1431
+ end
1432
+
1433
+ with_symbol_splitting "should set both SELECT and GROUP when using splittable symbols" do
1243
1434
  @d.select_group(:a, :b__c, :d___e).sql.must_equal 'SELECT a, b.c, d AS e FROM test GROUP BY a, b.c, d'
1244
1435
  end
1245
1436
 
@@ -1275,11 +1466,11 @@ describe "Dataset#select_all" do
1275
1466
  @d.select_all(:test, :foo).sql.must_equal 'SELECT test.*, foo.* FROM test'
1276
1467
  end
1277
1468
 
1278
- it "should work correctly with qualified symbols" do
1469
+ with_symbol_splitting "should work correctly with qualified symbols" do
1279
1470
  @d.select_all(:sch__test).sql.must_equal 'SELECT sch.test.* FROM test'
1280
1471
  end
1281
1472
 
1282
- it "should work correctly with aliased symbols" do
1473
+ with_symbol_splitting "should work correctly with aliased symbols" do
1283
1474
  @d.select_all(:test___al).sql.must_equal 'SELECT al.* FROM test'
1284
1475
  @d.select_all(:sch__test___al).sql.must_equal 'SELECT al.* FROM test'
1285
1476
  end
@@ -1296,10 +1487,15 @@ describe "Dataset#select_all" do
1296
1487
  @d.select_all(Sequel.expr(:test).as(:al)).sql.must_equal 'SELECT al.* FROM test'
1297
1488
  end
1298
1489
 
1299
- it "should work correctly with SQL::JoinClauses" do
1490
+ with_symbol_splitting "should work correctly with SQL::JoinClauses with splittable symbols" do
1300
1491
  d = @d.cross_join(:foo).cross_join(:test___al)
1301
1492
  @d.select_all(*d.opts[:join]).sql.must_equal 'SELECT foo.*, al.* FROM test'
1302
1493
  end
1494
+
1495
+ it "should work correctly with SQL::JoinClauses" do
1496
+ d = @d.cross_join(:foo).cross_join(Sequel[:test].as(:al))
1497
+ @d.select_all(*d.opts[:join]).sql.must_equal 'SELECT foo.*, al.* FROM test'
1498
+ end
1303
1499
  end
1304
1500
 
1305
1501
  describe "Dataset#select_more" do
@@ -1734,10 +1930,14 @@ describe "Dataset#qualified_column_name" do
1734
1930
  @dataset.literal(@dataset.send(:qualified_column_name, :b1, :items)).must_equal 'items.b1'
1735
1931
  end
1736
1932
 
1737
- it "should not changed the qualifed column's table if given a qualified symbol" do
1933
+ with_symbol_splitting "should not changed the qualifed column's table if given a qualified symbol" do
1738
1934
  @dataset.literal(@dataset.send(:qualified_column_name, :ccc__b, :items)).must_equal 'ccc.b'
1739
1935
  end
1740
1936
 
1937
+ it "should not changed the qualifed column's table if given a qualified identifier" do
1938
+ @dataset.literal(@dataset.send(:qualified_column_name, Sequel[:ccc][:b], :items)).must_equal 'ccc.b'
1939
+ end
1940
+
1741
1941
  it "should handle an aliased identifier" do
1742
1942
  @dataset.literal(@dataset.send(:qualified_column_name, :ccc, Sequel.expr(:items).as(:i))).must_equal 'i.ccc'
1743
1943
  end
@@ -2018,6 +2218,11 @@ describe "Dataset#group_and_count" do
2018
2218
  end
2019
2219
 
2020
2220
  it "should format column aliases in the select clause but not in the group clause" do
2221
+ @ds.group_and_count(Sequel[:name].as(:n)).sql.must_equal "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
2222
+ @ds.group_and_count(Sequel[:name][:n]).sql.must_equal "SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
2223
+ end
2224
+
2225
+ with_symbol_splitting "should format column aliases in the select clause but not in the group clause when using splittable symbols" do
2021
2226
  @ds.group_and_count(:name___n).sql.must_equal "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
2022
2227
  @ds.group_and_count(:name__n).sql.must_equal "SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
2023
2228
  end
@@ -2066,23 +2271,35 @@ describe "Dataset#first_source_alias" do
2066
2271
  @ds = Sequel.mock.dataset
2067
2272
  end
2068
2273
 
2274
+ it "should be the entire first source if not aliased" do
2275
+ deprecated do
2276
+ # SEQUEL5: Remove deprecation block, but keep code
2277
+ @ds.from(:s__t).first_source_alias.must_equal :s__t
2278
+ end
2279
+ end
2280
+
2281
+ with_symbol_splitting "should be the alias if aliased when using symbol splitting" do
2282
+ @ds.from(:t___a).first_source_alias.must_equal :a
2283
+ @ds.from(:s__t___a).first_source_alias.must_equal :a
2284
+ end
2285
+
2286
+ with_symbol_splitting "should be aliased as first_source when using symbol splitting" do
2287
+ @ds.from(:s__t___a).first_source.must_equal :a
2288
+ end
2289
+
2069
2290
  it "should be the entire first source if not aliased" do
2070
2291
  @ds.from(:t).first_source_alias.must_equal :t
2071
2292
  @ds.from(Sequel.identifier(:t__a)).first_source_alias.must_equal Sequel.identifier(:t__a)
2072
- @ds.from(:s__t).first_source_alias.must_equal :s__t
2073
2293
  @ds.from(Sequel.qualify(:s, :t)).first_source_alias.must_equal Sequel.qualify(:s, :t)
2074
2294
  end
2075
2295
 
2076
2296
  it "should be the alias if aliased" do
2077
- @ds.from(:t___a).first_source_alias.must_equal :a
2078
- @ds.from(:s__t___a).first_source_alias.must_equal :a
2079
2297
  @ds.from(Sequel.expr(:t).as(:a)).first_source_alias.must_equal :a
2080
2298
  end
2081
2299
 
2082
2300
  it "should be aliased as first_source" do
2083
2301
  @ds.from(:t).first_source.must_equal :t
2084
2302
  @ds.from(Sequel.identifier(:t__a)).first_source.must_equal Sequel.identifier(:t__a)
2085
- @ds.from(:s__t___a).first_source.must_equal :a
2086
2303
  @ds.from(Sequel.expr(:t).as(:a)).first_source.must_equal :a
2087
2304
  end
2088
2305
 
@@ -2098,14 +2315,23 @@ describe "Dataset#first_source_table" do
2098
2315
 
2099
2316
  it "should be the entire first source if not aliased" do
2100
2317
  @ds.from(:t).first_source_table.must_equal :t
2318
+ deprecated do
2319
+ # SEQUEL5: Remove deprecation block, but keep code
2320
+ @ds.from(:s__t).first_source_table.must_equal :s__t
2321
+ end
2322
+ end
2323
+
2324
+ it "should be the entire first source if not aliased" do
2101
2325
  @ds.from(Sequel.identifier(:t__a)).first_source_table.must_equal Sequel.identifier(:t__a)
2102
- @ds.from(:s__t).first_source_table.must_equal :s__t
2103
2326
  @ds.from(Sequel.qualify(:s, :t)).first_source_table.must_equal Sequel.qualify(:s, :t)
2104
2327
  end
2105
2328
 
2106
- it "should be the unaliased part if aliased" do
2329
+ with_symbol_splitting "should be the unaliased part if aliased symbols with embedded aliasing" do
2107
2330
  @ds.literal(@ds.from(:t___a).first_source_table).must_equal "t"
2108
2331
  @ds.literal(@ds.from(:s__t___a).first_source_table).must_equal "s.t"
2332
+ end
2333
+
2334
+ it "should be the unaliased part if aliased" do
2109
2335
  @ds.literal(@ds.from(Sequel.expr(:t).as(:a)).first_source_table).must_equal "t"
2110
2336
  end
2111
2337
 
@@ -2184,6 +2410,10 @@ describe "Dataset#join_table" do
2184
2410
  end
2185
2411
 
2186
2412
  it "should support multiple joins" do
2413
+ @d.join_table(:inner, :b, :items_id=>:id).join_table(:left_outer, :c, :b_id => Sequel[:b][:id]).sql.must_equal 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
2414
+ end
2415
+
2416
+ with_symbol_splitting "should support multiple joins with splittable symbols" do
2187
2417
  @d.join_table(:inner, :b, :items_id=>:id).join_table(:left_outer, :c, :b_id => :b__id).sql.must_equal 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
2188
2418
  end
2189
2419
 
@@ -2260,11 +2490,11 @@ describe "Dataset#join_table" do
2260
2490
  @d.from(Sequel.as(:foo, :f)).join_table(:inner, :bar, :id => :bar_id).sql.must_equal 'SELECT * FROM "foo" AS "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
2261
2491
  end
2262
2492
 
2263
- it "should support implicit schemas in from table symbols" do
2493
+ with_symbol_splitting "should support implicit schemas in from table symbols" do
2264
2494
  @d.from(:s__t).join(:u__v, {:id => :player_id}).sql.must_equal 'SELECT * FROM "s"."t" INNER JOIN "u"."v" ON ("u"."v"."id" = "s"."t"."player_id")'
2265
2495
  end
2266
2496
 
2267
- it "should support implicit aliases in from table symbols" do
2497
+ with_symbol_splitting "should support implicit aliases in from table symbols" do
2268
2498
  @d.from(:t___z).join(:v___y, {:id => :player_id}).sql.must_equal 'SELECT * FROM "t" AS "z" INNER JOIN "v" AS "y" ON ("y"."id" = "z"."player_id")'
2269
2499
  @d.from(:s__t___z).join(:u__v___y, {:id => :player_id}).sql.must_equal 'SELECT * FROM "s"."t" AS "z" INNER JOIN "u"."v" AS "y" ON ("y"."id" = "z"."player_id")'
2270
2500
  end
@@ -2318,6 +2548,11 @@ describe "Dataset#join_table" do
2318
2548
  end
2319
2549
 
2320
2550
  it "should support joining datasets and aliasing the join" do
2551
+ ds = Sequel.mock.dataset.from(:categories)
2552
+ @d.join_table(:left_outer, ds, {Sequel[:ds][:item_id] => :id}, :table_alias=>:ds).sql.must_equal 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "ds" ON ("ds"."item_id" = "items"."id")'
2553
+ end
2554
+
2555
+ with_symbol_splitting "should support joining datasets and aliasing the join when using symbols with embedded qualification" do
2321
2556
  ds = Sequel.mock.dataset.from(:categories)
2322
2557
  @d.join_table(:left_outer, ds, {:ds__item_id => :id}, :table_alias=>:ds).sql.must_equal 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "ds" ON ("ds"."item_id" = "items"."id")'
2323
2558
  end
@@ -2325,7 +2560,7 @@ describe "Dataset#join_table" do
2325
2560
  it "should support joining multiple datasets" do
2326
2561
  ds = Sequel.mock.dataset.from(:categories)
2327
2562
  ds2 = Sequel.mock.dataset.from(:nodes).select(:name)
2328
- ds3 = Sequel.mock.dataset.from(:attributes).filter("name = 'blah'")
2563
+ ds3 = Sequel.mock.dataset.from(:attributes).where(Sequel.lit("name = 'blah'"))
2329
2564
 
2330
2565
  @d.join_table(:left_outer, ds, :item_id => :id).join_table(:inner, ds2, :node_id=>:id).join_table(:right_outer, ds3, :attribute_id=>:id).sql.
2331
2566
  must_equal 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "t1" ON ("t1"."item_id" = "items"."id") ' \
@@ -2333,10 +2568,14 @@ describe "Dataset#join_table" do
2333
2568
  'RIGHT OUTER JOIN (SELECT * FROM attributes WHERE (name = \'blah\')) AS "t3" ON ("t3"."attribute_id" = "t2"."id")'
2334
2569
  end
2335
2570
 
2336
- it "should support using an SQL String as the join condition" do
2571
+ deprecated "should support using a string as the join condition" do
2337
2572
  @d.join(:categories, "c.item_id = items.id", :table_alias=>:c).sql.must_equal 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
2338
2573
  end
2339
2574
 
2575
+ it "should support using a literal string as the join condition" do
2576
+ @d.join(:categories, Sequel.lit("c.item_id = items.id"), :table_alias=>:c).sql.must_equal 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
2577
+ end
2578
+
2340
2579
  it "should support using a boolean column as the join condition" do
2341
2580
  @d.join(:categories, :active).sql.must_equal 'SELECT * FROM "items" INNER JOIN "categories" ON "active"'
2342
2581
  end
@@ -2374,6 +2613,14 @@ describe "Dataset#join_table" do
2374
2613
  proc{@d.join(:categories, [:id]){|j,lj,js|}}.must_raise(Sequel::Error)
2375
2614
  end
2376
2615
 
2616
+ with_symbol_splitting "should support using a block that receieves the join table/alias, last join table/alias, and array of previous joins when using splittable symbols" do
2617
+ @d.from(:items___i).join(:categories, nil, :table_alias=>:c) do |join_alias, last_join_alias, joins|
2618
+ join_alias.must_equal :c
2619
+ last_join_alias.must_equal :i
2620
+ joins.must_equal []
2621
+ end
2622
+ end
2623
+
2377
2624
  it "should support using a block that receieves the join table/alias, last join table/alias, and array of previous joins" do
2378
2625
  @d.join(:categories) do |join_alias, last_join_alias, joins|
2379
2626
  join_alias.must_equal :categories
@@ -2387,12 +2634,6 @@ describe "Dataset#join_table" do
2387
2634
  joins.must_equal []
2388
2635
  end
2389
2636
 
2390
- @d.from(:items___i).join(:categories, nil, :table_alias=>:c) do |join_alias, last_join_alias, joins|
2391
- join_alias.must_equal :c
2392
- last_join_alias.must_equal :i
2393
- joins.must_equal []
2394
- end
2395
-
2396
2637
  @d.join(:blah).join(:categories, nil, :table_alias=>:c) do |join_alias, last_join_alias, joins|
2397
2638
  join_alias.must_equal :c
2398
2639
  last_join_alias.must_equal :blah
@@ -2434,6 +2675,12 @@ describe "Dataset#join_table" do
2434
2675
  end
2435
2676
 
2436
2677
  it "should prefer explicit aliases over implicit" do
2678
+ @d.from(Sequel[:items].as(:i)).join(Sequel[:categories].as(:c), {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.must_equal 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2679
+ @d.from(Sequel.expr(:items).as(:i)).join(Sequel.expr(:categories).as(:c), {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.
2680
+ must_equal 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2681
+ end
2682
+
2683
+ with_symbol_splitting "should prefer explicit aliases over implicit when using splittable symbols" do
2437
2684
  @d.from(:items___i).join(:categories___c, {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.must_equal 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2438
2685
  @d.from(Sequel.expr(:items).as(:i)).join(Sequel.expr(:categories).as(:c), {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.
2439
2686
  must_equal 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
@@ -2478,7 +2725,7 @@ describe "Dataset aggregate methods" do
2478
2725
 
2479
2726
  it "should accept qualified columns" do
2480
2727
  5.times do
2481
- @d.avg(:test__bc).must_equal 'SELECT avg(test.bc) AS avg FROM test LIMIT 1'
2728
+ @d.avg(Sequel[:test][:bc]).must_equal 'SELECT avg(test.bc) AS avg FROM test LIMIT 1'
2482
2729
  end
2483
2730
  end
2484
2731
 
@@ -2586,9 +2833,9 @@ describe "Dataset #first and #last" do
2586
2833
  ds = @d.order(:a).freeze
2587
2834
  5.times do
2588
2835
  ds.first(:z => 26).must_equal(:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1')
2589
- ds.first('z = ?', 15).must_equal(:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1')
2836
+ ds.first([[:z, 15]]).must_equal(:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1')
2590
2837
  ds.last(:z => 26).must_equal(:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1')
2591
- ds.last('z = ?', 15).must_equal(:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1')
2838
+ ds.last([[:z, 15]]).must_equal(:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1')
2592
2839
  end
2593
2840
  end
2594
2841
 
@@ -2613,10 +2860,30 @@ describe "Dataset #first and #last" do
2613
2860
  ds = @d.order(:name).freeze
2614
2861
  5.times do
2615
2862
  @d.first(:y=>25){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
2616
- ds.last('y = ?', 16){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
2863
+ ds.last(:y=>16){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
2617
2864
  end
2618
2865
  end
2619
2866
 
2867
+ it "should combine block and standard argument filters if argument is a literal string" do
2868
+ ds = @d.order(:name).freeze
2869
+ 5.times do
2870
+ @d.first(Sequel.lit('y = 25')){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
2871
+ ds.last(Sequel.lit('y = 16')){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
2872
+ @d.first(Sequel.lit('y = ?', 25)){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
2873
+ ds.last(Sequel.lit('y = ?', 16)){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
2874
+ end
2875
+ end
2876
+
2877
+ deprecated "should combine block and standard argument filters if argument is a string" do
2878
+ ds = @d.order(:name).freeze
2879
+ 5.times do
2880
+ @d.first('y = 25'){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
2881
+ ds.last('y = 16'){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
2882
+ @d.first('y = ?', 25){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
2883
+ ds.last('y = ?', 16){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
2884
+ end
2885
+ end
2886
+
2620
2887
  it "should filter and return an array of records if an Integer argument is provided and a block is given" do
2621
2888
  ds = @d.order(:a).freeze
2622
2889
  5.times do
@@ -2657,7 +2924,7 @@ describe "Dataset #first!" do
2657
2924
 
2658
2925
  it "should return the first! matching record if argument is not an Integer" do
2659
2926
  @d.order(:a).first!(:z => 26).must_equal(:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1')
2660
- @d.order(:a).first!('z = ?', 15).must_equal(:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1')
2927
+ @d.order(:a).first!(Sequel.lit('z = ?', 15)).must_equal(:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1')
2661
2928
  end
2662
2929
 
2663
2930
  it "should set the limit and return an array of records if the given number is > 1" do
@@ -2902,7 +3169,7 @@ describe "Dataset#get" do
2902
3169
  it "should work with aliased fields" do
2903
3170
  @d.freeze
2904
3171
  5.times do
2905
- @d.get(Sequel.expr(:x__b).as(:name)).must_equal "SELECT x.b AS name FROM test LIMIT 1"
3172
+ @d.get(Sequel.expr(Sequel[:x][:b]).as(:name)).must_equal "SELECT x.b AS name FROM test LIMIT 1"
2906
3173
  end
2907
3174
  end
2908
3175
 
@@ -2914,7 +3181,7 @@ describe "Dataset#get" do
2914
3181
  end
2915
3182
 
2916
3183
  it "should accept a block that yields a virtual row" do
2917
- @d.get{|o| o.x__b.as(:name)}.must_equal "SELECT x.b AS name FROM test LIMIT 1"
3184
+ @d.get{|o| o.x_b.as(:name)}.must_equal "SELECT x_b AS name FROM test LIMIT 1"
2918
3185
  @d.get{x(1).as(:name)}.must_equal "SELECT x(1) AS name FROM test LIMIT 1"
2919
3186
  end
2920
3187
 
@@ -2937,26 +3204,32 @@ describe "Dataset#get" do
2937
3204
  @d.get([:name]).must_equal ['SELECT name FROM test LIMIT 1']
2938
3205
  end
2939
3206
 
2940
- it "should handle an array with aliases" do
3207
+ it "should handle an array with aliased expressions" do
2941
3208
  @d = @d.with_fetch(:name=>1, :abc=>2)
2942
- @d.get([:n___name, Sequel.as(:a, :abc)]).must_equal [1, 2]
3209
+ @d.get([Sequel[:n].as(:name), Sequel.as(:a, :abc)]).must_equal [1, 2]
2943
3210
  @d.db.sqls.must_equal ['SELECT n AS name, a AS abc FROM test LIMIT 1']
2944
3211
  end
2945
3212
 
3213
+ with_symbol_splitting "should handle an array with symbols with embedded aliases" do
3214
+ @d = @d.with_fetch(:name=>1, :abc=>2)
3215
+ @d.get([:n___name, :a__b___abc]).must_equal [1, 2]
3216
+ @d.db.sqls.must_equal ['SELECT n AS name, a.b AS abc FROM test LIMIT 1']
3217
+ end
3218
+
2946
3219
  it "should raise an Error if an alias cannot be determined" do
2947
3220
  proc{@d.with_fetch(:name=>1, :abc=>2).get([Sequel.+(:a, 1), :a])}.must_raise(Sequel::Error)
2948
3221
  end
2949
3222
 
2950
3223
  it "should support an array of expressions in a virtual row" do
2951
3224
  @d = @d.with_fetch(:name=>1, :abc=>2)
2952
- @d.get{[name, n__abc]}.must_equal [1, 2]
3225
+ @d.get{[name, n[abc]]}.must_equal [1, 2]
2953
3226
  @d.db.sqls.must_equal ['SELECT name, n.abc FROM test LIMIT 1']
2954
3227
  end
2955
3228
 
2956
3229
  it "should work with static SQL" do
2957
3230
  @d.with_sql('SELECT foo').get(:name).must_equal "SELECT foo"
2958
3231
  @d = @d.with_fetch(:name=>1, :abc=>2)
2959
- @d.with_sql('SELECT foo').get{[name, n__abc]}.must_equal [1, 2]
3232
+ @d.with_sql('SELECT foo').get{[name, n[abc]]}.must_equal [1, 2]
2960
3233
  @d.db.sqls.must_equal ['SELECT foo'] * 2
2961
3234
  end
2962
3235
 
@@ -3023,7 +3296,7 @@ describe "Dataset#columns" do
3023
3296
  @dataset.db.sqls.must_equal ['SELECT * FROM items LIMIT 1']
3024
3297
  @dataset.columns.must_equal [:a]
3025
3298
  @dataset.db.sqls.must_equal []
3026
- ds = @dataset.select{foo{}}
3299
+ ds = @dataset.select{foo.function}
3027
3300
  ds.columns.must_equal [:b]
3028
3301
  @dataset.db.sqls.must_equal ['SELECT foo() FROM items LIMIT 1']
3029
3302
  end
@@ -3251,7 +3524,7 @@ describe "Dataset#multi_insert" do
3251
3524
  'COMMIT']
3252
3525
  end
3253
3526
 
3254
- it "should handle different formats for tables" do
3527
+ with_symbol_splitting "should handle splittable symbols for tables" do
3255
3528
  @ds = @ds.from(:sch__tab)
3256
3529
  @ds.multi_insert(@list)
3257
3530
  @db.sqls.must_equal ['BEGIN',
@@ -3259,7 +3532,9 @@ describe "Dataset#multi_insert" do
3259
3532
  "INSERT INTO sch.tab (name) VALUES ('def')",
3260
3533
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
3261
3534
  'COMMIT']
3535
+ end
3262
3536
 
3537
+ it "should handle SQL::QualifiedIdentifier for tables" do
3263
3538
  @ds = @ds.from(Sequel.qualify(:sch, :tab))
3264
3539
  @ds.multi_insert(@list)
3265
3540
  @db.sqls.must_equal ['BEGIN',
@@ -3267,7 +3542,9 @@ describe "Dataset#multi_insert" do
3267
3542
  "INSERT INTO sch.tab (name) VALUES ('def')",
3268
3543
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
3269
3544
  'COMMIT']
3545
+ end
3270
3546
 
3547
+ it "should handle SQL::Identifier for tables" do
3271
3548
  @ds = @ds.from(Sequel.identifier(:sch__tab))
3272
3549
  @ds.multi_insert(@list)
3273
3550
  @db.sqls.must_equal ['BEGIN',
@@ -3320,11 +3597,19 @@ describe "Dataset#update_sql" do
3320
3597
  @ds = Sequel.mock.dataset.from(:items)
3321
3598
  end
3322
3599
 
3323
- it "should accept strings" do
3600
+ deprecated "should accept strings" do
3324
3601
  @ds.update_sql("a = b").must_equal "UPDATE items SET a = b"
3325
3602
  end
3326
3603
 
3327
- it "should handle implicitly qualified symbols" do
3604
+ it "should accept literal strings" do
3605
+ @ds.update_sql(Sequel.lit("a = b")).must_equal "UPDATE items SET a = b"
3606
+ end
3607
+
3608
+ it "should handle qualified identifiers" do
3609
+ @ds.update_sql(Sequel[:items][:a]=>:b).must_equal "UPDATE items SET items.a = b"
3610
+ end
3611
+
3612
+ with_symbol_splitting "should handle implicitly qualified symbols" do
3328
3613
  @ds.update_sql(:items__a=>:b).must_equal "UPDATE items SET items.a = b"
3329
3614
  end
3330
3615
 
@@ -3400,8 +3685,11 @@ describe "Dataset#insert_sql" do
3400
3685
  @ds.insert_sql([:a, :b, :c], Sequel.lit('VALUES (1, 2, 3)')).must_equal "INSERT INTO items (a, b, c) VALUES (1, 2, 3)"
3401
3686
  end
3402
3687
 
3403
- it "should use unaliased table name" do
3688
+ with_symbol_splitting "should use unaliased table name when using splittable symbol" do
3404
3689
  @ds.from(:items___i).insert_sql(1).must_equal "INSERT INTO items VALUES (1)"
3690
+ end
3691
+
3692
+ it "should use unaliased table name" do
3405
3693
  @ds.from(Sequel.as(:items, :i)).insert_sql(1).must_equal "INSERT INTO items VALUES (1)"
3406
3694
  end
3407
3695
  end
@@ -3724,11 +4012,16 @@ describe "Dataset prepared statements and bound variables " do
3724
4012
  '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
3725
4013
  end
3726
4014
 
3727
- it "should handle literal strings" do
4015
+ deprecated "should handle literal strings" do
3728
4016
  @ds.filter("num = ?", :$n).call(:select, :n=>1)
3729
4017
  @db.sqls.must_equal ['SELECT * FROM items WHERE (num = 1)']
3730
4018
  end
3731
4019
 
4020
+ it "should handle literal strings" do
4021
+ @ds.filter(Sequel.lit("num = ?", :$n)).call(:select, :n=>1)
4022
+ @db.sqls.must_equal ['SELECT * FROM items WHERE (num = 1)']
4023
+ end
4024
+
3732
4025
  it "should handle columns on prepared statements correctly" do
3733
4026
  @db.columns = [:num]
3734
4027
  @ds = @ds.with_extend{def select_where_sql(sql) super(sql); sql << " OR #{columns.first} = 1" if opts[:where] end}
@@ -3751,11 +4044,16 @@ describe "Dataset prepared statements and bound variables " do
3751
4044
  @db.sqls.must_equal ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num IN (SELECT num FROM items WHERE (num = 1))))))']
3752
4045
  end
3753
4046
 
3754
- it "should handle subselects with literal strings" do
4047
+ deprecated "should handle subselects with strings" do
3755
4048
  @ds.filter(:$b).filter(:num=>@ds.select(:num).filter("num = ?", :$n)).call(:select, :n=>1, :b=>0)
3756
4049
  @db.sqls.must_equal ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num = 1))))']
3757
4050
  end
3758
4051
 
4052
+ it "should handle subselects with literal strings" do
4053
+ @ds.filter(:$b).filter(:num=>@ds.select(:num).filter(Sequel.lit("num = ?", :$n))).call(:select, :n=>1, :b=>0)
4054
+ @db.sqls.must_equal ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num = 1))))']
4055
+ end
4056
+
3759
4057
  it "should handle subselects with static sql and placeholders" do
3760
4058
  @ds.filter(:$b).filter(:num=>@db["SELECT num FROM items WHERE (num = ?)", :$n]).call(:select, :n=>1, :b=>0)
3761
4059
  @db.sqls.must_equal ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num = 1))))']
@@ -3873,6 +4171,10 @@ describe "Sequel::Dataset#qualify" do
3873
4171
  end
3874
4172
 
3875
4173
  it "should handle symbols" do
4174
+ @ds.select(:a).qualify.sql.must_equal 'SELECT t.a FROM t'
4175
+ end
4176
+
4177
+ with_symbol_splitting "should handle splittable symbols" do
3876
4178
  @ds.select(:a, :b__c, :d___e, :f__g___h).qualify.sql.must_equal 'SELECT t.a, b.c, t.d AS e, f.g AS h FROM t'
3877
4179
  end
3878
4180
 
@@ -3922,11 +4224,11 @@ describe "Sequel::Dataset#qualify" do
3922
4224
  end
3923
4225
 
3924
4226
  it "should handle SQL::PlaceholderLiteralStrings" do
3925
- @ds.filter('? > ?', :a, 1).qualify.sql.must_equal 'SELECT t.* FROM t WHERE (t.a > 1)'
4227
+ @ds.filter(Sequel.lit('? > ?', :a, 1)).qualify.sql.must_equal 'SELECT t.* FROM t WHERE (t.a > 1)'
3926
4228
  end
3927
4229
 
3928
4230
  it "should handle SQL::PlaceholderLiteralStrings with named placeholders" do
3929
- @ds.filter(':a > :b', :a=>:c, :b=>1).qualify.sql.must_equal 'SELECT t.* FROM t WHERE (t.c > 1)'
4231
+ @ds.filter(Sequel.lit(':a > :b', :a=>:c, :b=>1)).qualify.sql.must_equal 'SELECT t.* FROM t WHERE (t.c > 1)'
3930
4232
  end
3931
4233
 
3932
4234
  it "should handle SQL::Wrappers" do
@@ -3960,7 +4262,7 @@ describe "Sequel::Dataset#qualify" do
3960
4262
  end
3961
4263
 
3962
4264
  it "should handle all other objects by returning them unchanged" do
3963
- @ds.select("a").filter{a(3)}.filter('blah').order(Sequel.lit('true')).group(Sequel.lit('a > ?', 1)).having(false).qualify.sql.must_equal "SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
4265
+ @ds.select("a").filter{a(3)}.filter(Sequel.lit('blah')).order(Sequel.lit('true')).group(Sequel.lit('a > ?', 1)).having(false).qualify.sql.must_equal "SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
3964
4266
  end
3965
4267
  end
3966
4268
 
@@ -4313,7 +4615,12 @@ describe "Sequel::Dataset#select_map" do
4313
4615
  @ds.db.sqls.must_equal ['SELECT a FROM t']
4314
4616
  end
4315
4617
 
4316
- it "should handle implicit qualifiers in arguments" do
4618
+ it "should handle qualified identifiers in arguments" do
4619
+ @ds.select_map(Sequel[:a][:b]).must_equal [1, 2]
4620
+ @ds.db.sqls.must_equal ['SELECT a.b FROM t']
4621
+ end
4622
+
4623
+ with_symbol_splitting "should handle implicit qualifiers in arguments" do
4317
4624
  @ds.select_map(:a__b).must_equal [1, 2]
4318
4625
  @ds.db.sqls.must_equal ['SELECT a.b FROM t']
4319
4626
  end
@@ -4321,14 +4628,19 @@ describe "Sequel::Dataset#select_map" do
4321
4628
  it "should raise if multiple arguments and can't determine alias" do
4322
4629
  proc{@ds.select_map([Sequel.function(:a), :b])}.must_raise(Sequel::Error)
4323
4630
  proc{@ds.select_map(Sequel.function(:a)){b}}.must_raise(Sequel::Error)
4324
- proc{@ds.select_map{[a{}, b]}}.must_raise(Sequel::Error)
4631
+ proc{@ds.select_map{[a.function, b]}}.must_raise(Sequel::Error)
4325
4632
  end
4326
4633
 
4327
- it "should handle implicit aliases in arguments" do
4634
+ with_symbol_splitting "should handle implicit aliases in arguments" do
4328
4635
  @ds.select_map(:a___b).must_equal [1, 2]
4329
4636
  @ds.db.sqls.must_equal ['SELECT a AS b FROM t']
4330
4637
  end
4331
4638
 
4639
+ it "should handle aliased expressions in arguments" do
4640
+ @ds.select_map(Sequel[:a].as(:b)).must_equal [1, 2]
4641
+ @ds.db.sqls.must_equal ['SELECT a AS b FROM t']
4642
+ end
4643
+
4332
4644
  it "should handle other objects" do
4333
4645
  @ds.select_map(Sequel.lit("a").as(:b)).must_equal [1, 2]
4334
4646
  @ds.db.sqls.must_equal ['SELECT a AS b FROM t']
@@ -4345,35 +4657,40 @@ describe "Sequel::Dataset#select_map" do
4345
4657
  end
4346
4658
 
4347
4659
  it "should handle an expression without a determinable alias" do
4348
- @ds.select_map{a(t__c)}.must_equal [1, 2]
4660
+ @ds.select_map{a(t[c])}.must_equal [1, 2]
4349
4661
  @ds.db.sqls.must_equal ['SELECT a(t.c) AS v FROM t']
4350
4662
  end
4351
4663
 
4352
4664
  it "should accept a block" do
4353
- @ds.select_map{a(t__c).as(b)}.must_equal [1, 2]
4665
+ @ds.select_map{a(t[c]).as(b)}.must_equal [1, 2]
4354
4666
  @ds.db.sqls.must_equal ['SELECT a(t.c) AS b FROM t']
4355
4667
  end
4356
4668
 
4357
4669
  it "should accept a block with an array of columns" do
4358
- @ds.select_map{[a(t__c).as(c), a(t__c).as(c)]}.must_equal [[1, 1], [2, 2]]
4670
+ @ds.select_map{[a(t[c]).as(c), a(t[c]).as(c)]}.must_equal [[1, 1], [2, 2]]
4359
4671
  @ds.db.sqls.must_equal ['SELECT a(t.c) AS c, a(t.c) AS c FROM t']
4360
4672
  end
4361
4673
 
4362
4674
  it "should accept a block with a column" do
4363
- @ds.select_map(:c){a(t__c).as(c)}.must_equal [[1, 1], [2, 2]]
4675
+ @ds.select_map(:c){a(t[c]).as(c)}.must_equal [[1, 1], [2, 2]]
4364
4676
  @ds.db.sqls.must_equal ['SELECT c, a(t.c) AS c FROM t']
4365
4677
  end
4366
4678
 
4367
4679
  it "should accept a block and array of arguments" do
4368
- @ds.select_map([:c, :c]){[a(t__c).as(c), a(t__c).as(c)]}.must_equal [[1, 1, 1, 1], [2, 2, 2, 2]]
4680
+ @ds.select_map([:c, :c]){[a(t[c]).as(c), a(t[c]).as(c)]}.must_equal [[1, 1, 1, 1], [2, 2, 2, 2]]
4369
4681
  @ds.db.sqls.must_equal ['SELECT c, c, a(t.c) AS c, a(t.c) AS c FROM t']
4370
4682
  end
4371
4683
 
4372
4684
  it "should handle an array of columns" do
4373
4685
  @ds.select_map([:c, :c]).must_equal [[1, 1], [2, 2]]
4374
4686
  @ds.db.sqls.must_equal ['SELECT c, c FROM t']
4375
- @ds.select_map([Sequel.expr(:d).as(:c), Sequel.qualify(:b, :c), Sequel.identifier(:c), Sequel.identifier(:c).qualify(:b), :a__c, :a__d___c]).must_equal [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2]]
4376
- @ds.db.sqls.must_equal ['SELECT d AS c, b.c, c, b.c, a.c, a.d AS c FROM t']
4687
+ @ds.select_map([Sequel.expr(:d).as(:c), Sequel.qualify(:b, :c), Sequel.identifier(:c), Sequel.identifier(:c).qualify(:b)]).must_equal [[1, 1, 1, 1], [2, 2, 2, 2]]
4688
+ @ds.db.sqls.must_equal ['SELECT d AS c, b.c, c, b.c FROM t']
4689
+ end
4690
+
4691
+ with_symbol_splitting "should handle an array of columns with splittable symbols" do
4692
+ @ds.select_map([:a__c, :a__d___c]).must_equal [[1, 1], [2, 2]]
4693
+ @ds.db.sqls.must_equal ['SELECT a.c, a.d AS c FROM t']
4377
4694
  end
4378
4695
 
4379
4696
  it "should handle an array with a single element" do
@@ -4392,7 +4709,12 @@ describe "Sequel::Dataset#select_order_map" do
4392
4709
  @ds.db.sqls.must_equal ['SELECT a FROM t ORDER BY a']
4393
4710
  end
4394
4711
 
4395
- it "should handle implicit qualifiers in arguments" do
4712
+ it "should handle qualified identifiers in arguments" do
4713
+ @ds.select_order_map(Sequel[:a][:b]).must_equal [1, 2]
4714
+ @ds.db.sqls.must_equal ['SELECT a.b FROM t ORDER BY a.b']
4715
+ end
4716
+
4717
+ with_symbol_splitting "should handle implicit qualifiers in arguments" do
4396
4718
  @ds.select_order_map(:a__b).must_equal [1, 2]
4397
4719
  @ds.db.sqls.must_equal ['SELECT a.b FROM t ORDER BY a.b']
4398
4720
  end
@@ -4400,15 +4722,15 @@ describe "Sequel::Dataset#select_order_map" do
4400
4722
  it "should raise if multiple arguments and can't determine alias" do
4401
4723
  proc{@ds.select_order_map([Sequel.function(:a), :b])}.must_raise(Sequel::Error)
4402
4724
  proc{@ds.select_order_map(Sequel.function(:a)){b}}.must_raise(Sequel::Error)
4403
- proc{@ds.select_order_map{[a{}, b]}}.must_raise(Sequel::Error)
4725
+ proc{@ds.select_order_map{[a.function, b]}}.must_raise(Sequel::Error)
4404
4726
  end
4405
4727
 
4406
- it "should handle implicit aliases in arguments" do
4728
+ with_symbol_splitting "should handle implicit aliases in arguments" do
4407
4729
  @ds.select_order_map(:a___b).must_equal [1, 2]
4408
4730
  @ds.db.sqls.must_equal ['SELECT a AS b FROM t ORDER BY a']
4409
4731
  end
4410
4732
 
4411
- it "should handle implicit qualifiers and aliases in arguments" do
4733
+ with_symbol_splitting "should handle implicit qualifiers and aliases in arguments" do
4412
4734
  @ds.select_order_map(:t__a___b).must_equal [1, 2]
4413
4735
  @ds.db.sqls.must_equal ['SELECT t.a AS b FROM t ORDER BY t.a']
4414
4736
  end
@@ -4416,6 +4738,10 @@ describe "Sequel::Dataset#select_order_map" do
4416
4738
  it "should handle AliasedExpressions" do
4417
4739
  @ds.select_order_map(Sequel.lit("a").as(:b)).must_equal [1, 2]
4418
4740
  @ds.db.sqls.must_equal ['SELECT a AS b FROM t ORDER BY a']
4741
+ @ds.select_order_map(Sequel[:a].as(:b)).must_equal [1, 2]
4742
+ @ds.db.sqls.must_equal ['SELECT a AS b FROM t ORDER BY a']
4743
+ @ds.select_order_map(Sequel[:t][:a].as(:b)).must_equal [1, 2]
4744
+ @ds.db.sqls.must_equal ['SELECT t.a AS b FROM t ORDER BY t.a']
4419
4745
  end
4420
4746
 
4421
4747
  it "should handle OrderedExpressions" do
@@ -4424,35 +4750,43 @@ describe "Sequel::Dataset#select_order_map" do
4424
4750
  end
4425
4751
 
4426
4752
  it "should handle an expression without a determinable alias" do
4427
- @ds.select_order_map{a(t__c)}.must_equal [1, 2]
4753
+ @ds.select_order_map{a(t[c])}.must_equal [1, 2]
4428
4754
  @ds.db.sqls.must_equal ['SELECT a(t.c) AS v FROM t ORDER BY a(t.c)']
4429
4755
  end
4430
4756
 
4431
4757
  it "should accept a block" do
4432
- @ds.select_order_map{a(t__c).as(b)}.must_equal [1, 2]
4758
+ @ds.select_order_map{a(t[c]).as(b)}.must_equal [1, 2]
4433
4759
  @ds.db.sqls.must_equal ['SELECT a(t.c) AS b FROM t ORDER BY a(t.c)']
4434
4760
  end
4435
4761
 
4436
4762
  it "should accept a block with an array of columns" do
4437
- @ds.select_order_map{[c.desc, a(t__c).as(c)]}.must_equal [[1, 1], [2, 2]]
4763
+ @ds.select_order_map{[c.desc, a(t[c]).as(c)]}.must_equal [[1, 1], [2, 2]]
4438
4764
  @ds.db.sqls.must_equal ['SELECT c, a(t.c) AS c FROM t ORDER BY c DESC, a(t.c)']
4439
4765
  end
4440
4766
 
4441
4767
  it "should accept a block with a column" do
4442
- @ds.select_order_map(:c){a(t__c).as(c)}.must_equal [[1, 1], [2, 2]]
4768
+ @ds.select_order_map(:c){a(t[c]).as(c)}.must_equal [[1, 1], [2, 2]]
4443
4769
  @ds.db.sqls.must_equal ['SELECT c, a(t.c) AS c FROM t ORDER BY c, a(t.c)']
4444
4770
  end
4445
4771
 
4446
4772
  it "should accept a block and array of arguments" do
4447
- @ds.select_order_map([:c, :c]){[a(t__c).as(c), c.desc]}.must_equal [[1, 1, 1, 1], [2, 2, 2, 2]]
4773
+ @ds.select_order_map([:c, :c]){[a(t[c]).as(c), c.desc]}.must_equal [[1, 1, 1, 1], [2, 2, 2, 2]]
4448
4774
  @ds.db.sqls.must_equal ['SELECT c, c, a(t.c) AS c, c FROM t ORDER BY c, c, a(t.c), c DESC']
4449
4775
  end
4450
4776
 
4451
4777
  it "should handle an array of columns" do
4452
4778
  @ds.select_order_map([:c, :c]).must_equal [[1, 1], [2, 2]]
4453
4779
  @ds.db.sqls.must_equal ['SELECT c, c FROM t ORDER BY c, c']
4454
- @ds.select_order_map([Sequel.expr(:d).as(:c), Sequel.qualify(:b, :c), Sequel.identifier(:c), Sequel.identifier(:c).qualify(:b), Sequel.identifier(:c).qualify(:b).desc, :a__c, Sequel.desc(:a__d___c), Sequel.desc(Sequel.expr(:a__d___c))]).must_equal [[1, 1, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2, 2, 2]]
4455
- @ds.db.sqls.must_equal ['SELECT d AS c, b.c, c, b.c, b.c, a.c, a.d AS c, a.d AS c FROM t ORDER BY d, b.c, c, b.c, b.c DESC, a.c, a.d DESC, a.d DESC']
4780
+ end
4781
+
4782
+ it "should handle an array of columns" do
4783
+ @ds.select_order_map([Sequel.expr(:d).as(:c), Sequel.qualify(:b, :c), Sequel.identifier(:c), Sequel.identifier(:c).qualify(:b), Sequel.identifier(:c).qualify(:b).desc]).must_equal [[1, 1, 1, 1, 1], [2, 2, 2, 2, 2]]
4784
+ @ds.db.sqls.must_equal ['SELECT d AS c, b.c, c, b.c, b.c FROM t ORDER BY d, b.c, c, b.c, b.c DESC']
4785
+ end
4786
+
4787
+ with_symbol_splitting "should handle an array of columns with splittable symbols" do
4788
+ @ds.select_order_map([:a__c, Sequel.desc(:a__d___c), Sequel.desc(Sequel.expr(:a__d___c))]).must_equal [[1, 1, 1], [2, 2, 2]]
4789
+ @ds.db.sqls.must_equal ['SELECT a.c, a.d AS c, a.d AS c FROM t ORDER BY a.c, a.d DESC, a.d DESC']
4456
4790
  end
4457
4791
 
4458
4792
  it "should handle an array with a single element" do
@@ -4472,17 +4806,32 @@ describe "Sequel::Dataset#select_hash" do
4472
4806
  @ds.db.sqls.must_equal ['SELECT a, b FROM t']
4473
4807
  end
4474
4808
 
4475
- it "should handle implicit qualifiers in arguments" do
4809
+ it "should handle qualified identifiers in arguments" do
4810
+ @ds.select_hash(Sequel[:t][:a], Sequel[:t][:b]).must_equal(1=>2, 3=>4)
4811
+ @ds.db.sqls.must_equal ['SELECT t.a, t.b FROM t']
4812
+ end
4813
+
4814
+ with_symbol_splitting "should handle implicit qualifiers in arguments" do
4476
4815
  @ds.select_hash(:t__a, :t__b).must_equal(1=>2, 3=>4)
4477
4816
  @ds.db.sqls.must_equal ['SELECT t.a, t.b FROM t']
4478
4817
  end
4479
4818
 
4480
- it "should handle implicit aliases in arguments" do
4819
+ it "should handle aliased expresssions in arguments" do
4820
+ @ds.select_hash(Sequel[:c].as(:a), Sequel[:d].as(:b)).must_equal(1=>2, 3=>4)
4821
+ @ds.db.sqls.must_equal ['SELECT c AS a, d AS b FROM t']
4822
+ end
4823
+
4824
+ with_symbol_splitting "should handle implicit aliases in arguments" do
4481
4825
  @ds.select_hash(:c___a, :d___b).must_equal(1=>2, 3=>4)
4482
4826
  @ds.db.sqls.must_equal ['SELECT c AS a, d AS b FROM t']
4483
4827
  end
4484
4828
 
4485
- it "should handle implicit qualifiers and aliases in arguments" do
4829
+ it "should handle qualified identifiers and aliased expressions in arguments" do
4830
+ @ds.select_hash(Sequel[:t][:c].as(:a), Sequel[:t][:d].as(:b)).must_equal(1=>2, 3=>4)
4831
+ @ds.db.sqls.must_equal ['SELECT t.c AS a, t.d AS b FROM t']
4832
+ end
4833
+
4834
+ with_symbol_splitting "should handle implicit qualifiers and aliases in arguments" do
4486
4835
  @ds.select_hash(:t__c___a, :t__d___b).must_equal(1=>2, 3=>4)
4487
4836
  @ds.db.sqls.must_equal ['SELECT t.c AS a, t.d AS b FROM t']
4488
4837
  end
@@ -4528,17 +4877,17 @@ describe "Sequel::Dataset#select_hash_groups" do
4528
4877
  @ds.db.sqls.must_equal ['SELECT a, b FROM t']
4529
4878
  end
4530
4879
 
4531
- it "should handle implicit qualifiers in arguments" do
4880
+ with_symbol_splitting "should handle implicit qualifiers in arguments" do
4532
4881
  @ds.select_hash_groups(:t__a, :t__b).must_equal(1=>[2], 3=>[4])
4533
4882
  @ds.db.sqls.must_equal ['SELECT t.a, t.b FROM t']
4534
4883
  end
4535
4884
 
4536
- it "should handle implicit aliases in arguments" do
4885
+ with_symbol_splitting "should handle implicit aliases in arguments" do
4537
4886
  @ds.select_hash_groups(:c___a, :d___b).must_equal(1=>[2], 3=>[4])
4538
4887
  @ds.db.sqls.must_equal ['SELECT c AS a, d AS b FROM t']
4539
4888
  end
4540
4889
 
4541
- it "should handle implicit qualifiers and aliases in arguments" do
4890
+ with_symbol_splitting "should handle implicit qualifiers and aliases in arguments" do
4542
4891
  @ds.select_hash_groups(:t__c___a, :t__d___b).must_equal(1=>[2], 3=>[4])
4543
4892
  @ds.db.sqls.must_equal ['SELECT t.c AS a, t.d AS b FROM t']
4544
4893
  end
@@ -4558,6 +4907,11 @@ describe "Sequel::Dataset#select_hash_groups" do
4558
4907
  @ds.db.sqls.must_equal ['SELECT c AS a, t AS b FROM t']
4559
4908
  end
4560
4909
 
4910
+ it "should handle SQL::QualifiedIdentifiers and SQL::AliasedExpressions in arguments" do
4911
+ @ds.select_hash_groups(Sequel[:t][:c].as(:a), Sequel[:t][:d].as(:b)).must_equal(1=>[2], 3=>[4])
4912
+ @ds.db.sqls.must_equal ['SELECT t.c AS a, t.d AS b FROM t']
4913
+ end
4914
+
4561
4915
  it "should work with arrays of columns" do
4562
4916
  @db.fetch = [{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}]
4563
4917
  @ds.select_hash_groups([:a, :c], :b).must_equal([1, 3]=>[2], [4, 6]=>[5])
@@ -4631,15 +4985,24 @@ describe "Dataset#skip_locked" do
4631
4985
  end
4632
4986
 
4633
4987
  describe "Custom ASTTransformer" do
4634
- it "should transform given objects" do
4635
- c = Class.new(Sequel::ASTTransformer) do
4988
+ before do
4989
+ @c = Class.new(Sequel::ASTTransformer) do
4636
4990
  def v(s)
4637
4991
  (s.is_a?(Symbol) || s.is_a?(String)) ? :"#{s}#{s}" : super
4638
4992
  end
4639
4993
  end.new
4994
+ end
4995
+
4996
+ it "should transform given objects" do
4997
+ ds = Sequel.mock.dataset.from(:t).cross_join(Sequel[:a].as(:g)).join(Sequel[:b].as(:h), [:c]).join(Sequel[:d].as(:i), :e=>:f)
4998
+ ds.sql.must_equal 'SELECT * FROM t CROSS JOIN a AS g INNER JOIN b AS h USING (c) INNER JOIN d AS i ON (i.e = h.f)'
4999
+ ds.clone(:from=>@c.transform(ds.opts[:from]), :join=>@c.transform(ds.opts[:join])).sql.must_equal 'SELECT * FROM tt CROSS JOIN aa AS g INNER JOIN bb AS h USING (cc) INNER JOIN dd AS i ON (ii.ee = hh.ff)'
5000
+ end
5001
+
5002
+ with_symbol_splitting "should transform given objects with splittable symbols" do
4640
5003
  ds = Sequel.mock.dataset.from(:t).cross_join(:a___g).join(:b___h, [:c]).join(:d___i, :e=>:f)
4641
5004
  ds.sql.must_equal 'SELECT * FROM t CROSS JOIN a AS g INNER JOIN b AS h USING (c) INNER JOIN d AS i ON (i.e = h.f)'
4642
- ds.clone(:from=>c.transform(ds.opts[:from]), :join=>c.transform(ds.opts[:join])).sql.must_equal 'SELECT * FROM tt CROSS JOIN aa AS g INNER JOIN bb AS h USING (cc) INNER JOIN dd AS i ON (ii.ee = hh.ff)'
5005
+ ds.clone(:from=>@c.transform(ds.opts[:from]), :join=>@c.transform(ds.opts[:join])).sql.must_equal 'SELECT * FROM tt CROSS JOIN aa AS g INNER JOIN bb AS h USING (cc) INNER JOIN dd AS i ON (ii.ee = hh.ff)'
4643
5006
  end
4644
5007
  end
4645
5008
 
@@ -4795,7 +5158,7 @@ describe "Dataset#schema_and_table" do
4795
5158
  @ds = Sequel.mock[:test]
4796
5159
  end
4797
5160
 
4798
- it "should correctly handle symbols" do
5161
+ with_symbol_splitting "should correctly handle symbols" do
4799
5162
  @ds.schema_and_table(:s).must_equal [nil, 's']
4800
5163
  @ds.schema_and_table(:s___a).must_equal [nil, 's']
4801
5164
  @ds.schema_and_table(:t__s).must_equal ['t', 's']
@@ -4827,6 +5190,9 @@ describe "Dataset#split_qualifiers" do
4827
5190
 
4828
5191
  it "should correctly handle symbols" do
4829
5192
  @ds.split_qualifiers(:s).must_equal ['s']
5193
+ end
5194
+
5195
+ with_symbol_splitting "should correctly handle splittable symbols" do
4830
5196
  @ds.split_qualifiers(:s___a).must_equal ['s']
4831
5197
  @ds.split_qualifiers(:t__s).must_equal ['t', 's']
4832
5198
  @ds.split_qualifiers(:t__s___a).must_equal ['t', 's']
@@ -4844,12 +5210,15 @@ describe "Dataset#split_qualifiers" do
4844
5210
  @ds.split_qualifiers(Sequel.qualify(:t, :s)).must_equal ['t', 's']
4845
5211
  end
4846
5212
 
4847
- it "should correctly handle complex qualified identifiers" do
5213
+ with_symbol_splitting "should correctly handle complex qualified identifiers with splittable symbols" do
4848
5214
  @ds.split_qualifiers(Sequel.qualify(:d__t, :s)).must_equal ['d', 't', 's']
4849
- @ds.split_qualifiers(Sequel.qualify(Sequel.qualify(:d, :t), :s)).must_equal ['d', 't', 's']
4850
5215
  @ds.split_qualifiers(Sequel.qualify(:d, :t__s)).must_equal ['d', 't', 's']
4851
- @ds.split_qualifiers(Sequel.qualify(:d, Sequel.qualify(:t, :s))).must_equal ['d', 't', 's']
4852
5216
  @ds.split_qualifiers(Sequel.qualify(:d__t, :s__s2)).must_equal ['d', 't', 's', 's2']
5217
+ end
5218
+
5219
+ it "should correctly handle complex qualified identifiers" do
5220
+ @ds.split_qualifiers(Sequel.qualify(Sequel.qualify(:d, :t), :s)).must_equal ['d', 't', 's']
5221
+ @ds.split_qualifiers(Sequel.qualify(:d, Sequel.qualify(:t, :s))).must_equal ['d', 't', 's']
4853
5222
  @ds.split_qualifiers(Sequel.qualify(Sequel.qualify(:d, :t), Sequel.qualify(:s, :s2))).must_equal ['d', 't', 's', 's2']
4854
5223
  end
4855
5224
  end
@@ -5115,6 +5484,9 @@ describe "#unqualified_column_for" do
5115
5484
 
5116
5485
  it "should handle Symbols" do
5117
5486
  @ds.unqualified_column_for(:a).must_equal Sequel.identifier('a')
5487
+ end
5488
+
5489
+ with_symbol_splitting "should handle splittable symbols" do
5118
5490
  @ds.unqualified_column_for(:b__a).must_equal Sequel.identifier('a')
5119
5491
  @ds.unqualified_column_for(:a___c).must_equal Sequel.identifier('a').as('c')
5120
5492
  @ds.unqualified_column_for(:b__a___c).must_equal Sequel.identifier('a').as('c')