turso 0.1.0 → 0.1.2
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.
- checksums.yaml +4 -4
- data/README.md +74 -0
- data/ext/turso_ruby/Cargo.lock +2990 -0
- data/ext/turso_ruby/Cargo.toml +7 -3
- data/ext/turso_ruby/extconf.rb +2 -4
- data/lib/turso/database.rb +109 -0
- data/lib/turso/result_set.rb +15 -19
- data/lib/turso/row.rb +23 -13
- data/lib/turso/statement.rb +146 -0
- data/lib/turso/transaction.rb +8 -2
- data/lib/turso/turso_ruby.so +0 -0
- data/lib/turso/version.rb +5 -0
- data/lib/turso.rb +8 -2
- metadata +22 -3
- data/lib/turso/db.rb +0 -52
data/ext/turso_ruby/Cargo.toml
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
|
|
1
3
|
[package]
|
|
2
4
|
name = "turso_ruby"
|
|
3
|
-
version = "0.1.
|
|
5
|
+
version = "0.1.2"
|
|
4
6
|
edition = "2021"
|
|
5
7
|
|
|
6
8
|
[lib]
|
|
7
9
|
crate-type = ["cdylib"]
|
|
10
|
+
path = "../../../src/lib.rs"
|
|
8
11
|
|
|
9
12
|
[dependencies]
|
|
10
13
|
magnus = { version = "0.8", features = ["rb-sys"] }
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
rb-sys = "0.9"
|
|
15
|
+
turso_sdk_kit = { path = "../../../../../sdk-kit", features = ["fts"] }
|
|
16
|
+
turso_core = { path = "../../../../../core", features = ["fts"] }
|
data/ext/turso_ruby/extconf.rb
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Turso
|
|
4
|
+
class Database
|
|
5
|
+
def initialize(path = ":memory:", **options)
|
|
6
|
+
options[:experimental_features] = normalize_experimental_features(options[:experimental_features])
|
|
7
|
+
@native = NativeDatabase.new(path, options)
|
|
8
|
+
@closed = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def normalize_experimental_features(features)
|
|
14
|
+
return nil if features.nil? || features.empty?
|
|
15
|
+
|
|
16
|
+
Array(features).join(",")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
public
|
|
20
|
+
|
|
21
|
+
def closed?
|
|
22
|
+
@closed || !@native.open?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def close
|
|
26
|
+
return if closed?
|
|
27
|
+
@native.close
|
|
28
|
+
@closed = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def prepare(sql)
|
|
32
|
+
raise Turso::Exception, "database is closed" if closed?
|
|
33
|
+
Statement.new(@native.connection.prepare(sql), @native.connection)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def execute(sql, *bind_args)
|
|
37
|
+
stmt = prepare(sql)
|
|
38
|
+
begin
|
|
39
|
+
stmt.bind(*bind_args) unless bind_args.empty?
|
|
40
|
+
stmt.run
|
|
41
|
+
ensure
|
|
42
|
+
stmt.close
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def execute_batch(sql)
|
|
47
|
+
raise Turso::Exception, "database is closed" if closed?
|
|
48
|
+
@native.connection.execute_batch(sql)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def query(sql, *bind_args)
|
|
52
|
+
stmt = prepare(sql)
|
|
53
|
+
stmt.bind(*bind_args) unless bind_args.empty?
|
|
54
|
+
ResultSet.new(stmt)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def get_first_row(sql, *bind_args)
|
|
58
|
+
stmt = prepare(sql)
|
|
59
|
+
begin
|
|
60
|
+
stmt.bind(*bind_args) unless bind_args.empty?
|
|
61
|
+
stmt.get
|
|
62
|
+
ensure
|
|
63
|
+
stmt.close
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def get_first_value(sql, *bind_args)
|
|
68
|
+
row = get_first_row(sql, *bind_args)
|
|
69
|
+
row&.first
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def transaction(mode = :deferred)
|
|
73
|
+
raise Turso::Exception, "database is closed" if closed?
|
|
74
|
+
execute("BEGIN #{mode.to_s.upcase}")
|
|
75
|
+
begin
|
|
76
|
+
result = yield self
|
|
77
|
+
execute("COMMIT")
|
|
78
|
+
result
|
|
79
|
+
rescue ::Exception
|
|
80
|
+
execute("ROLLBACK")
|
|
81
|
+
raise
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def busy_timeout=(ms)
|
|
86
|
+
@native.connection.busy_timeout = ms
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def busy_timeout
|
|
90
|
+
@native.connection.busy_timeout
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def total_changes
|
|
94
|
+
@native.total_changes
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def changes
|
|
98
|
+
@native.connection.changes
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def last_insert_rowid
|
|
102
|
+
@native.last_insert_rowid
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def interrupt
|
|
106
|
+
@native.connection.interrupt
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/turso/result_set.rb
CHANGED
|
@@ -8,28 +8,24 @@ module Turso
|
|
|
8
8
|
@statement = statement
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def
|
|
12
|
-
|
|
11
|
+
def fields
|
|
12
|
+
@statement.columns.map { |c| c[:name] }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def each(&block)
|
|
16
|
+
@statement.each(&block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def next
|
|
20
|
+
@statement.get
|
|
21
|
+
end
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
case @statement.step
|
|
17
|
-
when 1 # ROW
|
|
18
|
-
yield Row.new(columns, @statement.row)
|
|
19
|
-
when 0 # DONE
|
|
20
|
-
break
|
|
21
|
-
else
|
|
22
|
-
raise Turso::Error, "unexpected step status"
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
self
|
|
26
|
-
ensure
|
|
27
|
-
@statement.finalize
|
|
23
|
+
def reset
|
|
24
|
+
@statement.reset
|
|
28
25
|
end
|
|
29
26
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
(0...count).map { |i| @statement.column_name(i) }
|
|
27
|
+
def close
|
|
28
|
+
@statement.close
|
|
33
29
|
end
|
|
34
30
|
end
|
|
35
31
|
end
|
data/lib/turso/row.rb
CHANGED
|
@@ -2,31 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
module Turso
|
|
4
4
|
class Row
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
include Enumerable
|
|
6
|
+
|
|
7
|
+
def initialize(values, column_names)
|
|
7
8
|
@values = values
|
|
9
|
+
@column_names = column_names
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_a
|
|
13
|
+
@values.dup
|
|
8
14
|
end
|
|
9
15
|
|
|
10
16
|
def [](key)
|
|
11
17
|
case key
|
|
12
|
-
when Integer
|
|
13
|
-
|
|
18
|
+
when Integer
|
|
19
|
+
@values[key]
|
|
20
|
+
when String, Symbol
|
|
21
|
+
@values[column_index(key.to_s)]
|
|
22
|
+
else
|
|
23
|
+
raise ArgumentError, "invalid key type: #{key.class}"
|
|
14
24
|
end
|
|
15
25
|
end
|
|
16
26
|
|
|
17
|
-
def
|
|
18
|
-
@
|
|
27
|
+
def each(&block)
|
|
28
|
+
@values.each(&block)
|
|
19
29
|
end
|
|
20
30
|
|
|
21
|
-
def
|
|
22
|
-
|
|
31
|
+
def length
|
|
32
|
+
@values.length
|
|
33
|
+
end
|
|
34
|
+
alias size length
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
value = self[key]
|
|
26
|
-
return value unless value.nil?
|
|
27
|
-
return fallback.call(key) if fallback
|
|
36
|
+
private
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
def column_index(name)
|
|
39
|
+
@column_names.index(name) || raise(IndexError, "column #{name} not found")
|
|
30
40
|
end
|
|
31
41
|
end
|
|
32
42
|
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Turso
|
|
4
|
+
class Statement
|
|
5
|
+
def initialize(native_statement, native_database)
|
|
6
|
+
@native = native_statement
|
|
7
|
+
@database = native_database
|
|
8
|
+
@owner = Thread.current
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def bind(*args)
|
|
12
|
+
check_owner!
|
|
13
|
+
return self if args.empty?
|
|
14
|
+
|
|
15
|
+
if args.size == 1 && args.first.is_a?(Hash)
|
|
16
|
+
bind_hash(args.first)
|
|
17
|
+
else
|
|
18
|
+
args.each_with_index do |value, index|
|
|
19
|
+
bind_value(index + 1, value)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def run(*bind_args)
|
|
26
|
+
check_owner!
|
|
27
|
+
bind(*bind_args) unless bind_args.empty?
|
|
28
|
+
step_loop(consume_rows: false)
|
|
29
|
+
{ changes: @database.changes, last_insert_rowid: @database.last_insert_rowid }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get(*bind_args)
|
|
33
|
+
check_owner!
|
|
34
|
+
bind(*bind_args) unless bind_args.empty?
|
|
35
|
+
row = nil
|
|
36
|
+
step_loop(consume_rows: true) { |r| row ||= r; false }
|
|
37
|
+
row
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def all(*bind_args)
|
|
41
|
+
check_owner!
|
|
42
|
+
bind(*bind_args) unless bind_args.empty?
|
|
43
|
+
rows = []
|
|
44
|
+
step_loop(consume_rows: true) { |r| rows << r; true }
|
|
45
|
+
rows
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def each(*bind_args)
|
|
49
|
+
return to_enum(:each, *bind_args) unless block_given?
|
|
50
|
+
check_owner!
|
|
51
|
+
bind(*bind_args) unless bind_args.empty?
|
|
52
|
+
step_loop(consume_rows: true) { |r| yield r; true }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def columns
|
|
56
|
+
check_owner!
|
|
57
|
+
(1..@native.column_count).map do |i|
|
|
58
|
+
{ name: @native.column_name(i - 1), type: @native.column_decltype(i - 1) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def reader?
|
|
63
|
+
@native.column_count > 0
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def step
|
|
67
|
+
check_owner!
|
|
68
|
+
@native.step
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def reset
|
|
72
|
+
check_owner!
|
|
73
|
+
@native.reset
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def close
|
|
77
|
+
@native.finalize
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def check_owner!
|
|
83
|
+
unless Thread.current.equal?(@owner)
|
|
84
|
+
raise Turso::Exception, "statement is already in use by another thread"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def bind_hash(hash)
|
|
89
|
+
hash.each do |key, value|
|
|
90
|
+
name = key.to_s
|
|
91
|
+
name = ":#{name}" unless name.start_with?(/^[:@?$]/)
|
|
92
|
+
position = @native.named_position(name)
|
|
93
|
+
bind_value(position, value)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def bind_value(position, value)
|
|
98
|
+
case value
|
|
99
|
+
when nil
|
|
100
|
+
@native.bind_null(position)
|
|
101
|
+
when Integer
|
|
102
|
+
@native.bind_int(position, value)
|
|
103
|
+
when Float
|
|
104
|
+
@native.bind_double(position, value)
|
|
105
|
+
when String
|
|
106
|
+
if value.encoding == Encoding::ASCII_8BIT
|
|
107
|
+
@native.bind_blob(position, value.b)
|
|
108
|
+
else
|
|
109
|
+
@native.bind_text(position, value)
|
|
110
|
+
end
|
|
111
|
+
when true
|
|
112
|
+
@native.bind_int(position, 1)
|
|
113
|
+
when false
|
|
114
|
+
@native.bind_int(position, 0)
|
|
115
|
+
when Symbol
|
|
116
|
+
@native.bind_text(position, value.to_s)
|
|
117
|
+
when Time
|
|
118
|
+
@native.bind_text(position, value.iso8601)
|
|
119
|
+
else
|
|
120
|
+
@native.bind_text(position, value.to_s)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def step_loop(consume_rows:)
|
|
125
|
+
loop do
|
|
126
|
+
result = @native.step
|
|
127
|
+
case result
|
|
128
|
+
when 0 # done
|
|
129
|
+
break
|
|
130
|
+
when 1 # row
|
|
131
|
+
if consume_rows
|
|
132
|
+
row = build_row
|
|
133
|
+
continue = yield row
|
|
134
|
+
break unless continue
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def build_row
|
|
141
|
+
values = (0...@native.column_count).map { |i| @native.row_value(i) }
|
|
142
|
+
column_names = (0...@native.column_count).map { |i| @native.column_name(i) }
|
|
143
|
+
Row.new(values, column_names)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
data/lib/turso/transaction.rb
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Turso
|
|
4
|
+
VALID_TRANSACTION_MODES = %i[deferred immediate exclusive concurrent].freeze
|
|
5
|
+
|
|
4
6
|
class Transaction
|
|
5
|
-
def initialize(db)
|
|
7
|
+
def initialize(db, mode = :immediate)
|
|
8
|
+
unless VALID_TRANSACTION_MODES.include?(mode)
|
|
9
|
+
fail ArgumentError, "Invalid transaction mode: #{mode.inspect}. Valid modes: #{VALID_TRANSACTION_MODES.inspect}"
|
|
10
|
+
end
|
|
6
11
|
@db = db
|
|
12
|
+
@mode = mode
|
|
7
13
|
@active = false
|
|
8
14
|
end
|
|
9
15
|
|
|
10
16
|
def begin
|
|
11
|
-
@db.execute("BEGIN")
|
|
17
|
+
@db.execute("BEGIN #{@mode.to_s.upcase}")
|
|
12
18
|
@active = true
|
|
13
19
|
end
|
|
14
20
|
|
|
Binary file
|
data/lib/turso.rb
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require "turso/turso_ruby"
|
|
4
4
|
|
|
5
|
-
require_relative "turso/db"
|
|
6
|
-
require_relative "turso/result_set"
|
|
7
5
|
require_relative "turso/row"
|
|
6
|
+
require_relative "turso/result_set"
|
|
7
|
+
require_relative "turso/statement"
|
|
8
|
+
require_relative "turso/database"
|
|
8
9
|
require_relative "turso/transaction"
|
|
10
|
+
require_relative "turso/version"
|
|
11
|
+
|
|
12
|
+
module Turso
|
|
13
|
+
Error = Exception
|
|
14
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: turso
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Turso Team
|
|
@@ -23,18 +23,37 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0.9'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: irb
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
executables: []
|
|
27
41
|
extensions:
|
|
28
42
|
- ext/turso_ruby/extconf.rb
|
|
29
43
|
extra_rdoc_files: []
|
|
30
44
|
files:
|
|
45
|
+
- README.md
|
|
46
|
+
- ext/turso_ruby/Cargo.lock
|
|
31
47
|
- ext/turso_ruby/Cargo.toml
|
|
32
48
|
- ext/turso_ruby/extconf.rb
|
|
33
49
|
- lib/turso.rb
|
|
34
|
-
- lib/turso/
|
|
50
|
+
- lib/turso/database.rb
|
|
35
51
|
- lib/turso/result_set.rb
|
|
36
52
|
- lib/turso/row.rb
|
|
53
|
+
- lib/turso/statement.rb
|
|
37
54
|
- lib/turso/transaction.rb
|
|
55
|
+
- lib/turso/turso_ruby.so
|
|
56
|
+
- lib/turso/version.rb
|
|
38
57
|
licenses:
|
|
39
58
|
- MIT
|
|
40
59
|
metadata: {}
|
|
@@ -45,7 +64,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
45
64
|
requirements:
|
|
46
65
|
- - ">="
|
|
47
66
|
- !ruby/object:Gem::Version
|
|
48
|
-
version: 3.
|
|
67
|
+
version: 3.2.0
|
|
49
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
69
|
requirements:
|
|
51
70
|
- - ">="
|
data/lib/turso/db.rb
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Turso
|
|
4
|
-
class DB
|
|
5
|
-
def initialize(path, **opts)
|
|
6
|
-
@database = Database.new(path, opts)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def execute(sql, params = [])
|
|
10
|
-
stmt = @database.connection.prepare(sql)
|
|
11
|
-
stmt.bind_positional(params) unless params.empty?
|
|
12
|
-
case stmt.execute
|
|
13
|
-
when Integer
|
|
14
|
-
@database.last_insert_rowid
|
|
15
|
-
when 0
|
|
16
|
-
@database.changes
|
|
17
|
-
end
|
|
18
|
-
ensure
|
|
19
|
-
stmt&.finalize
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def query(sql, params = [])
|
|
23
|
-
stmt = @database.connection.prepare(sql)
|
|
24
|
-
stmt.bind_positional(params) unless params.empty?
|
|
25
|
-
ResultSet.new(stmt)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def transaction(&block)
|
|
29
|
-
if block
|
|
30
|
-
execute("BEGIN")
|
|
31
|
-
begin
|
|
32
|
-
result = block.call(self)
|
|
33
|
-
execute("COMMIT")
|
|
34
|
-
result
|
|
35
|
-
rescue StandardError
|
|
36
|
-
execute("ROLLBACK")
|
|
37
|
-
raise
|
|
38
|
-
end
|
|
39
|
-
else
|
|
40
|
-
Transaction.new(self)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def close
|
|
45
|
-
@database.close
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def closed?
|
|
49
|
-
!@database.open?
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|