extralite 0.1 → 0.5
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/.github/workflows/test.yml +31 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +66 -0
- data/LICENSE +1 -1
- data/README.md +14 -5
- data/Rakefile +1 -1
- data/ext/extralite/extralite.c +137 -47
- data/extralite.gemspec +3 -3
- data/lib/extralite/version.rb +1 -1
- data/test/helper.rb +10 -0
- data/test/test_database.rb +88 -0
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fa0672decf076e478bfd52359196baecbcf837e00cec91c7f9cf963b103af68
|
4
|
+
data.tar.gz: e54364225b96fc355a8198eab73c7a27848a672e04479a7eed06f724991ae238
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9606a7e0154d61adc29150161a44018638f7e05a3c17f4f8e8b992e166ad0cd46705cb4eab2e9e6f7cc7f4d9eece8406e468822ff8a84116ff6f58cc493a89dd
|
7
|
+
data.tar.gz: fa5e4e1425187986ec097990f84ae103428645f94caaa41471305b414c78f15fb9522311b4eb50d40bc91c715f9e20e1c2fac18c0fd02892b41e75994602691d
|
@@ -0,0 +1,31 @@
|
|
1
|
+
name: Tests
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
os: [ubuntu-latest]
|
11
|
+
ruby: [2.6, 2.7, 3.0]
|
12
|
+
|
13
|
+
name: >-
|
14
|
+
${{matrix.os}}, ${{matrix.ruby}}
|
15
|
+
|
16
|
+
runs-on: ${{matrix.os}}
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v1
|
19
|
+
- uses: actions/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: ${{matrix.ruby}}
|
22
|
+
- name: Install dependencies
|
23
|
+
run: |
|
24
|
+
gem install bundler
|
25
|
+
bundle install
|
26
|
+
- name: Show Linux kernel version
|
27
|
+
run: uname -r
|
28
|
+
- name: Compile C-extension
|
29
|
+
run: bundle exec rake compile
|
30
|
+
- name: Run tests
|
31
|
+
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
## 0.5 2021-05-25
|
2
|
+
|
3
|
+
- Implement `Database#query_single_row`
|
4
|
+
|
5
|
+
## 0.4 2021-05-24
|
6
|
+
|
7
|
+
- Add support for loading extensions
|
8
|
+
|
9
|
+
## 0.3 2021-05-24
|
10
|
+
|
11
|
+
- Add support for running multiple statements
|
12
|
+
|
13
|
+
## 0.2 2021-05-23
|
14
|
+
|
15
|
+
- Implement `Database#transaction_active?`
|
16
|
+
- Add tests
|
17
|
+
|
18
|
+
## 0.1 2021-05-21
|
19
|
+
|
20
|
+
- First release
|
21
|
+
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
extralite (0.5)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ansi (1.5.0)
|
10
|
+
ast (2.4.2)
|
11
|
+
builder (3.2.4)
|
12
|
+
coderay (1.1.3)
|
13
|
+
docile (1.4.0)
|
14
|
+
json (2.5.1)
|
15
|
+
method_source (1.0.0)
|
16
|
+
minitest (5.14.4)
|
17
|
+
minitest-reporters (1.4.2)
|
18
|
+
ansi
|
19
|
+
builder
|
20
|
+
minitest (>= 5.0)
|
21
|
+
ruby-progressbar
|
22
|
+
parallel (1.20.1)
|
23
|
+
parser (3.0.1.1)
|
24
|
+
ast (~> 2.4.1)
|
25
|
+
pry (0.13.1)
|
26
|
+
coderay (~> 1.1)
|
27
|
+
method_source (~> 1.0)
|
28
|
+
rainbow (3.0.0)
|
29
|
+
rake (13.0.3)
|
30
|
+
rake-compiler (1.1.1)
|
31
|
+
rake
|
32
|
+
regexp_parser (2.1.1)
|
33
|
+
rexml (3.2.5)
|
34
|
+
rubocop (0.85.1)
|
35
|
+
parallel (~> 1.10)
|
36
|
+
parser (>= 2.7.0.1)
|
37
|
+
rainbow (>= 2.2.2, < 4.0)
|
38
|
+
regexp_parser (>= 1.7)
|
39
|
+
rexml
|
40
|
+
rubocop-ast (>= 0.0.3)
|
41
|
+
ruby-progressbar (~> 1.7)
|
42
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
43
|
+
rubocop-ast (1.5.0)
|
44
|
+
parser (>= 3.0.1.1)
|
45
|
+
ruby-progressbar (1.11.0)
|
46
|
+
simplecov (0.17.1)
|
47
|
+
docile (~> 1.1)
|
48
|
+
json (>= 1.8, < 3)
|
49
|
+
simplecov-html (~> 0.10.0)
|
50
|
+
simplecov-html (0.10.2)
|
51
|
+
unicode-display_width (1.7.0)
|
52
|
+
|
53
|
+
PLATFORMS
|
54
|
+
ruby
|
55
|
+
|
56
|
+
DEPENDENCIES
|
57
|
+
extralite!
|
58
|
+
minitest (= 5.14.4)
|
59
|
+
minitest-reporters (= 1.4.2)
|
60
|
+
pry (= 0.13.1)
|
61
|
+
rake-compiler (= 1.1.1)
|
62
|
+
rubocop (= 0.85.1)
|
63
|
+
simplecov (= 0.17.1)
|
64
|
+
|
65
|
+
BUNDLED WITH
|
66
|
+
2.1.4
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -5,8 +5,8 @@ class with a minimal set of methods to interact with an SQLite3 database.
|
|
5
5
|
|
6
6
|
### Features
|
7
7
|
|
8
|
-
- A variety of
|
9
|
-
column, single value.
|
8
|
+
- A variety of methods for different data access patterns: row as hash, row as array, single
|
9
|
+
single row, single column, single value.
|
10
10
|
- Iterate over records with a block, or collect records into an array.
|
11
11
|
- Parameter binding.
|
12
12
|
- Get last insert rowid.
|
@@ -21,9 +21,11 @@ require 'extralite'
|
|
21
21
|
db = Extralite::Database.new('mydb')
|
22
22
|
|
23
23
|
# get query results as array of hashes
|
24
|
+
db.query('select 1 as foo') #=> [{ :foo => 1 }]
|
25
|
+
# or:
|
24
26
|
db.query_hash('select 1 as foo') #=> [{ :foo => 1 }]
|
25
27
|
# or iterate over results
|
26
|
-
db.
|
28
|
+
db.query('select 1 as foo') { |r| p r }
|
27
29
|
# { :foo => 1 }
|
28
30
|
|
29
31
|
# get query results as array of arrays
|
@@ -32,6 +34,9 @@ db.query_ary('select 1, 2, 3') #=> [[1, 2, 3]]
|
|
32
34
|
db.query_ary('select 1, 2, 3') { |r| p r }
|
33
35
|
# [1, 2, 3]
|
34
36
|
|
37
|
+
# get a single row as a hash
|
38
|
+
db.query_single_column("select 1 as foo") #=> { :foo => 1 }
|
39
|
+
|
35
40
|
# get single column query results as array of values
|
36
41
|
db.query_single_column('select 42') #=> [42]
|
37
42
|
# or iterate over results
|
@@ -45,7 +50,11 @@ db.query_single_value("select 'foo'") #=> "foo"
|
|
45
50
|
db.query_hash('select ? as foo, ? as bar', 1, 2) #=> [{ :foo => 1, :bar => 2 }]
|
46
51
|
|
47
52
|
# get last insert rowid
|
48
|
-
rowid = db.last_insert_id
|
49
|
-
```
|
53
|
+
rowid = db.last_insert_id
|
50
54
|
|
55
|
+
# get rows changed in last query
|
56
|
+
rows_changed = db.changes
|
51
57
|
|
58
|
+
# get db filename
|
59
|
+
Extralite::Database.new('/tmp/my.db').filename #=> "/tmp/my.db"
|
60
|
+
```
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ task :recompile => [:clean, :compile]
|
|
12
12
|
|
13
13
|
task :default => [:compile, :test]
|
14
14
|
task :test do
|
15
|
-
exec 'ruby test/
|
15
|
+
exec 'ruby test/test_database.rb'
|
16
16
|
end
|
17
17
|
|
18
18
|
CLEAN.include "**/*.o", "**/*.so", "**/*.so.*", "**/*.a", "**/*.bundle", "**/*.jar", "pkg", "tmp"
|
data/ext/extralite/extralite.c
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
#include "../sqlite3/sqlite3.h"
|
4
4
|
|
5
5
|
VALUE cError;
|
6
|
+
ID ID_STRIP;
|
6
7
|
|
7
8
|
typedef struct Database_t {
|
8
9
|
sqlite3 *sqlite3_db;
|
@@ -44,10 +45,14 @@ VALUE Database_initialize(VALUE self, VALUE path) {
|
|
44
45
|
|
45
46
|
rc = sqlite3_open(StringValueCStr(path), &db->sqlite3_db);
|
46
47
|
if (rc) {
|
47
|
-
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db->sqlite3_db));
|
48
48
|
sqlite3_close(db->sqlite3_db);
|
49
|
-
|
50
|
-
|
49
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
50
|
+
}
|
51
|
+
|
52
|
+
rc = sqlite3_enable_load_extension(db->sqlite3_db, 1);
|
53
|
+
if (rc) {
|
54
|
+
sqlite3_close(db->sqlite3_db);
|
55
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
51
56
|
}
|
52
57
|
|
53
58
|
return Qnil;
|
@@ -123,6 +128,41 @@ static inline VALUE row_to_hash(sqlite3_stmt *stmt, int column_count, VALUE colu
|
|
123
128
|
return row;
|
124
129
|
}
|
125
130
|
|
131
|
+
static inline VALUE row_to_ary(sqlite3_stmt *stmt, int column_count) {
|
132
|
+
VALUE row = rb_ary_new2(column_count);
|
133
|
+
for (int i = 0; i < column_count; i++) {
|
134
|
+
VALUE value = get_column_value(stmt, i, sqlite3_column_type(stmt, i));
|
135
|
+
rb_ary_push(row, value);
|
136
|
+
}
|
137
|
+
return row;
|
138
|
+
}
|
139
|
+
|
140
|
+
inline void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
|
141
|
+
const char *rest = 0;
|
142
|
+
const char *ptr = RSTRING_PTR(sql);
|
143
|
+
const char *end = ptr + RSTRING_LEN(sql);
|
144
|
+
while (1) {
|
145
|
+
int rc = sqlite3_prepare(db, ptr, end - ptr, stmt, &rest);
|
146
|
+
if (rc) {
|
147
|
+
sqlite3_finalize(*stmt);
|
148
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db));
|
149
|
+
}
|
150
|
+
|
151
|
+
if (rest == end) return;
|
152
|
+
|
153
|
+
// perform current query, but discard its results
|
154
|
+
rc = sqlite3_step(*stmt);
|
155
|
+
sqlite3_finalize(*stmt);
|
156
|
+
switch (rc) {
|
157
|
+
case SQLITE_BUSY:
|
158
|
+
rb_raise(cError, "Database is busy");
|
159
|
+
case SQLITE_ERROR:
|
160
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db));
|
161
|
+
}
|
162
|
+
ptr = rest;
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
126
166
|
VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
|
127
167
|
int rc;
|
128
168
|
sqlite3_stmt* stmt;
|
@@ -135,16 +175,12 @@ VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
|
|
135
175
|
VALUE sql;
|
136
176
|
|
137
177
|
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
138
|
-
sql = argv[0];
|
139
|
-
|
178
|
+
sql = rb_funcall(argv[0], ID_STRIP, 0);
|
179
|
+
if (RSTRING_LEN(sql) == 0) return Qnil;
|
140
180
|
|
141
|
-
|
142
|
-
if (rc) {
|
143
|
-
sqlite3_finalize(stmt);
|
144
|
-
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
145
|
-
return Qnil;
|
146
|
-
}
|
181
|
+
GetDatabase(self, db);
|
147
182
|
|
183
|
+
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
148
184
|
bind_all_parameters(stmt, argc, argv);
|
149
185
|
column_count = sqlite3_column_count(stmt);
|
150
186
|
column_names = get_column_names(stmt, column_count);
|
@@ -162,12 +198,16 @@ step:
|
|
162
198
|
case SQLITE_DONE:
|
163
199
|
break;
|
164
200
|
case SQLITE_BUSY:
|
201
|
+
sqlite3_finalize(stmt);
|
165
202
|
rb_raise(cError, "Database is busy");
|
166
203
|
case SQLITE_ERROR:
|
204
|
+
sqlite3_finalize(stmt);
|
167
205
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
168
206
|
default:
|
207
|
+
sqlite3_finalize(stmt);
|
169
208
|
rb_raise(cError, "Invalid return code for sqlite3_step: %d", rc);
|
170
209
|
}
|
210
|
+
// TODO, use ensure to finalize statement
|
171
211
|
sqlite3_finalize(stmt);
|
172
212
|
RB_GC_GUARD(column_names);
|
173
213
|
RB_GC_GUARD(row);
|
@@ -175,15 +215,6 @@ step:
|
|
175
215
|
return result;
|
176
216
|
}
|
177
217
|
|
178
|
-
static inline VALUE row_to_ary(sqlite3_stmt *stmt, int column_count) {
|
179
|
-
VALUE row = rb_ary_new2(column_count);
|
180
|
-
for (int i = 0; i < column_count; i++) {
|
181
|
-
VALUE value = get_column_value(stmt, i, sqlite3_column_type(stmt, i));
|
182
|
-
rb_ary_push(row, value);
|
183
|
-
}
|
184
|
-
return row;
|
185
|
-
}
|
186
|
-
|
187
218
|
VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
|
188
219
|
int rc;
|
189
220
|
sqlite3_stmt* stmt;
|
@@ -195,17 +226,11 @@ VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
|
|
195
226
|
VALUE sql;
|
196
227
|
|
197
228
|
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
198
|
-
sql = argv[0];
|
229
|
+
sql = rb_funcall(argv[0], ID_STRIP, 0);
|
230
|
+
if (RSTRING_LEN(sql) == 0) return Qnil;
|
199
231
|
GetDatabase(self, db);
|
200
232
|
|
201
|
-
|
202
|
-
if (rc) {
|
203
|
-
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db->sqlite3_db));
|
204
|
-
sqlite3_finalize(stmt);
|
205
|
-
// TODO: raise error
|
206
|
-
return Qfalse;
|
207
|
-
}
|
208
|
-
|
233
|
+
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
209
234
|
bind_all_parameters(stmt, argc, argv);
|
210
235
|
column_count = sqlite3_column_count(stmt);
|
211
236
|
|
@@ -221,10 +246,13 @@ step:
|
|
221
246
|
case SQLITE_DONE:
|
222
247
|
break;
|
223
248
|
case SQLITE_BUSY:
|
249
|
+
sqlite3_finalize(stmt);
|
224
250
|
rb_raise(cError, "Database is busy");
|
225
251
|
case SQLITE_ERROR:
|
252
|
+
sqlite3_finalize(stmt);
|
226
253
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
227
254
|
default:
|
255
|
+
sqlite3_finalize(stmt);
|
228
256
|
rb_raise(cError, "Invalid return code for sqlite3_step: %d", rc);
|
229
257
|
}
|
230
258
|
sqlite3_finalize(stmt);
|
@@ -233,6 +261,47 @@ step:
|
|
233
261
|
return result;
|
234
262
|
}
|
235
263
|
|
264
|
+
VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) {
|
265
|
+
int rc;
|
266
|
+
sqlite3_stmt* stmt;
|
267
|
+
int column_count;
|
268
|
+
Database_t *db;
|
269
|
+
VALUE sql;
|
270
|
+
VALUE row = Qnil;
|
271
|
+
VALUE column_names;
|
272
|
+
|
273
|
+
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
274
|
+
sql = rb_funcall(argv[0], ID_STRIP, 0);
|
275
|
+
if (RSTRING_LEN(sql) == 0) return Qnil;
|
276
|
+
|
277
|
+
GetDatabase(self, db);
|
278
|
+
|
279
|
+
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
280
|
+
bind_all_parameters(stmt, argc, argv);
|
281
|
+
column_count = sqlite3_column_count(stmt);
|
282
|
+
column_names = get_column_names(stmt, column_count);
|
283
|
+
|
284
|
+
rc = sqlite3_step(stmt);
|
285
|
+
switch (rc) {
|
286
|
+
case SQLITE_ROW:
|
287
|
+
row = row_to_hash(stmt, column_count, column_names);
|
288
|
+
break;
|
289
|
+
case SQLITE_DONE:
|
290
|
+
break;
|
291
|
+
case SQLITE_BUSY:
|
292
|
+
rb_raise(cError, "Database is busy");
|
293
|
+
case SQLITE_ERROR:
|
294
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
295
|
+
default:
|
296
|
+
rb_raise(cError, "Invalid return code for sqlite3_step: %d", rc);
|
297
|
+
}
|
298
|
+
|
299
|
+
sqlite3_finalize(stmt);
|
300
|
+
RB_GC_GUARD(row);
|
301
|
+
RB_GC_GUARD(column_names);
|
302
|
+
return row;
|
303
|
+
}
|
304
|
+
|
236
305
|
VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
|
237
306
|
int rc;
|
238
307
|
sqlite3_stmt* stmt;
|
@@ -244,17 +313,12 @@ VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
|
|
244
313
|
VALUE value;
|
245
314
|
|
246
315
|
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
247
|
-
sql = argv[0];
|
248
|
-
|
316
|
+
sql = rb_funcall(argv[0], ID_STRIP, 0);
|
317
|
+
if (RSTRING_LEN(sql) == 0) return Qnil;
|
249
318
|
|
250
|
-
|
251
|
-
if (rc) {
|
252
|
-
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db->sqlite3_db));
|
253
|
-
sqlite3_finalize(stmt);
|
254
|
-
// TODO: raise error
|
255
|
-
return Qfalse;
|
256
|
-
}
|
319
|
+
GetDatabase(self, db);
|
257
320
|
|
321
|
+
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
258
322
|
bind_all_parameters(stmt, argc, argv);
|
259
323
|
column_count = sqlite3_column_count(stmt);
|
260
324
|
if (column_count != 1)
|
@@ -272,10 +336,13 @@ step:
|
|
272
336
|
case SQLITE_DONE:
|
273
337
|
break;
|
274
338
|
case SQLITE_BUSY:
|
339
|
+
sqlite3_finalize(stmt);
|
275
340
|
rb_raise(cError, "Database is busy");
|
276
341
|
case SQLITE_ERROR:
|
342
|
+
sqlite3_finalize(stmt);
|
277
343
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
278
344
|
default:
|
345
|
+
sqlite3_finalize(stmt);
|
279
346
|
rb_raise(cError, "Invalid return code for sqlite3_step: %d", rc);
|
280
347
|
}
|
281
348
|
|
@@ -294,17 +361,12 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
294
361
|
VALUE value = Qnil;
|
295
362
|
|
296
363
|
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
297
|
-
sql = argv[0];
|
298
|
-
|
364
|
+
sql = rb_funcall(argv[0], ID_STRIP, 0);
|
365
|
+
if (RSTRING_LEN(sql) == 0) return Qnil;
|
299
366
|
|
300
|
-
|
301
|
-
if (rc) {
|
302
|
-
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db->sqlite3_db));
|
303
|
-
sqlite3_finalize(stmt);
|
304
|
-
// TODO: raise error
|
305
|
-
return Qfalse;
|
306
|
-
}
|
367
|
+
GetDatabase(self, db);
|
307
368
|
|
369
|
+
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
308
370
|
bind_all_parameters(stmt, argc, argv);
|
309
371
|
column_count = sqlite3_column_count(stmt);
|
310
372
|
if (column_count != 1)
|
@@ -315,6 +377,8 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
315
377
|
case SQLITE_ROW:
|
316
378
|
value = get_column_value(stmt, 0, sqlite3_column_type(stmt, 0));
|
317
379
|
break;
|
380
|
+
case SQLITE_DONE:
|
381
|
+
break;
|
318
382
|
case SQLITE_BUSY:
|
319
383
|
rb_raise(cError, "Database is busy");
|
320
384
|
case SQLITE_ERROR:
|
@@ -354,6 +418,28 @@ VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
|
|
354
418
|
return filename ? rb_str_new_cstr(filename) : Qnil;
|
355
419
|
}
|
356
420
|
|
421
|
+
VALUE Database_transaction_active_p(VALUE self) {
|
422
|
+
Database_t *db;
|
423
|
+
GetDatabase(self, db);
|
424
|
+
|
425
|
+
return sqlite3_get_autocommit(db->sqlite3_db) ? Qfalse : Qtrue;
|
426
|
+
}
|
427
|
+
|
428
|
+
VALUE Database_load_extension(VALUE self, VALUE path) {
|
429
|
+
Database_t *db;
|
430
|
+
GetDatabase(self, db);
|
431
|
+
char *err_msg;
|
432
|
+
|
433
|
+
int rc = sqlite3_load_extension(db->sqlite3_db, RSTRING_PTR(path), 0, &err_msg);
|
434
|
+
if (rc != SQLITE_OK) {
|
435
|
+
VALUE error = rb_exc_new2(cError, err_msg);
|
436
|
+
sqlite3_free(err_msg);
|
437
|
+
rb_exc_raise(error);
|
438
|
+
}
|
439
|
+
|
440
|
+
return self;
|
441
|
+
}
|
442
|
+
|
357
443
|
void Init_Extralite() {
|
358
444
|
VALUE mExtralite = rb_define_module("Extralite");
|
359
445
|
VALUE cDatabase = rb_define_class_under(mExtralite, "Database", rb_cObject);
|
@@ -364,12 +450,16 @@ void Init_Extralite() {
|
|
364
450
|
rb_define_method(cDatabase, "query", Database_query_hash, -1);
|
365
451
|
rb_define_method(cDatabase, "query_hash", Database_query_hash, -1);
|
366
452
|
rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
|
453
|
+
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
367
454
|
rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
|
368
455
|
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
369
456
|
|
370
457
|
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
371
458
|
rb_define_method(cDatabase, "changes", Database_changes, 0);
|
372
459
|
rb_define_method(cDatabase, "filename", Database_filename, -1);
|
460
|
+
rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
|
461
|
+
rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
|
373
462
|
|
374
463
|
cError = rb_define_class_under(mExtralite, "Error", rb_eRuntimeError);
|
464
|
+
ID_STRIP = rb_intern("strip");
|
375
465
|
}
|
data/extralite.gemspec
CHANGED
@@ -8,11 +8,11 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.author = 'Sharon Rosner'
|
9
9
|
s.email = 'sharon@noteflakes.com'
|
10
10
|
s.files = `git ls-files`.split
|
11
|
-
s.homepage = 'https://digital-fabric
|
11
|
+
s.homepage = 'https://github.com/digital-fabric/extralite'
|
12
12
|
s.metadata = {
|
13
13
|
"source_code_uri" => "https://github.com/digital-fabric/extralite",
|
14
|
-
"documentation_uri" => "https://digital-fabric
|
15
|
-
"homepage_uri" => "https://digital-fabric
|
14
|
+
"documentation_uri" => "https://github.com/digital-fabric/extralite",
|
15
|
+
"homepage_uri" => "https://github.com/digital-fabric/extralite",
|
16
16
|
"changelog_uri" => "https://github.com/digital-fabric/extralite/blob/master/CHANGELOG.md"
|
17
17
|
}
|
18
18
|
s.rdoc_options = ["--title", "extralite", "--main", "README.md"]
|
data/lib/extralite/version.rb
CHANGED
data/test/helper.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class DatabaseTest < MiniTest::Test
|
6
|
+
def setup
|
7
|
+
@db = Extralite::Database.new('/tmp/extralite.db')
|
8
|
+
@db.query('create table if not exists t (x,y,z)')
|
9
|
+
@db.query('delete from t')
|
10
|
+
@db.query('insert into t values (1, 2, 3)')
|
11
|
+
@db.query('insert into t values (4, 5, 6)')
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_database
|
15
|
+
r = @db.query('select 1 as foo')
|
16
|
+
assert_equal [{foo: 1}], r
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_query
|
20
|
+
r = @db.query('select * from t')
|
21
|
+
assert_equal [{x: 1, y: 2, z: 3}, {x: 4, y: 5, z: 6}], r
|
22
|
+
|
23
|
+
r = @db.query('select * from t where x = 2')
|
24
|
+
assert_equal [], r
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_query_hash
|
28
|
+
r = @db.query_hash('select * from t')
|
29
|
+
assert_equal [{x: 1, y: 2, z: 3}, {x: 4, y: 5, z: 6}], r
|
30
|
+
|
31
|
+
r = @db.query_hash('select * from t where x = 2')
|
32
|
+
assert_equal [], r
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_query_ary
|
36
|
+
r = @db.query_ary('select * from t')
|
37
|
+
assert_equal [[1, 2, 3], [4, 5, 6]], r
|
38
|
+
|
39
|
+
r = @db.query_ary('select * from t where x = 2')
|
40
|
+
assert_equal [], r
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_query_single_row
|
44
|
+
r = @db.query_single_row('select * from t order by x desc limit 1')
|
45
|
+
assert_equal({ x: 4, y: 5, z: 6 }, r)
|
46
|
+
|
47
|
+
r = @db.query_single_row('select * from t where x = 2')
|
48
|
+
assert_nil r
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_query_single_column
|
52
|
+
r = @db.query_single_column('select y from t')
|
53
|
+
assert_equal [2, 5], r
|
54
|
+
|
55
|
+
r = @db.query_single_column('select y from t where x = 2')
|
56
|
+
assert_equal [], r
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_query_single_value
|
60
|
+
r = @db.query_single_value('select z from t order by Z desc limit 1')
|
61
|
+
assert_equal 6, r
|
62
|
+
|
63
|
+
r = @db.query_single_value('select z from t where x = 2')
|
64
|
+
assert_nil r
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_transaction_active?
|
68
|
+
assert_equal false, @db.transaction_active?
|
69
|
+
@db.query('begin')
|
70
|
+
assert_equal true, @db.transaction_active?
|
71
|
+
@db.query('rollback')
|
72
|
+
assert_equal false, @db.transaction_active?
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_multiple_statements
|
76
|
+
@db.query("insert into t values ('a', 'b', 'c'); insert into t values ('d', 'e', 'f');")
|
77
|
+
|
78
|
+
assert_equal [1, 4, 'a', 'd'], @db.query_single_column('select x from t order by x')
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_empty_sql
|
82
|
+
r = @db.query(' ')
|
83
|
+
assert_nil r
|
84
|
+
|
85
|
+
r = @db.query('select 1 as foo; ')
|
86
|
+
assert_equal [{ foo: 1 }], r
|
87
|
+
end
|
88
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: extralite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.5'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -102,9 +102,11 @@ extensions:
|
|
102
102
|
extra_rdoc_files:
|
103
103
|
- README.md
|
104
104
|
files:
|
105
|
+
- ".github/workflows/test.yml"
|
105
106
|
- ".gitignore"
|
106
107
|
- CHANGELOG.md
|
107
108
|
- Gemfile
|
109
|
+
- Gemfile.lock
|
108
110
|
- LICENSE
|
109
111
|
- README.md
|
110
112
|
- Rakefile
|
@@ -116,13 +118,15 @@ files:
|
|
116
118
|
- extralite.gemspec
|
117
119
|
- lib/extralite.rb
|
118
120
|
- lib/extralite/version.rb
|
119
|
-
|
121
|
+
- test/helper.rb
|
122
|
+
- test/test_database.rb
|
123
|
+
homepage: https://github.com/digital-fabric/extralite
|
120
124
|
licenses:
|
121
125
|
- MIT
|
122
126
|
metadata:
|
123
127
|
source_code_uri: https://github.com/digital-fabric/extralite
|
124
|
-
documentation_uri: https://digital-fabric
|
125
|
-
homepage_uri: https://digital-fabric
|
128
|
+
documentation_uri: https://github.com/digital-fabric/extralite
|
129
|
+
homepage_uri: https://github.com/digital-fabric/extralite
|
126
130
|
changelog_uri: https://github.com/digital-fabric/extralite/blob/master/CHANGELOG.md
|
127
131
|
post_install_message:
|
128
132
|
rdoc_options:
|
@@ -143,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
147
|
- !ruby/object:Gem::Version
|
144
148
|
version: '0'
|
145
149
|
requirements: []
|
146
|
-
rubygems_version: 3.
|
150
|
+
rubygems_version: 3.1.4
|
147
151
|
signing_key:
|
148
152
|
specification_version: 4
|
149
153
|
summary: Extra-lightweight SQLite3 wrapper for Ruby
|