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
@@ -53,8 +53,19 @@ describe "Dataset::PlaceholderLiteralizer" do
53
53
  loader = @c.loader(@ds){|pl, ds| ds.where(pl.arg)}
54
54
  loader.first(:id=>1).must_equal @h
55
55
  loader.first(Sequel.expr{a(b)}).must_equal @h
56
+ @db.sqls.must_equal ["SELECT * FROM items WHERE (id = 1)", "SELECT * FROM items WHERE a(b)"]
57
+ end
58
+
59
+ deprecated "should handle calls with a literal strings used as filter arguments" do
60
+ loader = @c.loader(@ds){|pl, ds| ds.where(pl.arg)}
56
61
  loader.first("a = 1").must_equal @h
57
- @db.sqls.must_equal ["SELECT * FROM items WHERE (id = 1)", "SELECT * FROM items WHERE a(b)", "SELECT * FROM items WHERE (a = 1)"]
62
+ @db.sqls.must_equal ["SELECT * FROM items WHERE (a = 1)"]
63
+ end
64
+
65
+ it "should handle calls with a literal strings used as filter arguments" do
66
+ loader = @c.loader(@ds){|pl, ds| ds.where(pl.arg)}
67
+ loader.first(Sequel.lit("a = 1")).must_equal @h
68
+ @db.sqls.must_equal ["SELECT * FROM items WHERE (a = 1)"]
58
69
  end
59
70
 
60
71
  it "should handle calls with a placeholders used as right hand side of condition specifiers" do
@@ -72,13 +83,20 @@ describe "Dataset::PlaceholderLiteralizer" do
72
83
  @db.sqls.must_equal ["SELECT * FROM items WHERE ((a = 1) AND (b = 1))", "SELECT * FROM items WHERE ((a = 2) AND (b = 2))"]
73
84
  end
74
85
 
75
- it "should handle calls with a placeholder used multiple times in different capacities" do
86
+ deprecated "should handle calls with a placeholder used multiple times in different capacities" do
76
87
  loader = @c.loader(@ds){|pl, ds| a = pl.arg; ds.where(a).where(:b=>a)}
77
88
  loader.first("a = 1").must_equal @h
78
89
  loader.first(["a = ?", 2]).must_equal @h
79
90
  @db.sqls.must_equal ["SELECT * FROM items WHERE ((a = 1) AND (b = 'a = 1'))", "SELECT * FROM items WHERE ((a = 2) AND (b IN ('a = ?', 2)))"]
80
91
  end
81
92
 
93
+ it "should handle calls with a placeholder used multiple times in different capacities" do
94
+ loader = @c.loader(@ds){|pl, ds| a = pl.arg; ds.select(a).where(:b=>a)}
95
+ loader.first("a").must_equal @h
96
+ loader.first(["a = ?", 2]).must_equal @h
97
+ @db.sqls.must_equal ["SELECT 'a' FROM items WHERE (b = 'a')", "SELECT ('a = ?', 2) FROM items WHERE (b IN ('a = ?', 2))"]
98
+ end
99
+
82
100
  it "should handle calls with manually specified argument positions" do
83
101
  loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg(1)).where(:b=>pl.arg(0))}
84
102
  loader.first(1, 2).must_equal @h
@@ -1,8 +1,8 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
2
 
3
- describe Sequel::Schema::Generator do
3
+ describe Sequel::Schema::CreateTableGenerator do
4
4
  before do
5
- @generator = Sequel::Schema::Generator.new(Sequel.mock) do
5
+ @generator = Sequel::Schema::CreateTableGenerator.new(Sequel.mock) do
6
6
  string :title
7
7
  column :body, :text
8
8
  foreign_key :parent_id
@@ -31,7 +31,7 @@ describe Sequel::Schema::Generator do
31
31
  end
32
32
 
33
33
  it "should respect existing column order if primary_key :keep_order is used" do
34
- generator = Sequel::Schema::Generator.new(Sequel.mock) do
34
+ generator = Sequel::Schema::CreateTableGenerator.new(Sequel.mock) do
35
35
  string :title
36
36
  primary_key :id, :keep_order=>true
37
37
  end
@@ -44,7 +44,7 @@ describe Sequel::Schema::Generator do
44
44
  end
45
45
 
46
46
  it "should handle SQL::Identifier and SQL::QualifiedIdentifier as foreign_key arguments" do
47
- generator = Sequel::Schema::Generator.new(Sequel.mock) do
47
+ generator = Sequel::Schema::CreateTableGenerator.new(Sequel.mock) do
48
48
  foreign_key :a_id, Sequel.identifier(:as)
49
49
  foreign_key :b_id, Sequel.qualify(:c, :b)
50
50
  end
@@ -182,9 +182,9 @@ describe Sequel::Schema::AlterTableGenerator do
182
182
  end
183
183
  end
184
184
 
185
- describe "Sequel::Schema::Generator generic type methods" do
185
+ describe "Sequel::Schema::CreateTableGenerator generic type methods" do
186
186
  it "should store the type class in :type for each column" do
187
- Sequel::Schema::Generator.new(Sequel.mock) do
187
+ Sequel::Schema::CreateTableGenerator.new(Sequel.mock) do
188
188
  String :a
189
189
  Integer :b
190
190
  Fixnum :c
@@ -10,13 +10,17 @@ describe "DB#create_table" do
10
10
  @db.sqls.must_equal ['CREATE TABLE cats ()']
11
11
  end
12
12
 
13
- it "should accept the table name in multiple formats" do
13
+ with_symbol_splitting "should accept the table name with splittable symbols" do
14
14
  @db.create_table(:cats__cats) {}
15
+ @db.sqls.must_equal ['CREATE TABLE cats.cats ()']
16
+ end
17
+
18
+ it "should accept the table name in multiple formats" do
15
19
  @db.create_table(Sequel[:cats][:cats]) {}
16
20
  @db.create_table("cats__cats1") {}
17
21
  @db.create_table(Sequel.identifier(:cats__cats2)) {}
18
22
  @db.create_table(Sequel.qualify(:cats3, :cats)) {}
19
- @db.sqls.must_equal ['CREATE TABLE cats.cats ()', 'CREATE TABLE cats.cats ()', 'CREATE TABLE cats__cats1 ()', 'CREATE TABLE cats__cats2 ()', 'CREATE TABLE cats3.cats ()']
23
+ @db.sqls.must_equal ['CREATE TABLE cats.cats ()', 'CREATE TABLE cats__cats1 ()', 'CREATE TABLE cats__cats2 ()', 'CREATE TABLE cats3.cats ()']
20
24
  end
21
25
 
22
26
  it "should raise an error if the table name argument is not valid" do
@@ -1495,9 +1499,12 @@ describe "Database#create_view" do
1495
1499
  @db.sqls.must_equal ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c WITH LOCAL CHECK OPTION']
1496
1500
  end
1497
1501
 
1498
- it "should handle create_or_replace_view" do
1502
+ with_symbol_splitting "should handle create_or_replace_view with splittable symbols" do
1499
1503
  @db.create_or_replace_view :sch__test, "SELECT * FROM xyz"
1500
1504
  @db.sqls.must_equal ['DROP VIEW sch.test', 'CREATE VIEW sch.test AS SELECT * FROM xyz']
1505
+ end
1506
+
1507
+ it "should handle create_or_replace_view" do
1501
1508
  @db.create_or_replace_view :test, @db[:items].select(:a, :b).order(:c)
