extralite-bundle 1.23 → 1.24
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/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -1
- data/ext/extralite/common.c +2 -2
- data/ext/extralite/database.c +74 -21
- data/ext/extralite/extconf-bundle.rb +9 -2
- data/ext/extralite/extconf.rb +87 -109
- data/ext/extralite/extralite.h +7 -7
- data/ext/extralite/prepared_statement.c +3 -4
- data/lib/extralite/sqlite3_constants.rb +109 -1
- data/lib/extralite/version.rb +1 -1
- data/test/test_database.rb +36 -6
- data/test/test_prepared_statement.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eed963dc582b5492ef4601faf29359be35a322c4e0fee60cca6d4c532e7e41bf
|
4
|
+
data.tar.gz: 16776f46b60e4ed42408269a0a635de370830b390699991b56768fab625a2ce5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48547b23314d89314a3051de82db89eadee325bf553627b4c494dbe4399b2072338cd78d3603bafafae294b2f44250d004e85963b4bb39cd5f5793b7b7194a7b
|
7
|
+
data.tar.gz: cd824816449323ec6b35d2adc6f2ff9b4e3822c3c65d86af202a6dd571a7b5a9532a07f67be9e973f7931c68128aaa82d680a5790e2fb6cacbe8c63b144518b3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 1.24 2023-02-02
|
2
|
+
|
3
|
+
- Fix closing database with open statements
|
4
|
+
- Improve error reporting in `Database#initialize`
|
5
|
+
- Fix `extralite-bundle` gem compilation
|
6
|
+
- Improve error handling, add methods for error information
|
7
|
+
- Use extended result codes
|
8
|
+
- Add `Database#errcode`
|
9
|
+
- Add `Database#errmsg`
|
10
|
+
- Add `Database#error_offset`
|
11
|
+
|
1
12
|
# 1.23 2023-01-26
|
2
13
|
|
3
14
|
- Add `Database#trace` (#21)
|
data/Gemfile.lock
CHANGED
data/ext/extralite/common.c
CHANGED
@@ -23,7 +23,7 @@ static inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
|
|
23
23
|
void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value);
|
24
24
|
|
25
25
|
void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
|
26
|
-
VALUE keys = rb_funcall(hash,
|
26
|
+
VALUE keys = rb_funcall(hash, ID_keys, 0);
|
27
27
|
long len = RARRAY_LEN(keys);
|
28
28
|
for (long i = 0; i < len; i++) {
|
29
29
|
VALUE k = RARRAY_AREF(keys, i);
|
@@ -34,7 +34,7 @@ void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
|
|
34
34
|
bind_parameter_value(stmt, FIX2INT(k), v);
|
35
35
|
break;
|
36
36
|
case T_SYMBOL:
|
37
|
-
k = rb_funcall(k,
|
37
|
+
k = rb_funcall(k, ID_to_s, 0);
|
38
38
|
case T_STRING:
|
39
39
|
if(RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
|
40
40
|
int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
|
data/ext/extralite/database.c
CHANGED
@@ -7,11 +7,11 @@ VALUE cSQLError;
|
|
7
7
|
VALUE cBusyError;
|
8
8
|
VALUE cInterruptError;
|
9
9
|
|
10
|
-
ID
|
11
|
-
ID
|
12
|
-
ID
|
13
|
-
ID
|
14
|
-
ID
|
10
|
+
ID ID_call;
|
11
|
+
ID ID_keys;
|
12
|
+
ID ID_new;
|
13
|
+
ID ID_strip;
|
14
|
+
ID ID_to_s;
|
15
15
|
|
16
16
|
static size_t Database_size(const void *ptr) {
|
17
17
|
return sizeof(Database_t);
|
@@ -19,7 +19,7 @@ static size_t Database_size(const void *ptr) {
|
|
19
19
|
|
20
20
|
static void Database_free(void *ptr) {
|
21
21
|
Database_t *db = ptr;
|
22
|
-
if (db->sqlite3_db)
|
22
|
+
if (db->sqlite3_db) sqlite3_close_v2(db->sqlite3_db);
|
23
23
|
free(ptr);
|
24
24
|
}
|
25
25
|
|
@@ -81,14 +81,21 @@ VALUE Database_initialize(VALUE self, VALUE path) {
|
|
81
81
|
|
82
82
|
rc = sqlite3_open(StringValueCStr(path), &db->sqlite3_db);
|
83
83
|
if (rc) {
|
84
|
-
|
84
|
+
sqlite3_close_v2(db->sqlite3_db);
|
85
|
+
rb_raise(cError, "%s", sqlite3_errstr(rc));
|
86
|
+
}
|
87
|
+
|
88
|
+
// Enable extended result codes
|
89
|
+
rc = sqlite3_extended_result_codes(db->sqlite3_db, 1);
|
90
|
+
if (rc) {
|
91
|
+
sqlite3_close_v2(db->sqlite3_db);
|
85
92
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
86
93
|
}
|
87
94
|
|
88
95
|
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
|
89
96
|
rc = sqlite3_enable_load_extension(db->sqlite3_db, 1);
|
90
97
|
if (rc) {
|
91
|
-
|
98
|
+
sqlite3_close_v2(db->sqlite3_db);
|
92
99
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
93
100
|
}
|
94
101
|
#endif
|
@@ -108,7 +115,7 @@ VALUE Database_close(VALUE self) {
|
|
108
115
|
Database_t *db;
|
109
116
|
GetDatabase(self, db);
|
110
117
|
|
111
|
-
rc =
|
118
|
+
rc = sqlite3_close_v2(db->sqlite3_db);
|
112
119
|
if (rc) {
|
113
120
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
114
121
|
}
|
@@ -138,13 +145,15 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
|
|
138
145
|
|
139
146
|
// extract query from args
|
140
147
|
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
141
|
-
sql = rb_funcall(argv[0],
|
148
|
+
sql = rb_funcall(argv[0], ID_strip, 0);
|
142
149
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
143
150
|
|
144
151
|
// prepare query ctx
|
145
152
|
GetOpenDatabase(self, db);
|
146
|
-
if (db->trace_block != Qnil) rb_funcall(db->trace_block,
|
153
|
+
if (db->trace_block != Qnil) rb_funcall(db->trace_block, ID_call, 1, sql);
|
147
154
|
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
155
|
+
RB_GC_GUARD(sql);
|
156
|
+
|
148
157
|
bind_all_parameters(stmt, argc - 1, argv + 1);
|
149
158
|
query_ctx ctx = { self, db->sqlite3_db, stmt };
|
150
159
|
|
@@ -394,7 +403,7 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
|
|
394
403
|
* Creates a prepared statement with the given SQL query.
|
395
404
|
*/
|
396
405
|
VALUE Database_prepare(VALUE self, VALUE sql) {
|
397
|
-
return rb_funcall(cPreparedStatement,
|
406
|
+
return rb_funcall(cPreparedStatement, ID_new, 2, self, sql);
|
398
407
|
}
|
399
408
|
|
400
409
|
/* call-seq:
|
@@ -476,7 +485,7 @@ VALUE backup_cleanup(VALUE ptr) {
|
|
476
485
|
sqlite3_backup_finish(ctx->backup);
|
477
486
|
|
478
487
|
if (ctx->close_dst_on_cleanup)
|
479
|
-
|
488
|
+
sqlite3_close_v2(ctx->dst);
|
480
489
|
return Qnil;
|
481
490
|
}
|
482
491
|
|
@@ -508,7 +517,7 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
|
|
508
517
|
if (dst_is_fn) {
|
509
518
|
int rc = sqlite3_open(StringValueCStr(dst), &dst_db);
|
510
519
|
if (rc) {
|
511
|
-
|
520
|
+
sqlite3_close_v2(dst_db);
|
512
521
|
rb_raise(cError, "%s", sqlite3_errmsg(dst_db));
|
513
522
|
}
|
514
523
|
}
|
@@ -524,7 +533,7 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
|
|
524
533
|
backup = sqlite3_backup_init(dst_db, StringValueCStr(dst_name), src->sqlite3_db, StringValueCStr(src_name));
|
525
534
|
if (!backup) {
|
526
535
|
if (dst_is_fn)
|
527
|
-
|
536
|
+
sqlite3_close_v2(dst_db);
|
528
537
|
rb_raise(cError, "%s", sqlite3_errmsg(dst_db));
|
529
538
|
}
|
530
539
|
|
@@ -609,7 +618,6 @@ VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
|
|
609
618
|
GetOpenDatabase(self, db);
|
610
619
|
|
611
620
|
int ms = (sec == Qnil) ? 0 : (int)(NUM2DBL(sec) * 1000);
|
612
|
-
|
613
621
|
int rc = sqlite3_busy_timeout(db->sqlite3_db, ms);
|
614
622
|
if (rc != SQLITE_OK) rb_raise(cError, "Failed to set busy timeout");
|
615
623
|
|
@@ -644,6 +652,44 @@ VALUE Database_trace(VALUE self) {
|
|
644
652
|
return self;
|
645
653
|
}
|
646
654
|
|
655
|
+
/* call-seq:
|
656
|
+
* db.errcode -> errcode
|
657
|
+
*
|
658
|
+
* Returns the last error code for the database.
|
659
|
+
*/
|
660
|
+
VALUE Database_errcode(VALUE self) {
|
661
|
+
Database_t *db;
|
662
|
+
GetOpenDatabase(self, db);
|
663
|
+
|
664
|
+
return INT2NUM(sqlite3_errcode(db->sqlite3_db));
|
665
|
+
}
|
666
|
+
|
667
|
+
/* call-seq:
|
668
|
+
* db.errmsg -> errmsg
|
669
|
+
*
|
670
|
+
* Returns the last error message for the database.
|
671
|
+
*/
|
672
|
+
VALUE Database_errmsg(VALUE self) {
|
673
|
+
Database_t *db;
|
674
|
+
GetOpenDatabase(self, db);
|
675
|
+
|
676
|
+
return rb_str_new2(sqlite3_errmsg(db->sqlite3_db));
|
677
|
+
}
|
678
|
+
|
679
|
+
#ifdef HAVE_SQLITE3_ERROR_OFFSET
|
680
|
+
/* call-seq:
|
681
|
+
* db.error_offset -> ofs
|
682
|
+
*
|
683
|
+
* Returns the offset for the last error
|
684
|
+
*/
|
685
|
+
VALUE Database_error_offset(VALUE self) {
|
686
|
+
Database_t *db;
|
687
|
+
GetOpenDatabase(self, db);
|
688
|
+
|
689
|
+
return INT2NUM(sqlite3_error_offset(db->sqlite3_db));
|
690
|
+
}
|
691
|
+
#endif
|
692
|
+
|
647
693
|
void Init_ExtraliteDatabase(void) {
|
648
694
|
VALUE mExtralite = rb_define_module("Extralite");
|
649
695
|
rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
|
@@ -658,6 +704,13 @@ void Init_ExtraliteDatabase(void) {
|
|
658
704
|
rb_define_method(cDatabase, "close", Database_close, 0);
|
659
705
|
rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
|
660
706
|
rb_define_method(cDatabase, "columns", Database_columns, 1);
|
707
|
+
rb_define_method(cDatabase, "errcode", Database_errcode, 0);
|
708
|
+
rb_define_method(cDatabase, "errmsg", Database_errmsg, 0);
|
709
|
+
|
710
|
+
#ifdef HAVE_SQLITE3_ERROR_OFFSET
|
711
|
+
rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
|
712
|
+
#endif
|
713
|
+
|
661
714
|
rb_define_method(cDatabase, "execute_multi", Database_execute_multi, 2);
|
662
715
|
rb_define_method(cDatabase, "filename", Database_filename, -1);
|
663
716
|
rb_define_method(cDatabase, "initialize", Database_initialize, 1);
|
@@ -689,9 +742,9 @@ void Init_ExtraliteDatabase(void) {
|
|
689
742
|
rb_gc_register_mark_object(cBusyError);
|
690
743
|
rb_gc_register_mark_object(cInterruptError);
|
691
744
|
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
745
|
+
ID_call = rb_intern("call");
|
746
|
+
ID_keys = rb_intern("keys");
|
747
|
+
ID_new = rb_intern("new");
|
748
|
+
ID_strip = rb_intern("strip");
|
749
|
+
ID_to_s = rb_intern("to_s");
|
697
750
|
}
|
@@ -2,8 +2,15 @@
|
|
2
2
|
|
3
3
|
require 'mkmf'
|
4
4
|
|
5
|
-
$CFLAGS <<
|
6
|
-
$CFLAGS <<
|
5
|
+
$CFLAGS << ' -Wno-undef'
|
6
|
+
$CFLAGS << ' -Wno-discarded-qualifiers'
|
7
|
+
$CFLAGS << ' -Wno-unused-function'
|
8
|
+
|
9
|
+
$defs << "-DHAVE_SQLITE3_ENABLE_LOAD_EXTENSION"
|
10
|
+
$defs << "-DHAVE_SQLITE3_LOAD_EXTENSION"
|
11
|
+
$defs << "-DHAVE_SQLITE3_ERROR_OFFSET"
|
12
|
+
|
13
|
+
have_func('usleep')
|
7
14
|
|
8
15
|
dir_config('extralite_ext')
|
9
16
|
create_makefile('extralite_ext')
|
data/ext/extralite/extconf.rb
CHANGED
@@ -1,115 +1,93 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
cppflags = "#{brew_prefix}/include"
|
35
|
-
pkg_conf = "#{brew_prefix}/lib/pkgconfig"
|
3
|
+
if ENV['EXTRALITE_BUNDLE']
|
4
|
+
require_relative('extconf-bundle')
|
5
|
+
else
|
6
|
+
ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
|
7
|
+
|
8
|
+
require 'mkmf'
|
9
|
+
|
10
|
+
# :stopdoc:
|
11
|
+
|
12
|
+
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
13
|
+
|
14
|
+
ldflags = cppflags = nil
|
15
|
+
if RbConfig::CONFIG["host_os"] =~ /darwin/
|
16
|
+
begin
|
17
|
+
if with_config('sqlcipher')
|
18
|
+
brew_prefix = `brew --prefix sqlcipher`.chomp
|
19
|
+
ldflags = "#{brew_prefix}/lib"
|
20
|
+
cppflags = "#{brew_prefix}/include/sqlcipher"
|
21
|
+
pkg_conf = "#{brew_prefix}/lib/pkgconfig"
|
22
|
+
else
|
23
|
+
brew_prefix = `brew --prefix sqlite3`.chomp
|
24
|
+
ldflags = "#{brew_prefix}/lib"
|
25
|
+
cppflags = "#{brew_prefix}/include"
|
26
|
+
pkg_conf = "#{brew_prefix}/lib/pkgconfig"
|
27
|
+
end
|
28
|
+
|
29
|
+
# pkg_config should be less error prone than parsing compiler
|
30
|
+
# commandline options, but we need to set default ldflags and cpp flags
|
31
|
+
# in case the user doesn't have pkg-config installed
|
32
|
+
ENV['PKG_CONFIG_PATH'] ||= pkg_conf
|
33
|
+
rescue
|
36
34
|
end
|
37
|
-
|
38
|
-
# pkg_config should be less error prone than parsing compiler
|
39
|
-
# commandline options, but we need to set default ldflags and cpp flags
|
40
|
-
# in case the user doesn't have pkg-config installed
|
41
|
-
ENV['PKG_CONFIG_PATH'] ||= pkg_conf
|
42
|
-
rescue
|
43
35
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
pkg_config("sqlcipher")
|
48
|
-
else
|
49
|
-
pkg_config("sqlite3")
|
50
|
-
end
|
51
|
-
|
52
|
-
# --with-sqlite3-{dir,include,lib}
|
53
|
-
if with_config('sqlcipher')
|
54
|
-
$CFLAGS << ' -DUSING_SQLCIPHER'
|
55
|
-
dir_config("sqlcipher", cppflags, ldflags)
|
56
|
-
else
|
57
|
-
dir_config("sqlite3", cppflags, ldflags)
|
58
|
-
end
|
59
|
-
|
60
|
-
if RbConfig::CONFIG["host_os"] =~ /mswin/
|
61
|
-
$CFLAGS << ' -W3'
|
62
|
-
end
|
63
|
-
|
64
|
-
if RUBY_VERSION < '2.7'
|
65
|
-
$CFLAGS << ' -DTAINTING_SUPPORT'
|
66
|
-
end
|
67
|
-
|
68
|
-
def asplode missing
|
69
|
-
if RUBY_PLATFORM =~ /mingw|mswin/
|
70
|
-
abort "#{missing} is missing. Install SQLite3 from " +
|
71
|
-
"http://www.sqlite.org/ first."
|
36
|
+
|
37
|
+
if with_config('sqlcipher')
|
38
|
+
pkg_config("sqlcipher")
|
72
39
|
else
|
73
|
-
|
74
|
-
#{missing} is missing. Try 'brew install sqlite3',
|
75
|
-
'yum install sqlite-devel' or 'apt-get install libsqlite3-dev'
|
76
|
-
and check your shared library search path (the
|
77
|
-
location where your sqlite3 shared library is located).
|
78
|
-
error
|
40
|
+
pkg_config("sqlite3")
|
79
41
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
42
|
+
|
43
|
+
# --with-sqlite3-{dir,include,lib}
|
44
|
+
if with_config('sqlcipher')
|
45
|
+
$CFLAGS << ' -DUSING_SQLCIPHER'
|
46
|
+
dir_config("sqlcipher", cppflags, ldflags)
|
47
|
+
else
|
48
|
+
dir_config("sqlite3", cppflags, ldflags)
|
49
|
+
end
|
50
|
+
|
51
|
+
if RbConfig::CONFIG["host_os"] =~ /mswin/
|
52
|
+
$CFLAGS << ' -W3'
|
53
|
+
end
|
54
|
+
|
55
|
+
if RUBY_VERSION < '2.7'
|
56
|
+
$CFLAGS << ' -DTAINTING_SUPPORT'
|
57
|
+
end
|
58
|
+
|
59
|
+
def asplode missing
|
60
|
+
if RUBY_PLATFORM =~ /mingw|mswin/
|
61
|
+
abort "#{missing} is missing. Install SQLite3 from " +
|
62
|
+
"http://www.sqlite.org/ first."
|
63
|
+
else
|
64
|
+
abort <<-error
|
65
|
+
#{missing} is missing. Try 'brew install sqlite3',
|
66
|
+
'yum install sqlite-devel' or 'apt-get install libsqlite3-dev'
|
67
|
+
and check your shared library search path (the
|
68
|
+
location where your sqlite3 shared library is located).
|
69
|
+
error
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
asplode('sqlite3.h') unless find_header 'sqlite3.h'
|
74
|
+
find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
|
75
|
+
|
76
|
+
have_library 'dl' # for static builds
|
77
|
+
|
78
|
+
if with_config('sqlcipher')
|
79
|
+
asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
|
80
|
+
else
|
81
|
+
asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
|
82
|
+
end
|
83
|
+
|
84
|
+
have_func('sqlite3_enable_load_extension')
|
85
|
+
have_func('sqlite3_load_extension')
|
86
|
+
have_func('sqlite3_prepare_v2')
|
87
|
+
have_func('sqlite3_error_offset')
|
88
|
+
|
89
|
+
$defs << "-DEXTRALITE_NO_BUNDLE"
|
90
|
+
|
91
|
+
dir_config('extralite_ext')
|
92
|
+
create_makefile('extralite_ext')
|
93
|
+
end
|
data/ext/extralite/extralite.h
CHANGED
@@ -27,11 +27,11 @@ extern VALUE cSQLError;
|
|
27
27
|
extern VALUE cBusyError;
|
28
28
|
extern VALUE cInterruptError;
|
29
29
|
|
30
|
-
extern ID
|
31
|
-
extern ID
|
32
|
-
extern ID
|
33
|
-
extern ID
|
34
|
-
extern ID
|
30
|
+
extern ID ID_call;
|
31
|
+
extern ID ID_keys;
|
32
|
+
extern ID ID_new;
|
33
|
+
extern ID ID_strip;
|
34
|
+
extern ID ID_to_s;
|
35
35
|
|
36
36
|
typedef struct {
|
37
37
|
sqlite3 *sqlite3_db;
|
@@ -59,13 +59,13 @@ typedef struct {
|
|
59
59
|
sqlite3_backup *p;
|
60
60
|
} backup_t;
|
61
61
|
|
62
|
+
VALUE safe_execute_multi(query_ctx *ctx);
|
62
63
|
VALUE safe_query_ary(query_ctx *ctx);
|
64
|
+
VALUE safe_query_columns(query_ctx *ctx);
|
63
65
|
VALUE safe_query_hash(query_ctx *ctx);
|
64
66
|
VALUE safe_query_single_column(query_ctx *ctx);
|
65
67
|
VALUE safe_query_single_row(query_ctx *ctx);
|
66
68
|
VALUE safe_query_single_value(query_ctx *ctx);
|
67
|
-
VALUE safe_execute_multi(query_ctx *ctx);
|
68
|
-
VALUE safe_query_columns(query_ctx *ctx);
|
69
69
|
|
70
70
|
void prepare_single_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
71
71
|
void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
@@ -20,7 +20,7 @@ static void PreparedStatement_free(void *ptr) {
|
|
20
20
|
}
|
21
21
|
|
22
22
|
static const rb_data_type_t PreparedStatement_type = {
|
23
|
-
"
|
23
|
+
"PreparedStatement",
|
24
24
|
{PreparedStatement_mark, PreparedStatement_free, PreparedStatement_size,},
|
25
25
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
26
26
|
};
|
@@ -41,11 +41,10 @@ static VALUE PreparedStatement_allocate(VALUE klass) {
|
|
41
41
|
* Initializes a new SQLite prepared statement with the given path.
|
42
42
|
*/
|
43
43
|
VALUE PreparedStatement_initialize(VALUE self, VALUE db, VALUE sql) {
|
44
|
-
// int rc;
|
45
44
|
PreparedStatement_t *stmt;
|
46
45
|
GetPreparedStatement(self, stmt);
|
47
46
|
|
48
|
-
sql = rb_funcall(sql,
|
47
|
+
sql = rb_funcall(sql, ID_strip, 0);
|
49
48
|
if (!RSTRING_LEN(sql))
|
50
49
|
rb_raise(cError, "Cannot prepare an empty SQL query");
|
51
50
|
|
@@ -66,7 +65,7 @@ static inline VALUE PreparedStatement_perform_query(int argc, VALUE *argv, VALUE
|
|
66
65
|
if (!stmt->stmt)
|
67
66
|
rb_raise(cError, "Prepared statement is closed");
|
68
67
|
|
69
|
-
if (stmt->db_struct->trace_block != Qnil) rb_funcall(stmt->db_struct->trace_block,
|
68
|
+
if (stmt->db_struct->trace_block != Qnil) rb_funcall(stmt->db_struct->trace_block, ID_call, 1, stmt->sql);
|
70
69
|
|
71
70
|
sqlite3_reset(stmt->stmt);
|
72
71
|
sqlite3_clear_bindings(stmt->stmt);
|
@@ -46,5 +46,113 @@ module Extralite
|
|
46
46
|
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8
|
47
47
|
SQLITE_LIMIT_VARIABLE_NUMBER = 9
|
48
48
|
SQLITE_LIMIT_TRIGGER_DEPTH = 10
|
49
|
-
SQLITE_LIMIT_WORKER_THREADS = 11
|
49
|
+
SQLITE_LIMIT_WORKER_THREADS = 11
|
50
|
+
|
51
|
+
SQLITE_OK = 0
|
52
|
+
SQLITE_ERROR = 1
|
53
|
+
SQLITE_INTERNAL = 2
|
54
|
+
SQLITE_PERM = 3
|
55
|
+
SQLITE_ABORT = 4
|
56
|
+
SQLITE_BUSY = 5
|
57
|
+
SQLITE_LOCKED = 6
|
58
|
+
SQLITE_NOMEM = 7
|
59
|
+
SQLITE_READONLY = 8
|
60
|
+
SQLITE_INTERRUPT = 9
|
61
|
+
SQLITE_IOERR = 10
|
62
|
+
SQLITE_CORRUPT = 11
|
63
|
+
SQLITE_NOTFOUND = 12
|
64
|
+
SQLITE_FULL = 13
|
65
|
+
SQLITE_CANTOPEN = 14
|
66
|
+
SQLITE_PROTOCOL = 15
|
67
|
+
SQLITE_EMPTY = 16
|
68
|
+
SQLITE_SCHEMA = 17
|
69
|
+
SQLITE_TOOBIG = 18
|
70
|
+
SQLITE_CONSTRAINT = 19
|
71
|
+
SQLITE_MISMATCH = 20
|
72
|
+
SQLITE_MISUSE = 21
|
73
|
+
SQLITE_NOLFS = 22
|
74
|
+
SQLITE_AUTH = 23
|
75
|
+
SQLITE_FORMAT = 24
|
76
|
+
SQLITE_RANGE = 25
|
77
|
+
SQLITE_NOTADB = 26
|
78
|
+
SQLITE_NOTICE = 27
|
79
|
+
SQLITE_WARNING = 28
|
80
|
+
SQLITE_ROW = 100
|
81
|
+
SQLITE_DONE = 101
|
82
|
+
|
83
|
+
SQLITE_ERROR_MISSING_COLLSEQ = (SQLITE_ERROR | (1<<8))
|
84
|
+
SQLITE_ERROR_RETRY = (SQLITE_ERROR | (2<<8))
|
85
|
+
SQLITE_ERROR_SNAPSHOT = (SQLITE_ERROR | (3<<8))
|
86
|
+
SQLITE_IOERR_READ = (SQLITE_IOERR | (1<<8))
|
87
|
+
SQLITE_IOERR_SHORT_READ = (SQLITE_IOERR | (2<<8))
|
88
|
+
SQLITE_IOERR_WRITE = (SQLITE_IOERR | (3<<8))
|
89
|
+
SQLITE_IOERR_FSYNC = (SQLITE_IOERR | (4<<8))
|
90
|
+
SQLITE_IOERR_DIR_FSYNC = (SQLITE_IOERR | (5<<8))
|
91
|
+
SQLITE_IOERR_TRUNCATE = (SQLITE_IOERR | (6<<8))
|
92
|
+
SQLITE_IOERR_FSTAT = (SQLITE_IOERR | (7<<8))
|
93
|
+
SQLITE_IOERR_UNLOCK = (SQLITE_IOERR | (8<<8))
|
94
|
+
SQLITE_IOERR_RDLOCK = (SQLITE_IOERR | (9<<8))
|
95
|
+
SQLITE_IOERR_DELETE = (SQLITE_IOERR | (10<<8))
|
96
|
+
SQLITE_IOERR_BLOCKED = (SQLITE_IOERR | (11<<8))
|
97
|
+
SQLITE_IOERR_NOMEM = (SQLITE_IOERR | (12<<8))
|
98
|
+
SQLITE_IOERR_ACCESS = (SQLITE_IOERR | (13<<8))
|
99
|
+
SQLITE_IOERR_CHECKRESERVEDLOCK = (SQLITE_IOERR | (14<<8))
|
100
|
+
SQLITE_IOERR_LOCK = (SQLITE_IOERR | (15<<8))
|
101
|
+
SQLITE_IOERR_CLOSE = (SQLITE_IOERR | (16<<8))
|
102
|
+
SQLITE_IOERR_DIR_CLOSE = (SQLITE_IOERR | (17<<8))
|
103
|
+
SQLITE_IOERR_SHMOPEN = (SQLITE_IOERR | (18<<8))
|
104
|
+
SQLITE_IOERR_SHMSIZE = (SQLITE_IOERR | (19<<8))
|
105
|
+
SQLITE_IOERR_SHMLOCK = (SQLITE_IOERR | (20<<8))
|
106
|
+
SQLITE_IOERR_SHMMAP = (SQLITE_IOERR | (21<<8))
|
107
|
+
SQLITE_IOERR_SEEK = (SQLITE_IOERR | (22<<8))
|
108
|
+
SQLITE_IOERR_DELETE_NOENT = (SQLITE_IOERR | (23<<8))
|
109
|
+
SQLITE_IOERR_MMAP = (SQLITE_IOERR | (24<<8))
|
110
|
+
SQLITE_IOERR_GETTEMPPATH = (SQLITE_IOERR | (25<<8))
|
111
|
+
SQLITE_IOERR_CONVPATH = (SQLITE_IOERR | (26<<8))
|
112
|
+
SQLITE_IOERR_VNODE = (SQLITE_IOERR | (27<<8))
|
113
|
+
SQLITE_IOERR_AUTH = (SQLITE_IOERR | (28<<8))
|
114
|
+
SQLITE_IOERR_BEGIN_ATOMIC = (SQLITE_IOERR | (29<<8))
|
115
|
+
SQLITE_IOERR_COMMIT_ATOMIC = (SQLITE_IOERR | (30<<8))
|
116
|
+
SQLITE_IOERR_ROLLBACK_ATOMIC = (SQLITE_IOERR | (31<<8))
|
117
|
+
SQLITE_IOERR_DATA = (SQLITE_IOERR | (32<<8))
|
118
|
+
SQLITE_IOERR_CORRUPTFS = (SQLITE_IOERR | (33<<8))
|
119
|
+
SQLITE_LOCKED_SHAREDCACHE = (SQLITE_LOCKED | (1<<8))
|
120
|
+
SQLITE_LOCKED_VTAB = (SQLITE_LOCKED | (2<<8))
|
121
|
+
SQLITE_BUSY_RECOVERY = (SQLITE_BUSY | (1<<8))
|
122
|
+
SQLITE_BUSY_SNAPSHOT = (SQLITE_BUSY | (2<<8))
|
123
|
+
SQLITE_BUSY_TIMEOUT = (SQLITE_BUSY | (3<<8))
|
124
|
+
SQLITE_CANTOPEN_NOTEMPDIR = (SQLITE_CANTOPEN | (1<<8))
|
125
|
+
SQLITE_CANTOPEN_ISDIR = (SQLITE_CANTOPEN | (2<<8))
|
126
|
+
SQLITE_CANTOPEN_FULLPATH = (SQLITE_CANTOPEN | (3<<8))
|
127
|
+
SQLITE_CANTOPEN_CONVPATH = (SQLITE_CANTOPEN | (4<<8))
|
128
|
+
SQLITE_CANTOPEN_DIRTYWAL = (SQLITE_CANTOPEN | (5<<8))
|
129
|
+
SQLITE_CANTOPEN_SYMLINK = (SQLITE_CANTOPEN | (6<<8))
|
130
|
+
SQLITE_CORRUPT_VTAB = (SQLITE_CORRUPT | (1<<8))
|
131
|
+
SQLITE_CORRUPT_SEQUENCE = (SQLITE_CORRUPT | (2<<8))
|
132
|
+
SQLITE_CORRUPT_INDEX = (SQLITE_CORRUPT | (3<<8))
|
133
|
+
SQLITE_READONLY_RECOVERY = (SQLITE_READONLY | (1<<8))
|
134
|
+
SQLITE_READONLY_CANTLOCK = (SQLITE_READONLY | (2<<8))
|
135
|
+
SQLITE_READONLY_ROLLBACK = (SQLITE_READONLY | (3<<8))
|
136
|
+
SQLITE_READONLY_DBMOVED = (SQLITE_READONLY | (4<<8))
|
137
|
+
SQLITE_READONLY_CANTINIT = (SQLITE_READONLY | (5<<8))
|
138
|
+
SQLITE_READONLY_DIRECTORY = (SQLITE_READONLY | (6<<8))
|
139
|
+
SQLITE_ABORT_ROLLBACK = (SQLITE_ABORT | (2<<8))
|
140
|
+
SQLITE_CONSTRAINT_CHECK = (SQLITE_CONSTRAINT | (1<<8))
|
141
|
+
SQLITE_CONSTRAINT_COMMITHOOK = (SQLITE_CONSTRAINT | (2<<8))
|
142
|
+
SQLITE_CONSTRAINT_FOREIGNKEY = (SQLITE_CONSTRAINT | (3<<8))
|
143
|
+
SQLITE_CONSTRAINT_FUNCTION = (SQLITE_CONSTRAINT | (4<<8))
|
144
|
+
SQLITE_CONSTRAINT_NOTNULL = (SQLITE_CONSTRAINT | (5<<8))
|
145
|
+
SQLITE_CONSTRAINT_PRIMARYKEY = (SQLITE_CONSTRAINT | (6<<8))
|
146
|
+
SQLITE_CONSTRAINT_TRIGGER = (SQLITE_CONSTRAINT | (7<<8))
|
147
|
+
SQLITE_CONSTRAINT_UNIQUE = (SQLITE_CONSTRAINT | (8<<8))
|
148
|
+
SQLITE_CONSTRAINT_VTAB = (SQLITE_CONSTRAINT | (9<<8))
|
149
|
+
SQLITE_CONSTRAINT_ROWID = (SQLITE_CONSTRAINT |(10<<8))
|
150
|
+
SQLITE_CONSTRAINT_PINNED = (SQLITE_CONSTRAINT |(11<<8))
|
151
|
+
SQLITE_CONSTRAINT_DATATYPE = (SQLITE_CONSTRAINT |(12<<8))
|
152
|
+
SQLITE_NOTICE_RECOVER_WAL = (SQLITE_NOTICE | (1<<8))
|
153
|
+
SQLITE_NOTICE_RECOVER_ROLLBACK = (SQLITE_NOTICE | (2<<8))
|
154
|
+
SQLITE_WARNING_AUTOINDEX = (SQLITE_WARNING | (1<<8))
|
155
|
+
SQLITE_AUTH_USER = (SQLITE_AUTH | (1<<8))
|
156
|
+
SQLITE_OK_LOAD_PERMANENTLY = (SQLITE_OK | (1<<8))
|
157
|
+
SQLITE_OK_SYMLINK = (SQLITE_OK | (2<<8))
|
50
158
|
end
|
data/lib/extralite/version.rb
CHANGED
data/test/test_database.rb
CHANGED
@@ -169,8 +169,6 @@ end
|
|
169
169
|
assert_nil r
|
170
170
|
end
|
171
171
|
|
172
|
-
|
173
|
-
|
174
172
|
def test_extension_loading
|
175
173
|
case RUBY_PLATFORM
|
176
174
|
when /linux/
|
@@ -291,7 +289,7 @@ end
|
|
291
289
|
db1.query('begin exclusive')
|
292
290
|
assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
|
293
291
|
|
294
|
-
db2.busy_timeout =
|
292
|
+
db2.busy_timeout = 3
|
295
293
|
t0 = Time.now
|
296
294
|
t = Thread.new { sleep 0.1; db1.query('rollback') }
|
297
295
|
result = db2.query('begin exclusive')
|
@@ -300,19 +298,24 @@ end
|
|
300
298
|
assert_equal [], result
|
301
299
|
assert t1 - t0 >= 0.1
|
302
300
|
db2.query('rollback')
|
301
|
+
t.join
|
303
302
|
|
304
303
|
# try to provoke a timeout
|
305
304
|
db1.query('begin exclusive')
|
306
|
-
db2.busy_timeout =
|
305
|
+
db2.busy_timeout = nil
|
306
|
+
assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
|
307
|
+
|
308
|
+
db2.busy_timeout = 0.2
|
307
309
|
t0 = Time.now
|
308
310
|
t = Thread.new do
|
309
|
-
sleep
|
311
|
+
sleep 3
|
310
312
|
ensure
|
311
313
|
db1.query('rollback')
|
312
314
|
end
|
313
315
|
assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
|
316
|
+
|
314
317
|
t1 = Time.now
|
315
|
-
assert t1 - t0 >= 0.
|
318
|
+
assert t1 - t0 >= 0.2
|
316
319
|
t.kill
|
317
320
|
t.join
|
318
321
|
|
@@ -331,6 +334,33 @@ end
|
|
331
334
|
|
332
335
|
assert_equal 3, @db.total_changes
|
333
336
|
end
|
337
|
+
|
338
|
+
def test_database_errcode_errmsg
|
339
|
+
assert_equal 0, @db.errcode
|
340
|
+
assert_equal 'not an error', @db.errmsg
|
341
|
+
|
342
|
+
@db.query('select foo') rescue nil
|
343
|
+
|
344
|
+
assert_equal 1, @db.errcode
|
345
|
+
assert_equal 'no such column: foo', @db.errmsg
|
346
|
+
|
347
|
+
if Extralite.sqlite3_version >= '3.38.5'
|
348
|
+
assert_equal 7, @db.error_offset
|
349
|
+
end
|
350
|
+
|
351
|
+
@db.query('create table t2 (v not null)')
|
352
|
+
|
353
|
+
assert_raises(Extralite::Error) { @db.query('insert into t2 values (null)') }
|
354
|
+
assert_equal Extralite::SQLITE_CONSTRAINT_NOTNULL, @db.errcode
|
355
|
+
assert_equal 'NOT NULL constraint failed: t2.v', @db.errmsg
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
def test_close_with_open_prepared_statement
|
360
|
+
stmt = @db.prepare('select * from t')
|
361
|
+
stmt.query
|
362
|
+
@db.close
|
363
|
+
end
|
334
364
|
end
|
335
365
|
|
336
366
|
class ScenarioTest < MiniTest::Test
|
@@ -216,4 +216,10 @@ end
|
|
216
216
|
assert_equal 3, @stmt.status(Extralite::SQLITE_STMTSTATUS_RUN, true)
|
217
217
|
assert_equal 0, @stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
|
218
218
|
end
|
219
|
+
|
220
|
+
def test_query_after_db_close
|
221
|
+
assert_equal [{ x: 4, y: 5, z: 6}], @stmt.query(4)
|
222
|
+
@db.close
|
223
|
+
assert_equal [{ x: 4, y: 5, z: 6}], @stmt.query(4)
|
224
|
+
end
|
219
225
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: extralite-bundle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.24'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|