sequel 3.33.0 → 3.34.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 (152) hide show
  1. data/CHANGELOG +140 -0
  2. data/Rakefile +7 -0
  3. data/bin/sequel +22 -2
  4. data/doc/dataset_basics.rdoc +1 -1
  5. data/doc/mass_assignment.rdoc +3 -1
  6. data/doc/querying.rdoc +28 -4
  7. data/doc/reflection.rdoc +23 -3
  8. data/doc/release_notes/3.34.0.txt +671 -0
  9. data/doc/schema_modification.rdoc +18 -2
  10. data/doc/virtual_rows.rdoc +49 -0
  11. data/lib/sequel/adapters/do/mysql.rb +0 -5
  12. data/lib/sequel/adapters/ibmdb.rb +9 -4
  13. data/lib/sequel/adapters/jdbc.rb +9 -4
  14. data/lib/sequel/adapters/jdbc/h2.rb +8 -2
  15. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
  18. data/lib/sequel/adapters/mock.rb +24 -3
  19. data/lib/sequel/adapters/mysql.rb +29 -50
  20. data/lib/sequel/adapters/mysql2.rb +13 -28
  21. data/lib/sequel/adapters/oracle.rb +8 -2
  22. data/lib/sequel/adapters/postgres.rb +115 -20
  23. data/lib/sequel/adapters/shared/db2.rb +1 -1
  24. data/lib/sequel/adapters/shared/mssql.rb +14 -3
  25. data/lib/sequel/adapters/shared/mysql.rb +59 -11
  26. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +1 -1
  28. data/lib/sequel/adapters/shared/postgres.rb +127 -30
  29. data/lib/sequel/adapters/shared/sqlite.rb +55 -38
  30. data/lib/sequel/adapters/sqlite.rb +9 -3
  31. data/lib/sequel/adapters/swift.rb +2 -2
  32. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  33. data/lib/sequel/adapters/swift/postgres.rb +10 -0
  34. data/lib/sequel/ast_transformer.rb +4 -0
  35. data/lib/sequel/connection_pool.rb +8 -0
  36. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  37. data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
  38. data/lib/sequel/connection_pool/single.rb +5 -0
  39. data/lib/sequel/connection_pool/threaded.rb +14 -0
  40. data/lib/sequel/core.rb +24 -3
  41. data/lib/sequel/database/connecting.rb +24 -14
  42. data/lib/sequel/database/dataset_defaults.rb +1 -0
  43. data/lib/sequel/database/misc.rb +16 -25
  44. data/lib/sequel/database/query.rb +20 -2
  45. data/lib/sequel/database/schema_generator.rb +2 -2
  46. data/lib/sequel/database/schema_methods.rb +120 -23
  47. data/lib/sequel/dataset/actions.rb +91 -18
  48. data/lib/sequel/dataset/features.rb +5 -0
  49. data/lib/sequel/dataset/prepared_statements.rb +6 -2
  50. data/lib/sequel/dataset/sql.rb +68 -51
  51. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  52. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
  53. data/lib/sequel/extensions/migration.rb +4 -0
  54. data/lib/sequel/extensions/null_dataset.rb +90 -0
  55. data/lib/sequel/extensions/pg_array.rb +460 -0
  56. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  57. data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
  58. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  59. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  60. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  61. data/lib/sequel/extensions/pretty_table.rb +5 -71
  62. data/lib/sequel/extensions/query_literals.rb +79 -0
  63. data/lib/sequel/extensions/schema_caching.rb +76 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +227 -31
  65. data/lib/sequel/extensions/select_remove.rb +35 -0
  66. data/lib/sequel/extensions/sql_expr.rb +4 -110
  67. data/lib/sequel/extensions/to_dot.rb +1 -1
  68. data/lib/sequel/model.rb +11 -2
  69. data/lib/sequel/model/associations.rb +35 -7
  70. data/lib/sequel/model/base.rb +159 -36
  71. data/lib/sequel/no_core_ext.rb +2 -0
  72. data/lib/sequel/plugins/caching.rb +25 -18
  73. data/lib/sequel/plugins/composition.rb +1 -1
  74. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  75. data/lib/sequel/plugins/identity_map.rb +11 -3
  76. data/lib/sequel/plugins/instance_filters.rb +10 -0
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  78. data/lib/sequel/plugins/nested_attributes.rb +4 -3
  79. data/lib/sequel/plugins/prepared_statements.rb +3 -1
  80. data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
  81. data/lib/sequel/plugins/schema.rb +7 -2
  82. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  83. data/lib/sequel/plugins/static_cache.rb +99 -0
  84. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  85. data/lib/sequel/sql.rb +417 -7
  86. data/lib/sequel/version.rb +1 -1
  87. data/spec/adapters/firebird_spec.rb +1 -1
  88. data/spec/adapters/mssql_spec.rb +12 -15
  89. data/spec/adapters/mysql_spec.rb +81 -23
  90. data/spec/adapters/postgres_spec.rb +444 -77
  91. data/spec/adapters/spec_helper.rb +2 -0
  92. data/spec/adapters/sqlite_spec.rb +8 -8
  93. data/spec/core/connection_pool_spec.rb +85 -0
  94. data/spec/core/database_spec.rb +29 -5
  95. data/spec/core/dataset_spec.rb +171 -3
  96. data/spec/core/expression_filters_spec.rb +364 -0
  97. data/spec/core/mock_adapter_spec.rb +17 -3
  98. data/spec/core/schema_spec.rb +133 -0
  99. data/spec/extensions/association_dependencies_spec.rb +13 -13
  100. data/spec/extensions/caching_spec.rb +26 -3
  101. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  102. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
  103. data/spec/extensions/force_encoding_spec.rb +4 -2
  104. data/spec/extensions/hook_class_methods_spec.rb +5 -2
  105. data/spec/extensions/identity_map_spec.rb +17 -0
  106. data/spec/extensions/instance_filters_spec.rb +1 -1
  107. data/spec/extensions/lazy_attributes_spec.rb +2 -2
  108. data/spec/extensions/list_spec.rb +4 -4
  109. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  110. data/spec/extensions/migration_spec.rb +6 -2
  111. data/spec/extensions/nested_attributes_spec.rb +20 -0
  112. data/spec/extensions/null_dataset_spec.rb +85 -0
  113. data/spec/extensions/optimistic_locking_spec.rb +2 -2
  114. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  115. data/spec/extensions/pg_array_spec.rb +196 -0
  116. data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
  117. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  118. data/spec/extensions/pg_hstore_spec.rb +195 -0
  119. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  120. data/spec/extensions/prepared_statements_spec.rb +4 -0
  121. data/spec/extensions/pretty_table_spec.rb +6 -0
  122. data/spec/extensions/query_literals_spec.rb +168 -0
  123. data/spec/extensions/schema_caching_spec.rb +41 -0
  124. data/spec/extensions/schema_dumper_spec.rb +231 -11
  125. data/spec/extensions/schema_spec.rb +14 -2
  126. data/spec/extensions/select_remove_spec.rb +38 -0
  127. data/spec/extensions/sharding_spec.rb +6 -6
  128. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  129. data/spec/extensions/spec_helper.rb +2 -1
  130. data/spec/extensions/sql_expr_spec.rb +28 -19
  131. data/spec/extensions/static_cache_spec.rb +145 -0
  132. data/spec/extensions/touch_spec.rb +1 -1
  133. data/spec/extensions/typecast_on_load_spec.rb +9 -1
  134. data/spec/integration/associations_test.rb +6 -6
  135. data/spec/integration/database_test.rb +1 -1
  136. data/spec/integration/dataset_test.rb +89 -26
  137. data/spec/integration/migrator_test.rb +2 -3
  138. data/spec/integration/model_test.rb +3 -3
  139. data/spec/integration/plugin_test.rb +85 -22
  140. data/spec/integration/prepared_statement_test.rb +28 -8
  141. data/spec/integration/schema_test.rb +78 -7
  142. data/spec/integration/spec_helper.rb +1 -0
  143. data/spec/integration/timezone_test.rb +1 -1
  144. data/spec/integration/transaction_test.rb +4 -6
  145. data/spec/integration/type_test.rb +2 -2
  146. data/spec/model/associations_spec.rb +94 -8
  147. data/spec/model/base_spec.rb +4 -4
  148. data/spec/model/hooks_spec.rb +2 -2
  149. data/spec/model/model_spec.rb +19 -7
  150. data/spec/model/record_spec.rb +135 -58
  151. data/spec/model/spec_helper.rb +1 -0
  152. metadata +35 -7
