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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6622a72a5d4e7ea9670be11b21708f2f8eedda7d94e870ea283d6e8b0d13471e
4
- data.tar.gz: 1eed8cd7ca3fe7844dead0024463efe285f4979eef2be94741c96bf42b447a42
3
+ metadata.gz: eed963dc582b5492ef4601faf29359be35a322c4e0fee60cca6d4c532e7e41bf
4
+ data.tar.gz: 16776f46b60e4ed42408269a0a635de370830b390699991b56768fab625a2ce5
5
5
  SHA512:
6
- metadata.gz: 3300b9369bffded248aa4291db0f18da92a2c95015b0fbd4afc668c2783c223cb72d4cfb207ca1dace057a41c3e61a38ee37e0b8ffe5a1199a147143374c9f6d
7
- data.tar.gz: 17d532b9c92238f3b94f073b2e7c13d793a872511778f7138e371a8e0a8f4e776d830a00874df203de514d2143e8340d42f479e2f267105a907226892540d74b
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extralite (1.23)
4
+ extralite (1.24)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -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, ID_KEYS, 0);
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, ID_TO_S, 0);
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));
@@ -7,11 +7,11 @@ VALUE cSQLError;
7
7
  VALUE cBusyError;
8
8
  VALUE cInterruptError;
9
9
 
10
- ID ID_CALL;
11
- ID ID_KEYS;
12
- ID ID_NEW;
13
- ID ID_STRIP;
14
- ID ID_TO_S;
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) sqlite3_close(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
- sqlite3_close(db->sqlite3_db);
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
- sqlite3_close(db->sqlite3_db);
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 = sqlite3_close(db->sqlite3_db);
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], ID_STRIP, 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, ID_CALL, 1, sql);
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, ID_NEW, 2, self, sql);
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
- sqlite3_close(ctx->dst);
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
- sqlite3_close(dst_db);
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
- sqlite3_close(dst_db);
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
- ID_CALL = rb_intern("call");
693
- ID_KEYS = rb_intern("keys");
694
- ID_NEW = rb_intern("new");
695
- ID_STRIP = rb_intern("strip");
696
- ID_TO_S = rb_intern("to_s");
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 << " -Wno-undef"
6
- $CFLAGS << " -Wno-discarded-qualifiers"
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')
@@ -1,115 +1,93 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # require 'rubygems'
4
- # require 'mkmf'
5
-
6
- # # $CFLAGS << "-Wdiscarded-qualifier"
7
- # # $CFLAGS << " -Wno-comment"
8
- # # $CFLAGS << " -Wno-unused-result"
9
- # # $CFLAGS << " -Wno-dangling-else"
10
- # # $CFLAGS << " -Wno-parentheses"
11
-
12
- # dir_config 'extralite_ext'
13
- # create_makefile 'extralite_ext'
14
-
15
- ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
16
-
17
- require 'mkmf'
18
-
19
- # :stopdoc:
20
-
21
- RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
22
-
23
- ldflags = cppflags = nil
24
- if RbConfig::CONFIG["host_os"] =~ /darwin/
25
- begin
26
- if with_config('sqlcipher')
27
- brew_prefix = `brew --prefix sqlcipher`.chomp
28
- ldflags = "#{brew_prefix}/lib"
29
- cppflags = "#{brew_prefix}/include/sqlcipher"
30
- pkg_conf = "#{brew_prefix}/lib/pkgconfig"
31
- else
32
- brew_prefix = `brew --prefix sqlite3`.chomp
33
- ldflags = "#{brew_prefix}/lib"
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
- end
45
-
46
- if with_config('sqlcipher')
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
- abort <<-error
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
- end
81
-
82
- asplode('sqlite3.h') unless find_header 'sqlite3.h'
83
- find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
84
-
85
- have_library 'dl' # for static builds
86
-
87
- if with_config('sqlcipher')
88
- asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
89
- else
90
- asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
91
- end
92
-
93
- # Functions defined in 1.9 but not 1.8
94
- have_func('rb_proc_arity')
95
-
96
- # Functions defined in 2.1 but not 2.0
97
- have_func('rb_integer_pack')
98
-
99
- # These functions may not be defined
100
- have_func('sqlite3_initialize')
101
- have_func('sqlite3_enable_load_extension')
102
- have_func('sqlite3_load_extension')
103
-
104
- unless have_func('sqlite3_open_v2')
105
- abort 'Please use a newer version of SQLite3'
106
- end
107
-
108
- have_func('sqlite3_prepare_v2')
109
- have_type('sqlite3_int64', 'sqlite3.h')
110
- have_type('sqlite3_uint64', 'sqlite3.h')
111
-
112
- $defs << "-DEXTRALITE_NO_BUNDLE"
113
-
114
- dir_config('extralite_ext')
115
- create_makefile('extralite_ext')
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
@@ -27,11 +27,11 @@ extern VALUE cSQLError;
27
27
  extern VALUE cBusyError;
28
28
  extern VALUE cInterruptError;
29
29
 
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;
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
- "Database",
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, ID_STRIP, 0);
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, ID_CALL, 1, stmt->sql);
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
@@ -1,3 +1,3 @@
1
1
  module Extralite
2
- VERSION = '1.23'
2
+ VERSION = '1.24'
3
3
  end
@@ -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 = 0.3
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 = 0.1
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 0.5
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.1
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.23'
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-01-26 00:00:00.000000000 Z
11
+ date: 2023-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler