sqlite3 2.1.1 → 2.3.0

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: ba78bd4c240a97cfa57d6e56eea80709be29b1d07e94e6e1c7e79ca11719b481
4
- data.tar.gz: 8942cd19283f45b01ed0727f4d407d663dfcec1a3acedbeb742d08e8f0c45ac7
3
+ metadata.gz: a17035c1e1072a5231c5b05ea95a9bbe5e55090b92e51eae0098dcffed86dca2
4
+ data.tar.gz: f5bb0c4c6cc6baacef41d168ea6b00f5b20620bc348715722400f3cc9bf6e1ed
5
5
  SHA512:
6
- metadata.gz: d4bec2b4c73eb8b71475f142eda220ea7a2ce54e62d6170df84ba193d4d31cf61809864db855b480f34faad9665db64f0bce741e7dfd4d381bd2652d84fdc769
7
- data.tar.gz: 2d31507297abcf4dfa5da3f78301a39ae5b7fd31a54b0c92cb8d31f3af704129c70f021444c763136208057dee036bb402a1598aa9e89ad6ae69f7ae1db84a9e
6
+ metadata.gz: 77bd1ee33f9d84393f5b9e3cc5ce225a39b67de4017526ccb90d6bccb017f95c529ace626121373e382ee20164117ab1959cefc456c004ecfc1b82784f731011
7
+ data.tar.gz: 628d16452c3e2f1181da73f7d8af8e8247b2a84159b810ef5c9c3ea5d0eddc21c06fa3bb6c6d4d4d6a3beb0a7f6c8e3378cde9e7ea20ae6cff5342b46a2d0ef1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # sqlite3-ruby Changelog
2
2
 