@@ -32,6 +32,10 @@ describe "prepared_statements plugin" do
32
32
  end
33
33
 
34
34
  specify "should correctly create instance if dataset supports insert_select" do
35
+ ds = @c.instance_dataset
36
+ def ds.supports_insert_select?
37
+ true
38
+ end
35
39
  def @ds.supports_insert_select?
36
40
  true
37
41
  end
@@ -63,6 +63,12 @@ describe "PrettyTable" do
63
63
  /\n(\|x\|y\|)|(\|y\|x\|)\n/
64
64
  end
65
65
 
66
+ specify "should have #string return the string without printing" do
67
+ Sequel::PrettyTable.string(@data1).should =~ /\n(\|x\|y\|)|(\|y\|x\|)\n/
68
+ @output.rewind
69
+ @output.read.should == ''
70
+ end
71
+
66
72
  specify "should calculate the maximum width of each column correctly" do
67
73
  Sequel::PrettyTable.print(@data2, [:a, :b])
68
74
  @output.rewind
@@ -0,0 +1,168 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "query_literals extension" do
4
+ before do
5
+ @ds = Sequel::Dataset.new(nil).from(:t)
6
+ @ds.extend(Sequel::QueryLiterals)
7
+ end
8
+
9
+ it "should not use special support if given a block" do
10
+ @ds.select('a, b, c'){d}.sql.should == 'SELECT \'a, b, c\', d FROM t'
11
+ end
12
+
13
+ it "should have #select use literal string if given a single string" do
14
+ @ds.select('a, b, c').sql.should == 'SELECT a, b, c FROM t'
15
+ end
16
+
17
+ it "should have #select use placeholder literal string if given a string and additional arguments" do
18
+ @ds.select('a, b, ?', 1).sql.should == 'SELECT a, b, 1 FROM t'
19
+ end
20
+
21
+ it "should have #select work the standard way if initial string is a literal string already" do
22
+ @ds.select('a, b, ?'.lit, 1).sql.should == 'SELECT a, b, ?, 1 FROM t'
23
+ end
24
+
25
+ it "should have #select work regularly if not given a string as the first argument" do
26
+ @ds.select(:a, 1).sql.should == 'SELECT a, 1 FROM t'
27
+ end
28
+
29
+ describe 'with existing selection' do
30
+ before do
31
+ @ds = @ds.select(:d)
32
+ end
33
+
34
+ it "should have #select_more use literal string if given a single string" do
35
+ @ds.select_more('a, b, c').sql.should == 'SELECT d, a, b, c FROM t'
36
+ end
37
+
38
+ it "should have #select_ use placeholder literal string if given a string and additional arguments" do
39
+ @ds.select_more('a, b, ?', 1).sql.should == 'SELECT d, a, b, 1 FROM t'
40
+ end
41
+
42
+ it "should have #select_more work the standard way if initial string is a literal string already" do
43
+ @ds.select_more('a, b, ?'.lit, 1).sql.should == 'SELECT d, a, b, ?, 1 FROM t'
44
+ end
45
+
46
+ it "should have #select_more work regularly if not given a string as the first argument" do
47
+ @ds.select_more(:a, 1).sql.should == 'SELECT d, a, 1 FROM t'
48
+ end
49
+ end
50
+
51
+ it "should have #select_append use literal string if given a single string" do
52
+ @ds.select_append('a, b, c').sql.should == 'SELECT *, a, b, c FROM t'
53
+ end
54
+
55
+ it "should have #select_append use placeholder literal string if given a string and additional arguments" do
56
+ @ds.select_append('a, b, ?', 1).sql.should == 'SELECT *, a, b, 1 FROM t'
57
+ end
58
+
59
+ it "should have #select_append work the standard way if initial string is a literal string already" do
60
+ @ds.select_append('a, b, ?'.lit, 1).sql.should == 'SELECT *, a, b, ?, 1 FROM t'
61
+ end
62
+
63
+ it "should have #select_append work regularly if not given a string as the first argument" do
64
+ @ds.select_append(:a, 1).sql.should == 'SELECT *, a, 1 FROM t'
65
+ end
66
+
67
+ it "should have #select_group use literal string if given a single string" do
68
+ @ds.select_group('a, b, c').sql.should == 'SELECT a, b, c FROM t GROUP BY a, b, c'
69
+ end
70
+
71
+ it "should have #select_group use placeholder literal string if given a string and additional arguments" do
72
+ @ds.select_group('a, b, ?', 1).sql.should == 'SELECT a, b, 1 FROM t GROUP BY a, b, 1'
73
+ end
74
+
75
+ it "should have #select_group work the standard way if initial string is a literal string already" do
76
+ @ds.select_group('a, b, ?'.lit, 1).sql.should == 'SELECT a, b, ?, 1 FROM t GROUP BY a, b, ?, 1'
77
+ end
78
+
79
+ it "should have #select_group work regularly if not given a string as the first argument" do
80
+ @ds.select_group(:a, 1).sql.should == 'SELECT a, 1 FROM t GROUP BY a, 1'
81
+ end
82
+
83
+ it "should have #group use literal string if given a single string" do
84
+ @ds.group('a, b, c').sql.should == 'SELECT * FROM t GROUP BY a, b, c'
85
+ end
86
+
87
+ it "should have #group use placeholder literal string if given a string and additional arguments" do
88
+ @ds.group('a, b, ?', 1).sql.should == 'SELECT * FROM t GROUP BY a, b, 1'
89
+ end
90
+
91
+ it "should have #select_group work the standard way if initial string is a literal string already" do
92
+ @ds.group('a, b, ?'.lit, 1).sql.should == 'SELECT * FROM t GROUP BY a, b, ?, 1'
93
+ end
94
+
95
+ it "should have #select_group work regularly if not given a string as the first argument" do
96
+ @ds.group(:a, 1).sql.should == 'SELECT * FROM t GROUP BY a, 1'
97
+ end
98
+
99
+ it "should have #group_and_count use literal string if given a single string" do
100
+ @ds.group_and_count('a, b, c').sql.should == 'SELECT a, b, c, count(*) AS count FROM t GROUP BY a, b, c'
101
+ end
102
+
103
+ it "should have #group_and_count use placeholder literal string if given a string and additional arguments" do
104
+ @ds.group_and_count('a, b, ?', 1).sql.should == 'SELECT a, b, 1, count(*) AS count FROM t GROUP BY a, b, 1'
105
+ end
106
+
107
+ it "should have #group_and_count work the standard way if initial string is a literal string already" do
108
+ @ds.group_and_count('a, b, ?'.lit, 1).sql.should == 'SELECT a, b, ?, 1, count(*) AS count FROM t GROUP BY a, b, ?, 1'
109
+ end
110
+
111
+ it "should have #group_and_count work regularly if not given a string as the first argument" do
112
+ @ds.group_and_count(:a, 1).sql.should == 'SELECT a, 1, count(*) AS count FROM t GROUP BY a, 1'
113
+ end
114
+
115
+ it "should have #order use literal string if given a single string" do
116
+ @ds.order('a, b, c').sql.should == 'SELECT * FROM t ORDER BY a, b, c'
117
+ end
118
+
119
+ it "should have #order use placeholder literal string if given a string and additional arguments" do
120
+ @ds.order('a, b, ?', 1).sql.should == 'SELECT * FROM t ORDER BY a, b, 1'
121
+ end
122
+
123
+ it "should have #order work the standard way if initial string is a literal string already" do
124
+ @ds.order('a, b, ?'.lit, 1).sql.should == 'SELECT * FROM t ORDER BY a, b, ?, 1'
125
+ end
126
+
127
+ it "should have #order work regularly if not given a string as the first argument" do
128
+ @ds.order(:a, 1).sql.should == 'SELECT * FROM t ORDER BY a, 1'
129
+ end
130
+
131
+ describe 'with existing order' do
132
+ before do
133
+ @ds = @ds.order(:d)
134
+ end
135
+
136
+ it "should have #order_more use literal string if given a single string" do
137
+ @ds.order_more('a, b, c').sql.should == 'SELECT * FROM t ORDER BY d, a, b, c'
138
+ end
139
+
140
+ it "should have #order_more use placeholder literal string if given a string and additional arguments" do
141
+ @ds.order_more('a, b, ?', 1).sql.should == 'SELECT * FROM t ORDER BY d, a, b, 1'
142
+ end
143
+
144
+ it "should have #order_more work the standard way if initial string is a literal string already" do
145
+ @ds.order_more('a, b, ?'.lit, 1).sql.should == 'SELECT * FROM t ORDER BY d, a, b, ?, 1'
146
+ end
147
+
148
+ it "should have #order_more work regularly if not given a string as the first argument" do
149
+ @ds.order_more(:a, 1).sql.should == 'SELECT * FROM t ORDER BY d, a, 1'
150
+ end
151
+
152
+ it "should have #order_prepend use literal string if given a single string" do
153
+ @ds.order_prepend('a, b, c').sql.should == 'SELECT * FROM t ORDER BY a, b, c, d'
154
+ end
155
+
156
+ it "should have #order_append use placeholder literal string if given a string and additional arguments" do
157
+ @ds.order_prepend('a, b, ?', 1).sql.should == 'SELECT * FROM t ORDER BY a, b, 1, d'
158
+ end
159
+
160
+ it "should have #order_append work the standard way if initial string is a literal string already" do
161
+ @ds.order_prepend('a, b, ?'.lit, 1).sql.should == 'SELECT * FROM t ORDER BY a, b, ?, 1, d'
162
+ end
163
+
164
+ it "should have #order_append work regularly if not given a string as the first argument" do
165
+ @ds.order_prepend(:a, 1).sql.should == 'SELECT * FROM t ORDER BY a, 1, d'
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "schema_caching extension" do
4
+ before do
5
+ @db = Sequel.mock
6
+ @schemas = {'"table"'=>[[:column, {:db_type=>"integer", :default=>"nextval('table_id_seq'::regclass)", :allow_null=>false, :primary_key=>true, :type=>:integer, :ruby_default=>nil}]]}
7
+ @filename = "spec/files/test_schema_#$$.dump"
8
+ @db.instance_variable_set(:@schemas, @schemas)
9
+ end
10
+ after do
11
+ File.delete(@filename) if File.exist?(@filename)
12
+ end
13
+
14
+ it "Database#dump_schema_cache should dump cached schema to the given file" do
15
+ File.exist?(@filename).should be_false
16
+ @db.dump_schema_cache(@filename)
17
+ File.exist?(@filename).should be_true
18
+ File.size(@filename).should > 0
19
+ end
20
+
21
+ it "Database#load_schema_cache should load cached schema from the given file dumped by #dump_schema_cache" do
22
+ @db.dump_schema_cache(@filename)
23
+ db = Sequel::Database.new
24
+ db.load_schema_cache(@filename)
25
+ @db.instance_variable_get(:@schemas).should == @schemas
26
+ end
27
+
28
+ it "Database#dump_schema_cache? should dump cached schema to the given file unless the file exists" do
29
+ File.open(@filename, 'wb'){|f|}
30
+ File.size(@filename).should == 0
31
+ @db.dump_schema_cache?(@filename)
32
+ File.size(@filename).should == 0
33
+ end
34
+
35
+ it "Database#load_schema_cache? should load cached schema from the given file if it exists" do
36
+ db = Sequel::Database.new
37
+ File.exist?(@filename).should be_false
38
+ db.load_schema_cache?(@filename)
39
+ db.instance_variable_get(:@schemas).should == {}
40
+ end
41
+ end
@@ -73,15 +73,8 @@ describe "Sequel::Database dump methods" do
73
73
  when :t2