1502
1509
  @db.sqls.must_equal ['DROP VIEW test', 'CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
1503
1510
  @db.create_or_replace_view Sequel.identifier(:test), @db[:items].select(:a, :b).order(:c)
@@ -1516,12 +1523,16 @@ describe "Database#drop_view" do
1516
1523
  @db = Sequel.mock
1517
1524
  end
1518
1525
 
1526
+ with_symbol_splitting "should construct proper SQL for splittable symbols" do
1527
+ @db.drop_view :sch__test
1528
+ @db.sqls.must_equal ['DROP VIEW sch.test']
1529
+ end
1530
+
1519
1531
  it "should construct proper SQL" do
1520
1532
  @db.drop_view :test
1521
1533
  @db.drop_view Sequel.identifier(:test)
1522
- @db.drop_view :sch__test
1523
1534
  @db.drop_view Sequel.qualify(:sch, :test)
1524
- @db.sqls.must_equal ['DROP VIEW test', 'DROP VIEW test', 'DROP VIEW sch.test', 'DROP VIEW sch.test']
1535
+ @db.sqls.must_equal ['DROP VIEW test', 'DROP VIEW test', 'DROP VIEW sch.test']
1525
1536
  end
1526
1537
 
1527
1538
  it "should drop multiple views at once" do
@@ -1589,6 +1600,19 @@ describe "Schema Parser" do
1589
1600
  end
1590
1601
  @db.schema(:x)
1591
1602
  c.must_equal ["x", {}]
1603
+ @db.schema(Sequel[:s][:x])
1604
+ c.must_equal ["x", {:schema=>"s"}]
1605
+ ds = @db[Sequel[:s][:y]]
1606
+ @db.schema(ds)
1607
+ c.must_equal ["y", {:schema=>"s", :dataset=>ds}]
1608
+ end
1609
+
1610
+ with_symbol_splitting "should provide options if given a table name with splittable symbols" do
1611
+ c = nil
1612
+ meta_def(@db, :schema_parse_table) do |t, opts|
1613
+ c = [t, opts]
1614
+ [[:a, {:db_type=>t.to_s}]]
1615
+ end
1592
1616
  @db.schema(:s__x)
1593
1617
  c.must_equal ["x", {:schema=>"s"}]
1594
1618
  ds = @db[:s__y]
@@ -1646,6 +1670,31 @@ describe "Schema Parser" do
1646
1670
  @db.schema(:x).object_id.must_equal s1.object_id
1647
1671
  @db.schema(Sequel.identifier(:x)).object_id.must_equal s1.object_id
1648
1672
 
1673
+ s2 = @db.schema(Sequel[:x][:y])
1674
+ s2.must_equal [['y', {:schema=>'x', :ruby_default=>nil}]]
1675
+ @db.schema(Sequel[:x][:y]).object_id.must_equal s2.object_id
1676
+ @db.schema(Sequel.qualify(:x, :y)).object_id.must_equal s2.object_id
1677
+
1678
+ s2 = @db.schema(Sequel.qualify(:v, Sequel[:x][:y]))
1679
+ s2.must_equal [['y', {:schema=>'x', :ruby_default=>nil, :information_schema_schema=>Sequel.identifier('v')}]]
1680
+ @db.schema(Sequel.qualify(:v, Sequel[:x][:y])).object_id.must_equal s2.object_id
1681
+ @db.schema(Sequel.qualify(Sequel[:v][:x], :y)).object_id.must_equal s2.object_id
1682
+
1683
+ s2 = @db.schema(Sequel.qualify(Sequel[:u][:v], Sequel[:x][:y]))
1684
+ s2.must_equal [['y', {:schema=>'x', :ruby_default=>nil, :information_schema_schema=>Sequel.qualify('u', 'v')}]]
1685
+ @db.schema(Sequel.qualify(Sequel[:u][:v], Sequel[:x][:y])).object_id.must_equal s2.object_id
1686
+ @db.schema(Sequel.qualify(Sequel.qualify(:u, :v), Sequel.qualify(:x, :y))).object_id.must_equal s2.object_id
1687
+ end
1688
+
1689
+ with_symbol_splitting "should convert splittable symbol arguments" do
1690
+ meta_def(@db, :schema_parse_table) do |t, opts|
1691
+ [[t, opts]]
1692
+ end
1693
+ s1 = @db.schema(:x)
1694
+ s1.must_equal [['x', {:ruby_default=>nil}]]
1695
+ @db.schema(:x).object_id.must_equal s1.object_id
1696
+ @db.schema(Sequel.identifier(:x)).object_id.must_equal s1.object_id
1697
+
1649
1698
  s2 = @db.schema(:x__y)
1650
1699
  s2.must_equal [['y', {:schema=>'x', :ruby_default=>nil}]]
1651
1700
  @db.schema(:x__y).object_id.must_equal s2.object_id
@@ -6,9 +6,8 @@ if ENV['COVERAGE']
6
6
  end
7
7
 
8
8
  unless Object.const_defined?('Sequel') && Sequel.const_defined?('Model')
9
- $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
9
+ $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../lib/"))
10
10
  require 'sequel'
11
- Sequel::Deprecation.backtrace_filter = true
12
11
  end
13
12
 
14
13
  # SEQUEL5: Remove
@@ -26,8 +25,13 @@ if RUBY_VERSION < '1.9.0'
26
25
  Sequel.extension :ruby18_symbol_extensions
27
26
  end
28
27
  Sequel.extension :symbol_aref
28
+ Sequel.extension :virtual_row_method_block
29
29
 
30
+ gem 'minitest'
30
31
  require 'minitest/autorun'
32
+ require 'minitest/hooks/default'
33
+
34
+ require File.expand_path("#{File.dirname(__FILE__)}/deprecation_helper.rb")
31
35
 
32
36
  describe "Sequel core extensions" do
33
37
  it "should have Sequel.core_extensions? be true if enabled" do
@@ -90,6 +94,9 @@ describe "Core extensions" do
90
94
  end
91
95
  it "should support NOT via Symbol#~" do
92
96
  @d.l(~:x).must_equal 'NOT x'
97
+ end
98
+
99
+ with_symbol_splitting "should support NOT via Symbol#~ for splittable symbols" do
93
100
  @d.l(~:x__y).must_equal 'NOT x.y'
94
101
  end
95
102
 
@@ -231,7 +238,7 @@ describe "Core extensions" do
231
238
  @d.lit([:x.sql_function(1), 'y.z'.lit].sql_string_join(', ')).must_equal "(x(1) || ', ' || y.z)"
232
239
  @d.lit([:x, 1, :y].sql_string_join).must_equal "(x || '1' || y)"
233
240
  @d.lit([:x, 1, :y].sql_string_join(', ')).must_equal "(x || ', ' || '1' || ', ' || y)"
234
- @d.lit([:x, 1, :y].sql_string_join(:y__z)).must_equal "(x || y.z || '1' || y.z || y)"
241
+ @d.lit([:x, 1, :y].sql_string_join(Sequel[:y][:z])).must_equal "(x || y.z || '1' || y.z || y)"
235
242
  @d.lit([:x, 1, :y].sql_string_join(1)).must_equal "(x || '1' || '1' || '1' || y)"
236
243
  @d.lit([:x, :y].sql_string_join('y.x || x.y'.lit)).must_equal "(x || y.x || x.y || y)"
237
244
  @d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).must_equal "(x || y || a || b)"
