extralite 2.6 → 2.7.1

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/extralite.rb CHANGED
@@ -29,10 +29,9 @@ module Extralite
29
29
  class ParameterError < Error
30
30
  end
31
31
 
32
- # An SQLite database
33
32
  class Database
34
33
  # @!visibility private
35
- TABLES_SQL = <<~SQL
34
+ TABLES_SQL = (<<~SQL).freeze
36
35
  SELECT name FROM %<db>s.sqlite_master
37
36
  WHERE type ='table'
38
37
  AND name NOT LIKE 'sqlite_%%';
@@ -46,10 +45,11 @@ module Extralite
46
45
  # @param db [String] name of attached database
47
46
  # @return [Array] list of tables
48
47
  def tables(db = 'main')
49
- query_single_column(format(TABLES_SQL, db: db))
48
+ query_argv(format(TABLES_SQL, db: db))
50
49
  end
51
50
 
52
- # Gets or sets one or more pragmas:
51
+ # Gets or sets one or more database pragmas. For a list of available pragmas
52
+ # see: https://sqlite.org/pragma.html#toc
53
53
  #
54
54
  # db.pragma(:cache_size) # get
55
55
  # db.pragma(cache_size: -2000) # set
@@ -74,21 +74,29 @@ module Extralite
74
74
  # raise if db.query_single_value('select x from bar') > 42
75
75
  # end
76
76
  #
77
- # @param mode [Symbol, String] transaction mode (deferred, immediate or exclusive). Defaults to immediate.
77
+ # For more information on transactions see:
78
+ # https://sqlite.org/lang_transaction.html
79
+ #
80
+ # @param mode [Symbol, String] transaction mode (deferred, immediate or exclusive).
78
81
  # @return [Any] the given block's return value
79
82
  def transaction(mode = :immediate)
80
- execute "begin #{mode} transaction"
81
-
82
83
  abort = false
84
+ execute "begin #{mode} transaction"
83
85
  yield self
84
86
  rescue => e
85
87
  abort = true
86
- raise unless e.is_a?(Rollback)
88
+ e.is_a?(Rollback) ? nil : raise
87
89
  ensure
88
90
  execute(abort ? 'rollback' : 'commit')
89
91
  end
90
92
 
91
- # Creates a savepoint with the given name.
93
+ # Creates a savepoint with the given name. For more information on
94
+ # savepoints see: https://sqlite.org/lang_savepoint.html
95
+ #
96
+ # db.savepoint(:savepoint1)
97
+ # db.execute('insert into foo values (42)')
98
+ # db.rollback_to(:savepoint1)
99
+ # db.release(:savepoint1)
92
100
  #
93
101
  # @param name [String, Symbol] savepoint name
94
102
  # @return [Extralite::Database] database
@@ -97,7 +105,8 @@ module Extralite
97
105
  self
98
106
  end
99
107
 
100
- # Release a savepoint with the given name.
108
+ # Release a savepoint with the given name. For more information on
109
+ # savepoints see: https://sqlite.org/lang_savepoint.html
101
110
  #
102
111
  # @param name [String, Symbol] savepoint name
103
112
  # @return [Extralite::Database] database
@@ -106,7 +115,8 @@ module Extralite
106
115
  self
107
116
  end
108
117
 
109
- # Rolls back changes to a savepoint with the given name.
118
+ # Rolls back changes to a savepoint with the given name. For more
119
+ # information on savepoints see: https://sqlite.org/lang_savepoint.html
110
120
  #
111
121
  # @param name [String, Symbol] savepoint name
112
122
  # @return [Extralite::Database] database
@@ -116,14 +126,14 @@ module Extralite
116
126
  end
117
127
 
118
128
  # Rolls back the currently active transaction. This method should only be
119
- # called from within a block passed to Database#transaction. This method
129
+ # called from within a block passed to `Database#transaction`. This method
120
130
  # raises a Extralite::Rollback exception, which will stop execution of the
121
131
  # transaction block without propagating the exception.
122
132
  #
123
- # db.transaction do
124
- # db.execute('insert into foo (42)')
125
- # db.rollback!
126
- # end
133
+ # db.transaction do
134
+ # db.execute('insert into foo (42)')
135
+ # db.rollback!
136
+ # end
127
137
  #
128
138
  # @param name [String, Symbol] savepoint name
129
139
  # @return [Extralite::Database] database
@@ -139,7 +149,7 @@ module Extralite
139
149
  end
140
150
 
141
151
  def pragma_get(key)
142
- query_single_value("pragma #{key}")
152
+ query_single_argv("pragma #{key}")
143
153
  end
144
154
  end
145
155
 
@@ -362,7 +362,7 @@ module Sequel
362
362
  def fetch_rows(sql, &block)
363
363
  execute(sql) do |result, columns|