74
74
  [[:c1, {:db_type=>'integer', :primary_key=>true, :allow_null=>false}],
75
75
  [:c2, {:db_type=>'numeric', :primary_key=>true, :allow_null=>false}]]
76
- when :t3
77
- [[:c1, {:db_type=>'date', :default=>"'now()'", :allow_null=>true}],
78
- [:c2, {:db_type=>'datetime', :allow_null=>false}]]
79
76
  when :t5
80
77
  [[:c1, {:db_type=>'blahblah', :allow_null=>true}]]
81
- when :t6
82
- [[:c1, {:db_type=>'bigint', :primary_key=>true, :allow_null=>true}]]
83
- when :t7
84
- [[:c1, {:db_type=>'somedbspecifictype', :primary_key=>true, :allow_null=>false}]]
85
78
  end
86
79
  end
87
80
  end
@@ -99,10 +92,73 @@ describe "Sequel::Database dump methods" do
99
92
  end
100
93
 
101
94
  it "should dump non-Integer primary key columns with explicit :type" do
95
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'bigint', :primary_key=>true, :allow_null=>true}]]}
102
96
  @d.dump_table_schema(:t6).should == "create_table(:t6) do\n primary_key :c1, :type=>Bignum\nend"
103
97
  end
104
98
 
99
+ it "should handle foreign keys" do
100
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'integer', :allow_null=>true}]]}
101
+ @d.meta_def(:foreign_key_list){|*s| [{:columns=>[:c1], :table=>:t2, :key=>[:c2]}]}
102
+ @d.dump_table_schema(:t6).should == "create_table(:t6) do\n foreign_key :c1, :t2, :key=>[:c2]\nend"
103
+ end
104
+
105
+ it "should handle primary keys that are also foreign keys" do
106
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'integer', :primary_key=>true, :allow_null=>true}]]}
107
+ @d.meta_def(:foreign_key_list){|*s| [{:columns=>[:c1], :table=>:t2, :key=>[:c2]}]}
108
+ s = @d.dump_table_schema(:t6)
109
+ s.should =~ /create_table\(:t6\) do\n primary_key :c1, /
110
+ s.should =~ /:table=>:t2/
111
+ s.should =~ /:key=>\[:c2\]/
112
+ end
113
+
114
+ it "should handle foreign key options" do
115
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'integer', :allow_null=>true}]]}
116
+ @d.meta_def(:foreign_key_list){|*s| [{:columns=>[:c1], :table=>:t2, :key=>[:c2], :on_delete=>:restrict, :on_update=>:set_null, :deferrable=>true}]}
117
+ s = @d.dump_table_schema(:t6)
118
+ s.should =~ /create_table\(:t6\) do\n foreign_key :c1, :t2, /
119
+ s.should =~ /:key=>\[:c2\]/
120
+ s.should =~ /:on_delete=>:restrict/
121
+ s.should =~ /:on_update=>:set_null/
122
+ s.should =~ /:deferrable=>true/
123
+ end
124
+
125
+ it "should handle foreign key options in the primary key" do
126
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'integer', :primary_key=>true, :allow_null=>true}]]}
127
+ @d.meta_def(:foreign_key_list){|*s| [{:columns=>[:c1], :table=>:t2, :key=>[:c2], :on_delete=>:restrict, :on_update=>:set_null, :deferrable=>true}]}
128
+ s = @d.dump_table_schema(:t6)
129
+ s.should =~ /create_table\(:t6\) do\n primary_key :c1, /
130
+ s.should =~ /:table=>:t2/
131
+ s.should =~ /:key=>\[:c2\]/
132
+ s.should =~ /:on_delete=>:restrict/
133
+ s.should =~ /:on_update=>:set_null/
134
+ s.should =~ /:deferrable=>true/
135
+ end
136
+
137
+ it "should omit foreign key options that are the same as defaults" do
138
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'integer', :allow_null=>true}]]}
139
+ @d.meta_def(:foreign_key_list){|*s| [{:columns=>[:c1], :table=>:t2, :key=>[:c2], :on_delete=>:no_action, :on_update=>:no_action, :deferrable=>false}]}
140
+ s = @d.dump_table_schema(:t6)
141
+ s.should =~ /create_table\(:t6\) do\n foreign_key :c1, :t2, /
142
+ s.should =~ /:key=>\[:c2\]/
143
+ s.should_not =~ /:on_delete/
144
+ s.should_not =~ /:on_update/
145
+ s.should_not =~ /:deferrable/
146
+ end
147
+
148
+ it "should omit foreign key options that are the same as defaults in the primary key" do
149
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'integer', :primary_key=>true, :allow_null=>true}]]}
150
+ @d.meta_def(:foreign_key_list){|*s| [{:columns=>[:c1], :table=>:t2, :key=>[:c2], :on_delete=>:no_action, :on_update=>:no_action, :deferrable=>false}]}
151
+ s = @d.dump_table_schema(:t6)
152
+ s.should =~ /create_table\(:t6\) do\n primary_key :c1, /
153
+ s.should =~ /:table=>:t2/
154
+ s.should =~ /:key=>\[:c2\]/
155
+ s.should_not =~ /:on_delete/
156
+ s.should_not =~ /:on_update/
157
+ s.should_not =~ /:deferrable/
158
+ end
159
+
105
160
  it "should dump primary key columns with explicit :type equal to the database type when :same_db option is passed" do
