sequel 0.1.7 → 0.1.8

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.
data/lib/sequel/dbi.rb CHANGED
@@ -44,10 +44,11 @@ module Sequel
44
44
  end
45
45
  end
46
46
 
47
- def each(opts = nil, &block)
47
+ def fetch_rows(sql, &block)
48
48
  @db.synchronize do
49
- s = @db.execute select_sql(opts)
49
+ s = @db.execute sql
50
50
  begin
51
+ @columns = stmt.column_names.map {|c| c.to_sym}
51
52
  s.fetch {|r| yield hash_row(s, r)}
52
53
  ensure
53
54
  s.finish rescue nil
@@ -57,8 +58,8 @@ module Sequel
57
58
  end
58
59
 
59
60
  def hash_row(stmt, row)
60
- stmt.column_names.inject({}) do |m, n|
61
- m[n.to_sym] = row.shift
61
+ @columns.inject({}) do |m, c|
62
+ m[c] = row.shift
62
63
  m
63
64
  end
64
65
  end
data/lib/sequel/model.rb CHANGED
@@ -26,14 +26,14 @@ module Sequel
26
26
  raise SequelError, "Database not specified for #{self}."
27
27
  end
28
28
  @dataset = db[table_name]
29
- @dataset.model_class = self
29
+ @dataset.set_model(self)
30
30
  @dataset
31
31
  end
32
32
 
33
33
  def self.set_dataset(ds)
34
34
  @db = ds.db
35
35
  @dataset = ds
36
- @dataset.model_class = self
36
+ @dataset.set_model(self)
37
37
  end
38
38
 
39
39
  def self.cache_by(column, expiration)
data/lib/sequel/mysql.rb CHANGED
@@ -48,15 +48,6 @@ module Sequel
48
48
  end
49
49
 
50
50
  class Dataset < Sequel::Dataset
51
- def each(opts = nil, &block)
52
- query_each(select_sql(opts), true, &block)
53
- self
54
- end
55
-
56
- def count(opts = nil)
57
- query_single_value(count_sql(opts)).to_i
58
- end
59
-
60
51
  def insert(*values)
61
52
  @db.execute_insert(insert_sql(*values))
62
53
  end
@@ -69,15 +60,12 @@ module Sequel
69
60
  @db.execute_affected(delete_sql(opts))
70
61
  end
71
62
 
72
- def query_each(sql, use_model_class = false)
63
+ def fetch_rows(sql)
73
64
  @db.synchronize do
74
65
  result = @db.execute(sql)
75
66
  begin
76
- if use_model_class && @model_class
77
- result.each_hash {|r| yield @model_class.new(r)}
78
- else
79
- result.each_hash {|r| yield r}
80
- end
67
+ fetch_columns(result)
68
+ result.each_hash {|r| yield r}
81
69
  ensure
82
70
  result.free
83
71
  end
@@ -85,39 +73,8 @@ module Sequel
85
73
  self
86
74
  end
87
75
 
88
- def single_record(opts = nil)
89
- query_single(select_sql(opts), true)
90
- end
91
-
92
- def single_value(opts = nil)
93
- query_single_value(select_sql(opts))
94
- end
95
-
96
- def query_single(sql, use_model_class = false)
97
- @db.synchronize do
98
- result = @db.execute(sql)
99
- begin
100
- if use_model_class && @model_class
101
- row = @model_class.new(result.fetch_hash)
102
- else
103
- row = result.fetch_hash
104
- end
105
- ensure
106
- result.free
107
- end
108
- row
109
- end
110
- end
111
-
112
- def query_single_value(sql)
113
- @db.synchronize do
114
- result = @db.execute(sql)
115
- begin
116
- return result.fetch_hash.values[0]
117
- ensure
118
- result.free
119
- end
120
- end
76
+ def fetch_columns(result)
77
+ @columns = result.fetch_fields.map {|c| c.name.to_sym}
121
78
  end
122
79
  end
123
80
  end
data/lib/sequel/odbc.rb CHANGED
@@ -44,12 +44,13 @@ module Sequel
44
44
  end
45
45
  end
46
46
 
47
- def each(opts = nil, &block)
47
+ def fetch_rows(sql, &block)
48
48
  @db.synchronize do
49
- s = @db.execute select_sql(opts)
49
+ s = @db.execute select_sql(sql)
50
50
  begin
51
- fetch_rows(s, &block)
52
- s.fetch {|r| yield hash_row(s, r)}
51
+ @columns = s.columns(true).map {|c| c.name.to_sym}
52
+ rows = s.fetch_all
53
+ rows.each {|row| yield hash_row(row)}
53
54
  ensure