@@ -282,7 +289,7 @@ describe "Array#case and Hash#case" do
282
289
  @d.literal([[:x, :y]].case(:z)).must_equal '(CASE WHEN x THEN y ELSE z END)'
283
290
  @d.literal([[:x, :y], [:a, :b]].case(:z)).must_equal '(CASE WHEN x THEN y WHEN a THEN b ELSE z END)'
284
291
  @d.literal([[:x, :y], [:a, :b]].case(:z, :exp)).must_equal '(CASE exp WHEN x THEN y WHEN a THEN b ELSE z END)'
285
- @d.literal([[:x, :y], [:a, :b]].case(:z, :exp__w)).must_equal '(CASE exp.w WHEN x THEN y WHEN a THEN b ELSE z END)'
292
+ @d.literal([[:x, :y], [:a, :b]].case(:z, Sequel[:exp][:w])).must_equal '(CASE exp.w WHEN x THEN y WHEN a THEN b ELSE z END)'
286
293
  end
287
294
 
288
295
  it "should return SQL CASE expression with expression even if nil" do
@@ -307,9 +314,9 @@ describe "Array#sql_value_list and #sql_array" do
307
314
  end
308
315
 
309
316
  it "should treat the array as an SQL value list instead of conditions when used as a placeholder value" do
