sequel_core 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,4 +1,42 @@
1
- === HEAD
1
+ === 2.2.0 (2008-07-05)
2
+
3
+ * Filter blocks now yield a SQL::VirtualRow argument, which is useful if another library defines operator methods on Symbol (jeremyevans)
4
+
5
+ * Add Symbol#identifier method, to make x__a be treated as "x__a" instead of "x"."a" (jeremyevans)
6
+
7
+ * Dataset#update no longer takes a block, please use a hash argument with the expression syntax instead (jeremyevans)
8
+
9
+ * ParseTree support has been removed from Sequel (jeremyevans)
10
+
11
+ * Database#drop_column is now supported in the SQLite adapter (abhay)
12
+
13
+ * Tinyint columns can now be considered integers instead of booleans by setting Sequel.convert_tinyint_to_bool = false (samsouder)
14
+
15
+ * Allow the use of URL parameters in connection strings (jeremyevans)
16
+
17
+ * Ignore any previously selected columns when using Dataset#graph for the first time (jeremyevans)
18
+
19
+ * Dataset#graph now accepts a block which is passed to join_table (jeremyevans)
20
+
21
+ * Make Dataset#columns ignore any filtering, ordering, and distinct clauses (jeremyevans)
22
+
23
+ * Use the safer connection-specific string escaping methods for PostgreSQL (jeremyevans)
24
+
25
+ * Database#transaction now yields a connection when using the Postgres adapter, just like it does for other adapters (jeremyevans)
26
+
27
+ * Dataset#count now works for a limited dataset (divoxx)
28
+
29
+ * Database#add_index is now supported in the SQLite adapter (abhay)
30
+
31
+ * Sequel's MySQL adapter should no longer conflict with ActiveRecord's use of MySQL (careo)
32
+
33
+ * Treat Hash as expression instead of column alias when used in DISTINCT, ORDER BY, and GROUP BY clauses (jeremyevans)
34
+
35
+ * PostgreSQL bytea fields are now fully supported (dlee)
36
+
37
+ * For PostgreSQL, don't raise an error when assigning a value to a SERIAL PRIMARY KEY field when inserting records (jeremyevans)
38
+
39
+ === 2.1.0 (2008-06-17)
2
40
 
3
41
  * Support bitwise operators for NumericExpressions: &, |, ^, ~, <<, >> (jeremyevans)
4
42
 
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ include FileUtils
9
9
  # Configuration
10
10
  ##############################################################################
11
11
  NAME = "sequel_core"
12
- VERS = "2.1.0"
12
+ VERS = "2.2.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', \
@@ -145,114 +145,32 @@ Or against SQL functions:
145
145
 
146
146
  You can search SQL strings using the #like method:
147
147
 
