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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6daa0c1ef4f76e1d9bfdeedbf9208bf32646ab20
4
- data.tar.gz: f2cf0e139a84f465fe1146de98b767fad029fca9
3
+ metadata.gz: bd1a779cad72f3c5831e46f1cb9ee5c5fa0cb75a
4
+ data.tar.gz: 561c34f1d23f0eb77d59e483b2beab3ebed21172
5
5
  SHA512:
6
- metadata.gz: 28f1f4b25bd2c5db83ce6630ebd9310524dafb59a60ce9e72e3c8e6d12f0a9ea9eff9e1e2ec492fa3f7f655b76ee51b6f5a8e233a36b80370d51922d3024f504
7
- data.tar.gz: 7309df3435d53878e8aa1945fc728fedad302727d503af24c399285fbf511b70571ca7cbef4f9c1b5c615aac689d9971f8072b0c9e2fb77f89491410384be665
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
- # RSpec 1
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|
@@ -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']}, (:price - 100 > 200))}.sql
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{Sequel.&({:price => (100..200)}, :active)}.sql
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
@@ -226,6 +226,7 @@ These methods check that the specified attributes can be valid integers or valid
226
226
 
227
227
  class Album < Sequel::Model
228
228
  def validate
229
+ super
229
230
  validates_includes [1, 2, 3, 4, 5], :rating
230
231
  end
231
232
  end
@@ -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. However, they
189
- have special handling of some operator methods to make certain things easier. The
190
- operators all use a prefix form.
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
- ds = where(Sequel.lit(["(", " @@ ", ")"], vector, query))
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
- ds = ds.grep(cols, "%#{escape_like(terms)}%", :case_insensitive=>true)
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(vector, query)}
1322
+ ds = ds.order{ts_rank_cd(cols, terms)}
1303
1323
  end
1304
1324
 
1305
1325
  ds
@@ -64,7 +64,7 @@ module Sequel
64
64
  rescue TinyTds::Error => e
65
65
  raise_error(e, :disconnect=>!c.active?)
66
66
  ensure
67
- r.cancel if r && c.sqlsent?
67
+ r.cancel if r && c.sqlsent? && c.active?
68
68
  end
69
69
  end
70
70
  end
@@ -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
- begin
292
- if committed
293
- after_transaction_commit(conn)
294
- else
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 normal has to do when literalizing a
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 resepctive
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
- add_named_conversion_procs(conversion_procs, :hstore=>PG_NAMED_TYPES[:hstore])
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::JSONArray or Postgres::JSONHash.
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.pg_json_op(v)
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
- root = model.send(:underscore, model.to_s) unless root.is_a?(String)
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
@@ -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 = 20
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
@@ -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.20.0
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-03-03 00:00:00.000000000 Z
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