sequel 2.11.0 → 2.12.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 (162) hide show
  1. data/CHANGELOG +168 -0
  2. data/README.rdoc +77 -95
  3. data/Rakefile +100 -80
  4. data/bin/sequel +2 -1
  5. data/doc/advanced_associations.rdoc +23 -32
  6. data/doc/cheat_sheet.rdoc +23 -40
  7. data/doc/dataset_filtering.rdoc +6 -6
  8. data/doc/prepared_statements.rdoc +22 -22
  9. data/doc/release_notes/2.12.0.txt +534 -0
  10. data/doc/schema.rdoc +3 -1
  11. data/doc/sharding.rdoc +8 -8
  12. data/doc/virtual_rows.rdoc +65 -0
  13. data/lib/sequel.rb +1 -1
  14. data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
  15. data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
  16. data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
  17. data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
  18. data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
  19. data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
  20. data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
  21. data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
  22. data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
  23. data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
  24. data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
  25. data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
  26. data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
  27. data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
  28. data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
  29. data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
  30. data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
  31. data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
  32. data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
  33. data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
  34. data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
  35. data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
  36. data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
  37. data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
  38. data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
  39. data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
  40. data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
  41. data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
  42. data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
  43. data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
  44. data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
  45. data/lib/sequel/core.rb +221 -0
  46. data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
  47. data/lib/{sequel_core → sequel}/database.rb +264 -149
  48. data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
  49. data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
  50. data/lib/sequel/database/schema_sql.rb +224 -0
  51. data/lib/{sequel_core → sequel}/dataset.rb +78 -236
  52. data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
  53. data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
  54. data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
  55. data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
  56. data/lib/sequel/deprecated.rb +593 -0
  57. data/lib/sequel/deprecated_migration.rb +91 -0
  58. data/lib/sequel/exceptions.rb +48 -0
  59. data/lib/sequel/extensions/blank.rb +42 -0
  60. data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
  61. data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
  62. data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
  63. data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
  64. data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
  65. data/lib/sequel/extensions/string_date_time.rb +47 -0
  66. data/lib/sequel/metaprogramming.rb +43 -0
  67. data/lib/sequel/model.rb +110 -0
  68. data/lib/sequel/model/associations.rb +1300 -0
  69. data/lib/sequel/model/base.rb +937 -0
  70. data/lib/sequel/model/deprecated.rb +204 -0
  71. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  72. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  73. data/lib/sequel/model/deprecated_validations.rb +388 -0
  74. data/lib/sequel/model/errors.rb +39 -0
  75. data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
  76. data/lib/sequel/model/inflections.rb +208 -0
  77. data/lib/sequel/model/plugins.rb +76 -0
  78. data/lib/sequel/plugins/caching.rb +122 -0
  79. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  80. data/lib/sequel/plugins/schema.rb +53 -0
  81. data/lib/sequel/plugins/serialization.rb +117 -0
  82. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  83. data/lib/sequel/plugins/validation_class_methods.rb +384 -0
  84. data/lib/sequel/plugins/validation_helpers.rb +150 -0
  85. data/lib/{sequel_core → sequel}/sql.rb +125 -190
  86. data/lib/{sequel_core → sequel}/version.rb +2 -1
  87. data/lib/sequel_core.rb +1 -172
  88. data/lib/sequel_model.rb +1 -91
  89. data/spec/adapters/firebird_spec.rb +5 -5
  90. data/spec/adapters/informix_spec.rb +1 -1
  91. data/spec/adapters/mysql_spec.rb +128 -42
  92. data/spec/adapters/oracle_spec.rb +47 -19
  93. data/spec/adapters/postgres_spec.rb +64 -52
  94. data/spec/adapters/spec_helper.rb +1 -1
  95. data/spec/adapters/sqlite_spec.rb +12 -17
  96. data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
  97. data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
  98. data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
  99. data/spec/{sequel_core → core}/database_spec.rb +135 -99
  100. data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
  101. data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
  102. data/spec/core/migration_spec.rb +263 -0
  103. data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
  104. data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
  105. data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
  106. data/spec/{sequel_core → core}/schema_spec.rb +8 -10
  107. data/spec/{sequel_core → core}/spec_helper.rb +29 -2
  108. data/spec/{sequel_core → core}/version_spec.rb +0 -0
  109. data/spec/extensions/blank_spec.rb +67 -0
  110. data/spec/extensions/caching_spec.rb +201 -0
  111. data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
  112. data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
  113. data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
  114. data/spec/extensions/pagination_spec.rb +99 -0
  115. data/spec/extensions/pretty_table_spec.rb +91 -0
  116. data/spec/extensions/query_spec.rb +85 -0
  117. data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
  118. data/spec/extensions/serialization_spec.rb +109 -0
  119. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  120. data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
  121. data/spec/extensions/string_date_time_spec.rb +93 -0
  122. data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
  123. data/spec/extensions/validation_helpers_spec.rb +291 -0
  124. data/spec/integration/dataset_test.rb +31 -0
  125. data/spec/integration/eager_loader_test.rb +17 -30
  126. data/spec/integration/schema_test.rb +8 -5
  127. data/spec/integration/spec_helper.rb +17 -0
  128. data/spec/integration/transaction_test.rb +68 -0
  129. data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
  130. data/spec/{sequel_model → model}/associations_spec.rb +23 -10
  131. data/spec/{sequel_model → model}/base_spec.rb +29 -20
  132. data/spec/{sequel_model → model}/caching_spec.rb +16 -14
  133. data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
  134. data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
  135. data/spec/model/hooks_spec.rb +472 -0
  136. data/spec/model/inflector_spec.rb +126 -0
  137. data/spec/{sequel_model → model}/model_spec.rb +25 -20
  138. data/spec/model/plugins_spec.rb +142 -0
  139. data/spec/{sequel_model → model}/record_spec.rb +121 -62
  140. data/spec/model/schema_spec.rb +92 -0
  141. data/spec/model/spec_helper.rb +124 -0
  142. data/spec/model/validations_spec.rb +1080 -0
  143. metadata +136 -107
  144. data/lib/sequel_core/core_ext.rb +0 -217
  145. data/lib/sequel_core/dataset/callback.rb +0 -13
  146. data/lib/sequel_core/dataset/schema.rb +0 -15
  147. data/lib/sequel_core/deprecated.rb +0 -26
  148. data/lib/sequel_core/exceptions.rb +0 -44
  149. data/lib/sequel_core/schema.rb +0 -2
  150. data/lib/sequel_core/schema/sql.rb +0 -325
  151. data/lib/sequel_model/association_reflection.rb +0 -267
  152. data/lib/sequel_model/associations.rb +0 -499
  153. data/lib/sequel_model/base.rb +0 -539
  154. data/lib/sequel_model/caching.rb +0 -82
  155. data/lib/sequel_model/dataset_methods.rb +0 -26
  156. data/lib/sequel_model/eager_loading.rb +0 -370
  157. data/lib/sequel_model/hooks.rb +0 -101
  158. data/lib/sequel_model/plugins.rb +0 -62
  159. data/lib/sequel_model/record.rb +0 -568
  160. data/lib/sequel_model/schema.rb +0 -49
  161. data/lib/sequel_model/validations.rb +0 -429
  162. data/spec/sequel_model/plugins_spec.rb +0 -80