364
364
  self.columns = columns
365
- max = columns.size
365
+ # max = columns.size
366
366
  result.each(&block)
367
367
  end
368
368
  end
data/test/helper.rb CHANGED
@@ -7,7 +7,8 @@ require 'minitest/autorun'
7
7
  puts "sqlite3 version: #{Extralite.sqlite3_version}"
8
8
 
9
9
  IS_LINUX = RUBY_PLATFORM =~ /linux/
10
- SKIP_RACTOR_TESTS = !IS_LINUX || (RUBY_VERSION =~ /^3\.[01]/)
10
+ # Ractors are kinda flaky, there's no point in testing this
11
+ SKIP_RACTOR_TESTS = true #!IS_LINUX || (RUBY_VERSION =~ /^3\.[01]/)
11
12
 
12
13
  module Minitest::Assertions
13
14
  def assert_in_range exp_range, act
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Run on Ruby 3.3 with YJIT enabled
4
+
5
+ require 'bundler/inline'
6
+
7
+ gemfile do
8
+ source 'https://rubygems.org'
9
+ gem 'extralite', path: '..'
10
+ gem 'benchmark-ips'
11
+ end
12
+
13
+ require 'benchmark/ips'
14
+ require 'fileutils'
15
+
16
+ DB_PATH = "/tmp/extralite_sqlite3_perf-#{Time.now.to_i}-#{rand(10000)}.db"
17
+ puts "DB_PATH = #{DB_PATH.inspect}"
18
+
19
+ $extralite_db = Extralite::Database.new(DB_PATH, gvl_release_threshold: -1)
20
+
21
+ def prepare_database(count)
22
+ $extralite_db.query('create table if not exists foo (b text)')
23
+ $extralite_db.query('delete from foo')
24
+ $extralite_db.query('begin')
25
+ count.times { $extralite_db.query('insert into foo (b) values (?)', "hello#{rand(1000)}" )}
26
+ $extralite_db.query('commit')
27
+ end
28
+
29
+ class Model
30
+ def initialize(h)
31
+ @h = h
32
+ end
33
+
34
+ def values
35
+ @h
36
+ end
37
+ end
38
+
39
+ TRANSFORM = ->(b) { { b: b } }
40
+
41
+ def extralite_run_ary_map(count)
42
+ results = []
43
+ $extralite_db.query_ary('select * from foo') { |(b)| results << { b: b } }
44
+ raise unless results.size == count
45
+ end
46
+
47
+ def extralite_run_argv_map(count)
48
+ results = []
49
+ $extralite_db.query_argv('select * from foo') { |b| results << { b: b } }
50
+ raise unless results.size == count
51
+ end
52
+
53
+ def extralite_run_transform(count)
54
+ results = $extralite_db.query_argv(TRANSFORM, 'select * from foo')
55
+ raise unless results.size == count
56
+ end
57
+
58
+ [10, 1000, 100000].each do |c|
59
+ puts "Record count: #{c}"
60
+ prepare_database(c)
61
+
62
+ bm = Benchmark.ips do |x|
63
+ x.config(:time => 5, :warmup => 2)
64
+
65
+ x.report("ary_map") { extralite_run_ary_map(c) }
66
+ x.report("argv_map") { extralite_run_argv_map(c) }
67
+ x.report("transform") { extralite_run_transform(c) }
68
+
69
+ x.compare!
70
+ end
71
+ puts;
72
+ bm.entries.each { |e| puts "#{e.label}: #{(e.ips * c).round.to_i} rows/s" }
73
+ puts;
74
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Run on Ruby 3.3 with YJIT enabled
4
+
5
+ require 'bundler/inline'
6
+
7
+ gemfile do
8
+ source 'https://rubygems.org'
9
+ gem 'extralite', path: '..'
10
+ gem 'benchmark-ips'
11
+ end
12
+
13
+ require 'benchmark/ips'
14
+ require 'fileutils'
15
+
16
+ DB_PATH = "/tmp/extralite_sqlite3_perf-#{Time.now.to_i}-#{rand(10000)}.db"
17
+ puts "DB_PATH = #{DB_PATH.inspect}"
18
+
19
+ $extralite_db = Extralite::Database.new(DB_PATH, gvl_release_threshold: -1)
20
+
21
+ def prepare_database(count)
22
+ $extralite_db.query('create table if not exists foo ( a integer primary key, b text )')
23
+ $extralite_db.query('delete from foo')
24
+ $extralite_db.query('begin')
25
+ count.times { $extralite_db.query('insert into foo (b) values (?)', "hello#{rand(1000)}" )}
26
+ $extralite_db.query('commit')
27
+ end
28
+
29
+ class Model
30
+ def initialize(h)
31
+ @h = h
32
+ end
33
+
34
+ def values
35
+ @h
36
+ end
37
+ end
38
+
39
+ TRANSFORM = ->(h) { Model.new(h) }
40
+
41
+ def extralite_run_map(count)
42
+ results = $extralite_db.query('select * from foo').map(&TRANSFORM)
43
+ raise unless results.size == count
44
+ end
45
+
46
+ def extralite_run_transform(count)
47
+ results = $extralite_db.query(TRANSFORM, 'select * from foo')
48
+ raise unless results.size == count
49
+ end
50
+
51
+ [10, 1000, 100000].each do |c|
52
+ puts "Record count: #{c}"
53
+ prepare_database(c)
54
+
55
+ bm = Benchmark.ips do |x|
56
+ x.config(:time => 5, :warmup => 2)
57
+
58
+ x.report("map") { extralite_run_map(c) }
59
+ x.report("transform") { extralite_run_transform(c) }
60
+
61
+ x.compare!
62
+ end
63
+ puts;
64
+ bm.entries.each { |e| puts "#{e.label}: #{(e.ips * c).round.to_i} rows/s" }
65
+ puts;
66
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Run on Ruby 3.3 with YJIT enabled
4
+
5
+ require 'bundler/inline'
6
+ gemfile do
7
+ gem 'polyphony'
8
+ gem 'extralite', path: '.'
9
+ gem 'benchmark-ips'
10
+ end
11
+
12
+ require 'benchmark/ips'
13
+ require 'polyphony'
14
+
15
+ DB_PATH = "/tmp/extralite_sqlite3_perf-#{Time.now.to_i}-#{rand(10000)}.db"
16
+ puts "DB_PATH = #{DB_PATH.inspect}"
17
+
18
+ $db1 = Extralite::Database.new(DB_PATH, gvl_release_threshold: -1)
19
+ $db2 = Extralite::Database.new(DB_PATH, gvl_release_threshold: 0)
20
+ $db3 = Extralite::Database.new(DB_PATH)
21
+
22
+ $snooze_count = 0
23
+ $db3.on_progress(25) { $snooze_count += 1; snooze }
24
+
25
+ def prepare_database(count)
26
+ $db1.execute('create table if not exists foo ( a integer primary key, b text )')
27
+ $db1.transaction do
28
+ $db1.execute('delete from foo')
29
+ rows = count.times.map { "hello#{rand(1000)}" }
30
+ $db1.batch_execute('insert into foo (b) values (?)', rows)
31
+ end
32
+ end
33
+
34
+ def extralite_run1(count)
35
+ results = $db1.query('select * from foo')
36
+ raise unless results.size == count
37
+ end
38
+
39
+ def extralite_run2(count)
40
+ results = $db2.query('select * from foo')
41
+ raise unless results.size == count
42
+ end
43
+
44
+ def extralite_run3(count)
45
+ results = $db3.query('select * from foo')
46
+ raise unless results.size == count
47
+ end
48
+
49
+ [10, 1000, 100000].each do |c|
50
+ puts "Record count: #{c}"
51
+ prepare_database(c)
52
+
53
+ bm = Benchmark.ips do |x|
54
+ x.config(:time => 3, :warmup => 1)
55
+
56
+ x.report('GVL threshold -1') { extralite_run1(c) }
57
+ x.report('GVL threshold 0') { extralite_run2(c) }
58
+ $snooze_count = 0
59
+ x.report('on_progress 1000') { extralite_run3(c) }
60
+
61
+ x.compare!
62
+ end
63
+ puts;
64
+ bm.entries.each do |e|
65
+ score = (e.ips * c).round.to_i
66
+ if e.label == 'on_progress 1000'
67
+ snooze_rate = ($snooze_count / e.seconds).to_i
68
+ puts "#{e.label}: #{score} rows/s snoozes: #{snooze_rate} i/s"
69
+ else
70
+ puts "#{e.label}: #{score} rows/s"
71
+ end
72
+ end
73
+ puts;
74
+ end
@@ -5,7 +5,7 @@ require_relative 'helper'
5
5
  require 'date'
6
6
  require 'tempfile'
7
7
 
8
- class ChangesetTest < MiniTest::Test
8
+ class ChangesetTest < Minitest::Test
9
9
  def setup
10
10
  @db = Extralite::Database.new(':memory:')
11
11
  skip if !@db.respond_to?(:track_changes)
@@ -130,7 +130,7 @@ class ChangesetTest < MiniTest::Test
130
130
 
131
131
  def test_blob
132
132
  changeset = Extralite::Changeset.new
133
- assert_equal "", changeset.to_blob
133
+ assert_equal '', changeset.to_blob
134
134
 
135
135
  changeset.track(@db, [:t]) do
136
136
  @db.execute('insert into t values (1, 2, 3)')