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.
Files changed (40) hide show
  1. data/CHANGELOG +50 -0
  2. data/README +1 -2
  3. data/Rakefile +1 -1
  4. data/bin/sequel +26 -11
  5. data/doc/dataset_filtering.rdoc +9 -2
  6. data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -2
  7. data/lib/sequel_core/adapters/mysql.rb +11 -97
  8. data/lib/sequel_core/adapters/odbc_mssql.rb +1 -1
  9. data/lib/sequel_core/adapters/oracle.rb +1 -1
  10. data/lib/sequel_core/adapters/postgres.rb +33 -17
  11. data/lib/sequel_core/adapters/sqlite.rb +1 -1
  12. data/lib/sequel_core/connection_pool.rb +12 -35
  13. data/lib/sequel_core/core_ext.rb +5 -19
  14. data/lib/sequel_core/core_sql.rb +17 -6
  15. data/lib/sequel_core/database.rb +14 -2
  16. data/lib/sequel_core/dataset/callback.rb +0 -3
  17. data/lib/sequel_core/dataset/convenience.rb +4 -3
  18. data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +0 -21
  19. data/lib/sequel_core/dataset/sequelizer.rb +42 -43
  20. data/lib/sequel_core/dataset/sql.rb +121 -62
  21. data/lib/sequel_core/dataset.rb +20 -4
  22. data/lib/sequel_core/deprecated.rb +0 -6
  23. data/lib/sequel_core/migration.rb +4 -0
  24. data/lib/sequel_core/object_graph.rb +8 -6
  25. data/lib/sequel_core/pretty_table.rb +1 -1
  26. data/lib/sequel_core/schema/sql.rb +2 -0
  27. data/lib/sequel_core/sql.rb +393 -153
  28. data/lib/sequel_core.rb +22 -7
  29. data/spec/adapters/mysql_spec.rb +11 -7
  30. data/spec/adapters/postgres_spec.rb +32 -4
  31. data/spec/blockless_filters_spec.rb +33 -6
  32. data/spec/connection_pool_spec.rb +18 -16
  33. data/spec/core_ext_spec.rb +0 -13
  34. data/spec/core_sql_spec.rb +59 -13
  35. data/spec/database_spec.rb +5 -5
  36. data/spec/dataset_spec.rb +167 -55
  37. data/spec/object_graph_spec.rb +9 -4
  38. data/spec/sequelizer_spec.rb +8 -17
  39. data/spec/spec_helper.rb +4 -3
  40. 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
- * {RubyForge page}[http://rubyforge.org/projects/sequel/]
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.1"
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:///blog.db"
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("-E", "--echo", "echo SQL statements") do |v|
36
- echo = v
42
+ opts.on("-e", "--env ENV", "use environment config for database") do |v|
43
+ env = v
37
44
  end
38
45
 
39
- opts.on("-e", "--env ENV", "use environment config for database") do |env|
40
- env = env
46
+ opts.on("-E", "--echo", "echo SQL statements") do
47
+ echo = true
41
48
  end
42
49
 
43
- opts.on_tail("-?", "--help", "Show this message") do
44
- puts opts
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
@@ -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 == (100..200)} & :active).sql
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. While it is not officially deprecated, usage of blocks when filtering is discouraged.
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
 
@@ -1,5 +1,3 @@
1
- # require 'adapter_lib'
2
-
3
1
  module Sequel
4
2
  module Adapter
5
3
  class Database < Sequel::Database
@@ -256,7 +256,7 @@ module Sequel
256
256
 
257
257
  private
258
258
  def connection_pool_default_options
259
- super.merge(:pool_reuse_connections=>:last_resort, :pool_convert_exceptions=>false)
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
- return super(type, table, expr, table_alias) unless (server_version >= 50014) && /natural|cross|straight/.match(type.to_s)
348
-
349
- table = if Array === table
350
- "( #{table.collect{|t| quote_identifier(t)}.join(', ')} )"
351
- else
352
- quote_identifier(table)
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
@@ -54,7 +54,7 @@ module Sequel
54
54
  end
55
55
 
56
56
  if join = opts[:join]
57
- sql << join
57
+ join.each{|j| sql << literal(j)}
58
58
  end
59
59
 
60
60
  if where = opts[:where]
@@ -135,7 +135,7 @@ module Sequel
135
135
  end
136
136
 
137
137
  if join = opts[:join]
138
- sql << join
138
+ join.each{|j| sql << literal(j)}
139
139
  end
140
140
 
141
141
  if where = opts[:where]
@@ -63,7 +63,7 @@ module Sequel
63
63
  end
64
64
  end
65
65
 
66
- attr_accessor :transaction_in_progress
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.transaction_in_progress
300
- yield conn
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
- begin
305
- conn.transaction_in_progress = true
306
- yield
307
- rescue ::Exception => e
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
- raise convert_pgerror(e) unless Error::Rollback === e
311
- ensure
312
- unless e
313
- begin
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
- rescue => e
317
- log_info(e.message)
318
- raise convert_pgerror(e)
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(:pool_reuse_connections=>:always, :pool_convert_exceptions=>false)
379
+ super.merge(:pool_convert_exceptions=>false)
364
380
  end
365
381
 
366
382
  def schema_ds_filter(table_name, opts)