sequel 2.11.0 → 2.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +168 -0
- data/README.rdoc +77 -95
- data/Rakefile +100 -80
- data/bin/sequel +2 -1
- data/doc/advanced_associations.rdoc +23 -32
- data/doc/cheat_sheet.rdoc +23 -40
- data/doc/dataset_filtering.rdoc +6 -6
- data/doc/prepared_statements.rdoc +22 -22
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/schema.rdoc +3 -1
- data/doc/sharding.rdoc +8 -8
- data/doc/virtual_rows.rdoc +65 -0
- data/lib/sequel.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
- data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
- data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
- data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
- data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
- data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
- data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
- data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
- data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
- data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
- data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
- data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
- data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
- data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
- data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
- data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
- data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
- data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
- data/lib/sequel/core.rb +221 -0
- data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
- data/lib/{sequel_core → sequel}/database.rb +264 -149
- data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
- data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
- data/lib/sequel/database/schema_sql.rb +224 -0
- data/lib/{sequel_core → sequel}/dataset.rb +78 -236
- data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
- data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
- data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
- data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
- data/lib/sequel/deprecated.rb +593 -0
- data/lib/sequel/deprecated_migration.rb +91 -0
- data/lib/sequel/exceptions.rb +48 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
- data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
- data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
- data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
- data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +43 -0
- data/lib/sequel/model.rb +110 -0
- data/lib/sequel/model/associations.rb +1300 -0
- data/lib/sequel/model/base.rb +937 -0
- data/lib/sequel/model/deprecated.rb +204 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +388 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
- data/lib/sequel/model/inflections.rb +208 -0
- data/lib/sequel/model/plugins.rb +76 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/serialization.rb +117 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +384 -0
- data/lib/sequel/plugins/validation_helpers.rb +150 -0
- data/lib/{sequel_core → sequel}/sql.rb +125 -190
- data/lib/{sequel_core → sequel}/version.rb +2 -1
- data/lib/sequel_core.rb +1 -172
- data/lib/sequel_model.rb +1 -91
- data/spec/adapters/firebird_spec.rb +5 -5
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mysql_spec.rb +128 -42
- data/spec/adapters/oracle_spec.rb +47 -19
- data/spec/adapters/postgres_spec.rb +64 -52
- data/spec/adapters/spec_helper.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +12 -17
- data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
- data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
- data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
- data/spec/{sequel_core → core}/database_spec.rb +135 -99
- data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
- data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
- data/spec/core/migration_spec.rb +263 -0
- data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
- data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
- data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
- data/spec/{sequel_core → core}/schema_spec.rb +8 -10
- data/spec/{sequel_core → core}/spec_helper.rb +29 -2
- data/spec/{sequel_core → core}/version_spec.rb +0 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
- data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
- data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
- data/spec/extensions/serialization_spec.rb +109 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
- data/spec/extensions/validation_helpers_spec.rb +291 -0
- data/spec/integration/dataset_test.rb +31 -0
- data/spec/integration/eager_loader_test.rb +17 -30
- data/spec/integration/schema_test.rb +8 -5
- data/spec/integration/spec_helper.rb +17 -0
- data/spec/integration/transaction_test.rb +68 -0
- data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
- data/spec/{sequel_model → model}/associations_spec.rb +23 -10
- data/spec/{sequel_model → model}/base_spec.rb +29 -20
- data/spec/{sequel_model → model}/caching_spec.rb +16 -14
- data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
- data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/{sequel_model → model}/model_spec.rb +25 -20
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/{sequel_model → model}/record_spec.rb +121 -62
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- metadata +136 -107
- data/lib/sequel_core/core_ext.rb +0 -217
- data/lib/sequel_core/dataset/callback.rb +0 -13
- data/lib/sequel_core/dataset/schema.rb +0 -15
- data/lib/sequel_core/deprecated.rb +0 -26
- data/lib/sequel_core/exceptions.rb +0 -44
- data/lib/sequel_core/schema.rb +0 -2
- data/lib/sequel_core/schema/sql.rb +0 -325
- data/lib/sequel_model/association_reflection.rb +0 -267
- data/lib/sequel_model/associations.rb +0 -499
- data/lib/sequel_model/base.rb +0 -539
- data/lib/sequel_model/caching.rb +0 -82
- data/lib/sequel_model/dataset_methods.rb +0 -26
- data/lib/sequel_model/eager_loading.rb +0 -370
- data/lib/sequel_model/hooks.rb +0 -101
- data/lib/sequel_model/plugins.rb +0 -62
- data/lib/sequel_model/record.rb +0 -568
- data/lib/sequel_model/schema.rb +0 -49
- data/lib/sequel_model/validations.rb +0 -429
- 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
|
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 =
|
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|
|
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
|
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
|
-
#
|
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
|
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
|
185
|
-
# is
|
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
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
def
|
197
|
-
|
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
|
-
|
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::
|
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
|
-
|
100
|
+
unless klass = ADAPTER_MAP[scheme]
|
101
101
|
# attempt to load the adapter file
|
102
102
|
begin
|
103
|
-
require "
|
103
|
+
Sequel.require "adapters/#{scheme}"
|
104
104
|
rescue LoadError => e
|
105
|
-
raise
|
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
|
-
|
110
|
-
raise
|
109
|
+
unless klass = ADAPTER_MAP[scheme]
|
110
|
+
raise AdapterNotFound, "Could not load #{scheme} adapter"
|
111
111
|
end
|
112
112
|
end
|
113
|
-
|
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
|
125
|
-
c = adapter_class(
|
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.
|
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
|
-
#
|
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
|
-
|
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
|
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].
|
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
|
251
|
-
(String === args.first) ? fetch(*args
|
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.
|
271
|
-
# connections
|
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').
|
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).
|
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(
|
335
|
-
dataset.get(
|
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
|
-
#
|
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
|
-
#
|
429
|
-
|
430
|
-
|
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(
|
471
|
-
|
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.
|
493
|
-
#
|
494
|
-
#
|
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
|
-
|
500
|
-
|
501
|
-
|
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] ||
|
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
|
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
|
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
|