161
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'somedbspecifictype', :primary_key=>true, :allow_null=>false}]]}
106
162
  @d.dump_table_schema(:t7, :same_db => true).should == "create_table(:t7) do\n primary_key :c1, :type=>\"somedbspecifictype\"\nend"
107
163
  end
108
164
 
@@ -110,6 +166,12 @@ describe "Sequel::Database dump methods" do
110
166
  @d.dump_table_schema(:t2).should == "create_table(:t2) do\n Integer :c1, :null=>false\n BigDecimal :c2, :null=>false\n \n primary_key [:c1, :c2]\nend"
111
167
  end
112
168
 
169
+ it "should use a composite foreign_key calls if there is a composite foreign key" do
170
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'integer'}], [:c2, {:db_type=>'integer'}]]}
171
+ @d.meta_def(:foreign_key_list){|*s| [{:columns=>[:c1, :c2], :table=>:t2, :key=>[:c3, :c4]}]}
172
+ @d.dump_table_schema(:t1).should == "create_table(:t1) do\n Integer :c1\n Integer :c2\n \n foreign_key [:c1, :c2], :t2, :key=>[:c3, :c4]\nend"
173
+ end
174
+
113
175
  it "should include index information if available" do
114
176
  @d.meta_def(:indexes) do |t|