54
55
  s.drop unless s.nil? rescue nil
55
56
  end
@@ -57,16 +58,10 @@ module Sequel
57
58
  self
58
59
  end
59
60
 
60
- def fetch_rows(stmt)
61
- columns = stmt.columns(true).map {|c| c.name.to_sym}
62
- rows = stmt.fetch_all
63
- rows.each {|row| yield hash_row(stmt, columns, row)}
64
- end
65
-
66
- def hash_row(stmt, columns, row)
61
+ def hash_row(row)
67
62
  hash = {}
68
63
  row.each_with_index do |v, idx|
69
- hash[columns[idx]] = convert_odbc_value(v)
64
+ hash[@columns[idx]] = convert_odbc_value(v)
70
65
  end
71
66
  hash
72
67
  end
@@ -277,19 +277,6 @@ module Sequel
277
277
 
278
278
  def literal(v)
279
279
  case v
280
- # when String: "'%s'" % v.gsub(/'/, "''")
281
- # when Integer, Float: v.to_s
282
- # when NilClass: NULL
283
- # when Symbol: v.to_field_name
284
- # when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
285
- # when Time: v.strftime(TIMESTAMP_FORMAT)
286
- # when Date: v.strftime(DATE_FORMAT)
287
- # when Dataset: "(#{v.sql})"
288
- # when true: TRUE
289
- # when false: FALSE
290
- # else
291
- # raise SequelError, "can't express #{v.inspect}:#{v.class} as a SQL literal"
292
- # end
293
280
  when String, Fixnum, Float, TrueClass, FalseClass: PGconn.quote(v)
294
281
  else
295
282
  super
@@ -309,11 +296,6 @@ module Sequel
309
296
  end
310
297
  end
311
298
 
312
- def each(opts = nil, &block)
313
- query_each(select_sql(opts), true, &block)
314
- self
315
- end
316
-
317
299
  FOR_UPDATE = ' FOR UPDATE'.freeze
318
300
  FOR_SHARE = ' FOR SHARE'.freeze
319
301
 
@@ -328,19 +310,28 @@ module Sequel
328
310
  end
329
311
 
330
312
  def for_update
331
- dup_merge(:lock => :update)
313
+ clone_merge(:lock => :update)
332
314
  end
333
315
 
334
316
  def for_share
335
- dup_merge(:lock => :share)
317
+ clone_merge(:lock => :share)
336
318
  end
337
319
 
338
320
  EXPLAIN = 'EXPLAIN '.freeze
321
+ EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
339
322
  QUERY_PLAN = 'QUERY PLAN'.to_sym
340
323
 
341
324
  def explain(opts = nil)
342
325
  analysis = []
343
- query_each(select_sql(EXPLAIN + select_sql(opts))) do |r|
326
+ fetch_rows(EXPLAIN + select_sql(opts)) do |r|
327
+ analysis << r[QUERY_PLAN]
328
+ end
329
+ analysis.join("\r\n")
330
+ end
331
+
332
+ def analyze(opts = nil)
333
+ analysis = []
334
+ fetch_rows(EXPLAIN_ANALYZE + select_sql(opts)) do |r|
344
335
  analysis << r[QUERY_PLAN]
345
336
  end
346
337
  analysis.join("\r\n")
@@ -399,20 +390,11 @@ module Sequel
399
390
  end
400
391
  end
401
392
 
402
- def single_record(opts = nil)
403
- query_single(select_sql(opts), true)
404
- end
405
-
406
- def single_value(opts = nil)
407
- query_single_value(select_sql(opts))
408
- end
409
-
410
- def query_each(sql, use_model_class = false, &block)
393
+ def fetch_rows(sql, &block)
411
394
  @db.synchronize do
412
395
  result = @db.execute(sql)
413
396
  begin
414
- # each_row(result, use_model_class, &block)
415
- conv = row_converter(result, use_model_class)
397
+ conv = row_converter(result)
416
398
  result.each {|r| yield conv[r]}
417
399
  ensure
418
400
  result.clear
@@ -420,54 +402,25 @@ module Sequel
420
402
  end
421
403
  end
422
404
 
423
- def query_single(sql, use_model_class = false)
424
- @db.synchronize do
425
- result = @db.execute(sql)
426
- begin
427
- row = nil
428
- conv = row_converter(result, use_model_class)
429
- result.each {|r| row = conv.call(r)}
430
- ensure
431
- result.clear
432
- end
433
- row
434
- end
435
- end
436
-
437
- def query_single_value(sql)
438
- @db.synchronize do
439
- result = @db.execute(sql)
440
- begin
441
- value = result.getvalue(0, 0)
442
- if value
443
- value = value.send(PG_TYPES[result.type(0)])
444
- end
445
- ensure
446
- result.clear
447
- end
448
- value
449
- end
450
- end
451
-
452
405
  @@converters_mutex = Mutex.new
