sequel 4.20.0 → 4.21.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.
- 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
|