sequel 2.11.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|