sequel 4.20.0 → 4.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +18 -0
- data/README.rdoc +2 -0
- data/Rakefile +7 -6
- data/doc/dataset_filtering.rdoc +21 -3
- data/doc/querying.rdoc +21 -0
- data/doc/release_notes/4.21.0.txt +94 -0
- data/doc/sql.rdoc +26 -1
- data/doc/testing.rdoc +1 -0
- data/doc/validations.rdoc +1 -0
- data/doc/virtual_rows.rdoc +15 -3
- data/lib/sequel/adapters/shared/postgres.rb +28 -8
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/database/misc.rb +1 -1
- data/lib/sequel/database/transactions.rb +4 -22
- data/lib/sequel/dataset/mutation.rb +1 -1
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -3
- data/lib/sequel/extensions/dataset_source_alias.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +8 -0
- data/lib/sequel/extensions/pg_json.rb +2 -2
- data/lib/sequel/plugins/json_serializer.rb +4 -2
- data/lib/sequel/sql.rb +33 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +5 -0
- data/spec/core/database_spec.rb +10 -0
- data/spec/core/expression_filters_spec.rb +28 -0
- data/spec/extensions/json_serializer_spec.rb +13 -0
- data/spec/extensions/pg_json_ops_spec.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd1a779cad72f3c5831e46f1cb9ee5c5fa0cb75a
|
4
|
+
data.tar.gz: 561c34f1d23f0eb77d59e483b2beab3ebed21172
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3a11392dd6ab72a6af845fe59b85a1bbde7827d27aa255b434129b76034ad1be980a5e45a37a0ea82b0849c1ab4cd2013c108c22f13e898c3d9c6da345d32f6
|
7
|
+
data.tar.gz: 327de977bc6020cd9413bc50d0df32a8432784ee99e9fa277dd74a89f7b6f3e3ef7a89159534bafeeb5b42c99d38f3881d5aee67e5bcb832354f94e8ed7391ef
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
=== 4.21.0 (2015-04-01)
|
2
|
+
|
3
|
+
* Support :tsquery and :tsvector options in Dataset#full_text_search on PostgreSQL, for using existing tsquery/tsvector expressions (jeremyevans)
|
4
|
+
|
5
|
+
* Fix TinyTds::Error being raised when trying to cancel a query on a closed connection in the tinytds adapter (jeremyevans)
|
6
|
+
|
7
|
+
* Add GenericExpression#!~ for inverting =~ on ruby 1.9 (similar to inverting a hash) (jeremyevans) (#979)
|
8
|
+
|
9
|
+
* Add GenericExpression#=~ for equality, inclusion, and pattern matching (similar to using a hash) (jeremyevans) (#979)
|
10
|
+
|
11
|
+
* Add Database#add_named_conversion_proc on PostgreSQL to make it easier to add conversion procs for types by name (jeremyevans)
|
12
|
+
|
13
|
+
* Make Sequel.pg_jsonb return JSONBOp instances instead of JSONOp instances when passed other than Array or Hash (jeremyevans) (#977)
|
14
|
+
|
15
|
+
* Demodulize default root name in json_serializer plugin (janko-m) (#968)
|
16
|
+
|
17
|
+
* Make Database#transaction work in after_commit/after_rollback blocks (jeremyevans)
|
18
|
+
|
1
19
|
=== 4.20.0 (2015-03-03)
|
2
20
|
|
3
21
|
* Restore the use of AUTOINCREMENT on SQLite (jeremyevans) (#965)
|
data/README.rdoc
CHANGED
@@ -223,6 +223,8 @@ Sequel also accepts expressions:
|
|
223
223
|
|
224
224
|
my_posts = posts.where{stamp > Date.today << 1}
|
225
225
|
# WHERE stamp > '2010-06-14'
|
226
|
+
my_posts = posts.where{stamp =~ Date.today}
|
227
|
+
# WHERE stamp = '2010-07-14'
|
226
228
|
|
227
229
|
Some adapters will also let you specify Regexps:
|
228
230
|
|
data/Rakefile
CHANGED
@@ -84,17 +84,18 @@ end
|
|
84
84
|
|
85
85
|
begin
|
86
86
|
begin
|
87
|
-
|
88
|
-
require "spec/rake/spectask"
|
89
|
-
spec_class = Spec::Rake::SpecTask
|
90
|
-
spec_files_meth = :spec_files=
|
91
|
-
spec_opts_meth = :spec_opts=
|
92
|
-
rescue LoadError
|
87
|
+
raise LoadError if ENV['RSPEC1']
|
93
88
|
# RSpec 2
|
94
89
|
require "rspec/core/rake_task"
|
95
90
|
spec_class = RSpec::Core::RakeTask
|
96
91
|
spec_files_meth = :pattern=
|
97
92
|
spec_opts_meth = :rspec_opts=
|
93
|
+
rescue LoadError
|
94
|
+
# RSpec 1
|
95
|
+
require "spec/rake/spectask"
|
96
|
+
spec_class = Spec::Rake::SpecTask
|
97
|
+
spec_files_meth = :spec_files=
|
98
|
+
spec_opts_meth = :spec_opts=
|
98
99
|
end
|
99
100
|
|
100
101
|
spec = lambda do |name, files, d|
|
data/doc/dataset_filtering.rdoc
CHANGED
@@ -105,17 +105,22 @@ To filter by equality, you use the standard hash, which can be combined with oth
|
|
105
105
|
items.where{Sequel.&({:category => 'ruby'}, (price + 100 < 200))}.sql
|
106
106
|
#=> "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
|
107
107
|
|
108
|
+
You can also use the =~ operator:
|
109
|
+
|
110
|
+
items.where{(category =~ 'ruby') & (price + 100 < 200)}.sql
|
111
|
+
#=> "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
|
112
|
+
|
108
113
|
This works with other hash values, such as arrays and ranges:
|
109
114
|
|
110
|
-
items.where{Sequel.|({:category => ['ruby', 'other']}, (
|
115
|
+
items.where{Sequel.|({:category => ['ruby', 'other']}, (price - 100 > 200))}.sql
|
111
116
|
#=> "SELECT * FROM items WHERE ((category IN ('ruby', 'other')) OR ((price - 100) <= 200))"
|
112
117
|
|
113
|
-
items.where{
|
118
|
+
items.where{(price =~ (100..200)) & :active}.sql
|
114
119
|
#=> "SELECT * FROM items WHERE ((price >= 100 AND price <= 200) AND active)"
|
115
120
|
|
116
121
|
=== Negating conditions
|
117
122
|
|
118
|
-
You can use the exclude method to exclude conditions:
|
123
|
+
You can use the exclude method to exclude whole conditions:
|
119
124
|
|
120
125
|
items.exclude(:category => 'ruby').sql
|
121
126
|
#=> "SELECT * FROM items WHERE (category != 'ruby')"
|
@@ -126,6 +131,19 @@ You can use the exclude method to exclude conditions:
|
|
126
131
|
items.exclude{price / 100 >= 200}.sql
|
127
132
|
#=> "SELECT * FROM items WHERE ((price / 100) < 200)
|
128
133
|
|
134
|
+
To exclude only parts of conditions, you can use when in combination with Sequel.~ or the ~ method on Sequel expressions:
|
135
|
+
|
136
|
+
items.where{Sequel.&(Sequel.~(:category => 'ruby'), (price + 100 < 200))}.sql
|
137
|
+
#=> "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
|
138
|
+
|
139
|
+
items.where{~(category =~ 'ruby') & (price + 100 < 200)}.sql
|
140
|
+
#=> "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
|
141
|
+
|
142
|
+
On Ruby 1.9+, you can also use the !~ method:
|
143
|
+
|
144
|
+
items.where{(category !~ 'ruby') & (price + 100 < 200)}.sql
|
145
|
+
#=> "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
|
146
|
+
|
129
147
|
=== Comparing against column references
|
130
148
|
|
131
149
|
You can also compare against other columns:
|
data/doc/querying.rdoc
CHANGED
@@ -388,6 +388,12 @@ will be ANDed together:
|
|
388
388
|
Artist.where(:name=>'A'...'M'){id > 5}
|
389
389
|
# SELECT * FROM artists WHERE name >= 'A' AND name < 'M' AND id > 5
|
390
390
|
|
391
|
+
Using virtual row blocks, what you can do with single entry hash or an array with
|
392
|
+
a single two element array can also be done using the =~ method:
|
393
|
+
|
394
|
+
Artist.where{id =~ 5}
|
395
|
+
# SELECT * FROM artists WHERE id = 5
|
396
|
+
|
391
397
|
=== Symbols
|
392
398
|
|
393
399
|
If you have a boolean column in the database, and you want only true
|
@@ -494,7 +500,22 @@ Or to use the NOT LIKE operator:
|
|
494
500
|
|
495
501
|
Artist.exclude(Sequel.like(:name, '%J%'))
|
496
502
|
# SELECT * FROM artists WHERE name NOT LIKE '%J%' ESCAPE '\'
|
503
|
+
|
504
|
+
You can use Sequel.~ to negate expressions:
|
505
|
+
|
506
|
+
Artist.where(Sequel.~(:id=>5))
|
507
|
+
# SELECT * FROM artists WHERE id != 5
|
508
|
+
|
509
|
+
On Sequel expression objects, you can use ~ to negate them:
|
497
510
|
|
511
|
+
Artist.where(~Sequel.like(:name, '%J%'))
|
512
|
+
# SELECT * FROM artists WHERE name NOT LIKE '%J%' ESCAPE '\'
|
513
|
+
|
514
|
+
On Ruby 1.9+, you can use !~ on Sequel expressions to create negated expressions:
|
515
|
+
|
516
|
+
Artist.where{ id !~ 5}
|
517
|
+
# SELECT * FROM artists WHERE id != 5
|
518
|
+
|
498
519
|
=== Removing
|
499
520
|
|
500
521
|
To remove all existing filters, use +unfiltered+:
|
@@ -0,0 +1,94 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* SQL::GenericExpression#=~ has been added as an alternative method
|
4
|
+
of specifying equality/inclusion/identity. Previously, you had to
|
5
|
+
use a hash. This led to some slightly weird looking syntax when
|
6
|
+
used inside virtual rows:
|
7
|
+
|
8
|
+
DB[:items].where{{function(:column)=>0}}
|
9
|
+
# SELECT FROM items WHERE function(column) = 0
|
10
|
+
|
11
|
+
You can now use =~ as an equivalent:
|
12
|
+
|
13
|
+
DB[:items].where{function(:column) =~ 0}
|
14
|
+
# SELECT FROM items WHERE function(column) = 0
|
15
|
+
|
16
|
+
Like when using a hash, this works also for inclusion:
|
17
|
+
|
18
|
+
DB[:items].where{function(:column) =~ [1,2,3]}
|
19
|
+
# SELECT FROM items WHERE function(column) IN (1, 2, 3)
|
20
|
+
|
21
|
+
for identity:
|
22
|
+
|
23
|
+
DB[:items].where{function(:column) =~ nil}
|
24
|
+
# SELECT FROM items WHERE function(column) IS NULL
|
25
|
+
|
26
|
+
and for matching (on MySQL/PostgreSQL):
|
27
|
+
|
28
|
+
DB[:items].where{function(:column) =~ /foo/i}
|
29
|
+
# SELECT FROM items WHERE function(column) ~* 'foo'
|
30
|
+
|
31
|
+
This new syntax makes more complex conditions simpler to express:
|
32
|
+
|
33
|
+
DB[:items].where{(function(:column) =~ 0) | (column =~ 1)}
|
34
|
+
# SELECT FROM items WHERE function(column) = 0 OR column = 1
|
35
|
+
|
36
|
+
compared to previous versions of Sequel:
|
37
|
+
|
38
|
+
DB[:items].where{Sequel.|({function(:column) => 0}, {:column => 1})}
|
39
|
+
|
40
|
+
On ruby 1.9+, you can also use SQL::GenericExpression#!~ to invert
|
41
|
+
the condition:
|
42
|
+
|
43
|
+
DB[:items].where{function(:column) !~ 0}
|
44
|
+
# SELECT FROM items WHERE function(column) != 0
|
45
|
+
|
46
|
+
DB[:items].where{function(:column) !~ [1,2,3]}
|
47
|
+
# SELECT FROM items WHERE function(column) NOT IN (1, 2, 3)
|
48
|
+
|
49
|
+
DB[:items].where{function(:column) !~ nil}
|
50
|
+
# SELECT FROM items WHERE function(column) IS NOT NULL
|
51
|
+
|
52
|
+
DB[:items].where{function(:column) !~ /foo/i}
|
53
|
+
# SELECT FROM items WHERE function(column) !~* 'foo'
|
54
|
+
|
55
|
+
This makes it simpler to write inverted conditions. Ruby 1.8
|
56
|
+
doesn't support overriding the !~ method, but you can still use the
|
57
|
+
unary ~ method to invert:
|
58
|
+
|
59
|
+
DB[:items].where{~(function(:column) =~ 0)}
|
60
|
+
|
61
|
+
* Database#add_named_conversion_proc has been added on PostgreSQL to
|
62
|
+
make it easier to add conversion procs by name instead of by OID:
|
63
|
+
|
64
|
+
DB.add_named_conversion_proc(:citext){|s| s}
|
65
|
+
|
66
|
+
* Database#full_text_search on PostgreSQL now supports :tsquery and
|
67
|
+
:tsvector options for using existing tsquery and/or tsvector
|
68
|
+
arguments, instead of assuming the arguments are query terms or
|
69
|
+
the text to be search.
|
70
|
+
|
71
|
+
= Other Improvements
|
72
|
+
|
73
|
+
* Database#transaction now works inside after_commit and
|
74
|
+
after_rollback hooks. Previously, it didn't work correctly as it
|
75
|
+
thought it was already inside the previously committed/rolled back
|
76
|
+
transaction.
|
77
|
+
|
78
|
+
* Sequel.pg_jsonb now returns JSONBOp instances instead of JSONOp
|
79
|
+
instances when passed other than Array or Hash.
|
80
|
+
|
81
|
+
* The tinytds adapter no longer tries to cancel a query on a closed
|
82
|
+
connection, which was causing an exception to be raised.
|
83
|
+
|
84
|
+
= Backwards Compatibility
|
85
|
+
|
86
|
+
* The default root name used in the JSON serializer is now demodulized
|
87
|
+
before being underscored. This changes the behavior when the model
|
88
|
+
is namespaced. For example, if the model class name is Mod::Model,
|
89
|
+
the previous default root name would be "mod/model", the new default
|
90
|
+
root name is "model".
|
91
|
+
|
92
|
+
* If you were calling =~ or !~ on SQL::GenericExpression objects and
|
93
|
+
expecting the default ruby behavior of returning nil for =~ and
|
94
|
+
true for !~, you'll have to update your code.
|
data/doc/sql.rdoc
CHANGED
@@ -247,6 +247,10 @@ You can also specify this as an array of two element arrays:
|
|
247
247
|
|
248
248
|
[[:column, 1]] # ("column" = 1)
|
249
249
|
|
250
|
+
For expression objects, you can also use the =~ method:
|
251
|
+
|
252
|
+
where{column =~ 1} # ("column" = 1)
|
253
|
+
|
250
254
|
=== Not Equal Operator (!=)
|
251
255
|
|
252
256
|
You can specify a not equals condition by inverting the hash or array of two element arrays using <tt>Sequel.negate</tt> or <tt>Sequel.~</tt>:
|
@@ -256,11 +260,19 @@ You can specify a not equals condition by inverting the hash or array of two ele
|
|
256
260
|
Sequel.~(:column => 1) # ("column" != 1)
|
257
261
|
Sequel.~([[:column, 1]]) # ("column" != 1)
|
258
262
|
|
259
|
-
The difference between the two is that negate only works on hashes and arrays of element arrays, and it negates all entries in the, while ~ does a general inversion. This is best shown by an example with multiple entries:
|
263
|
+
The difference between the two is that negate only works on hashes and arrays of element arrays, and it negates all entries in the hash or array, while ~ does a general inversion. This is best shown by an example with multiple entries:
|
260
264
|
|
261
265
|
Sequel.negate(:column => 1, :foo => 2) # (("column" != 1) AND (foo != 2))
|
262
266
|
Sequel.~(:column => 1, :foo => 2) # (("column" != 1) OR (foo != 2))
|
263
267
|
|
268
|
+
You can also use the ~ method on an equality expression:
|
269
|
+
|
270
|
+
where{~(column =~ 1)} # ("column" != 1)
|
271
|
+
|
272
|
+
On Ruby 1.9+, you can use the !~ method:
|
273
|
+
|
274
|
+
where{column !~ 1} # ("column" != 1)
|
275
|
+
|
264
276
|
The most common need for not equals is in filters, in which case you can use the +exclude+ method:
|
265
277
|
|
266
278
|
DB[:albums].exclude(:column=>1) # SELECT * FROM "albums" WHERE ("column" != 1)
|
@@ -279,6 +291,14 @@ As you may have guessed, Sequel switches from an = to an IN when the hash value
|
|
279
291
|
{:column=>DB[:albums].select(:id)} # ("column" IN (SELECT "id" FROM "albums"))
|
280
292
|
Sequel.~(:column=>DB[:albums].select(:id)) # ("column" NOT IN (SELECT "id" FROM "albums"))
|
281
293
|
|
294
|
+
Similar to =, you can also use =~ with expressions for inclusion:
|
295
|
+
|
296
|
+
where{column =~ [1, 2, 3]} # ("column" IN (1, 2, 3))
|
297
|
+
|
298
|
+
and on Ruby 1.9, !~ for exclusion:
|
299
|
+
|
300
|
+
where{column !~ [1, 2, 3]} # ("column" NOT IN (1, 2, 3))
|
301
|
+
|
282
302
|
Sequel also supports the SQL EXISTS operator using <tt>Dataset#exists</tt>:
|
283
303
|
|
284
304
|
DB[:albums].exists # EXISTS (SELECT * FROM albums)
|
@@ -297,6 +317,11 @@ Negation works the same way as it does for equality and inclusion:
|
|
297
317
|
Sequel.~(:column=>true) # ("column" IS NOT TRUE)
|
298
318
|
Sequel.~(:column=>false) # ("column" IS NOT FALSE)
|
299
319
|
|
320
|
+
Likewise, =~ works for identity (and Ruby 1.9, !~ for negative identity):
|
321
|
+
|
322
|
+
where{column =~ nil} # ("column" IS NULL)
|
323
|
+
where{column !~ nil} # ("column" IS NOT NULL)
|
324
|
+
|
300
325
|
=== Inversion Operator (NOT)
|
301
326
|
|
302
327
|
Sequel's general inversion operator is ~, which works on symbols and most Sequel-specific expression objects:
|
data/doc/testing.rdoc
CHANGED
@@ -166,6 +166,7 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
|
|
166
166
|
|
167
167
|
=== Other
|
168
168
|
|
169
|
+
RSPEC1 :: Use RSpec 1 to run the tests even if a newer version is installed
|
169
170
|
SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
|
170
171
|
SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
|
171
172
|
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
data/doc/validations.rdoc
CHANGED
data/doc/virtual_rows.rdoc
CHANGED
@@ -185,9 +185,21 @@ object returned, with the options for the window:
|
|
185
185
|
|
186
186
|
== Operators
|
187
187
|
|
188
|
-
VirtualRows use method_missing to handle almost all method calls.
|
189
|
-
|
190
|
-
|
188
|
+
VirtualRows use method_missing to handle almost all method calls. Since the
|
189
|
+
objects given by method_missing are SQL::Identifiers, SQL::QualifiedIdentifiers
|
190
|
+
or SQL::Functions, you can use all operators that they provide (see
|
191
|
+
DatasetFiltering[http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html#label-Filtering+using+expressions]):
|
192
|
+
|
193
|
+
ds.select{|o| o.price - 100}
|
194
|
+
ds.select{o.price - 100}
|
195
|
+
# SELECT (price - 100)
|
196
|
+
|
197
|
+
ds.where{|o| (o.price < 200) & (o.tax * 100 >= 23)}
|
198
|
+
ds.where{(price < 200) & (tax * 100 >= 0.23)}
|
199
|
+
# WHERE ((price < 200) AND ((tax * 100) >= 0.23))
|
200
|
+
|
201
|
+
However, VirtualRows have special handling of some operator methods to make
|
202
|
+
certain things easier. The operators all use a prefix form.
|
191
203
|
|
192
204
|
=== Math Operators
|
193
205
|
|
@@ -154,6 +154,15 @@ module Sequel
|
|
154
154
|
# having callable values for the conversion proc for that type.
|
155
155
|
attr_reader :conversion_procs
|
156
156
|
|
157
|
+
# Add a conversion proc for a named type. This should be used
|
158
|
+
# for types without fixed OIDs, which includes all types that
|
159
|
+
# are not included in a default PostgreSQL installation. If
|
160
|
+
# a block is given, it is used as the conversion proc, otherwise
|
161
|
+
# the conversion proc is looked up in the PG_NAMED_TYPES hash.
|
162
|
+
def add_named_conversion_proc(name, &block)
|
163
|
+
add_named_conversion_procs(conversion_procs, name=>(block || PG_NAMED_TYPES[name]))
|
164
|
+
end
|
165
|
+
|
157
166
|
# Commit an existing prepared transaction with the given transaction
|
158
167
|
# identifier string.
|
159
168
|
def commit_prepared_transaction(transaction_id, opts=OPTS)
|
@@ -1284,22 +1293,33 @@ module Sequel
|
|
1284
1293
|
# :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
|
1285
1294
|
# returned rows also include the exact phrase used.
|
1286
1295
|
# :rank :: Set to true to order by the rank, so that closer matches are returned first.
|
1296
|
+
# :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
|
1297
|
+
# tsquery, and can be used directly in the query.
|
1298
|
+
# :tsvector :: Specifies the cols argument is already a valid SQL expression returning a
|
1299
|
+
# tsvector, and can be used directly in the query.
|
1287
1300
|
def full_text_search(cols, terms, opts = OPTS)
|
1288
1301
|
lang = Sequel.cast(opts[:language] || 'simple', :regconfig)
|
1289
|
-
terms = terms.join(' | ') if terms.is_a?(Array)
|
1290
|
-
columns = full_text_string_join(cols)
|
1291
|
-
query_func = (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
|
1292
|
-
vector = Sequel.function(:to_tsvector, lang, columns)
|
1293
|
-
query = Sequel.function(query_func, lang, terms)
|
1294
1302
|
|
1295
|
-
|
1303
|
+
unless opts[:tsvector]
|
1304
|
+
phrase_cols = full_text_string_join(cols)
|
1305
|
+
cols = Sequel.function(:to_tsvector, lang, phrase_cols)
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
unless opts[:tsquery]
|
1309
|
+
phrase_terms = terms.is_a?(Array) ? terms.join(' | ') : terms
|
1310
|
+
query_func = (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
|
1311
|
+
terms = Sequel.function(query_func, lang, phrase_terms)
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
ds = where(Sequel.lit(["(", " @@ ", ")"], cols, terms))
|
1296
1315
|
|
1297
1316
|
if opts[:phrase]
|
1298
|
-
|
1317
|
+
raise Error, "can't use :phrase with either :tsvector or :tsquery arguments to full_text_search together" if opts[:tsvector] || opts[:tsquery]
|
1318
|
+
ds = ds.grep(phrase_cols, "%#{escape_like(phrase_terms)}%", :case_insensitive=>true)
|
1299
1319
|
end
|
1300
1320
|
|
1301
1321
|
if opts[:rank]
|
1302
|
-
ds = ds.order{ts_rank_cd(
|
1322
|
+
ds = ds.order{ts_rank_cd(cols, terms)}
|
1303
1323
|
end
|
1304
1324
|
|
1305
1325
|
ds
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -199,7 +199,7 @@ module Sequel
|
|
199
199
|
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
200
200
|
pr.call(self)
|
201
201
|
else
|
202
|
-
raise(Error, "Extension #{ext} does not have specific support handling individual databases")
|
202
|
+
raise(Error, "Extension #{ext} does not have specific support handling individual databases (try: Sequel.extension #{ext.inspect})")
|
203
203
|
end
|
204
204
|
end
|
205
205
|
self
|
@@ -187,20 +187,6 @@ module Sequel
|
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
-
# Call all stored after_commit blocks for the given transaction
|
191
|
-
def after_transaction_commit(conn)
|
192
|
-
if ary = _trans(conn)[:after_commit]
|
193
|
-
ary.each{|b| b.call}
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
# Call all stored after_rollback blocks for the given transaction
|
198
|
-
def after_transaction_rollback(conn)
|
199
|
-
if ary = _trans(conn)[:after_rollback]
|
200
|
-
ary.each{|b| b.call}
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
190
|
# Whether the current thread/connection is already inside a transaction
|
205
191
|
def already_in_transaction?(conn, opts)
|
206
192
|
_trans(conn) && (!supports_savepoints? || !opts[:savepoint])
|
@@ -288,14 +274,10 @@ module Sequel
|
|
288
274
|
# Remove the current thread from the list of active transactions
|
289
275
|
def remove_transaction(conn, committed)
|
290
276
|
if transaction_finished?(conn)
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
after_transaction_rollback(conn)
|
296
|
-
end
|
297
|
-
ensure
|
298
|
-
Sequel.synchronize{@transactions.delete(conn)}
|
277
|
+
callbacks = _trans(conn)[committed ? :after_commit : :after_rollback]
|
278
|
+
Sequel.synchronize{@transactions.delete(conn)}
|
279
|
+
if callbacks
|
280
|
+
callbacks.each{|b| b.call}
|
299
281
|
end
|
300
282
|
end
|
301
283
|
end
|
@@ -42,7 +42,7 @@ module Sequel
|
|
42
42
|
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
43
43
|
pr.call(self)
|
44
44
|
else
|
45
|
-
raise(Error, "Extension #{ext} does not have specific support handling individual datasets")
|
45
|
+
raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
|
46
46
|
end
|
47
47
|
end
|
48
48
|
self
|
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
# PlaceholderLiteralizer allows you to record the application of arbitrary changes
|
4
4
|
# to a dataset with placeholder arguments, recording where those placeholder arguments
|
5
5
|
# are used in the query. When running the query, the literalization process is much
|
6
|
-
# faster as Sequel can skip most of the work it
|
6
|
+
# faster as Sequel can skip most of the work it normally has to do when literalizing a
|
7
7
|
# dataset.
|
8
8
|
#
|
9
9
|
# Basically, this enables optimizations that allow Sequel to cache the SQL produced
|
@@ -27,7 +27,7 @@ module Sequel
|
|
27
27
|
# loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
|
28
28
|
# ds.join(pl.arg, :item_id=>:id)
|
29
29
|
# end
|
30
|
-
# loader(:cart_items)
|
30
|
+
# loader.all(:cart_items)
|
31
31
|
#
|
32
32
|
# Will not qualify the item_id column with cart_items. In this type of situation it's
|
33
33
|
# best to add a table alias when joining:
|
@@ -35,7 +35,7 @@ module Sequel
|
|
35
35
|
# loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
|
36
36
|
# ds.join(Sequel.as(pl.arg, :t), :item_id=>:id)
|
37
37
|
# end
|
38
|
-
# loader(:cart_items)
|
38
|
+
# loader.all(:cart_items)
|
39
39
|
#
|
40
40
|
# There are other similar cases that are not handled, mainly when Sequel changes the
|
41
41
|
# SQL produced depending on the types of the arguments.
|
@@ -41,7 +41,7 @@ module Sequel
|
|
41
41
|
class Dataset
|
42
42
|
module DatasetSourceAlias
|
43
43
|
# Preprocess the list of sources and attempt to alias any
|
44
|
-
# datasets in the sources to the first source of the
|
44
|
+
# datasets in the sources to the first source of the respective
|
45
45
|
# dataset.
|
46
46
|
def from(*source, &block)
|
47
47
|
virtual_row_columns(source, block)
|
@@ -141,7 +141,7 @@ module Sequel
|
|
141
141
|
module DatabaseMethods
|
142
142
|
def self.extended(db)
|
143
143
|
db.instance_eval do
|
144
|
-
|
144
|
+
add_named_conversion_proc(:hstore)
|
145
145
|
@schema_type_classes[:hstore] = HStore
|
146
146
|
end
|
147
147
|
end
|
@@ -52,6 +52,14 @@
|
|
52
52
|
# h.to_matrix # hstore_to_matrix(hstore_column)
|
53
53
|
# h.values # avals(hstore_column)
|
54
54
|
#
|
55
|
+
# Here are a couple examples for updating an existing hstore column:
|
56
|
+
#
|
57
|
+
# # Add a key, or update an existing key with a new value
|
58
|
+
# DB[:tab].update(:h=>Sequel.hstore_op(:h).concat('c'=>3))
|
59
|
+
#
|
60
|
+
# # Delete a key
|
61
|
+
# DB[:tab].update(:h=>Sequel.hstore_op(:h).delete('k1'))
|
62
|
+
#
|
55
63
|
# See the PostgreSQL hstore function and operator documentation for more
|
56
64
|
# details on what these functions and operators do.
|
57
65
|
#
|
@@ -282,7 +282,7 @@ module Sequel
|
|
282
282
|
end
|
283
283
|
end
|
284
284
|
|
285
|
-
# Wrap the array or hash in a Postgres::
|
285
|
+
# Wrap the array or hash in a Postgres::JSONBArray or Postgres::JSONBHash.
|
286
286
|
def pg_jsonb(v)
|
287
287
|
case v
|
288
288
|
when Postgres::JSONBArray, Postgres::JSONBHash
|
@@ -296,7 +296,7 @@ module Sequel
|
|
296
296
|
when Postgres::JSONHash
|
297
297
|
Postgres::JSONBHash.new(v.to_hash)
|
298
298
|
else
|
299
|
-
Sequel.
|
299
|
+
Sequel.pg_jsonb_op(v)
|
300
300
|
end
|
301
301
|
end
|
302
302
|
end
|
@@ -290,7 +290,9 @@ module Sequel
|
|
290
290
|
end
|
291
291
|
|
292
292
|
if root = opts[:root]
|
293
|
-
|
293
|
+
unless root.is_a?(String)
|
294
|
+
root = model.send(:underscore, model.send(:demodulize, model.to_s))
|
295
|
+
end
|
294
296
|
h = {root => h}
|
295
297
|
end
|
296
298
|
|
@@ -330,7 +332,7 @@ module Sequel
|
|
330
332
|
opts.delete(:root)
|
331
333
|
end
|
332
334
|
unless collection_root.is_a?(String)
|
333
|
-
collection_root = model.send(:pluralize, model.send(:underscore, model.to_s))
|
335
|
+
collection_root = model.send(:pluralize, model.send(:underscore, model.send(:demodulize, model.to_s)))
|
334
336
|
end
|
335
337
|
end
|
336
338
|
|
data/lib/sequel/sql.rb
CHANGED
@@ -791,6 +791,38 @@ module Sequel
|
|
791
791
|
end
|
792
792
|
end
|
793
793
|
|
794
|
+
# This module includes methods for overriding the =~ method for SQL equality,
|
795
|
+
# inclusion, and pattern matching. It returns the same result that Sequel would
|
796
|
+
# return when using a hash with a single entry, where the receiver was the key
|
797
|
+
# and the argument was the value. Example:
|
798
|
+
#
|
799
|
+
# Sequel.expr(:a) =~ 1 # (a = 1)
|
800
|
+
# Sequel.expr(:a) =~ [1, 2] # (a IN [1, 2])
|
801
|
+
# Sequel.expr(:a) =~ nil # (a IS NULL)
|
802
|
+
#
|
803
|
+
# On Ruby 1.9+, this also adds the !~ method, for easily setting up not equals,
|
804
|
+
# exclusion, and inverse pattern matching. This is the same as as inverting the
|
805
|
+
# result of the =~ method
|
806
|
+
#
|
807
|
+
# Sequel.expr(:a) !~ 1 # (a != 1)
|
808
|
+
# Sequel.expr(:a) !~ [1, 2] # (a NOT IN [1, 2])
|
809
|
+
# Sequel.expr(:a) !~ nil # (a IS NOT NULL)
|
810
|
+
module PatternMatchMethods
|
811
|
+
# Set up an equality, inclusion, or pattern match operation, based on the type
|
812
|
+
# of the argument.
|
813
|
+
def =~(other)
|
814
|
+
BooleanExpression.send(:from_value_pair, self, other)
|
815
|
+
end
|
816
|
+
|
817
|
+
if RUBY_VERSION >= '1.9'
|
818
|
+
module_eval(<<-END, __FILE__, __LINE__+1)
|
819
|
+
def !~(other)
|
820
|
+
~(self =~ other)
|
821
|
+
end
|
822
|
+
END
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
794
826
|
# These methods are designed as replacements for the core extension operator
|
795
827
|
# methods, so that Sequel is still easy to use if the core extensions are not
|
796
828
|
# enabled.
|
@@ -1358,6 +1390,7 @@ module Sequel
|
|
1358
1390
|
include InequalityMethods
|
1359
1391
|
include NumericMethods
|
1360
1392
|
include OrderMethods
|
1393
|
+
include PatternMatchMethods
|
1361
1394
|
include StringMethods
|
1362
1395
|
include SubscriptMethods
|
1363
1396
|
end
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 4
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
5
5
|
# release, generally around once a month.
|
6
|
-
MINOR =
|
6
|
+
MINOR = 21
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
8
8
|
# releases that fix regressions from previous versions.
|
9
9
|
TINY = 0
|
@@ -924,6 +924,11 @@ describe "A PostgreSQL database" do
|
|
924
924
|
@db[:posts].full_text_search(:title, 'rubinius ruby', :plain=>true).select_order_map(:title).should == ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
|
925
925
|
@db[:posts].full_text_search(:title, 'jruby maglev', :plain=>true).select_order_map(:title).should == ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
|
926
926
|
|
927
|
+
@db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rails', :tsvector=>true).all.should == [{:title=>'ruby rails', :body=>'yowsa'}]
|
928
|
+
@db[:posts].full_text_search(:title, Sequel.function(:to_tsquery, 'simple', 'rails'), :tsquery=>true).all.should == [{:title=>'ruby rails', :body=>'yowsa'}]
|
929
|
+
proc{@db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rubinius ruby', :tsvector=>true, :phrase=>true)}.should raise_error(Sequel::Error)
|
930
|
+
proc{@db[:posts].full_text_search(:title, Sequel.function(:to_tsquery, 'simple', 'rails'), :tsquery=>true, :phrase=>true)}.should raise_error(Sequel::Error)
|
931
|
+
|
927
932
|
@db[:posts].delete
|
928
933
|
t1 = "bork " * 1000 + "ruby sequel"
|
929
934
|
t2 = "ruby sequel " * 1000
|
data/spec/core/database_spec.rb
CHANGED
@@ -899,6 +899,16 @@ shared_examples_for "Database#transaction" do
|
|
899
899
|
@db.sqls.should == ['BEGIN', 'COMMIT']
|
900
900
|
end
|
901
901
|
|
902
|
+
specify "should have transaction inside after_commit work correctly" do
|
903
|
+
@db.transaction{@db.after_commit{@db.transaction{@db.execute('foo')}}}
|
904
|
+
@db.sqls.should == ['BEGIN', 'COMMIT', 'BEGIN', 'foo', 'COMMIT']
|
905
|
+
end
|
906
|
+
|
907
|
+
specify "should have transaction inside after_rollback work correctly" do
|
908
|
+
@db.transaction(:rollback=>:always){@db.after_rollback{@db.transaction{@db.execute('foo')}}}
|
909
|
+
@db.sqls.should == ['BEGIN', 'ROLLBACK', 'BEGIN', 'foo', 'COMMIT']
|
910
|
+
end
|
911
|
+
|
902
912
|
specify "should not call after_commit if the transaction rolls back" do
|
903
913
|
@db.transaction{@db.after_commit{@db.execute('foo')}; raise Sequel::Rollback}
|
904
914
|
@db.sqls.should == ['BEGIN', 'ROLLBACK']
|
@@ -59,6 +59,34 @@ describe "Blockless Ruby Filters" do
|
|
59
59
|
@d.l(~Sequel.expr(:x => nil)).should == '(x IS NOT NULL)'
|
60
60
|
end
|
61
61
|
|
62
|
+
it "should support = and similar operations via =~ method" do
|
63
|
+
@d.l{x =~ 100}.should == '(x = 100)'
|
64
|
+
@d.l{x =~ 'a'}.should == '(x = \'a\')'
|
65
|
+
@d.l{x =~ true}.should == '(x IS TRUE)'
|
66
|
+
@d.l{x =~ false}.should == '(x IS FALSE)'
|
67
|
+
@d.l{x =~ nil}.should == '(x IS NULL)'
|
68
|
+
@d.l{x =~ (1...5)}.should == '((x >= 1) AND (x < 5))'
|
69
|
+
@d.l{x =~ [1,2,3]}.should == '(x IN (1, 2, 3))'
|
70
|
+
|
71
|
+
def @d.supports_regexp?; true end
|
72
|
+
@d.l{x =~ /blah/}.should == '(x ~ \'blah\')'
|
73
|
+
end
|
74
|
+
|
75
|
+
if RUBY_VERSION >= '1.9'
|
76
|
+
it "should support != and similar inversions via !~ method" do
|
77
|
+
@d.l{x !~ 100}.should == '(x != 100)'
|
78
|
+
@d.l{x !~ 'a'}.should == '(x != \'a\')'
|
79
|
+
@d.l{x !~ true}.should == '(x IS NOT TRUE)'
|
80
|
+
@d.l{x !~ false}.should == '(x IS NOT FALSE)'
|
81
|
+
@d.l{x !~ nil}.should == '(x IS NOT NULL)'
|
82
|
+
@d.l{x !~ (1...5)}.should == '((x < 1) OR (x >= 5))'
|
83
|
+
@d.l{x !~ [1,2,3]}.should == '(x NOT IN (1, 2, 3))'
|
84
|
+
|
85
|
+
def @d.supports_regexp?; true end
|
86
|
+
@d.l{x !~ /blah/}.should == '(x !~ \'blah\')'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
62
90
|
it "should support ~ via Hash and Regexp (if supported by database)" do
|
63
91
|
def @d.supports_regexp?; true end
|
64
92
|
@d.l(:x => /blah/).should == '(x ~ \'blah\')'
|
@@ -206,6 +206,19 @@ describe "Sequel::Plugins::JsonSerializer" do
|
|
206
206
|
@album.to_json(:root=>true, :except => [:name, :artist_id]).to_s.should == '{"album":{"id":1}}'
|
207
207
|
@album.to_json(:root=>true, :only => :name).to_s.should == '{"album":{"name":"RF"}}'
|
208
208
|
end
|
209
|
+
|
210
|
+
it "should handle the :root option to qualify single records of namespaced models" do
|
211
|
+
module ::Namespace
|
212
|
+
class Album < Sequel::Model
|
213
|
+
plugin :json_serializer, :naked=>true
|
214
|
+
end
|
215
|
+
end
|
216
|
+
Namespace::Album.new({}).to_json(:root=>true).to_s.should == '{"album":{}}'
|
217
|
+
Namespace::Album.dataset._fetch = [{}]
|
218
|
+
Namespace::Album.dataset.to_json(:root=>:collection).to_s.should == '{"albums":[{}]}'
|
219
|
+
Namespace::Album.dataset.to_json(:root=>:both).to_s.should == '{"albums":[{"album":{}}]}'
|
220
|
+
Object.send(:remove_const, :Namespace)
|
221
|
+
end
|
209
222
|
|
210
223
|
it "should handle the :root option with a string to qualify single records using the string as the key" do
|
211
224
|
@album.to_json(:root=>"foo", :except => [:name, :artist_id]).to_s.should == '{"foo":{"id":1}}'
|
@@ -206,6 +206,7 @@ describe "Sequel::Postgres::JSONOp" do
|
|
206
206
|
it "should be able to turn symbols into json ops using Sequel.pg_json" do
|
207
207
|
@db.literal(Sequel.pg_json(:a)[1]).should == "(a -> 1)"
|
208
208
|
@db.literal(Sequel.pg_jsonb(:a)[1]).should == "(a -> 1)"
|
209
|
+
@db.literal(Sequel.pg_jsonb(:a).contains('a'=>1)).should == "(a @> '{\"a\":1}'::jsonb)"
|
209
210
|
end
|
210
211
|
|
211
212
|
it "should allow transforming JSONArray instances into ArrayOp instances" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -193,6 +193,7 @@ extra_rdoc_files:
|
|
193
193
|
- doc/release_notes/4.18.0.txt
|
194
194
|
- doc/release_notes/4.19.0.txt
|
195
195
|
- doc/release_notes/4.20.0.txt
|
196
|
+
- doc/release_notes/4.21.0.txt
|
196
197
|
files:
|
197
198
|
- CHANGELOG
|
198
199
|
- MIT-LICENSE
|
@@ -301,6 +302,7 @@ files:
|
|
301
302
|
- doc/release_notes/4.19.0.txt
|
302
303
|
- doc/release_notes/4.2.0.txt
|
303
304
|
- doc/release_notes/4.20.0.txt
|
305
|
+
- doc/release_notes/4.21.0.txt
|
304
306
|
- doc/release_notes/4.3.0.txt
|
305
307
|
- doc/release_notes/4.4.0.txt
|
306
308
|
- doc/release_notes/4.5.0.txt
|