148
- items.filter(:name.like('Acme%').sql
148
+ items.filter(:name.like('Acme%')).sql
149
149
  #=> "SELECT * FROM items WHERE (name LIKE 'Acme%')"
150
150
 
151
151
  You can specify a Regexp as a like argument, but this will probably only work
152
152
  on PostgreSQL and MySQL:
153
153
 
154
- items.filter(:name.like(/Acme.*/).sql
154
+ items.filter(:name.like(/Acme.*/)).sql
155
155
  #=> "SELECT * FROM items WHERE (name ~ 'Acme.*')"
156
156
 
157
157
  Like can also take more than one argument:
158
158
 
159
- items.filter(:name.like('Acme%', /Beta.*/).sql
159
+ items.filter(:name.like('Acme%', /Beta.*/)).sql
160
160
  #=> "SELECT * FROM items WHERE ((name LIKE 'Acme%') OR (name ~ 'Beta.*'))"
161
161
 
162
162
  == String concatenation
163
163
 
164
164
  You can concatenate SQL strings using Array#sql_string_join:
165
165
 
166
- items.filter([:name, :comment].sql_string_join.like('%acme%').sql
166
+ items.filter([:name, :comment].sql_string_join.like('%acme%')).sql
167
167
  #=> "SELECT * FROM items WHERE ((name || comment) LIKE 'Acme%')"
168
168
 
169
169
  Array#sql_string_join also takes a join argument:
170
170
 
171
- items.filter([:name, :comment].sql_string_join(' ').like('%acme%').sql
171
+ items.filter([:name, :comment].sql_string_join(' ').like('%acme%')).sql
172
172
  #=> "SELECT * FROM items WHERE ((name || ' ' || comment) LIKE 'Acme%')"
173
173
 
174
- == Filtering using expressions with blocks
175
-
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
-
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.
186
-
187
- To filter with a block, supply a block to filter with the appropriate ruby code:
188
-
189
- items.filter{:price < 100}.sql
190
- #=> "SELECT * FROM items WHERE (price < 100)
191
-
192
- Sequel is smart enough to literalize values correctly, even if you compare against arrays or ranges:
193
-
194
- items.filter{:category == 'ruby'}.sql
195
- #=> "SELECT * FROM items WHERE (category = 'ruby')"
196
-
197
- items.filter{:category == ['ruby', 'other']}.sql
198
- #=> "SELECT * FROM items WHERE (category IN ('ruby', 'other'))"
199
-
200
- items.filter{:price == (100..200)}.sql
201
- #=> "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
202
-
203
- === Negating conditions
204
-
205
- You can use the negation operator (!) anywhere:
206
-
207
- items.filter{:category != 'ruby'}.sql
208
- #=> "SELECT * FROM items WHERE NOT (category = 'ruby')"
209
-
210
- items.filter{!:active}.sql
211
- #=> "SELECT * FROM items WHERE (active = 'f')"
212
-
213
- === Comparing against column references
214
-
215
- You can also compare against other columns:
216
-
217
- items.filter{:credit > :debit}.sql
218
- #=> "SELECT * FROM items WHERE (credit > debit)
219
-
220
- Or against SQL functions:
221
-
222
- items.filter{:price < :max[:price] + 100}.sql
223
- #=> "SELECT * FROM items WHERE (price < (max(price) + 100))"
224
-
225
- === Concatenating conditions with logical operators
226
-
227
- Expressions can be nested and combined using logical operators:
228
-
229
- items.filter{(:price < 100 && :age < 27) || :category == 'ruby'}.sql
230
- #=> "SELECT * FROM items WHERE (((price < 100) AND (age < 27)) OR (category = 'ruby'))"
231
-
232
- Another way to concatenate conditions is to specify each condition as a separate statement:
233
-
234
- items.filter do
235
- :price < 100
236
- :category == 'ruby'
237
- end.sql
238
- #=> "SELECT * FROM items WHERE ((price < 100) AND (category = 'ruby'))"
239
-
240
- === Special methods
241
-
242
- Inside blocks, Sequel recognizes the special methods nil/nil?, in/in? and like/like?:
243
-
244
- items.filter{:price.nil?}.sql
245
- #=> "SELECT * FROM items WHERE (price IS NULL)"
246
-
247
- items.filter{:category.in ['ruby', 'other']}.sql
248
- #=> "SELECT * FROM items WHERE (category IN ('ruby', 'other'))"
249
-
250
- items.filter{:price.in 100..200}.sql
251
- #=> "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
252
-
253
- items.filter{:category.like 'ruby%'}.sql
254
- #=> "SELECT * FROM items WHERE (category LIKE 'ruby%')"
255
-
256
174
  == Filtering using sub-queries
257
175
 
258
176
  One of the best features of Sequel is the ability to use datasets as sub-queries. Sub-queries can be very useful for filtering records, and many times provide a simpler alternative to table joins. Sub-queries can be used in all forms of filters:
data/lib/sequel_core.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  require f
3
3
  end
4
4
  %w"core_ext sql core_sql connection_pool exceptions pretty_table
5
- dataset migration schema database worker object_graph deprecated".each do |f|
5
+ dataset migration schema database worker object_graph".each do |f|
6
6
  require "sequel_core/#{f}"
7
7
  end
8
8
 
@@ -13,8 +13,8 @@ end
13
13
  #
14
14
  # DB = Sequel.sqlite # Memory database
15
15
  # DB = Sequel.sqlite('blog.db')
16
- # DB = Sequel.postgres('database_name', :user=>'user', \
17
- # :password=>'password', :host=>'host', :port=>5432, \
16
+ # DB = Sequel.postgres('database_name', :user=>'user',
17
+ # :password=>'password', :host=>'host', :port=>5432,
18
18
  # :max_connections=>10)
19
19
  #
20
20
  # If a block is given to these methods, it is passed the opened Database
@@ -27,20 +27,16 @@ end
27
27
  #
28
28
  # Sequel.datetime_class = DateTime
29
29
  #
30
- # Sequel can either use ParseTree for block filters (deprecated but works),
31
- # or it can use the block filter syntax inside block filters (which will
32
- # be the only behavior allowed in Sequel 2.2). To set it not to use
33
- # ParseTree filters:
30
+ # Sequel converts the column type tinyint to a boolean by default,
31
+ # you can override the conversion to use tinyint as an integer:
34
32
  #
35
- # Sequel.use_parse_tree = false
33
+ # Sequel.convert_tinyint_to_bool = false
36
34
  module Sequel
37
35
  @datetime_class = Time
38
- @use_parse_tree = !defined?(SEQUEL_NO_PARSE_TREE)
39
-
36
+ @convert_tinyint_to_bool = true
37
+
40
38
  metaattr_accessor :datetime_class
41
- metaattr_accessor :use_parse_tree
42
-
43
- Deprecation.deprecation_message_stream = $stderr
39
+ metaattr_accessor :convert_tinyint_to_bool
44
40
 
45
41
  # Creates a new database object based on the supplied connection string
46
42
  # and optional arguments. The specified scheme determines the database
@@ -97,6 +93,17 @@ module Sequel
97
93
  def self.single_threaded=(value)
98
94
  Database.single_threaded = value
99
95
  end
96
+
97
+ # Always returns false, since ParseTree support has been removed.
98
+ def self.use_parse_tree
99
+ false
100
+ end
101
+
102
+ # Raises an error if attempting to turn ParseTree support on (since it no longer exists).
103
+ # Otherwise, is a no-op.
104
+ def self.use_parse_tree=(val)
105
+ raise(Error, 'ParseTree support has been removed from Sequel') if val
106
+ end
100
107
 
101
108
  ### Private Class Methods ###
102
109
 
@@ -4,7 +4,7 @@ require 'mysql'
4
4
  class Mysql::Result
5
5
  MYSQL_TYPES = {
6
6
  0 => :to_d, # MYSQL_TYPE_DECIMAL
7
- #1 => :to_i, # MYSQL_TYPE_TINY
7
+ 1 => :to_i, # MYSQL_TYPE_TINY
8
8
  2 => :to_i, # MYSQL_TYPE_SHORT
9
9
  3 => :to_i, # MYSQL_TYPE_LONG
10
10
  4 => :to_f, # MYSQL_TYPE_FLOAT
@@ -22,11 +22,11 @@ class Mysql::Result
22
22
  # 16 => :to_s, # MYSQL_TYPE_BIT
23
23
  246 => :to_d, # MYSQL_TYPE_NEWDECIMAL
24
24
  247 => :to_i, # MYSQL_TYPE_ENUM
25
- 248 => :to_i # MYSQL_TYPE_SET
26
- # 249 => :to_s, # MYSQL_TYPE_TINY_BLOB
27
- # 250 => :to_s, # MYSQL_TYPE_MEDIUM_BLOB
28
- # 251 => :to_s, # MYSQL_TYPE_LONG_BLOB
29
- # 252 => :to_s, # MYSQL_TYPE_BLOB
25
+ 248 => :to_i, # MYSQL_TYPE_SET
26
+ 249 => :to_blob, # MYSQL_TYPE_TINY_BLOB
27
+ 250 => :to_blob, # MYSQL_TYPE_MEDIUM_BLOB
28
+ 251 => :to_blob, # MYSQL_TYPE_LONG_BLOB
29
+ 252 => :to_blob, # MYSQL_TYPE_BLOB
30
30
  # 253 => :to_s, # MYSQL_TYPE_VAR_STRING
31
31
  # 254 => :to_s, # MYSQL_TYPE_STRING
32
32
  # 255 => :to_s # MYSQL_TYPE_GEOMETRY
@@ -34,7 +34,7 @@ class Mysql::Result
34
34
 
35
35
  def convert_type(v, type)
36
36
  if v
37
- if type == 1
37
+ if type == 1 && Sequel.convert_tinyint_to_bool
38
38
  # We special case tinyint here to avoid adding
39
39
  # a method to an ancestor of Fixnum
40
40
  v.to_i == 0 ? false : true
@@ -69,7 +69,7 @@ class Mysql::Result
69
69
  end
70
70
  end
71
71
 
72
- def each_hash(with_table = nil)
72
+ def sequel_each_hash(with_table = nil)
73
73
  c = columns
74
74
  while row = fetch_row
75
75
  h = {}
@@ -77,6 +77,7 @@ class Mysql::Result
77
77
  yield h
78
78
  end
79
79
  end
80
+
80
81
  end
81
82
 
82
83
  module Sequel
@@ -388,7 +389,7 @@ module Sequel
388
389
  opts = opts ? @opts.merge(opts) : @opts
389
390
 
390
391
  if order = opts[:order]
391
- sql << " ORDER BY #{column_list(order)}"
392
+ sql << " ORDER BY #{expression_list(order)}"
392
393
  end
393
394
  if limit = opts[:limit]
394
395
  sql << " LIMIT #{limit}"
@@ -442,7 +443,7 @@ module Sequel
442
443
  opts = opts ? @opts.merge(opts) : @opts
443
444
 
444
445
  if order = opts[:order]
445
- sql << " ORDER BY #{column_list(order)}"
446
+ sql << " ORDER BY #{expression_list(order)}"
446
447
  end
447
448
  if limit = opts[:limit]
448
449
  sql << " LIMIT #{limit}"
@@ -470,7 +471,7 @@ module Sequel
470
471
  def fetch_rows(sql)
471
472
  @db.execute_select(sql) do |r|
472
473
  @columns = r.columns
473
- r.each_hash {|row| yield row}
474
+ r.sequel_each_hash {|row| yield row}
474
475
  end
475
476
  self
476
477
  end
@@ -38,7 +38,7 @@ module Sequel
38
38
  select_columns = columns ? column_list(columns) : WILDCARD
39
39
 
40
40
  if distinct = opts[:distinct]
41
- distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{column_list(distinct)})"
41
+ distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{expression_list(distinct)})"
42
42
  sql = "SELECT #{top}#{distinct_clause} #{select_columns}"
43
43
  else
44
44
  sql = "SELECT #{top}#{select_columns}"
@@ -62,11 +62,11 @@ module Sequel
62
62
  end
63
63
 
64
64
  if group = opts[:group]
65
- sql << " GROUP BY #{column_list(group)}"
65
+ sql << " GROUP BY #{expression_list(group)}"
66
66
  end
67
67
 
68
68
  if order = opts[:order]
69
- sql << " ORDER BY #{column_list(order)}"
69
+ sql << " ORDER BY #{expression_list(order)}"
70
70
  end
71
71
 
72
72
  if having = opts[:having]
@@ -143,7 +143,7 @@ module Sequel
143
143
  end
144
144
 
145
145
  if group = opts[:group]
146
- sql << " GROUP BY #{column_list(group)}"
146
+ sql << " GROUP BY #{expression_list(group)}"
147
147
  end
148
148
 
149
149
  if having = opts[:having]
@@ -162,7 +162,7 @@ module Sequel
162
162
  end
163
163
 
164
164
  if order = opts[:order]
165
- sql << " ORDER BY #{column_list(order)}"
165
+ sql << " ORDER BY #{expression_list(order)}"
166
166
  end
167
167
 
168
168
  if limit = opts[:limit]
@@ -4,7 +4,41 @@ rescue LoadError => e
4
4
  begin
5
5
  require 'postgres'
6
6
  class PGconn
7
- metaalias :escape_string, :escape unless self.respond_to?(:escape_string)
7
+ unless method_defined?(:escape_string)
8
+ if self.respond_to?(:escape)
9
+ def escape_string(str)
10
+ self.class.escape(str)
11
+ end
12
+ else
13
+ def escape_string(obj)
14
+ raise Sequel::Error, "string escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
15
+ end
16
+ end
17
+ end
18
+ unless method_defined?(:escape_bytea)
19
+ if self.respond_to?(:escape_bytea)
20
+ def escape_bytea(obj)
21
+ self.class.escape_bytea(obj)
22
+ end
23
+ else
24
+ begin
25
+ require 'postgres-pr/typeconv/conv'
26
+ require 'postgres-pr/typeconv/bytea'
27
+ extend Postgres::Conversion
28
+ def escape_bytea(obj)
29
+ self.class.encode_bytea(obj)
30
+ end
31
+ metaalias :unescape_bytea, :decode_bytea
32
+ rescue
33
+ def escape_bytea(obj)
34
+ raise Sequel::Error, "bytea escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
35
+ end
36
+ def self.unescape_bytea(obj)
37
+ raise Sequel::Error, "bytea unescaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
38
+ end
39
+ end
40
+ end
41
+ end
8
42
  alias_method :finish, :close unless method_defined?(:finish)
9
43
  end
10
44
  class PGresult
@@ -22,24 +56,6 @@ end
22
56
  module Sequel
23
57
  module Postgres
24
58
  class Adapter < ::PGconn
25
- # the pure-ruby postgres adapter does not have a quote method.
26
- TRUE = 'true'.freeze
27
- FALSE = 'false'.freeze
28
- NULL = 'NULL'.freeze
29
-
30
- def self.quote(obj)
31
- case obj
32
- when TrueClass
33
- TRUE
34
- when FalseClass
35
- FALSE
36
- when NilClass
37
- NULL
38
- else
39
- "'#{escape_string(obj.to_s)}'"
40
- end
41
- end
42
-
43
59
  def connected?
44
60
  status == Adapter::CONNECTION_OK
45
61
  end
@@ -160,7 +176,7 @@ module Sequel
160
176
 
161
177
  PG_TYPES = {
162
178
  16 => lambda{ |s| Adapter.string_to_bool(s) }, # boolean
163
- 17 => lambda{ |s| Adapter.unescape_bytea(s) }, # bytea
179
+ 17 => lambda{ |s| Adapter.unescape_bytea(s).to_blob }, # bytea
164
180
  20 => lambda{ |s| s.to_i }, # int8
165
181
  21 => lambda{ |s| s.to_i }, # int2
166
182
  22 => lambda{ |s| s.to_i }, # int2vector
@@ -245,9 +261,7 @@ module Sequel
245
261
  result = conn.last_insert_id(table)
246
262
  return result if result
247
263
  rescue PGError => e
248
- # An error could occur if the inserted values include a primary key
249
- # value, while the primary key is serial.
250
- raise Error.new(e.message =~ RE_CURRVAL_ERROR ? "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?" : e.message)
264
+ raise(Error, e.message) unless RE_CURRVAL_ERROR.match(e.message)
251
265
  end
252
266
 
253
267
  case values
@@ -310,7 +324,7 @@ module Sequel
310
324
  end
311
325
  begin
312
326
  conn.transaction_depth += 1
313
- yield
327
+ yield conn
314
328
  rescue ::Exception => e
315
329
  if conn.transaction_depth > 1
316
330
  log_info(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
@@ -393,17 +407,23 @@ module Sequel
393
407
  end
394
408
 
395
409
  PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
410
+ BOOL_FALSE = 'false'.freeze
411
+ BOOL_TRUE = 'true'.freeze
396
412
 
397
413
  def literal(v)
398
414
  case v
399
415
  when LiteralString
400
416
  v
401
- when String, TrueClass, FalseClass
402
- Adapter.quote(v)
417
+ when String
418
+ db.synchronize{|c| "'#{SQL::Blob === v ? c.escape_bytea(v) : c.escape_string(v)}'"}
403
419
  when Time
404
420
  "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
405
421
  when DateTime
406
422
  "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
423
+ when TrueClass
424
+ BOOL_TRUE
425
+ when FalseClass
426
+ BOOL_FALSE
407
427
  else
408
428
  super
409
429
  end