@@ -1,12 +1,34 @@
1
+ # Sequel extends the Array class to add methods to implement the SQL DSL.
2
+ # Most of these methods require that the array not be empty and that it
3
+ # must consist solely of other arrays that have exactly two elements.
1
4
  class Array
2
- # Return a Sequel::SQL::BooleanExpression created from this array, not matching any of the
5
+ # Return a Sequel::SQL::BooleanExpression created from this array, not matching all of the
3
6
  # conditions.
7
+ #
8
+ # ~[[:a, true]] # SQL: a IS NOT TRUE
9
+ # ~[[:a, 1], [:b, [2, 3]]] # SQL: a != 1 OR b NOT IN (2, 3)
4
10
  def ~
5
11
  sql_expr_if_all_two_pairs(:OR, true)
6
12
  end
7
13
 
14
+ # True if the array is not empty and all of its elements are
15
+ # arrays of size 2, false otherwise. This is used to determine if the array
16
+ # could be a specifier of conditions, used similarly to a hash
17
+ # but allowing for duplicate keys and a specific order.
18
+ #
19
+ # [].to_a.all_two_pairs? # => false
20
+ # [:a].to_a.all_two_pairs? # => false
21
+ # [[:b]].to_a.all_two_pairs? # => false
22
+ # [[:a, 1]].to_a.all_two_pairs? # => true
23
+ def all_two_pairs?
24
+ !empty? && all?{|i| (Array === i) && (i.length == 2)}
25
+ end
26
+
8
27
  # Return a Sequel::SQL::CaseExpression with this array as the conditions and the given
9
- # default value.
28
+ # default value and expression.
29
+ #
30
+ # [[{:a=>[2,3]}, 1]].case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
31
+ # [[:a, 1], [:b, 2]].case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
10
32
  def case(default, expression = nil)
11
33
  ::Sequel::SQL::CaseExpression.new(self, default, expression)
12
34
  end
@@ -14,24 +36,37 @@ class Array
14
36
  # Return a Sequel::SQL::Array created from this array. Used if this array contains
15
37
  # all two pairs and you want it treated as an SQL array instead of a ordered hash-like
16
38
  # conditions.
39
+ #
40
+ # [[1, 2], [3, 4]] # SQL: 1 = 2 AND 3 = 4
41
+ # [[1, 2], [3, 4]].sql_array # SQL: ((1, 2), (3, 4))
17
42
  def sql_array
18
43
  ::Sequel::SQL::SQLArray.new(self)
19
44
  end
20
45
 
21
46
  # Return a Sequel::SQL::BooleanExpression created from this array, matching all of the
22
- # conditions.
47
+ # conditions. Rarely do you need to call this explicitly, as Sequel generally
48
+ # assumes that arrays of all two pairs specify this type of condition.
49
+ #
50
+ # [[:a, true]].sql_expr # SQL: a IS TRUE
51
+ # [[:a, 1], [:b, [2, 3]]].sql_expr # SQL: a = 1 AND b IN (2, 3)
23
52
  def sql_expr
24
53
  sql_expr_if_all_two_pairs
25
54
  end
26
55
 
27
56
  # Return a Sequel::SQL::BooleanExpression created from this array, matching none
28
57
  # of the conditions.
58
+ #
59
+ # [[:a, true]].sql_negate # SQL: a IS NOT TRUE
60
+ # [[:a, 1], [:b, [2, 3]]].sql_negate # SQL: a != 1 AND b NOT IN (2, 3)
29
61
  def sql_negate
30
62
  sql_expr_if_all_two_pairs(:AND, true)
31
63
  end
32
64
 
33
65
  # Return a Sequel::SQL::BooleanExpression created from this array, matching any of the
34
66
  # conditions.
67
+ #
68
+ # [[:a, true]].sql_or # SQL: a IS TRUE
69
+ # [[:a, 1], [:b, [2, 3]]].sql_or # SQL: a = 1 OR b IN (2, 3)
35
70
  def sql_or
36
71
  sql_expr_if_all_two_pairs(:OR)
37
72
  end
@@ -39,28 +74,23 @@ class Array
39
74
  # Return a Sequel::SQL::BooleanExpression representing an SQL string made up of the
40
75
  # concatenation of this array's elements. If an argument is passed
41
76
  # it is used in between each element of the array in the SQL
42
- # concatenation.
77
+ # concatenation. This does not require that the array be made up of all two pairs.
78
+ #
79
+ # [:a].sql_string_join # SQL: a
80
+ # [:a, :b].sql_string_join # SQL: a || b
81
+ # [:a, 'b'].sql_string_join # SQL: a || 'b'
82
+ # ['a', :b].sql_string_join(' ') # SQL: 'a' || ' ' || b
43
83
  def sql_string_join(joiner=nil)
