sequel 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
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