115
177
  {:i1=>{:columns=>[:c1], :unique=>false},
@@ -136,7 +198,7 @@ Sequel.migration do
136
198
  end
137
199
 
138
200
  down do
139
- drop_table(:t1, :t2)
201
+ drop_table(:t2, :t1)
140
202
  end
141
203
  end
142
204
  END_MIG
@@ -160,6 +222,92 @@ Sequel.migration do
160
222
  end
161
223
  end
162
224
 
225
+ down do
226
+ drop_table(:t2, :t1)
227
+ end
228
+ end
229
+ END_MIG
230
+ end
231
+
232
+ it "should sort table names topologically when dumping a migration with foreign keys" do
233
+ @d.meta_def(:tables){|o| [:t1, :t2]}
234
+ @d.meta_def(:schema) do |t|
235
+ t == :t1 ? [[:c2, {:db_type=>'integer'}]] : [[:c1, {:db_type=>'integer', :primary_key=>true}]]
236
+ end
237
+ @d.meta_def(:foreign_key_list) do |t|
238
+ t == :t1 ? [{:columns=>[:c2], :table=>:t2, :key=>[:c1]}] : []
239
+ end
240
+ @d.dump_schema_migration.should == <<-END_MIG
241
+ Sequel.migration do
242
+ up do
243
+ create_table(:t2) do
244
+ primary_key :c1
245
+ end
246
+
247
+ create_table(:t1) do
248
+ foreign_key :c2, :t2, :key=>[:c1]
249
+ end
250
+ end
251
+
252
+ down do
253
+ drop_table(:t1, :t2)
254
+ end
255
+ end
256
+ END_MIG
257
+ end
258
+
259
+ it "should handle circular dependencies when dumping a migration with foreign keys" do
260
+ @d.meta_def(:tables){|o| [:t1, :t2]}
261
+ @d.meta_def(:schema) do |t|
262
+ t == :t1 ? [[:c2, {:db_type=>'integer'}]] : [[:c1, {:db_type=>'integer'}]]
263
+ end
264
+ @d.meta_def(:foreign_key_list) do |t|
265
+ t == :t1 ? [{:columns=>[:c2], :table=>:t2, :key=>[:c1]}] : [{:columns=>[:c1], :table=>:t1, :key=>[:c2]}]
266
+ end
267
+ @d.dump_schema_migration.should == <<-END_MIG
268
+ Sequel.migration do
269
+ up do
270
+ create_table(:t1) do
271
+ Integer :c2
272
+ end
273
+
274
+ create_table(:t2) do
275
+ foreign_key :c1, :t1, :key=>[:c2]
276
+ end
277
+
278
+ alter_table(:t1) do
279
+ add_foreign_key [:c2], :t2, :key=>[:c1]
280
+ end
281
+ end
282
+
283
+ down do
284
+ drop_table(:t2, :t1)
285
+ end
286
+ end
287
+ END_MIG
288
+ end
289
+
290
+ it "should sort topologically even if the database raises an error when trying to parse foreign keys for a non-existent table" do
291
+ @d.meta_def(:tables){|o| [:t1, :t2]}
292
+ @d.meta_def(:schema) do |t|
293
+ t == :t1 ? [[:c2, {:db_type=>'integer'}]] : [[:c1, {:db_type=>'integer', :primary_key=>true}]]
294
+ end
295
+ @d.meta_def(:foreign_key_list) do |t|
296
+ raise Sequel::DatabaseError unless [:t1, :t2].include?(t)
297
+ t == :t1 ? [{:columns=>[:c2], :table=>:t2, :key=>[:c1]}] : []
298
+ end
299
+ @d.dump_schema_migration.should == <<-END_MIG
300
+ Sequel.migration do
301
+ up do
302
+ create_table(:t2) do
303
+ primary_key :c1
304
+ end
305
+
306
+ create_table(:t1) do
307
+ foreign_key :c2, :t2, :key=>[:c1]
308
+ end
309
+ end
310
+
163
311
  down do
164
312
  drop_table(:t1, :t2)
165
313
  end
@@ -186,7 +334,7 @@ Sequel.migration do
186
334
  end
187
335
 
188
336
  down do
189
- drop_table(:t1, :t2)
337
+ drop_table(:t2, :t1)
190
338
  end
191
339
  end
192
340
  END_MIG
@@ -215,12 +363,26 @@ Sequel.migration do
215
363
  end
216
364
 
217
365
  down do
218
- drop_table(:t1, :t2)
366
+ drop_table(:t2, :t1)
219
367
  end
220
368
  end
221
369
  END_MIG
222
370
  end
223
371
 
372
+ it "should have :indexes => false option disable foreign keys as well when dumping a whole migration" do
373
+ @d.meta_def(:foreign_key_list) do |t|
374
+ t == :t1 ? [{:columns=>[:c2], :table=>:t2, :key=>[:c1]}] : []
375
+ end
376
+ @d.dump_schema_migration(:indexes=>false).should_not =~ /foreign_key/
377
+ end
378
+
379
+ it "should have :foreign_keys option override :indexes => false disabling of foreign keys" do
380
+ @d.meta_def(:foreign_key_list) do |t|
381
+ t == :t1 ? [{:columns=>[:c2], :table=>:t2, :key=>[:c1]}] : []
382
+ end
383
+ @d.dump_schema_migration(:indexes=>false, :foreign_keys=>true).should =~ /foreign_key/
384
+ end
385
+
224
386
  it "should support dumping just indexes as a migration" do
225
387
  @d.meta_def(:tables){|o| [:t1]}
226
388
  @d.meta_def(:indexes) do |t|
@@ -242,7 +404,64 @@ end
242
404
  END_MIG
243
405
  end
244
406
 
407
+ it "should handle missing index parsing support when dumping index migration" do
408
+ @d.meta_def(:tables){|o| [:t1]}
409
+ @d.dump_indexes_migration.should == <<-END_MIG
410
+ Sequel.migration do
411
+ up do
412
+
413
+ end
414
+
415
+ down do
416
+
417
+ end
418
+ end
419
+ END_MIG
420
+ end
421
+
422
+ it "should handle missing foreign key parsing support when dumping foreign key migration" do
423
+ @d.meta_def(:tables){|o| [:t1]}
424
+ @d.dump_foreign_key_migration.should == <<-END_MIG
425
+ Sequel.migration do
426
+ up do
427
+
428
+ end
429
+ end
430
+ END_MIG
431
+ end
432
+
433
+ it "should support dumping just foreign_keys as a migration" do
434
+ @d.meta_def(:tables){|o| [:t1, :t2, :t3]}
435
+ @d.meta_def(:schema) do |t|
436
+ t == :t1 ? [[:c2, {:db_type=>'integer'}]] : [[:c1, {:db_type=>'integer'}]]
437
+ end
438
+ @d.meta_def(:foreign_key_list) do |t, *a|
439
+ case t
440
+ when :t1
441
+ [{:columns=>[:c2], :table=>:t2, :key=>[:c1]}]
442
+ when :t2
443
+ [{:columns=>[:c1, :c3], :table=>:t1, :key=>[:c2, :c4]}]
444
+ else
445
+ []
446
+ end
447
+ end
448
+ @d.dump_foreign_key_migration.should == <<-END_MIG
449
+ Sequel.migration do
450
+ up do
451
+ alter_table(:t1) do
452
+ add_foreign_key [:c2], :t2, :key=>[:c1]
453
+ end
454
+
455
+ alter_table(:t2) do
456
+ add_foreign_key [:c1, :c3], :t1, :key=>[:c2, :c4]
457
+ end
458
+ end
459
+ end
460
+ END_MIG
461
+ end
462
+
245
463
  it "should handle not null values and defaults" do
464
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'date', :default=>"'now()'", :allow_null=>true}], [:c2, {:db_type=>'datetime', :allow_null=>false}]]}
246
465
  @d.dump_table_schema(:t3).should == "create_table(:t3) do\n Date :c1\n DateTime :c2, :null=>false\nend"
247
466
  end
248
467
 
@@ -289,7 +508,7 @@ END_MIG
289
508
  ["double precision", "timestamp with time zone", "timestamp without time zone",
290
509
  "time with time zone", "time without time zone", "character varying(20)"] +
291
510
  %w"nvarchar ntext smalldatetime smallmoney binary varbinary nchar" +
292
- ["timestamp(6) without time zone", "timestamp(6) with time zone", "int(12) unsigned", 'bigint unsigned']
511
+ ["timestamp(6) without time zone", "timestamp(6) with time zone", "int(12) unsigned", 'bigint unsigned', 'tinyint(3) unsigned']
293
512
  @d.meta_def(:schema) do |t, *o|
294
513
  i = 0
295
514
  types.map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
@@ -361,6 +580,7 @@ create_table(:x) do
361
580
  DateTime :c63, :size=>6
362
581
  Integer :c64
363
582
  Bignum :c65
583
+ Integer :c66
364
584
  end
365
585
  END_MIG
366
586
  end