453
406
  @@converters = {}
454
407
 
455
- def row_converter(result, use_model_class)
408
+ def row_converter(result)
456
409
  fields = []; translators = []
457
410
  result.fields.each_with_index do |f, idx|
458
411
  fields << f.to_sym
459
412
  translators << PG_TYPES[result.type(idx)]
460
413
  end
461
- klass = use_model_class && @model_class
414
+ @columns = fields
462
415
 
463
416
  # create result signature and memoize the converter
464
- sig = [fields, translators, klass].hash
417
+ sig = [fields, translators].hash
465
418
  @@converters_mutex.synchronize do
466
- @@converters[sig] ||= compile_converter(fields, translators, klass)
419
+ @@converters[sig] ||= compile_converter(fields, translators)
467
420
  end
468
421
  end
469
422
 
470
- def compile_converter(fields, translators, klass)
423
+ def compile_converter(fields, translators)
471
424
  used_fields = []
472
425
  kvs = []
473
426
  fields.each_with_index do |field, idx|
@@ -475,16 +428,12 @@ module Sequel
475
428
  used_fields << field
476
429
 
477
430
  if translator = translators[idx]
478
- kvs << ":#{field} => ((t = r[#{idx}]) ? t.#{translator} : nil)"
431
+ kvs << ":\"#{field}\" => ((t = r[#{idx}]) ? t.#{translator} : nil)"
479
432
  else
480
- kvs << ":#{field} => r[#{idx}]"
433
+ kvs << ":\"#{field}\" => r[#{idx}]"
481
434
  end
482
435
  end
483
- if klass
484
- eval("lambda {|r| #{klass}.new(#{kvs.join(COMMA_SEPARATOR)})}")
485
- else
486
- eval("lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}")
487
- end
436
+ eval("lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}")
488
437
  end
489
438
  end
490
439
  end
data/lib/sequel/sqlite.rb CHANGED
@@ -44,20 +44,52 @@ module Sequel
44
44
  @pool.hold {|conn| conn.get_first_value(sql)}
45
45
  end
46
46
 
47
- def result_set(sql, model_class, &block)
47
+ def query(sql, &block)
48
48
  @logger.info(sql) if @logger
49
- @pool.hold do |conn|
50
- conn.query(sql) do |result|
51
- columns = result.columns
52
- column_count = columns.size
53
- result.each do |values|
54
- row = {}
55
- column_count.times {|i| row[columns[i].to_sym] = values[i]}
56
- block.call(model_class ? model_class.new(row) : row)
57
- end
58
- end
59
- end
49
+ @pool.hold {|conn| conn.query(sql, &block)}
50
+ end
51
+
52
+ def pragma_get(name)
53
+ single_value("PRAGMA #{name};")
60
54
  end
