sequel_core 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +50 -0
- data/README +1 -2
- data/Rakefile +1 -1
- data/bin/sequel +26 -11
- data/doc/dataset_filtering.rdoc +9 -2
- data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -2
- data/lib/sequel_core/adapters/mysql.rb +11 -97
- data/lib/sequel_core/adapters/odbc_mssql.rb +1 -1
- data/lib/sequel_core/adapters/oracle.rb +1 -1
- data/lib/sequel_core/adapters/postgres.rb +33 -17
- data/lib/sequel_core/adapters/sqlite.rb +1 -1
- data/lib/sequel_core/connection_pool.rb +12 -35
- data/lib/sequel_core/core_ext.rb +5 -19
- data/lib/sequel_core/core_sql.rb +17 -6
- data/lib/sequel_core/database.rb +14 -2
- data/lib/sequel_core/dataset/callback.rb +0 -3
- data/lib/sequel_core/dataset/convenience.rb +4 -3
- data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +0 -21
- data/lib/sequel_core/dataset/sequelizer.rb +42 -43
- data/lib/sequel_core/dataset/sql.rb +121 -62
- data/lib/sequel_core/dataset.rb +20 -4
- data/lib/sequel_core/deprecated.rb +0 -6
- data/lib/sequel_core/migration.rb +4 -0
- data/lib/sequel_core/object_graph.rb +8 -6
- data/lib/sequel_core/pretty_table.rb +1 -1
- data/lib/sequel_core/schema/sql.rb +2 -0
- data/lib/sequel_core/sql.rb +393 -153
- data/lib/sequel_core.rb +22 -7
- data/spec/adapters/mysql_spec.rb +11 -7
- data/spec/adapters/postgres_spec.rb +32 -4
- data/spec/blockless_filters_spec.rb +33 -6
- data/spec/connection_pool_spec.rb +18 -16
- data/spec/core_ext_spec.rb +0 -13
- data/spec/core_sql_spec.rb +59 -13
- data/spec/database_spec.rb +5 -5
- data/spec/dataset_spec.rb +167 -55
- data/spec/object_graph_spec.rb +9 -4
- data/spec/sequelizer_spec.rb +8 -17
- data/spec/spec_helper.rb +4 -3
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,49 @@
|
|
1
|
+
=== HEAD
|
2
|
+
|
3
|
+
* Support bitwise operators for NumericExpressions: &, |, ^, ~, <<, >> (jeremyevans)
|
4
|
+
|
5
|
+
* No longer raise an error for Dataset#filter(true) or Dataset#filter(false) (jeremyevans)
|
6
|
+
|
7
|
+
* Allow Dataset #filter, #or, #exclude and other methods that call them to use both the block and regular arguments (jeremyevans)
|
8
|
+
|
9
|
+
* ParseTree support is now officially deprecated, use Sequel.use_parse_tree = false to use the expression (blockless) filters inside blocks (jeremyevans)
|
10
|
+
|
11
|
+
* Remove :pool_reuse_connections ConnectionPool/Database option, MySQL users need to be careful with nested queries (jeremyevans)
|
12
|
+
|
13
|
+
* Allow Dataset#graph :select option to take an array of columns to select (jeremyevans)
|
14
|
+
|
15
|
+
* Allow Dataset#to_hash to be called with only one argument, allowing for easy creation of lookup tables for a single key (jeremyevans)
|
16
|
+
|
17
|
+
* Allow join_table to accept a block providing the aliases and previous joins, that allows you to specify arbitrary conditions properly qualified (jeremyevans)
|
18
|
+
|
19
|
+
* Support NATURAL, CROSS, and USING joins in join_table (jeremyevans)
|
20
|
+
|
21
|
+
* Make sure HAVING comes before ORDER BY, per the SQL standard and at least MySQL, PostgreSQL, and SQLite (juco)
|
22
|
+
|
23
|
+
* Add cast_numeric and cast_string methods for use in the Sequel DSL, that have default types and wrap the object in the correct class (jeremyevans)
|
24
|
+
|
25
|
+
* Add Symbol#qualify, for adding a table/schema qualifier to a column/table name (jeremyevans)
|
26
|
+
|
27
|
+
* Remove Module#metaprivate, since it duplicates the standard Module#private_class_method (jeremyevans)
|
28
|
+
|
29
|
+
* Support the SQL CASE expression via Array#case and Hash#case (jeremyevans)
|
30
|
+
|
31
|
+
* Support the SQL EXTRACT function: :date.extract(:year) (jeremyevans)
|
32
|
+
|
33
|
+
* Convert numeric fields to BigDecimal in PostgreSQL adapter (jeremyevans)
|
34
|
+
|
35
|
+
* Add :decimal fields to the schema parser (jeremyevans)
|
36
|
+
|
37
|
+
* The expr argument in join table now allows the same argument as filter, so it can take a string or a blockless filter expression (brushbox, jeremyevans)
|
38
|
+
|
39
|
+
* No longer assume the expr argument to join_table references the primary key column (jeremyevans)
|
40
|
+
|
41
|
+
* Rename the Sequel.time_class setting to Sequel.datetime_class (jeremyevans)
|
42
|
+
|
43
|
+
* Add savepoint/nesting support to postgresql transactions (elven)
|
44
|
+
|
45
|
+
* Use the specified table alias when joining a dataset, instead of the automatically generated alias (brushbox)
|
46
|
+
|
1
47
|
=== 2.0.1 (2008-06-04)
|
2
48
|
|
3
49
|
* Have PostgreSQL money type use BigDecimal instead of Float (jeremyevans)
|
@@ -10,6 +56,10 @@
|
|
10
56
|
|
11
57
|
* Add StringExpression#+, for simple SQL string concatenation (:x.sql_string + :y) (jeremyevans)
|
12
58
|
|
59
|
+
* Make StringMethods.like to a case sensensitive search on MySQL (use ilike for the old behavior) (jeremyevans)
|
60
|
+
|
61
|
+
* Add StringMethods.ilike, for case insensitive pattern matching (jeremyevans)
|
62
|
+
|
13
63
|
* Refactor ComplexExpression into three subclasses and a few modules, so operators that don't make sense are not defined for the class (jeremyevans)
|
14
64
|
|
15
65
|
=== 2.0.0 (2008-06-01)
|
data/README
CHANGED
@@ -9,8 +9,7 @@ Sequel makes it easy to deal with multiple records without having to break your
|
|
9
9
|
* {Source code}[http://github.com/jeremyevans/sequel]
|
10
10
|
* {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
|
11
11
|
* {Google group}[http://groups.google.com/group/sequel-talk]
|
12
|
-
* {
|
13
|
-
* {API RDoc}[http://sequel.rubyforge.org]
|
12
|
+
* {RDoc}[http://sequel.rubyforge.org]
|
14
13
|
|
15
14
|
To check out the source code:
|
16
15
|
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ include FileUtils
|
|
9
9
|
# Configuration
|
10
10
|
##############################################################################
|
11
11
|
NAME = "sequel_core"
|
12
|
-
VERS = "2.0
|
12
|
+
VERS = "2.1.0"
|
13
13
|
CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage"]
|
14
14
|
RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
|
15
15
|
'Sequel: The Database Toolkit for Ruby: Core Library and Adapters', \
|
data/bin/sequel
CHANGED
@@ -14,13 +14,15 @@ db_opts = {}
|
|
14
14
|
echo = nil
|
15
15
|
env = nil
|
16
16
|
logfile = nil
|
17
|
+
migrate_dir = nil
|
18
|
+
migrate_ver = nil
|
17
19
|
|
18
20
|
opts = OptionParser.new do |opts|
|
19
21
|
opts.banner = "Sequel: The Database Toolkit for Ruby"
|
20
22
|
opts.define_head "Usage: sequel <uri|path> [options]"
|
21
23
|
opts.separator ""
|
22
24
|
opts.separator "Examples:"
|
23
|
-
opts.separator " sequel sqlite
|
25
|
+
opts.separator " sequel sqlite://blog.db"
|
24
26
|
opts.separator " sequel postgres://localhost/my_blog"
|
25
27
|
opts.separator " sequel config/database.yml"
|
26
28
|
opts.separator ""
|
@@ -28,23 +30,31 @@ opts = OptionParser.new do |opts|
|
|
28
30
|
opts.separator ""
|
29
31
|
opts.separator "Options:"
|
30
32
|
|
33
|
+
opts.on_tail("-?", "--help", "Show this message") do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
31
38
|
opts.on("-l", "--log logfile", "log SQL statements to log file") do |v|
|
32
39
|
logfile = v
|
33
40
|
end
|
34
41
|
|
35
|
-
opts.on("-
|
36
|
-
|
42
|
+
opts.on("-e", "--env ENV", "use environment config for database") do |v|
|
43
|
+
env = v
|
37
44
|
end
|
38
45
|
|
39
|
-
opts.on("-
|
40
|
-
|
46
|
+
opts.on("-E", "--echo", "echo SQL statements") do
|
47
|
+
echo = true
|
41
48
|
end
|
42
49
|
|
43
|
-
opts.
|
44
|
-
|
45
|
-
exit
|
50
|
+
opts.on("-m", "--migrate-directory DIR", "run the migrations in directory") do |v|
|
51
|
+
migrate_dir = v
|
46
52
|
end
|
47
|
-
|
53
|
+
|
54
|
+
opts.on("-M", "--migrate-version VER", "migrate the database to version given") do |v|
|
55
|
+
migrate_ver = Integer(v)
|
56
|
+
end
|
57
|
+
|
48
58
|
opts.on_tail("-v", "--version", "Show version") do
|
49
59
|
class << Gem; attr_accessor :loaded_specs; end
|
50
60
|
begin
|
@@ -66,7 +76,7 @@ db = ARGV.shift
|
|
66
76
|
|
67
77
|
if db.blank?
|
68
78
|
puts opts
|
69
|
-
exit
|
79
|
+
exit 1
|
70
80
|
end
|
71
81
|
|
72
82
|
if logfile || echo
|
@@ -91,12 +101,17 @@ begin
|
|
91
101
|
end
|
92
102
|
DB = Sequel.connect(*opts)
|
93
103
|
DB.test_connection
|
104
|
+
if migrate_dir
|
105
|
+
Sequel::Migrator.apply(DB, migrate_dir, migrate_ver)
|
106
|
+
exit
|
107
|
+
end
|
94
108
|
rescue => e
|
95
109
|
puts e.message
|
96
110
|
puts e.backtrace.first
|
97
|
-
exit
|
111
|
+
exit 1
|
98
112
|
end
|
99
113
|
|
114
|
+
|
100
115
|
require 'irb'
|
101
116
|
puts "Your database is stored in DB..."
|
102
117
|
IRB.start
|
data/doc/dataset_filtering.rdoc
CHANGED
@@ -113,7 +113,7 @@ This works with other hash values, such as arrays and ranges:
|
|
113
113
|
items.filter({:category => ['ruby', 'other']} | (:price - 100 > 200)).sql
|
114
114
|
#=> "SELECT * FROM items WHERE ((category IN ('ruby', 'other')) OR ((price - 100) <= 200))"
|
115
115
|
|
116
|
-
items.filter({:price
|
116
|
+
items.filter({:price => (100..200)} & :active).sql
|
117
117
|
#=> "SELECT * FROM items WHERE ((price >= 100 AND price <= 200) AND active)"
|
118
118
|
|
119
119
|
=== Negating conditions
|
@@ -175,7 +175,14 @@ Array#sql_string_join also takes a join argument:
|
|
175
175
|
|
176
176
|
Most SQL expressions that you can can create with expressions you can also express inside blocks. This was previously the only way to specify expressions using ruby code. Filtering with blocks requires that you install ParseTree, ruby2ruby, and their dependencies. It's slower than using the equivalent expression without a block, and the syntax inside the block is different in some cases. Because it requires ParseTree, it can only be used with MRI (Matz's Ruby Interpreter) 1.8.*, as ParseTree doesn't run on any other ruby implementation (blockless filters should work on other ruby implementations).
|
177
177
|
|
178
|
-
In general, filtering with a block should only be used with legacy code.
|
178
|
+
In general, filtering with a block should only be used with legacy code. ParseTree filters are currently deprecated, and support for them will be removed in Sequel 2.2. To use the expression filtering syntax inside blocks, set:
|
179
|
+
|
180
|
+
Sequel.use_parse_tree = false
|
181
|
+
# or
|
182
|
+
SEQUEL_NO_PARSE_TREE = true
|
183
|
+
require 'sequel'
|
184
|
+
|
185
|
+
These will become no-ops in Sequel 2.2, as the expression syntax inside blocks will be the only supported behavior.
|
179
186
|
|
180
187
|
To filter with a block, supply a block to filter with the appropriate ruby code:
|
181
188
|
|
@@ -256,7 +256,7 @@ module Sequel
|
|
256
256
|
|
257
257
|
private
|
258
258
|
def connection_pool_default_options
|
259
|
-
super.merge(:
|
259
|
+
super.merge(:pool_convert_exceptions=>false)
|
260
260
|
end
|
261
261
|
|
262
262
|
def schema_ds_dataset
|
@@ -285,28 +285,6 @@ module Sequel
|
|
285
285
|
TRUE = '1'
|
286
286
|
FALSE = '0'
|
287
287
|
|
288
|
-
# Join processing changed after MySQL v5.0.12. NATURAL
|
289
|
-
# joins are SQL:2003 consistent.
|
290
|
-
JOIN_TYPES = { :cross => 'INNER JOIN'.freeze,
|
291
|
-
:straight => 'STRAIGHT_JOIN'.freeze,
|
292
|
-
:natural_left => 'NATURAL LEFT JOIN'.freeze,
|
293
|
-
:natural_right => 'NATURAL RIGHT JOIN'.freeze,
|
294
|
-
:natural_left_outer => 'NATURAL LEFT OUTER JOIN'.freeze,
|
295
|
-
:natural_right_outer => 'NATURAL RIGHT OUTER JOIN'.freeze,
|
296
|
-
:left => 'LEFT JOIN'.freeze,
|
297
|
-
:right => 'RIGHT JOIN'.freeze,
|
298
|
-
:left_outer => 'LEFT OUTER JOIN'.freeze,
|
299
|
-
:right_outer => 'RIGHT OUTER JOIN'.freeze,
|
300
|
-
:natural_inner => 'NATURAL LEFT JOIN'.freeze,
|
301
|
-
# :full_outer => 'FULL OUTER JOIN'.freeze,
|
302
|
-
#
|
303
|
-
# A full outer join, nor a workaround implementation of
|
304
|
-
# :full_outer, is not yet possible in Sequel. See issue
|
305
|
-
# #195 which probably depends on issue #113 being
|
306
|
-
# resolved.
|
307
|
-
:inner => 'INNER JOIN'.freeze
|
308
|
-
}
|
309
|
-
|
310
288
|
def literal(v)
|
311
289
|
case v
|
312
290
|
when LiteralString
|
@@ -340,18 +318,18 @@ module Sequel
|
|
340
318
|
# @ds.join_table(:natural_left_outer, :nodes)
|
341
319
|
# # join SQL is 'NATURAL LEFT OUTER JOIN nodes'
|
342
320
|
def join_table(type, table, expr=nil, table_alias=nil)
|
343
|
-
raise(Error::InvalidJoinType, "Invalid join type: #{type}") unless join_type = JOIN_TYPES[type || :inner]
|
344
|
-
|
345
|
-
server_version = (@opts[:server_version] ||= @db.server_version)
|
346
321
|
type = :inner if (type == :cross) && !expr.nil?
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
322
|
+
raise(Sequel::Error::InvalidJoinType, "MySQL doesn't support FULL OUTER JOIN") if type == :full_outer
|
323
|
+
super(type, table, expr, table_alias)
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
def join_type_sql(join_type)
|
328
|
+
case join_type
|
329
|
+
when :straight then 'STRAIGHT_JOIN'
|
330
|
+
when :natural_inner then 'NATURAL LEFT JOIN'
|
331
|
+
else super
|
353
332
|
end
|
354
|
-
clone(:join => "#{@opts[:join]} #{join_type} #{table}")
|
355
333
|
end
|
356
334
|
|
357
335
|
def insert_default_values_sql
|
@@ -384,70 +362,6 @@ module Sequel
|
|
384
362
|
end
|
385
363
|
end
|
386
364
|
|
387
|
-
# MySQL expects the having clause before the order by clause.
|
388
|
-
def select_sql(opts = nil)
|
389
|
-
opts = opts ? @opts.merge(opts) : @opts
|
390
|
-
|
391
|
-
if sql = opts[:sql]
|
392
|
-
return sql
|
393
|
-
end
|
394
|
-
|
395
|
-
columns = opts[:select]
|
396
|
-
select_columns = columns ? column_list(columns) : WILDCARD
|
397
|
-
|
398
|
-
if distinct = opts[:distinct]
|
399
|
-
distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{column_list(distinct)})"
|
400
|
-
sql = "SELECT #{distinct_clause} #{select_columns}"
|
401
|
-
else
|
402
|
-
sql = "SELECT #{select_columns}"
|
403
|
-
end
|
404
|
-
|
405
|
-
if opts[:from]
|
406
|
-
sql << " FROM #{source_list(opts[:from])}"
|
407
|
-
end
|
408
|
-
|
409
|
-
if join = opts[:join]
|
410
|
-
sql << join
|
411
|
-
end
|
412
|
-
|
413
|
-
if where = opts[:where]
|
414
|
-
sql << " WHERE #{literal(where)}"
|
415
|
-
end
|
416
|
-
|
417
|
-
if group = opts[:group]
|
418
|
-
sql << " GROUP BY #{column_list(group)}"
|
419
|
-
end
|
420
|
-
|
421
|
-
if having = opts[:having]
|
422
|
-
sql << " HAVING #{literal(having)}"
|
423
|
-
end
|
424
|
-
|
425
|
-
if order = opts[:order]
|
426
|
-
sql << " ORDER BY #{column_list(order)}"
|
427
|
-
end
|
428
|
-
|
429
|
-
if limit = opts[:limit]
|
430
|
-
sql << " LIMIT #{limit}"
|
431
|
-
if offset = opts[:offset]
|
432
|
-
sql << " OFFSET #{offset}"
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
if union = opts[:union]
|
437
|
-
sql << (opts[:union_all] ? \
|
438
|
-
" UNION ALL #{union.sql}" : " UNION #{union.sql}")
|
439
|
-
elsif intersect = opts[:intersect]
|
440
|
-
sql << (opts[:intersect_all] ? \
|
441
|
-
" INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
|
442
|
-
elsif except = opts[:except]
|
443
|
-
sql << (opts[:except_all] ? \
|
444
|
-
" EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
|
445
|
-
end
|
446
|
-
|
447
|
-
sql
|
448
|
-
end
|
449
|
-
alias_method :sql, :select_sql
|
450
|
-
|
451
365
|
def full_text_search(cols, terms, opts = {})
|
452
366
|
mode = opts[:boolean] ? " IN BOOLEAN MODE" : ""
|
453
367
|
s = if Array === terms
|
@@ -63,7 +63,7 @@ module Sequel
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
attr_accessor :
|
66
|
+
attr_accessor :transaction_depth
|
67
67
|
|
68
68
|
SELECT_CURRVAL = "SELECT currval('%s')".freeze
|
69
69
|
|
@@ -174,7 +174,8 @@ module Sequel
|
|
174
174
|
1114 => lambda{ |s| s.to_sequel_time }, # timestamp without time zone
|
175
175
|
1184 => lambda{ |s| s.to_sequel_time }, # timestamp with time zone
|
176
176
|
1186 => lambda{ |s| s.to_i }, # interval
|
177
|
-
1266 => lambda{ |s| s.to_time } # time with time zone
|
177
|
+
1266 => lambda{ |s| s.to_time }, # time with time zone
|
178
|
+
1700 => lambda{ |s| s.to_d }, # numeric
|
178
179
|
}
|
179
180
|
|
180
181
|
if Adapter.respond_to?(:translate_results=)
|
@@ -291,35 +292,50 @@ module Sequel
|
|
291
292
|
end
|
292
293
|
|
293
294
|
SQL_BEGIN = 'BEGIN'.freeze
|
295
|
+
SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
|
294
296
|
SQL_COMMIT = 'COMMIT'.freeze
|
297
|
+
SQL_ROLLBACK_TO_SAVEPOINT = 'ROLLBACK TO SAVEPOINT autopoint_%d'.freeze
|
295
298
|
SQL_ROLLBACK = 'ROLLBACK'.freeze
|
299
|
+
SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
|
296
300
|
|
297
301
|
def transaction
|
298
302
|
@pool.hold do |conn|
|
299
|
-
if conn.
|
300
|
-
|
303
|
+
conn.transaction_depth = 0 if conn.transaction_depth.nil?
|
304
|
+
if conn.transaction_depth > 0
|
305
|
+
log_info(SQL_SAVEPOINT % conn.transaction_depth)
|
306
|
+
conn.execute(SQL_SAVEPOINT % conn.transaction_depth)
|
301
307
|
else
|
302
308
|
log_info(SQL_BEGIN)
|
303
309
|
conn.execute(SQL_BEGIN)
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
310
|
+
end
|
311
|
+
begin
|
312
|
+
conn.transaction_depth += 1
|
313
|
+
yield
|
314
|
+
rescue ::Exception => e
|
315
|
+
if conn.transaction_depth > 1
|
316
|
+
log_info(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
|
317
|
+
conn.execute(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
|
318
|
+
else
|
308
319
|
log_info(SQL_ROLLBACK)
|
309
320
|
conn.execute(SQL_ROLLBACK) rescue nil
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
321
|
+
end
|
322
|
+
raise convert_pgerror(e) unless Error::Rollback === e
|
323
|
+
ensure
|
324
|
+
unless e
|
325
|
+
begin
|
326
|
+
if conn.transaction_depth < 2
|
314
327
|
log_info(SQL_COMMIT)
|
315
328
|
conn.execute(SQL_COMMIT)
|
316
|
-
|
317
|
-
log_info(
|
318
|
-
|
329
|
+
else
|
330
|
+
log_info(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
331
|
+
conn.execute(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
319
332
|
end
|
333
|
+
rescue => e
|
334
|
+
log_info(e.message)
|
335
|
+
raise convert_pgerror(e)
|
320
336
|
end
|
321
|
-
conn.transaction_in_progress = nil
|
322
337
|
end
|
338
|
+
conn.transaction_depth -= 1
|
323
339
|
end
|
324
340
|
end
|
325
341
|
end
|
@@ -360,7 +376,7 @@ module Sequel
|
|
360
376
|
|
361
377
|
# PostgreSQL currently can always reuse connections. It doesn't need the pool to convert exceptions, either.
|
362
378
|
def connection_pool_default_options
|
363
|
-
super.merge(:
|
379
|
+
super.merge(:pool_convert_exceptions=>false)
|
364
380
|
end
|
365
381
|
|
366
382
|
def schema_ds_filter(table_name, opts)
|