extralite 1.23 → 1.24

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7466fb655d5f2cc65862d604190d52208773a9cc8fb611b184bc46a20ccb8d1f
4
- data.tar.gz: bbc9b59c5c2297df34d716a19b53ccc95008bea3be4d38d3cdd91340089962b5
3
+ metadata.gz: bbe4f33eced375588c90c6f0e2c3dd5b25ca19e44e3a9b4a20f21e43d7a833ff
4
+ data.tar.gz: 709a92b6edd0b54531652d3c572b5dcac21e8db09b6c516c8f07332bb4daa6ee
5
5
  SHA512:
6
- metadata.gz: 227a7fb703cca6eed8b25f0adb32cf8421f3c503149a715006c381d5cca475e692048650acc561ccfcb5ff1203c489fa6fe429922aecb2c0486cd0ec83c85360
7
- data.tar.gz: 131aa35e939c4623a306be6e7a756d40cfe77a5b199ffc90cb1937194e7066ba6fa4de3d4cc8618b8123b68dcfe9e92a60b3e821c49b51145497deb005467f24
6
+ metadata.gz: b1c8f1cfc27bfe888f6c92be36f00631dda67471f937c7d94d5b731c94b90fd3b83f322891186f1cc9baf792dafe2df02cd1dc09f8cef7e4030bce79c048f9c1
7
+ data.tar.gz: 6e4de82d209255d623bc090b29b5d71da1922a6d70d5b3879a475ace8acb77de8b75ee688c51b45b71c9e9b66c56e71bc341da6377366baf66388cbc261f65b5
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
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