55
+
56
+ def pragma_set(name, value)
57
+ execute("PRAGMA #{name} = #{value};")
58
+ end
59
+
60
+ AUTO_VACUUM = {'0' => :none, '1' => :full, '2' => :incremental}.freeze
61
+
62
+ def auto_vacuum
63
+ AUTO_VACUUM[pragma_get(:auto_vacuum)]
64
+ end
65
+
66
+ def auto_vacuum=(value)
67
+ value = AUTO_VACUUM.index(value) || (raise SequelError, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
68
+ pragma_set(:auto_vacuum, value)
69
+ end
70
+
71
+ SYNCHRONOUS = {'0' => :off, '1' => :normal, '2' => :full}.freeze
72
+
73
+ def synchronous
74
+ SYNCHRONOUS[pragma_get(:synchronous)]
75
+ end
76
+
77
+ def synchronous=(value)
78
+ value = SYNCHRONOUS.index(value) || (raise SequelError, "Invalid value for synchronous option. Please specify one of :off, :normal, :full.")
79
+ pragma_set(:synchronous, value)
80
+ end
81
+
82
+ TEMP_STORE = {'0' => :default, '1' => :file, '2' => :memory}.freeze
83
+
84
+ def temp_store
85
+ TEMP_STORE[pragma_get(:temp_store)]
86
+ end
87
+
88
+ def temp_store=(value)
89
+ value = TEMP_STORE.index(value) || (raise SequelError, "Invalid value for temp_store option. Please specify one of :default, :file, :memory.")
90
+ pragma_set(:temp_store, value)
91
+ end
92
+
61
93
  end
62
94
 
63
95
  class Dataset < Sequel::Dataset
@@ -69,9 +101,16 @@ module Sequel
69
101
  end
70
102
  end
71
103
 
72
- def each(opts = nil, &block)
73
- @db.result_set(select_sql(opts), @model_class, &block)
74
- self
104
+ def fetch_rows(sql, &block)
105
+ @db.query(sql) do |result|
106
+ @columns = result.columns.map {|c| c.to_sym}
107
+ column_count = @columns.size
108
+ result.each do |values|
109
+ row = {}
110
+ column_count.times {|i| row[@columns[i]] = values[i]}
111
+ block.call(row)
112
+ end
113
+ end
75
114
  end
76
115
 
77
116
  def insert(*values)
@@ -0,0 +1,104 @@
1
+ require File.join(File.dirname(__FILE__), '../../lib/sequel/sqlite')
2
+
3
+ SQLITE_DB = Sequel('sqlite:/')
4
+ SQLITE_DB.create_table :items do
5
+ integer :id, :primary_key => true, :auto_increment => true
6
+ text :name
7
+ float :value
8
+ end
9
+
10
+ context "An SQLite database" do
11
+ setup do
12
+ @db = Sequel('sqlite:/')
13
+ end
14
+
15
+ specify "should provide a list of existing tables" do
16
+ @db.tables.should == []
17
+
18
+ @db.create_table :testing do
19
+ text :name
20
+ end
21
+ @db.tables.should include(:testing)
22
+ end
23
+
24
+ specify "should support getting pragma values" do
25
+ @db.pragma_get(:auto_vacuum).should == '0'
26
+ end
27
+
28
+ specify "should support setting pragma values" do
29
+ @db.pragma_set(:auto_vacuum, '1')
30
+ @db.pragma_get(:auto_vacuum).should == '1'
31
+ end
32
+
33
+ specify "should support getting and setting the auto_vacuum pragma" do
34
+ @db.auto_vacuum = :full
35
+ @db.auto_vacuum.should == :full
36
+ @db.auto_vacuum = :none
37
+ @db.auto_vacuum.should == :none
38
+
39
+ proc {@db.auto_vacuum = :invalid}.should raise_error(SequelError)
40
+ end
41
+
42
+ specify "should support getting and setting the synchronous pragma" do
43
+ @db.synchronous = :off
44
+ @db.synchronous.should == :off
45
+ @db.synchronous = :normal
46
+ @db.synchronous.should == :normal
47
+ @db.synchronous = :full
48
+ @db.synchronous.should == :full
49
+
50
+ proc {@db.synchronous = :invalid}.should raise_error(SequelError)
51
+ end
52
+
53
+ specify "should support getting and setting the temp_store pragma" do
54
+ @db.temp_store = :default
55
+ @db.temp_store.should == :default
56
+ @db.temp_store = :file
57
+ @db.temp_store.should == :file
58
+ @db.temp_store = :memory
59
+ @db.temp_store.should == :memory
60
+
61
+ proc {@db.temp_store = :invalid}.should raise_error(SequelError)
62
+ end
63
+ end
64
+
65
+ context "An SQLite dataset" do
66
+ setup do
67
+ @d = SQLITE_DB[:items]
68
+ @d.delete # remove all records
69
+ end
70
+
71
+ specify "should return the correct record count" do
72
+ @d.count.should == 0
73
+ @d << {:name => 'abc', :value => 1.23}
74
+ @d << {:name => 'abc', :value => 4.56}
75
+ @d << {:name => 'def', :value => 7.89}
76
+ @d.count.should == 3
77
+ end
78
+
79
+ specify "should return the last inserted id when inserting records" do
80
+ id = @d << {:name => 'abc', :value => 1.23}
81
+ id.should == @d.first[:id]
82
+ end
83
+
84
+ specify "should update records correctly" do
85
+ @d << {:name => 'abc', :value => 1.23}
86
+ @d << {:name => 'abc', :value => 4.56}
87
+ @d << {:name => 'def', :value => 7.89}
88
+ @d.filter(:name => 'abc').update(:value => 5.3)
89
+
90
+ # the third record should stay the same
91
+ @d[:name => 'def'][:value].should == 7.89
92
+ @d.filter(:value => 5.3).count.should == 2
93
+ end
94
+
95
+ specify "should delete records correctly" do
96
+ @d << {:name => 'abc', :value => 1.23}
97
+ @d << {:name => 'abc', :value => 4.56}
98
+ @d << {:name => 'def', :value => 7.89}
99
+ @d.filter(:name => 'abc').delete
100
+
101
+ @d.count.should == 1
102
+ @d.first[:name].should == 'def'
103
+ end
104
+ end