44
84
  if joiner
45
- args = self.inject([]) do |m, a|
46
- m << a
47
- m << joiner
48
- end
85
+ args = zip([joiner]*length).flatten
49
86
  args.pop
50
87
  else
51
88
  args = self
52
89
  end
53
- args = args.collect{|a| a.is_one_of?(Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass) ? a : a.to_s}
90
+ args = args.collect{|a| [Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass].any?{|c| a.is_a?(c)} ? a : a.to_s}
54
91
  ::Sequel::SQL::StringExpression.new(:'||', *args)
55
92
  end
56
93
 
57
- # Concatenates an array of strings into an SQL string. ANSI SQL and C-style
58
- # comments are removed, as well as excessive white-space.
59
- def to_sql
60
- map {|l| ((m = /^(.*)--/.match(l)) ? m[1] : l).chomp}.join(' '). \
61
- gsub(/\/\*.*\*\//, '').gsub(/\s+/, ' ').strip
62
- end
63
-
64
94
  private
65
95
 
66
96
  # Raise an error if this array is not made up of all two pairs, otherwise create a Sequel::SQL::BooleanExpression from this array.
@@ -70,10 +100,14 @@ class Array
70
100
  end
71
101
  end
72
102
 
103
+ # Sequel extends the Hash class to add methods to implement the SQL DSL.
73
104
  class Hash
74
105
  # Return a Sequel::SQL::BooleanExpression created from this hash, matching
75
106
  # all of the conditions in this hash and the condition specified by
76
107
  # the given argument.
108
+ #
109
+ # {:a=>1} & :b # SQL: a = 1 AND b
110
+ # {:a=>true} & ~:b # SQL: a IS TRUE AND NOT b
77
111
  def &(ce)
78
112
  ::Sequel::SQL::BooleanExpression.new(:AND, self, ce)
79
113
  end
@@ -81,12 +115,18 @@ class Hash
81
115
  # Return a Sequel::SQL::BooleanExpression created from this hash, matching
82
116
  # all of the conditions in this hash or the condition specified by
83
117
  # the given argument.
118
+ #
119
+ # {:a=>1} | :b # SQL: a = 1 OR b
120
+ # {:a=>true} | ~:b # SQL: a IS TRUE OR NOT b
84
121
  def |(ce)
85
122
  ::Sequel::SQL::BooleanExpression.new(:OR, self, ce)
86
123
  end
87
124
 
88
- # Return a Sequel::SQL::BooleanExpression created from this hash, not matching any of the
125
+ # Return a Sequel::SQL::BooleanExpression created from this hash, not matching all of the
89
126
  # conditions.
127
+ #
128
+ # ~{:a=>true} # SQL: a IS NOT TRUE
129
+ # ~{:a=>1, :b=>[2, 3]} # SQL: a != 1 OR b NOT IN (2, 3)
90
130
  def ~
91
131
  ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR, true)
92
132
  end
@@ -94,29 +134,44 @@ class Hash
94
134
  # Return a Sequel::SQL::CaseExpression with this hash as the conditions and the given
95
135
  # default value. Note that the order of the conditions will be arbitrary, so all
96
136
  # conditions should be orthogonal.
137
+ #
138
+ # {{:a=>[2,3]}=>1}.case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
139
+ # {:a=>1, {:b=>2}].case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
140
+ # # or: CASE c WHEN b THEN 2 WHEN a THEN 1 ELSE d END
97
141
  def case(default, expression = nil)
98
142
  ::Sequel::SQL::CaseExpression.new(to_a, default, expression)
99
143
  end
100
144
 
101
145
  # Return a Sequel::SQL::BooleanExpression created from this hash, matching all of the
102
- # conditions.
146
+ # conditions. Rarely do you need to call this explicitly, as Sequel generally
147
+ # assumes that hashes specify this type of condition.
148
+ #
149
+ # {:a=>true}.sql_expr # SQL: a IS TRUE
150
+ # {:a=>1, :b=>[2, 3]}.sql_expr # SQL: a = 1 AND b IN (2, 3)
103
151
  def sql_expr
104
152
  ::Sequel::SQL::BooleanExpression.from_value_pairs(self)
105
153
  end
106
154
 
107
155
  # Return a Sequel::SQL::BooleanExpression created from this hash, matching none
108
156
  # of the conditions.
157
+ #
158
+ # {:a=>true}.sql_negate # SQL: a IS NOT TRUE
159
+ # {:a=>1, :b=>[2, 3]}.sql_negate # SQL: a != 1 AND b NOT IN (2, 3)
109
160
  def sql_negate
110
161
  ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :AND, true)
111
162
  end
112
163
 
113
164
  # Return a Sequel::SQL::BooleanExpression created from this hash, matching any of the
114
165
  # conditions.
166
+ #
167
+ # {:a=>true}.sql_or # SQL: a IS TRUE
168
+ # {:a=>1, :b=>[2, 3]}.sql_or # SQL: a = 1 OR b IN (2, 3)
115
169
  def sql_or
116
170
  ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR)
117
171
  end
118
172
  end
119
173
 
174
+ # Sequel extends the String class to add methods to implement the SQL DSL.
120
175
  class String
121
176
  include Sequel::SQL::AliasMethods
122
177
  include Sequel::SQL::CastMethods
@@ -137,28 +192,15 @@ class String
137
192
  def lit(*args)
138
193
  args.empty? ? Sequel::LiteralString.new(self) : Sequel::SQL::PlaceholderLiteralString.new(self, args)
139
194
  end
140
- alias_method :expr, :lit
141
195
 
142
- # Splits a string into separate SQL statements, removing comments
143
- # and excessive white-space.
144
- def split_sql
145
- to_sql.split(';').map {|s| s.strip}
146
- end
147
-
148
- # Converts a string into an SQL string by removing comments.
149
- # See also Array#to_sql.
150
- def to_sql
151
- split("\n").to_sql
152
- end
153
-
154
- # Returns a Blob that holds the same data as this string. Blobs provide proper
196
+ # Returns a Sequel::SQL::Blob that holds the same data as this string. Blobs provide proper
155
197
  # escaping of binary data.
156
198
  def to_sequel_blob
157
- ::Sequel::SQL::Blob.new self
199
+ ::Sequel::SQL::Blob.new(self)
158
200
  end
159
- alias to_blob to_sequel_blob
160
201
  end
161
202
 
203
+ # Sequel extends the Symbol class to add methods to implement the SQL DSL.
162
204
  class Symbol
163
205
  include Sequel::SQL::QualifyingMethods
164
206
  include Sequel::SQL::IdentifierMethods
@@ -175,32 +217,32 @@ class Symbol
175
217
  # columns for this table.
176
218
  # If an argument is given, returns a Sequel::SQL::NumericExpression using the *
177
219
  # (multiplication) operator with this and the given argument.
220
+ #
221
+ # :table.* # SQL: table.*
222
+ # :column * 2 # SQL: column * 2
178
223
  def *(ce=(arg=false;nil))
179
224
  return super(ce) unless arg == false
180
225
  Sequel::SQL::ColumnAll.new(self);
181
226
  end
182
227
 
183
228
  # Returns a Sequel::SQL::Function with this as the function name,
184
- # and the given arguments. This is aliased as Symbol#[] if ruby 1.9
185
- # is not being used. ruby 1.9 includes Symbol#[], and Sequel
229
+ # and the given arguments. This is aliased as Symbol#[] if the RUBY_VERSION
230
+ # is less than 1.9.0. Ruby 1.9 defines Symbol#[], and Sequel
186
231
  # doesn't override methods defined by ruby itself.
232
+ #
233
+ # :now.sql_function # SQL: now()
234
+ # :sum.sql_function(:a) # SQL: sum(a)
235
+ # :concat.sql_function(:a, :b) # SQL: concat(a, b)
187
236
  def sql_function(*args)
188
237
  Sequel::SQL::Function.new(self, *args)
189
238
  end
190
239
  alias_method(:[], :sql_function) if RUBY_VERSION < '1.9.0'
191
240
 
192
- # If the given argument is an Integer or an array containing an Integer, returns
193
- # a Sequel::SQL::Subscript with this column and the given arg.
194
- # Otherwise returns a Sequel::SQL::BooleanExpression where this column (which should be boolean)
195
- # or the given argument is true.
196
- def |(sub)
197
- return super unless (Integer === sub) || ((Array === sub) && sub.any?{|x| Integer === x})
198
- Sequel::SQL::Subscript.new(self, Array(sub))
199
- end
200
-
201
- # Delegate the creation of the resulting SQL to the given dataset,
202
- # since it may be database dependent.
203
- def to_column_ref(ds)
204
- ds.symbol_to_column_ref(self)
241
+ # Return an SQL array subscript with the given arguments.
242
+ #
243
+ # :array.sql_subscript(1) # SQL: array[1]
244
+ # :array.sql_subscript(1, 2) # SQL: array[1, 2]
245
+ def sql_subscript(*sub)
246
+ Sequel::SQL::Subscript.new(self, sub.flatten)
205
247
  end
206
248
  end
@@ -1,6 +1,8 @@
1
- require 'sequel_core/database/schema'
2
-
3
1
  module Sequel
2
+ # Hash of adapters that have been used. The key is the adapter scheme
3
+ # symbol, and the value is the Database subclass.
4
+ ADAPTER_MAP = {}
5
+
4
6
  # Array of all databases to which Sequel has connected. If you are
5
7
  # developing an application that can connect to an arbitrary number of
6
8
  # databases, delete the database objects from this or they will not get
@@ -11,7 +13,8 @@ module Sequel
11
13
  # The Database class is meant to be subclassed by database adapters in order
12
14
  # to provide the functionality needed for executing queries.
13
15
  class Database
14
- include Schema::SQL
16
+ extend Metaprogramming
17
+ include Metaprogramming
15
18
 
16
19
  # Array of supported database adapters
17
20
  ADAPTERS = %w'ado db2 dbi do firebird informix jdbc mysql odbc openbase oracle postgres sqlite'.collect{|x| x.to_sym}
@@ -20,9 +23,6 @@ module Sequel
20
23
  SQL_COMMIT = 'COMMIT'.freeze
21
24
  SQL_ROLLBACK = 'ROLLBACK'.freeze
22
25
 
23
- # Hash of adapters that have been used
24
- @@adapters = Hash.new
25
-
26
26
  # The identifier input method to use by default
27
27
  @@identifier_input_method = nil
28
28
 
@@ -35,7 +35,7 @@ module Sequel
35
35
  # Whether to quote identifiers (columns and tables) by default
36
36
  @@quote_identifiers = nil
37
37
 
38
- # The default schema to use
38
+ # The default schema to use, generally should be nil.
39
39
  attr_accessor :default_schema
40
40
 
41
41
  # Array of SQL loggers to use for this database
@@ -63,7 +63,6 @@ module Sequel
63
63
  # * :loggers : An array of loggers to use.
64
64
  # * :quote_identifiers : Whether to quote identifiers
65
65
  # * :single_threaded : Whether to use a single-threaded connection pool
66
- # * :upcase_identifiers : Whether to upcase identifiers going into the database
67
66
  #
68
67
  # All options given are also passed to the ConnectionPool. If a block
69
68
  # is given, it is used as the connection_proc for the ConnectionPool.
@@ -79,6 +78,7 @@ module Sequel
79
78
  @identifier_output_method = nil
80
79
  @quote_identifiers = nil
81
80
  if opts.include?(:upcase_identifiers)
81
+ Deprecation.deprecate('The :upcase_identifiers Database option', 'Use the :identifier_input_method => :upcase option instead')
82
82
  @identifier_input_method = opts[:upcase_identifiers] ? :upcase : ""
83
83
  end
84
84
  @pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
@@ -92,25 +92,25 @@ module Sequel
92
92
  ### Class Methods ###
93
93
 
94
94
  # The Database subclass for the given adapter scheme.
95
- # Raises Sequel::Error::AdapterNotFound if the adapter
95
+ # Raises Sequel::AdapterNotFound if the adapter
96
96
  # could not be loaded.
97
97
  def self.adapter_class(scheme)
98
98
  scheme = scheme.to_s.gsub('-', '_').to_sym
99
99
 
100
- if (klass = @@adapters[scheme]).nil?
100
+ unless klass = ADAPTER_MAP[scheme]
101
101
  # attempt to load the adapter file
102
102
  begin
103
- require "sequel_core/adapters/#{scheme}"
103
+ Sequel.require "adapters/#{scheme}"
104
104
  rescue LoadError => e
105
- raise Error::AdapterNotFound, "Could not load #{scheme} adapter:\n #{e.message}"
105
+ raise AdapterNotFound, "Could not load #{scheme} adapter:\n #{e.message}"
106
106
  end
107
107
 
108
108
  # make sure we actually loaded the adapter
109
- if (klass = @@adapters[scheme]).nil?
110
- raise Error::AdapterNotFound, "Could not load #{scheme} adapter"
109
+ unless klass = ADAPTER_MAP[scheme]
110
+ raise AdapterNotFound, "Could not load #{scheme} adapter"
111
111
  end
112
112
  end
113
- return klass
113
+ klass
114
114
  end
115
115
 
116
116
  # Returns the scheme for the Database class.
@@ -121,11 +121,8 @@ module Sequel
121
121
  # Connects to a database. See Sequel.connect.
122
122
  def self.connect(conn_string, opts = {}, &block)
123
123
  if conn_string.is_a?(String)
124
- if conn_string =~ /\Ajdbc:/
125
- c = adapter_class(:jdbc)
126
- opts = {:uri=>conn_string}.merge(opts)
127
- elsif conn_string =~ /\Ado:/
128
- c = adapter_class(:do)
124
+ if match = /\A(jdbc|do):/o.match(conn_string)
125
+ c = adapter_class(match[1].to_sym)
129
126
  opts = {:uri=>conn_string}.merge(opts)
130
127
  else
131
128
  uri = URI.parse(conn_string)
@@ -133,7 +130,7 @@ module Sequel
133
130
  scheme = :dbi if scheme =~ /\Adbi-/
134
131
  c = adapter_class(scheme)
135
132
  uri_options = {}
136
- uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.blank?
133
+ uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.to_s.strip.empty?
137
134
  opts = c.send(:uri_to_options, uri).merge(uri_options).merge(opts)
138
135
  end
139
136
  else
@@ -165,6 +162,7 @@ module Sequel
165
162
  end
166
163
 
167
164
  # Set the method to call on identifiers going into the database
165
+ # See Sequel.identifier_input_method=.
168
166
  def self.identifier_input_method=(v)
169
167
  @@identifier_input_method = v || ""
170
168
  end
@@ -175,6 +173,7 @@ module Sequel
175
173
  end
176
174
 
177
175
  # Set the method to call on identifiers coming from the database
176
+ # See Sequel.identifier_output_method=.
178
177
  def self.identifier_output_method=(v)
179
178
  @@identifier_output_method = v || ""
180
179
  end
@@ -191,16 +190,10 @@ module Sequel
191
190
  @@single_threaded = value
192
191
  end
193
192
 
194
- # Sets the default quote_identifiers mode for new databases.
195
- # See Sequel.quote_identifiers=.
196
- def self.upcase_identifiers=(value)
197
- self.identifier_input_method = value ? :upcase : nil
198
- end
199
-
200
193
  ### Private Class Methods ###
201
194
 
202
195
  # Sets the adapter scheme for the Database class. Call this method in
203
- # descendnants of Database to allow connection using a URL. For example the
196
+ # descendants of Database to allow connection using a URL. For example the
204
197
  # following:
205
198
  #
206
199
  # class Sequel::MyDB::Database < Sequel::Database
@@ -213,7 +206,7 @@ module Sequel
213
206
  # Sequel.connect('mydb://user:password@dbserver/mydb')
214
207
  def self.set_adapter_scheme(scheme) # :nodoc:
215
208
  @scheme = scheme
216
- @@adapters[scheme.to_sym] = self
209
+ ADAPTER_MAP[scheme.to_sym] = self
217
210
  end
218
211
 
219
212
  # Converts a uri to an options hash. These options are then passed
@@ -230,10 +223,9 @@ module Sequel
230
223
 
231
224
  ### Instance Methods ###
232
225
 
233
- # Executes the supplied SQL statement. The SQL can be supplied as a string
234
- # or as an array of strings. If an array is given, comments and excessive
235
- # white space are removed. See also Array#to_sql.
226
+ # Executes the supplied SQL statement string.
236
227
  def <<(sql)
228
+ Deprecation.deprecate('Passing an array argument to Database#<<', 'Use array.each{|x| database << x}') if Array === sql
237
229
  execute_ddl((Array === sql) ? sql.to_sql : sql)
238
230
  end
239
231
 
@@ -241,14 +233,14 @@ module Sequel
241
233
  # the method acts as an alias for Database#fetch, returning a dataset for
242
234
  # arbitrary SQL:
243
235
  #
244
- # DB['SELECT * FROM items WHERE name = ?', my_name].print
236
+ # DB['SELECT * FROM items WHERE name = ?', my_name].all
245
237
  #
246
238
  # Otherwise, acts as an alias for Database#from, setting the primary
247
239
  # table for the dataset:
248
240
  #
249
241
  # DB[:items].sql #=> "SELECT * FROM items"
250
- def [](*args, &block)
251
- (String === args.first) ? fetch(*args, &block) : from(*args, &block)
242
+ def [](*args)
243
+ (String === args.first) ? fetch(*args) : from(*args)
252
244
  end
253
245
 
254
246
  # Call the prepared statement with the given name with the given hash
@@ -262,36 +254,40 @@ module Sequel
262
254
  raise NotImplementedError, "#connect should be overridden by adapters"
263
255
  end
264
256
 
265
- # Returns a blank dataset
257
+ # Returns a blank dataset for this database
266
258
  def dataset
267
259
  ds = Sequel::Dataset.new(self)
268
260
  end
269
261
 
270
- # Disconnects all available connections from the connection pool. If any
271
- # connections are currently in use, they will not be disconnected.
262
+ # Disconnects all available connections from the connection pool. Any
263
+ # connections currently in use will not be disconnected.
272
264
  def disconnect
273
265
  pool.disconnect
274
266
  end
275
267
 
276
- # Executes the given SQL. This method should be overridden in descendants.
268
+ # Executes the given SQL on the database. This method should be overridden in descendants.
269
+ # This method should not be called directly by user code.
277
270
  def execute(sql, opts={})
278
271
  raise NotImplementedError, "#execute should be overridden by adapters"
279
272
  end
280
273
 
281
274
  # Method that should be used when submitting any DDL (Data Definition
282
275
  # Language) SQL. By default, calls execute_dui.
276
+ # This method should not be called directly by user code.
283
277
  def execute_ddl(sql, opts={}, &block)
284
278
  execute_dui(sql, opts, &block)
285
279
  end
286
280
 
287
281
  # Method that should be used when issuing a DELETE, UPDATE, or INSERT
288
282
  # statement. By default, calls execute.
283
+ # This method should not be called directly by user code.
289
284
  def execute_dui(sql, opts={}, &block)
290
285
  execute(sql, opts, &block)
291
286
  end
292
287
 
293
288
  # Method that should be used when issuing a INSERT
294
289
  # statement. By default, calls execute_dui.
290
+ # This method should not be called directly by user code.
295
291
  def execute_insert(sql, opts={}, &block)
296
292
  execute_dui(sql, opts, &block)
297
293
  end
@@ -303,19 +299,17 @@ module Sequel
303
299
  #
304
300
  # The method returns a dataset instance:
305
301
  #
306
- # DB.fetch('SELECT * FROM items').print
302
+ # DB.fetch('SELECT * FROM items').all
307
303
  #
308
304
  # Fetch can also perform parameterized queries for protection against SQL
309
305
  # injection:
310
306
  #
311
- # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).print
307
+ # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).all
312
308
  def fetch(sql, *args, &block)
313
- ds = dataset
314
- ds.opts[:sql] = Sequel::SQL::PlaceholderLiteralString.new(sql, args)
309
+ ds = dataset.with_sql(sql, *args)
315
310
  ds.each(&block) if block
316
311
  ds
317
312
  end
318
- alias_method :>>, :fetch
319
313
 
320
314
  # Returns a new dataset with the from method invoked. If a block is given,
321
315
  # it is used as a filter on the dataset.
@@ -331,8 +325,8 @@ module Sequel
331
325
  #
332
326
  # # SELECT version()
333
327
  # DB.get(:version.sql_function) #=> ...
334
- def get(expr)
335
- dataset.get(expr)
328
+ def get(*args, &block)
329
+ dataset.get(*args, &block)
336
330
  end
337
331
 
338
332
  # The method to call on identifiers going into the database
@@ -380,6 +374,11 @@ module Sequel
380
374
  "#<#{self.class}: #{(uri rescue opts).inspect}>"
381
375
  end
382
376
 
377
+ # Proxy the literal call to the dataset, used for default values.
378
+ def literal(v)
379
+ schema_utility_dataset.literal(v)
380
+ end
381
+
383
382
  # Log a message at level info to all loggers. All SQL logging
384
383
  # goes through this method.
385
384
  def log_info(message, args=nil)
@@ -387,27 +386,11 @@ module Sequel
387
386
  @loggers.each{|logger| logger.info(message)}
388
387
  end
389
388
 
390
- # Return the first logger or nil if no loggers are being used.
391
- # Should only be used for backwards compatibility.
392
- def logger
393
- @loggers.first
394
- end
395
-
396
- # Replace the array of loggers with the given logger(s).
389
+ # Remove any existing loggers and just use the given logger.
397
390
  def logger=(logger)
398
391
  @loggers = Array(logger)
399
392
  end
400
393
 
401
- # Returns true unless the database is using a single-threaded connection pool.
402
- def multi_threaded?
403
- !@single_threaded
404
- end
405
-
406
- # Returns a dataset modified by the given query block. See Dataset#query.
407
- def query(&block)
408
- dataset.query(&block)
409
- end
410
-
411
394
  # Whether to quote identifiers (columns and tables) for this database
412
395
  def quote_identifiers=(v)
413
396
  reset_schema_utility_dataset
@@ -421,15 +404,64 @@ module Sequel
421
404
  end
422
405
 
423
406
  # Returns a new dataset with the select method invoked.
424
- def select(*args)
425
- dataset.select(*args)
407
+ def select(*args, &block)
408
+ dataset.select(*args, &block)
426
409
  end
427
410
 
428
- # Default serial primary key options.
429
- def serial_primary_key_options
430
- {:primary_key => true, :type => Integer, :auto_increment => true}
411
+ # Parse the schema from the database.
412
+ # If the table_name is not given, returns the schema for all tables as a hash.
413
+ # If the table_name is given, returns the schema for a single table as an
414
+ # array with all members being arrays of length 2. Available options are:
415
+ #
416
+ # * :reload - Get fresh information from the database, instead of using
417
+ # cached information. If table_name is blank, :reload should be used
418
+ # unless you are sure that schema has not been called before with a
419
+ # table_name, otherwise you may only getting the schemas for tables
420
+ # that have been requested explicitly.
421
+ # * :schema - An explicit schema to use. It may also be implicitly provided
422
+ # via the table name.
423
+ def schema(table = nil, opts={})
424
+ Deprecation.deprecate('Calling Database#schema without a table argument', 'Use database.tables.inject({}){|h, m| h[m] = database.schema(m); h}') unless table
425
+ raise(Error, 'schema parsing is not implemented on this database') unless respond_to?(:schema_parse_table, true)
426
+
427
+ if table
428
+ sch, table_name = schema_and_table(table)
429
+ quoted_name = quote_schema_table(table)
430
+ end
431
+ opts = opts.merge(:schema=>sch) if sch && !opts.include?(:schema)
432
+ if opts[:reload] && @schemas
433
+ if table_name
434
+ @schemas.delete(quoted_name)
435
+ else
436
+ @schemas = nil
437
+ end
438
+ end
439
+
440
+ if @schemas
441
+ if table_name
442
+ return @schemas[quoted_name] if @schemas[quoted_name]
443
+ else
444
+ return @schemas
445
+ end
446
+ end
447
+
448
+ raise(Error, '#tables does not exist, you must provide a specific table to #schema') if table.nil? && !respond_to?(:tables, true)
449
+
450
+ @schemas ||= Hash.new do |h,k|
451
+ quote_name = quote_schema_table(k)
452
+ h[quote_name] if h.include?(quote_name)
453
+ end
454
+
455
+ if table_name
456
+ cols = schema_parse_table(table_name, opts)
457
+ raise(Error, 'schema parsing returned no columns, table probably doesn\'t exist') if cols.nil? || cols.empty?
458
+ @schemas[quoted_name] = cols
459
+ else
460
+ tables.each{|t| @schemas[quote_schema_table(t)] = schema_parse_table(t.to_s, opts)}
461
+ @schemas
462
+ end
431
463
  end
432
-
464
+
433
465
  # Returns true if the database is using a single-threaded connection pool.
434
466
  def single_threaded?
435
467
  @single_threaded
@@ -467,8 +499,12 @@ module Sequel
467
499
  # supported - calling #transaction within a transaction will reuse the
468
500
  # current transaction. Should be overridden for databases that support nested
469
501
  # transactions.
470
- def transaction(server=nil)
471
- synchronize(server) do |conn|
502
+ def transaction(opts={})
503
+ unless opts.is_a?(Hash)
504
+ Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
505
+ opts = {:server=>opts}
506
+ end
507
+ synchronize(opts[:server]) do |conn|
472
508
  return yield(conn) if @transactions.include?(Thread.current)
473
509
  log_info(begin_transaction_sql)
474
510
  conn.execute(begin_transaction_sql)
@@ -489,90 +525,23 @@ module Sequel
489
525
  end
490
526
  end
491
527
 
492
- # Typecast the value to the given column_type. Can be overridden in
493
- # adapters to support database specific column types.
494
- # This method should raise Sequel::Error::InvalidValue if assigned value
528
+ # Typecast the value to the given column_type. Calls
529
+ # typecast_value_#{column_type} if the method exists,
530
+ # otherwise returns the value.
531
+ # This method should raise Sequel::InvalidValue if assigned value
495
532
  # is invalid.
496
533
  def typecast_value(column_type, value)
497
534
  return nil if value.nil?
535
+ meth = "typecast_value_#{column_type}"
498
536
  begin
499
- case column_type
500
- when :integer
501
- Integer(value)
502
- when :string
503
- value.to_s
504
- when :float
505
- Float(value)
506
- when :decimal
507
- case value
508
- when BigDecimal
509
- value
510
- when String, Float
511
- value.to_d
512
- when Integer
513
- value.to_s.to_d
514
- else
515
- raise Sequel::Error::InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
516
- end
517
- when :boolean
518
- case value
519
- when false, 0, "0", /\Af(alse)?\z/i
520
- false
521
- else
522
- value.blank? ? nil : true
523
- end
524
- when :date
525
- case value
526
- when Date
527
- value
528
- when DateTime, Time
529
- Date.new(value.year, value.month, value.day)
530
- when String
531
- value.to_date
532
- else
533
- raise Sequel::Error::InvalidValue, "invalid value for Date: #{value.inspect}"
534
- end
535
- when :time
536
- case value
537
- when Time
538
- value
539
- when String
540
- value.to_time
541
- else
542
- raise Sequel::Error::InvalidValue, "invalid value for Time: #{value.inspect}"
543
- end
544
- when :datetime
545
- raise(Sequel::Error::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
546
- if Sequel.datetime_class === value
547
- # Already the correct class, no need to convert
548
- value
549
- else
550
- # First convert it to standard ISO 8601 time, then
551
- # parse that string using the time class.
552
- (Time === value ? value.iso8601 : value.to_s).to_sequel_time
553
- end
554
- when :blob
555
- ::Sequel::SQL::Blob.new(value)
556
- else
557
- value
558
- end
559
- rescue ArgumentError => exp
560
- e = Sequel::Error::InvalidValue.new("#{exp.class} #{exp.message}")
537
+ respond_to?(meth, true) ? send(meth, value) : value
538
+ rescue ArgumentError, TypeError => exp
539
+ e = Sequel::InvalidValue.new("#{exp.class} #{exp.message}")
561
540
  e.set_backtrace(exp.backtrace)
562
541
  raise e
563
542
  end
564
543
  end
565
544
 
566
- # Set whether to upcase identifiers going into the database.
567
- def upcase_identifiers=(v)
568
- self.identifier_input_method = v ? :upcase : nil
569
- end
570
-
571
- # Returns true if the database upcases identifiers.
572
- def upcase_identifiers?
573
- identifier_input_method == :upcase
574
- end
575
-
576
545
  # Returns the URI identifying the database.
577
546
  # This method can raise an error if the database used options
578
547
  # instead of a connection string.
@@ -605,6 +574,23 @@ module Sequel
605
574
  SQL_BEGIN
606
575
  end
607
576
 
577
+ # Returns true when the object is considered blank.
578
+ # The only objects that are blank are nil, false,
579
+ # strings with all whitespace, and ones that respond
580
+ # true to empty?
581
+ def blank_object?(obj)
582
+ case obj
583
+ when NilClass, FalseClass
584
+ true
585
+ when Numeric, TrueClass
586
+ false
587
+ when String
588
+ obj.strip.empty?
589
+ else
590
+ obj.respond_to?(:empty?) ? obj.empty? : false
591
+ end
592
+ end
593
+
608
594
  # SQL to COMMIT a transaction.
609
595
  def commit_transaction_sql
610
596
  SQL_COMMIT
@@ -636,6 +622,8 @@ module Sequel
636
622
  :downcase
637
623
  end
638
624
 
625
+ # Whether to quote identifiers by default for this database, true
626
+ # by default.
639
627
  def quote_identifiers_default
640
628
  true
641
629
  end
@@ -648,7 +636,7 @@ module Sequel
648
636
  # Convert the given exception to a DatabaseError, keeping message
649
637
  # and traceback.
650
638
  def raise_error(exception, opts={})
651
- if !opts[:classes] || exception.is_one_of?(*opts[:classes])
639
+ if !opts[:classes] || Array(opts[:classes]).any?{|c| exception.is_a?(c)}
652
640
  e = DatabaseError.new("#{exception.class} #{exception.message}")
653
641
  e.set_backtrace(exception.backtrace)
654
642
  raise e
@@ -657,11 +645,57 @@ module Sequel
657
645
  end
658
646
  end
659
647
 
648
+ # Remove the cached schema for the given schema name
649
+ def remove_cached_schema(table)
650
+ @schemas.delete(quote_schema_table(table)) if @schemas
651
+ end
652
+
653
+ # Remove the cached schema_utility_dataset, because the identifier
654
+ # quoting has changed.
655
+ def reset_schema_utility_dataset
656
+ @schema_utility_dataset = nil
657
+ end
658
+
660
659
  # Split the schema information from the table
661
660
  def schema_and_table(table_name)
662
661
  schema_utility_dataset.schema_and_table(table_name)
663
662
  end
664
663
 
664
+ # Match the database's column type to a ruby type via a
665
+ # regular expression. The following ruby types are supported:
666
+ # integer, string, date, datetime, boolean, and float.
667
+ def schema_column_type(db_type)
668
+ case db_type
669
+ when /\Atinyint/io
670
+ Sequel.convert_tinyint_to_bool ? :boolean : :integer
671
+ when /\Ainterval\z/io
672
+ :interval
673
+ when /\A(character( varying)?|varchar|text)/io
674
+ :string
675
+ when /\A(int(eger)?|bigint|smallint)/io
676
+ :integer
677
+ when /\Adate\z/io
678
+ :date
679
+ when /\A(datetime|timestamp( with(out)? time zone)?)\z/io
680
+ :datetime
681
+ when /\Atime( with(out)? time zone)?\z/io
682
+ :time
683
+ when /\Aboolean\z/io
684
+ :boolean
685
+ when /\A(real|float|double( precision)?)\z/io
686
+ :float
687
+ when /\A(numeric(\(\d+,\d+\))?|decimal|money)\z/io
688
+ :decimal
689
+ when /bytea|blob/io
690
+ :blob
691
+ end
692
+ end
693
+
694
+ # The dataset to use for proxying certain schema methods.
695
+ def schema_utility_dataset
696
+ @schema_utility_dataset ||= dataset
697
+ end
698
+
665
699
  # Return the options for the given server by merging the generic
666
700
  # options for all server with the specific options for the given
667
701
  # server specified in the :servers option.
@@ -682,9 +716,90 @@ module Sequel
682
716
  opts
683
717
  end
684
718
 
685
- # Raise a database error unless the exception is an Error::Rollback.
719
+ # Raise a database error unless the exception is an Rollback.
686
720
  def transaction_error(e, *classes)
687
- raise_error(e, :classes=>classes) unless Error::Rollback === e
721
+ raise_error(e, :classes=>classes) unless Rollback === e
722
+ end
723
+
724
+ # Typecast the value to an SQL::Blob
725
+ def typecast_value_blob(value)
726
+ value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
727
+ end
728
+
729
+ # Typecast the value to true, false, or nil
730
+ def typecast_value_boolean(value)
731
+ case value
732
+ when false, 0, "0", /\Af(alse)?\z/i
733
+ false
734
+ else
735
+ blank_object?(value) ? nil : true
736
+ end
737
+ end
738
+
739
+ # Typecast the value to a Date
740
+ def typecast_value_date(value)
741
+ case value
742
+ when Date
743
+ value
744
+ when DateTime, Time
745
+ Date.new(value.year, value.month, value.day)
746
+ when String
747
+ Sequel.string_to_date(value)
748
+ else
749
+ raise InvalidValue, "invalid value for Date: #{value.inspect}"
750
+ end
751
+ end
752
+
753
+ # Typecast the value to a DateTime or Time depending on Sequel.datetime_class
754
+ def typecast_value_datetime(value)
755
+ raise(Sequel::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless [DateTime, Date, Time, String].any?{|c| value.is_a?(c)}
756
+ if Sequel.datetime_class === value
757
+ # Already the correct class, no need to convert
758
+ value
759
+ else
760
+ # First convert it to standard ISO 8601 time, then
761
+ # parse that string using the time class.
762
+ Sequel.string_to_datetime(Time === value ? value.iso8601 : value.to_s)
763
+ end
764
+ end
765
+
766
+ # Typecast the value to a BigDecimal
767
+ def typecast_value_decimal(value)
768
+ case value
769
+ when BigDecimal
770
+ value
771
+ when String, Numeric
772
+ BigDecimal.new(value.to_s)
773
+ else
774
+ raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
775
+ end
776
+ end
777
+
778
+ # Typecast the value to a Float
779
+ def typecast_value_float(value)
780
+ Float(value)
781
+ end
782
+
783
+ # Typecast the value to an Integer
784
+ def typecast_value_integer(value)
785
+ Integer(value)
786
+ end
787
+
788
+ # Typecast the value to a String
789
+ def typecast_value_string(value)
790
+ value.to_s
791
+ end
792
+
793
+ # Typecast the value to a Time
794
+ def typecast_value_time(value)
795
+ case value
796
+ when Time
797
+ value
798
+ when String
799
+ Sequel.string_to_time(value)
800
+ else
801
+ raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
802
+ end
688
803
  end
689
804
  end
690
805
  end