310
- @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]]).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x = 1) AND (y = 2)))'
311
- @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_value_list).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
312
- @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_array).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
317
+ @d.filter(Sequel.lit("(a, b) IN ?", [[:x, 1], [:y, 2]])).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x = 1) AND (y = 2)))'
318
+ @d.filter(Sequel.lit("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_value_list)).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
319
+ @d.filter(Sequel.lit("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_array)).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
313
320
  end
314
321
 
315
322
  it "should be no difference when used as a hash value" do
@@ -401,7 +408,9 @@ describe "#desc" do
401
408
 
402
409
  it "should format a DESC clause for a column ref" do
403
410
  @ds.literal(:test.desc).must_equal 'test DESC'
411
+ end
404
412
 
413
+ with_symbol_splitting "should format a DESC clause for a column ref with a splitting symbol" do
405
414
  @ds.literal(:items__price.desc).must_equal 'items.price DESC'
406
415
  end
407
416
 
@@ -417,7 +426,9 @@ describe "#asc" do
417
426
 
418
427
  it "should format a ASC clause for a column ref" do
419
428
  @ds.literal(:test.asc).must_equal 'test ASC'
429
+ end
420
430
 
431
+ with_symbol_splitting "should format a ASC clause for a column ref for a splittable symbol" do
421
432
  @ds.literal(:items__price.asc).must_equal 'items.price ASC'
422
433
  end
423
434
 
@@ -433,7 +444,9 @@ describe "#as" do
433
444
 
434
445
  it "should format a AS clause for a column ref" do
435
446
  @ds.literal(:test.as(:t)).must_equal 'test AS t'
436
-
447
+ end
448
+
449
+ with_symbol_splitting "should format a AS clause for a column ref for splittable symbols" do
437
450
  @ds.literal(:items__price.as(:p)).must_equal 'items.price AS p'
438
451
  end
439
452
 
@@ -453,15 +466,9 @@ describe "Column references" do
453
466
 
454
467
  it "should be quoted properly" do
455
468
  @ds.literal(:xyz).must_equal "`xyz`"
456
- @ds.literal(:xyz__abc).must_equal "`xyz`.`abc`"
457
-
458
469
  @ds.literal(:xyz.as(:x)).must_equal "`xyz` AS `x`"
459
- @ds.literal(:xyz__abc.as(:x)).must_equal "`xyz`.`abc` AS `x`"
460
-
461
- @ds.literal(:xyz___x).must_equal "`xyz` AS `x`"
462
- @ds.literal(:xyz__abc___x).must_equal "`xyz`.`abc` AS `x`"
463
470
  end
464
-
471
+
465
472
  it "should be quoted properly in SQL functions" do
466
473
  @ds.literal(:avg.sql_function(:xyz)).must_equal "avg(`xyz`)"
467
474
  @ds.literal(:avg.sql_function(:xyz, 1)).must_equal "avg(`xyz`, 1)"
@@ -475,6 +482,13 @@ describe "Column references" do
475
482
 
476
483
  it "should be quoted properly in a cast function" do
477
484
  @ds.literal(:x.cast(:integer)).must_equal "CAST(`x` AS integer)"
485
+ end
486
+
487
+ with_symbol_splitting "should be quoted properly when using symbol splitting" do
488
+ @ds.literal(:xyz__abc).must_equal "`xyz`.`abc`"
489
+ @ds.literal(:xyz__abc.as(:x)).must_equal "`xyz`.`abc` AS `x`"
490
+ @ds.literal(:xyz___x).must_equal "`xyz` AS `x`"
491
+ @ds.literal(:xyz__abc___x).must_equal "`xyz`.`abc` AS `x`"
478
492
  @ds.literal(:x__y.cast('varchar(20)')).must_equal "CAST(`x`.`y` AS varchar(20))"
479
493
  end
480
494
  end
@@ -512,7 +526,7 @@ describe "Symbol#*" do
512
526
  @ds.literal(:abc.*(5)).must_equal '(abc * 5)'
513
527
  end
514
528
 
515
- it "should support qualified symbols if no argument" do
529
+ with_symbol_splitting "should support qualified symbols if no argument" do
516
530
  @ds.literal(:xyz__abc.*).must_equal 'xyz.abc.*'
517
531
  end
518
532
  end
@@ -534,7 +548,7 @@ describe "Symbol" do
534
548
  @ds.literal(:xyz.qualify(:abc).qualify(:def)).must_equal '"def"."abc"."xyz"'
535
549
  end
536
550
 
537
- it "should be able to qualify an identifier" do
551
+ with_symbol_splitting "should be able to qualify an identifier" do
538
552
  @ds.literal(:xyz.identifier.qualify(:xyz__abc)).must_equal '"xyz"."abc"."xyz"'
539
553
  end
540
554
 
@@ -575,11 +589,14 @@ describe "Symbol" do
575
589
 
576
590
  it "should support sql array accesses via sql_subscript" do
577
591
  @ds.literal(:abc.sql_subscript(1)).must_equal "abc[1]"
578
- @ds.literal(:abc__def.sql_subscript(1)).must_equal "abc.def[1]"
579
592
  @ds.literal(:abc.sql_subscript(1)|2).must_equal "abc[1, 2]"
580
593
  @ds.literal(:abc.sql_subscript(1)[2]).must_equal "abc[1][2]"
581
594
  end
582
595
 
596
+ with_symbol_splitting "should support sql array accesses via sql_subscript for splittable symbols" do
597
+ @ds.literal(:abc__def.sql_subscript(1)).must_equal "abc.def[1]"
598
+ end
599
+
583
600
  it "should support cast_numeric and cast_string" do
584
601
  x = :abc.cast_numeric
585
602
  x.must_be_kind_of(Sequel::SQL::NumericExpression)
@@ -726,3 +743,90 @@ describe "symbol_aref extensions" do
726
743
  end
727
744
  end
728
745
  end
746
+
747
+ describe Sequel::SQL::VirtualRow do
748
+ before do
749
+ @d = Sequel.mock[:items].with_quote_identifiers(true).with_extend do
750
+ def supports_window_functions?; true end
751
+ def l(*args, &block)
752
+ literal(filter_expr(*args, &block))
753
+ end
754
+ end
755
+ end
756
+
757
+ it "should treat methods without blocks normally" do
758
+ @d.l{column}.must_equal '"column"'
759
+ @d.l{foo(a)}.must_equal 'foo("a")'
760
+ end
761
+
762
+
763
+ it "should treat methods with a block and no arguments as a function call with no arguments" do
764
+ @d.l{version{}}.must_equal 'version()'
765
+ end
766
+
767
+ it "should treat methods with a block and a leading argument :* as a function call with the SQL wildcard" do
768
+ @d.l{count(:*){}}.must_equal 'count(*)'
769
+ end
770
+
771
+ it "should treat methods with a block and a leading argument :distinct as a function call with DISTINCT and the additional method arguments" do
772
+ @d.l{count(:distinct, column1){}}.must_equal 'count(DISTINCT "column1")'
773
+ @d.l{count(:distinct, column1, column2){}}.must_equal 'count(DISTINCT "column1", "column2")'
774
+ end
775
+
776
+ it "should raise an error if an unsupported argument is used with a block" do
777
+ proc{@d.where{count(:blah){}}}.must_raise(Sequel::Error)
778
+ end
779
+
780
+ it "should treat methods with a block and a leading argument :over as a window function call" do
781
+ @d.l{rank(:over){}}.must_equal 'rank() OVER ()'
782
+ end
783
+
784
+ it "should support :partition options for window function calls" do
785
+ @d.l{rank(:over, :partition=>column1){}}.must_equal 'rank() OVER (PARTITION BY "column1")'
786
+ @d.l{rank(:over, :partition=>[column1, column2]){}}.must_equal 'rank() OVER (PARTITION BY "column1", "column2")'
787
+ end
788
+
789
+ it "should support :args options for window function calls" do
790
+ @d.l{avg(:over, :args=>column1){}}.must_equal 'avg("column1") OVER ()'
791
+ @d.l{avg(:over, :args=>[column1, column2]){}}.must_equal 'avg("column1", "column2") OVER ()'
792
+ end
793
+
794
+ it "should support :order option for window function calls" do
795
+ @d.l{rank(:over, :order=>column1){}}.must_equal 'rank() OVER (ORDER BY "column1")'
796
+ @d.l{rank(:over, :order=>[column1, column2]){}}.must_equal 'rank() OVER (ORDER BY "column1", "column2")'
797
+ end
798
+
799
+ it "should support :window option for window function calls" do
800
+ @d.l{rank(:over, :window=>:win){}}.must_equal 'rank() OVER ("win")'
801
+ end
802
+
803
+ it "should support :*=>true option for window function calls" do
804
+ @d.l{count(:over, :* =>true){}}.must_equal 'count(*) OVER ()'
805
+ end
806
+
807
+ it "should support :frame=>:all option for window function calls" do
808
+ @d.l{rank(:over, :frame=>:all){}}.must_equal 'rank() OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)'
809
+ end
810
+
811
+ it "should support :frame=>:rows option for window function calls" do
812
+ @d.l{rank(:over, :frame=>:rows){}}.must_equal 'rank() OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)'
813
+ end
814
+
815
+ it "should support :frame=>'some string' option for window function calls" do
816
+ @d.l{rank(:over, :frame=>'RANGE BETWEEN 3 PRECEDING AND CURRENT ROW'){}}.must_equal 'rank() OVER (RANGE BETWEEN 3 PRECEDING AND CURRENT ROW)'
817
+ end
818
+
819
+ it "should raise an error if an invalid :frame option is used" do
820
+ proc{@d.l{rank(:over, :frame=>:blah){}}}.must_raise(Sequel::Error)
821
+ end
822
+
823
+ it "should support all these options together" do
824
+ @d.l{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}.must_equal 'count(*) OVER ("win" PARTITION BY "a" ORDER BY "b" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)'
825
+ end
826
+
827
+ it "should raise an error if window functions are not supported" do
828
+ proc{@d.with_extend{def supports_window_functions?; false end}.l{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}}.must_raise(Sequel::Error)
829
+ proc{Sequel.mock.dataset.filter{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}.sql}.must_raise(Sequel::Error)
830
+ end
831
+ end
832
+
@@ -1,8 +1,8 @@
1
1
  Sequel::Deprecation.backtrace_filter = lambda{|line, lineno| lineno < 4 || line =~ /_spec\.rb/}
2
2
 
3
3
  class Minitest::HooksSpec
4
- def self.deprecated(*a, &block)
5
- it(*a) do
4
+ def self.deprecated(a, &block)
5
+ it("#{a} (deprecated)") do
6
6
  deprecated{instance_exec(&block)}
7
7
  end
8
8
  end
@@ -14,4 +14,29 @@ class Minitest::HooksSpec
14
14
  ensure
15
15
  Sequel::Deprecation.output = output
16
16
  end
17
+
18
+ def self.with_symbol_splitting(a, &block)
19
+ it(a) do
20
+ with_symbol_splitting{instance_exec(&block)}
21
+ end
22
+ it("#{a}, except when symbol_splitting disabled") do
23
+ without_symbol_splitting{instance_exec(&block)}
24
+ end
25
+ end
26
+
27
+ def without_symbol_splitting
28
+ Sequel.split_symbols = false
29
+ DB.send(:reset_default_dataset) if defined?(DB)
30
+ proc{yield}.must_raise Minitest::Assertion
31
+ ensure
32
+ Sequel.split_symbols = :deprecated
33
+ DB.send(:reset_default_dataset) if defined?(DB)
34
+ end
35
+
36
+ def with_symbol_splitting
37
+ Sequel.split_symbols = true
38
+ yield
39
+ ensure
40
+ Sequel.split_symbols = :deprecated
41
+ end
17
42
  end