3
+ ## 2.3.0 / 2024-11-20
4
+
5
+ ### Added
6
+
7
+ - The SQLITE_DBPAGE extension is now enabled by default, which implements an eponymous-only virtual table that provides direct access to the underlying database file by interacting with the pager. See https://www.sqlite.org/dbpage.html for more information. [#578] @flavorjones
8
+ - The DBSTAT extension is now enabled by default, which implements a read-only eponymous virtual table that returns information about the amount of disk space used to store the content of an SQLite database. See https://sqlite.org/dbstat.html for more information. [#580] @pawurb @flavorjones
9
+ - `Database#optimize` which wraps the `pragma optimize;` statement. Also added `Constants::Optimize` to allow advanced users to pass a bitmask of options. See https://www.sqlite.org/pragma.html#pragma_optimize. [#572] @alexcwatt @flavorjones
10
+ - `SQLite3::VERSION_INFO` is contains a bag of metadata about the gem and the sqlite library used. `SQLite3::SQLITE_PACKAGED_LIBRARIES` and `SQLite3::SQLITE_PRECOMPILED_LIBRARIES` are indicate how the gem was built. [#581] @flavorjones
11
+
12
+
13
+ ### Fixed
14
+
15
+ - `Database#encoding=` support for switching the database encoding to `UTF-16BE`, which has been broken since `Database#encoding=` was introduced in v1.3.12 in 2016. [#575] @miyucy
16
+ - Omit mention of the `pkg-config` gem when failing to build from source, since it is not used. [#358] @flavorjones
17
+
18
+
19
+ ## 2.2.0 / 2024-10-30
20
+
21
+ ### Added
22
+
23
+ - URI filenames are now allowed. This allows the injection of some behavior via recognized query parameters. See https://www.sqlite.org/uri.html for more information. [#571] @flavorjones
24
+
25
+
26
+ ### Improved
27
+
28
+ - SQL Syntax errors during `Database#prepare` will raise a verbose exception with a multiline message indicating with a "^" exactly where in the statement the error occurred. [#554] @fractaledmind @flavorjones
29
+
30
+
3
31
  ## 2.1.1 / 2024-10-22
4
32
 
5
33
  ### Dependencies
@@ -1,101 +1,82 @@
1
1
  #include <sqlite3_ruby.h>
2
2
 
3
- void
4
- rb_sqlite3_raise(sqlite3 *db, int status)
3
+ static VALUE
4
+ status2klass(int status)
5
5
  {
6
- VALUE klass = Qnil;
7
-
8
6
  /* Consider only lower 8 bits, to work correctly when
9
7
  extended result codes are enabled. */
10
8
  switch (status & 0xff) {
11
9
  case SQLITE_OK:
12
- return;
13
- break;
10
+ return Qnil;
14
11
  case SQLITE_ERROR:
15
- klass = rb_path2class("SQLite3::SQLException");
16
- break;
12
+ return rb_path2class("SQLite3::SQLException");
17
13
  case SQLITE_INTERNAL:
18
- klass = rb_path2class("SQLite3::InternalException");
19
- break;
14
+ return rb_path2class("SQLite3::InternalException");
20
15
  case SQLITE_PERM:
21
- klass = rb_path2class("SQLite3::PermissionException");
22
- break;
16
+ return rb_path2class("SQLite3::PermissionException");
23
17
  case SQLITE_ABORT:
24
- klass = rb_path2class("SQLite3::AbortException");
25
- break;
18
+ return rb_path2class("SQLite3::AbortException");
26
19
  case SQLITE_BUSY:
27
- klass = rb_path2class("SQLite3::BusyException");
28
- break;
20
+ return rb_path2class("SQLite3::BusyException");
29
21
  case SQLITE_LOCKED:
30
- klass = rb_path2class("SQLite3::LockedException");
31
- break;
22
+ return rb_path2class("SQLite3::LockedException");
32
23
  case SQLITE_NOMEM:
33
- klass = rb_path2class("SQLite3::MemoryException");
34
- break;
24
+ return rb_path2class("SQLite3::MemoryException");
35
25
  case SQLITE_READONLY:
36
- klass = rb_path2class("SQLite3::ReadOnlyException");
37
- break;
26
+ return rb_path2class("SQLite3::ReadOnlyException");
38
27
  case SQLITE_INTERRUPT:
39
- klass = rb_path2class("SQLite3::InterruptException");
40
- break;
28
+ return rb_path2class("SQLite3::InterruptException");
41
29
  case SQLITE_IOERR:
42
- klass = rb_path2class("SQLite3::IOException");
43
- break;
30
+ return rb_path2class("SQLite3::IOException");
44
31
  case SQLITE_CORRUPT:
45
- klass = rb_path2class("SQLite3::CorruptException");
46
- break;
32
+ return rb_path2class("SQLite3::CorruptException");
47
33
  case SQLITE_NOTFOUND:
48
- klass = rb_path2class("SQLite3::NotFoundException");
49
- break;
34
+ return rb_path2class("SQLite3::NotFoundException");
50
35
  case SQLITE_FULL:
51
- klass = rb_path2class("SQLite3::FullException");
52
- break;
36
+ return rb_path2class("SQLite3::FullException");
53
37
  case SQLITE_CANTOPEN:
54
- klass = rb_path2class("SQLite3::CantOpenException");
55
- break;
38
+ return rb_path2class("SQLite3::CantOpenException");
56
39
  case SQLITE_PROTOCOL:
57
- klass = rb_path2class("SQLite3::ProtocolException");
58
- break;
40
+ return rb_path2class("SQLite3::ProtocolException");
59
41
  case SQLITE_EMPTY:
60
- klass = rb_path2class("SQLite3::EmptyException");
61
- break;
42
+ return rb_path2class("SQLite3::EmptyException");
62
43
  case SQLITE_SCHEMA:
63
- klass = rb_path2class("SQLite3::SchemaChangedException");
64
- break;
44
+ return rb_path2class("SQLite3::SchemaChangedException");
65
45
  case SQLITE_TOOBIG:
66
- klass = rb_path2class("SQLite3::TooBigException");
67
- break;
46
+ return rb_path2class("SQLite3::TooBigException");
68
47
  case SQLITE_CONSTRAINT:
69
- klass = rb_path2class("SQLite3::ConstraintException");
70
- break;
48
+ return rb_path2class("SQLite3::ConstraintException");
71
49
  case SQLITE_MISMATCH:
72
- klass = rb_path2class("SQLite3::MismatchException");
73
- break;
50
+ return rb_path2class("SQLite3::MismatchException");
74
51
  case SQLITE_MISUSE:
75
- klass = rb_path2class("SQLite3::MisuseException");
76
- break;
52
+ return rb_path2class("SQLite3::MisuseException");
77
53
  case SQLITE_NOLFS:
78
- klass = rb_path2class("SQLite3::UnsupportedException");
79
- break;
54
+ return rb_path2class("SQLite3::UnsupportedException");
80
55
  case SQLITE_AUTH:
81
- klass = rb_path2class("SQLite3::AuthorizationException");
82
- break;
56
+ return rb_path2class("SQLite3::AuthorizationException");
83
57
  case SQLITE_FORMAT:
84
- klass = rb_path2class("SQLite3::FormatException");
85
- break;
58
+ return rb_path2class("SQLite3::FormatException");
86
59
  case SQLITE_RANGE:
87
- klass = rb_path2class("SQLite3::RangeException");
88
- break;
60
+ return rb_path2class("SQLite3::RangeException");
89
61
  case SQLITE_NOTADB:
90
- klass = rb_path2class("SQLite3::NotADatabaseException");
91
- break;
62
+ return rb_path2class("SQLite3::NotADatabaseException");
92
63
  default:
93
- klass = rb_path2class("SQLite3::Exception");
64
+ return rb_path2class("SQLite3::Exception");
65
+ }
66
+ }
67
+
68
+ void
69
+ rb_sqlite3_raise(sqlite3 *db, int status)
70
+ {
71
+ VALUE klass = status2klass(status);
72
+ if (NIL_P(klass)) {
73
+ return;
94
74
  }
95
75
 
96
- klass = rb_exc_new2(klass, sqlite3_errmsg(db));
97
- rb_iv_set(klass, "@code", INT2FIX(status));
98
- rb_exc_raise(klass);
76
+ VALUE exception = rb_exc_new2(klass, sqlite3_errmsg(db));
77
+ rb_iv_set(exception, "@code", INT2FIX(status));
78
+
79
+ rb_exc_raise(exception);
99
80
  }
100
81
 
101
82
  /*
@@ -104,14 +85,38 @@ rb_sqlite3_raise(sqlite3 *db, int status)
104
85
  void
105
86
  rb_sqlite3_raise_msg(sqlite3 *db, int status, const char *msg)
106
87
  {
107
- VALUE exception;
108
-
109
- if (status == SQLITE_OK) {
88
+ VALUE klass = status2klass(status);
89
+ if (NIL_P(klass)) {
110
90
  return;
111
91
  }
112
92
 
113
- exception = rb_exc_new2(rb_path2class("SQLite3::Exception"), msg);
93
+ VALUE exception = rb_exc_new2(klass, msg);
94
+ rb_iv_set(exception, "@code", INT2FIX(status));
114
95
  sqlite3_free((void *)msg);
96
+
97
+ rb_exc_raise(exception);
98
+ }
99
+
100
+ void
101
+ rb_sqlite3_raise_with_sql(sqlite3 *db, int status, const char *sql)
102
+ {
103
+ VALUE klass = status2klass(status);
104
+ if (NIL_P(klass)) {
105
+ return;
106
+ }
107
+
108
+ const char *error_msg = sqlite3_errmsg(db);
109
+ int error_offset = -1;
110
+ #ifdef HAVE_SQLITE3_ERROR_OFFSET
111
+ error_offset = sqlite3_error_offset(db);
112
+ #endif
113
+
114
+ VALUE exception = rb_exc_new2(klass, error_msg);
115
115
  rb_iv_set(exception, "@code", INT2FIX(status));
116
+ if (sql) {
117
+ rb_iv_set(exception, "@sql", rb_str_new2(sql));
118
+ rb_iv_set(exception, "@sql_offset", INT2FIX(error_offset));
119
+ }
120
+
116
121
  rb_exc_raise(exception);
117
122
  }
@@ -3,8 +3,10 @@
3
3
 
4
4
  #define CHECK(_db, _status) rb_sqlite3_raise(_db, _status);
5
5
  #define CHECK_MSG(_db, _status, _msg) rb_sqlite3_raise_msg(_db, _status, _msg);
6
+ #define CHECK_PREPARE(_db, _status, _sql) rb_sqlite3_raise_with_sql(_db, _status, _sql)
6
7
 
7
8
  void rb_sqlite3_raise(sqlite3 *db, int status);
8
9
  void rb_sqlite3_raise_msg(sqlite3 *db, int status, const char *msg);
10
+ void rb_sqlite3_raise_with_sql(sqlite3 *db, int status, const char *sql);
9
11
 
10
12
  #endif
@@ -50,8 +50,9 @@ module Sqlite3
50
50
  def configure_packaged_libraries
51
51
  minimal_recipe.tap do |recipe|
52
52
  recipe.configure_options += [
53
- "--enable-shared=no",
54
- "--enable-static=yes",
53
+ "--disable-shared",
54
+ "--enable-static",
55
+ "--disable-tcl",
55
56
  "--enable-fts5"
56
57
  ]
57
58
  ENV.to_h.tap do |env|
@@ -60,7 +61,10 @@ module Sqlite3
60
61
  "-fPIC", # needed for linking the static library into a shared library
61
62
  "-O2", # see https://github.com/sparklemotion/sqlite3-ruby/issues/335 for some benchmarks
62
63
  "-fvisibility=hidden", # see https://github.com/rake-compiler/rake-compiler-dock/issues/87
63
- "-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1"
64
+ "-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1",
65
+ "-DSQLITE_USE_URI=1",
66
+ "-DSQLITE_ENABLE_DBPAGE_VTAB=1",
67
+ "-DSQLITE_ENABLE_DBSTAT_VTAB=1"
64
68
  ]
65
69
  env["CFLAGS"] = [user_cflags, env["CFLAGS"], more_cflags].flatten.join(" ")
66
70
  recipe.configure_options += env.select { |k, v| ENV_ALLOWLIST.include?(k) }
@@ -93,6 +97,9 @@ module Sqlite3
93
97
  end
94
98
 
95
99
  ldflags.each { |ldflag| append_ldflags(ldflag) }
100
+
101
+ append_cppflags("-DUSING_PACKAGED_LIBRARIES")
102
+ append_cppflags("-DUSING_PRECOMPILED_LIBRARIES") if cross_build?
96
103
  end
97
104
  end
98
105
 
@@ -132,6 +139,7 @@ module Sqlite3
132
139
 
133
140
  have_func("sqlite3_prepare_v2")
134
141
  have_func("sqlite3_db_name", "sqlite3.h") # v3.39.0
142
+ have_func("sqlite3_error_offset", "sqlite3.h") # v3.38.0
135
143
 
136
144
  have_type("sqlite3_int64", "sqlite3.h")
137
145
  have_type("sqlite3_uint64", "sqlite3.h")
@@ -168,7 +176,7 @@ module Sqlite3
168
176
  end
169
177
 
170
178
  def abort_pkg_config(id)
171
- abort("\nCould not configure the build properly (#{id}). Please install either the `pkg-config` utility or the `pkg-config` rubygem.\n\n")
179
+ abort("\nCould not configure the build properly (#{id}). Please install the `pkg-config` utility.\n\n")
172
180
  end
173
181
 
174
182
  def cross_build?
@@ -201,8 +201,25 @@ Init_sqlite3_native(void)
201
201
  rb_define_singleton_method(mSqlite3, "libversion", libversion, 0);
202
202
  rb_define_singleton_method(mSqlite3, "threadsafe", threadsafe_p, 0);
203
203
  rb_define_singleton_method(mSqlite3, "status", rb_sqlite3_status, -1);
204
+
205
+ /* (String) The version of the sqlite3 library compiled with (e.g., "3.46.1") */
204
206
  rb_define_const(mSqlite3, "SQLITE_VERSION", rb_str_new2(SQLITE_VERSION));
207
+
208
+ /* (Integer) The version of the sqlite3 library compiled with (e.g., 346001) */
205
209
  rb_define_const(mSqlite3, "SQLITE_VERSION_NUMBER", INT2FIX(SQLITE_VERSION_NUMBER));
210
+
211
+ /* (String) The version of the sqlite3 library loaded at runtime (e.g., "3.46.1") */
206
212
  rb_define_const(mSqlite3, "SQLITE_LOADED_VERSION", rb_str_new2(sqlite3_libversion()));
207
213
 
214
+ #ifdef USING_PACKAGED_LIBRARIES
215
+ rb_define_const(mSqlite3, "SQLITE_PACKAGED_LIBRARIES", Qtrue);
216
+ #else
217
+ rb_define_const(mSqlite3, "SQLITE_PACKAGED_LIBRARIES", Qfalse);
218
+ #endif
219
+
220
+ #ifdef USING_PRECOMPILED_LIBRARIES
221
+ rb_define_const(mSqlite3, "SQLITE_PRECOMPILED_LIBRARIES", Qtrue);
222
+ #else
223
+ rb_define_const(mSqlite3, "SQLITE_PRECOMPILED_LIBRARIES", Qfalse);
224
+ #endif
208
225
  }
@@ -78,7 +78,7 @@ prepare(VALUE self, VALUE db, VALUE sql)
78
78
  &tail
79
79
  );
80
80
 
81
- CHECK(db_ctx->db, status);
81
+ CHECK_PREPARE(db_ctx->db, status, StringValuePtr(sql));
82
82
  timespecclear(&db_ctx->stmt_deadline);
83
83
 
84
84
  return rb_utf8_str_new_cstr(tail);
@@ -170,5 +170,29 @@ module SQLite3
170
170
  # This parameter records the number of separate memory allocations currently checked out.
171
171
  MALLOC_COUNT = 9
172
172
  end
173
+
174
+ module Optimize
175
+ # Debugging mode. Do not actually perform any optimizations but instead return one line of
176
+ # text for each optimization that would have been done. Off by default.
177
+ DEBUG = 0x00001
178
+
179
+ # Run ANALYZE on tables that might benefit. On by default.
180
+ ANALYZE_TABLES = 0x00002
181
+
182
+ # When running ANALYZE, set a temporary PRAGMA analysis_limit to prevent excess run-time. On
183
+ # by default.
184
+ LIMIT_ANALYZE = 0x00010
185
+
186
+ # Check the size of all tables, not just tables that have not been recently used, to see if
187
+ # any have grown and shrunk significantly and hence might benefit from being re-analyzed. Off
188
+ # by default.
189
+ CHECK_ALL_TABLES = 0x10000
190
+
191
+ # Useful for adding a bit to the default behavior, for example
192
+ #
193
+ # db.optimize(Optimize::DEFAULT | Optimize::CHECK_ALL_TABLES)
194
+ #
195
+ DEFAULT = ANALYZE_TABLES | LIMIT_ANALYZE
196
+ end
173
197
  end
174
198
  end
@@ -4,6 +4,34 @@ module SQLite3
4
4
  class Exception < ::StandardError
5
5
  # A convenience for accessing the error code for this exception.
6
6
  attr_reader :code
7
+
8
+ # If the error is associated with a SQL query, this is the query
9
+ attr_reader :sql
10
+
11
+ # If the error is associated with a particular offset in a SQL query, this is the non-negative
12
+ # offset. If the offset is not available, this will be -1.
13
+ attr_reader :sql_offset
14
+
15
+ def message
16
+ [super, sql_error].compact.join(":\n")
17
+ end
18
+
19
+ private def sql_error
20
+ return nil unless @sql
21
+ return @sql.chomp unless @sql_offset >= 0
22
+
23
+ offset = @sql_offset
24
+ sql.lines.flat_map do |line|
25
+ if offset >= 0 && line.length > offset
26
+ blanks = " " * offset
27
+ offset = -1
28
+ [line.chomp, blanks + "^"]
29
+ else
30
+ offset -= line.length if offset
31
+ line.chomp
32
+ end
33
+ end.join("\n")
34
+ end
7
35
  end
8
36
 
9
37
  class SQLException < Exception; end
@@ -2,10 +2,10 @@
2
2
 
3
3
  require "weakref"
4
4
 
5
- # based on Rails's active_support/fork_tracker.rb
6
5
  module SQLite3
6
+ # based on Rails's active_support/fork_tracker.rb
7
7
  module ForkSafety
8
- module CoreExt
8
+ module CoreExt # :nodoc:
9
9
  def _fork
10
10
  pid = super
11
11
  if pid == 0
@@ -20,17 +20,17 @@ module SQLite3
20
20
  @suppress = false
21
21
 
22
22
  class << self
23
- def hook!
23
+ def hook! # :nodoc:
24
24
  ::Process.singleton_class.prepend(CoreExt)
25
25
  end
26
26
 
27
- def track(database)
27
+ def track(database) # :nodoc:
28
28
  @mutex.synchronize do
29
29
  @databases << WeakRef.new(database)
30
30
  end
31
31
  end
32
32
 
33
- def discard
33
+ def discard # :nodoc:
34
34
  warned = @suppress
35
35
  @databases.each do |db|
36
36
  next unless db.weakref_alive?
@@ -93,7 +93,7 @@ module SQLite3
93
93
  LOCKING_MODES = [["normal"], ["exclusive"]]
94
94
 
95
95
  # The list of valid encodings.
96
- ENCODINGS = [["utf-8"], ["utf-16"], ["utf-16le"], ["utf-16be "]]
96
+ ENCODINGS = [["utf-8"], ["utf-16"], ["utf-16le"], ["utf-16be"]]
97
97
 
98
98
  # The list of valid WAL checkpoints.
99
99
  WAL_CHECKPOINTS = [["passive"], ["full"], ["restart"], ["truncate"]]
@@ -338,6 +338,20 @@ module SQLite3
338
338
  set_int_pragma "mmap_size", size
339
339
  end
340
340
 
341
+ # Attempt to optimize the database.
342
+ #
343
+ # To customize the optimization options, pass +bitmask+ with a combination
344
+ # of the Constants::Optimize masks.
345
+ #
346
+ # See https://www.sqlite.org/pragma.html#pragma_optimize for more information.
347
+ def optimize(bitmask = nil)
348
+ if bitmask
349
+ set_int_pragma "optimize", bitmask
350
+ else
351
+ execute("PRAGMA optimize")
352
+ end
353
+ end
354
+
341
355
  def page_count
342
356
  get_int_pragma "page_count"
343
357
  end
@@ -1,3 +1,4 @@
1
1
  module SQLite3
2
- VERSION = "2.1.1"
2
+ # (String) the version of the sqlite3 gem, e.g. "2.1.1"
3
+ VERSION = "2.3.0"
3
4
  end
@@ -0,0 +1,17 @@
1
+ module SQLite3
2
+ # a hash of descriptive metadata about the current version of the sqlite3 gem
3
+ VERSION_INFO = {
4
+ ruby: RUBY_DESCRIPTION,
5
+ gem: {
6
+ version: SQLite3::VERSION
7
+ },
8
+ sqlite: {
9
+ compiled: SQLite3::SQLITE_VERSION,
10
+ loaded: SQLite3::SQLITE_LOADED_VERSION,
11
+ packaged: SQLite3::SQLITE_PACKAGED_LIBRARIES,
12
+ precompiled: SQLite3::SQLITE_PRECOMPILED_LIBRARIES,
13
+ sqlcipher: SQLite3.sqlcipher?,
14
+ threadsafe: SQLite3.threadsafe?
15
+ }
16
+ }
17
+ end
data/lib/sqlite3.rb CHANGED
@@ -15,3 +15,5 @@ module SQLite3
15
15
  threadsafe > 0
16
16
  end
17
17
  end
18
+
19
+ require "sqlite3/version_info"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqlite3
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamis Buck
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2024-10-22 00:00:00.000000000 Z
14
+ date: 2024-11-20 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: mini_portile2
@@ -76,6 +76,7 @@ files:
76
76
  - lib/sqlite3/statement.rb
77
77
  - lib/sqlite3/value.rb
78
78
  - lib/sqlite3/version.rb
79
+ - lib/sqlite3/version_info.rb
79
80
  - ports/archives/sqlite-autoconf-3470000.tar.gz
80
81
  homepage: https://github.com/sparklemotion/sqlite3-ruby
81
82
  licenses: