sequel 2.11.0 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
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