extralite-bundle 2.4 → 2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test-bundle.yml +30 -0
- data/.github/workflows/test.yml +2 -12
- data/CHANGELOG.md +39 -10
- data/Gemfile.lock +1 -1
- data/README.md +45 -11
- data/TODO.md +0 -3
- data/ext/extralite/common.c +222 -15
- data/ext/extralite/database.c +185 -16
- data/ext/extralite/extralite.h +5 -1
- data/ext/extralite/extralite_ext.c +4 -0
- data/ext/extralite/query.c +213 -12
- data/ext/sqlite3/sqlite3.c +5420 -2501
- data/ext/sqlite3/sqlite3.h +73 -18
- data/gemspec.rb +1 -1
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +14 -6
- data/test/helper.rb +1 -0
- data/test/issue-54.rb +21 -0
- data/test/issue-59.rb +70 -0
- data/test/test_database.rb +471 -12
- data/test/test_query.rb +362 -2
- metadata +6 -3
data/ext/sqlite3/sqlite3.h
CHANGED
@@ -146,9 +146,9 @@ extern "C" {
|
|
146
146
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
147
147
|
** [sqlite_version()] and [sqlite_source_id()].
|
148
148
|
*/
|
149
|
-
#define SQLITE_VERSION "3.
|
150
|
-
#define SQLITE_VERSION_NUMBER
|
151
|
-
#define SQLITE_SOURCE_ID "
|
149
|
+
#define SQLITE_VERSION "3.45.0"
|
150
|
+
#define SQLITE_VERSION_NUMBER 3045000
|
151
|
+
#define SQLITE_SOURCE_ID "2024-01-15 17:01:13 1066602b2b1976fe58b5150777cced894af17c803e068f5918390d6915b46e1d"
|
152
152
|
|
153
153
|
/*
|
154
154
|
** CAPI3REF: Run-Time Library Version Numbers
|
@@ -3954,15 +3954,17 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
|
|
3954
3954
|
** </ul>
|
3955
3955
|
**
|
3956
3956
|
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
|
3957
|
-
** text that describes the error, as either UTF-8 or UTF-16 respectively
|
3957
|
+
** text that describes the error, as either UTF-8 or UTF-16 respectively,
|
3958
|
+
** or NULL if no error message is available.
|
3958
3959
|
** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
|
3959
3960
|
** ^(Memory to hold the error message string is managed internally.
|
3960
3961
|
** The application does not need to worry about freeing the result.
|
3961
3962
|
** However, the error string might be overwritten or deallocated by
|
3962
3963
|
** subsequent calls to other SQLite interface functions.)^
|
3963
3964
|
**
|
3964
|
-
** ^The sqlite3_errstr() interface returns the English-language text
|
3965
|
-
** that describes the [result code], as UTF-8
|
3965
|
+
** ^The sqlite3_errstr(E) interface returns the English-language text
|
3966
|
+
** that describes the [result code] E, as UTF-8, or NULL if E is not an
|
3967
|
+
** result code for which a text error message is available.
|
3966
3968
|
** ^(Memory to hold the error message string is managed internally
|
3967
3969
|
** and must not be freed by the application)^.
|
3968
3970
|
**
|
@@ -8037,9 +8039,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
|
|
8037
8039
|
**
|
8038
8040
|
** ^(Some systems (for example, Windows 95) do not support the operation
|
8039
8041
|
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
|
8040
|
-
** will always return SQLITE_BUSY.
|
8041
|
-
** sqlite3_mutex_try() as an optimization so this is acceptable
|
8042
|
-
** behavior.
|
8042
|
+
** will always return SQLITE_BUSY. In most cases the SQLite core only uses
|
8043
|
+
** sqlite3_mutex_try() as an optimization, so this is acceptable
|
8044
|
+
** behavior. The exceptions are unix builds that set the
|
8045
|
+
** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
|
8046
|
+
** sqlite3_mutex_try() is required.)^
|
8043
8047
|
**
|
8044
8048
|
** ^The sqlite3_mutex_leave() routine exits a mutex that was
|
8045
8049
|
** previously entered by the same thread. The behavior
|
@@ -8298,6 +8302,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
|
|
8298
8302
|
#define SQLITE_TESTCTRL_ASSERT 12
|
8299
8303
|
#define SQLITE_TESTCTRL_ALWAYS 13
|
8300
8304
|
#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
|
8305
|
+
#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
|
8301
8306
|
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
|
8302
8307
|
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
|
8303
8308
|
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
|
@@ -12811,8 +12816,11 @@ struct Fts5PhraseIter {
|
|
12811
12816
|
** created with the "columnsize=0" option.
|
12812
12817
|
**
|
12813
12818
|
** xColumnText:
|
12814
|
-
**
|
12815
|
-
**
|
12819
|
+
** If parameter iCol is less than zero, or greater than or equal to the
|
12820
|
+
** number of columns in the table, SQLITE_RANGE is returned.
|
12821
|
+
**
|
12822
|
+
** Otherwise, this function attempts to retrieve the text of column iCol of
|
12823
|
+
** the current document. If successful, (*pz) is set to point to a buffer
|
12816
12824
|
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
|
12817
12825
|
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
|
12818
12826
|
** if an error occurs, an SQLite error code is returned and the final values
|
@@ -12822,8 +12830,10 @@ struct Fts5PhraseIter {
|
|
12822
12830
|
** Returns the number of phrases in the current query expression.
|
12823
12831
|
**
|
12824
12832
|
** xPhraseSize:
|
12825
|
-
**
|
12826
|
-
**
|
12833
|
+
** If parameter iCol is less than zero, or greater than or equal to the
|
12834
|
+
** number of phrases in the current query, as returned by xPhraseCount,
|
12835
|
+
** 0 is returned. Otherwise, this function returns the number of tokens in
|
12836
|
+
** phrase iPhrase of the query. Phrases are numbered starting from zero.
|
12827
12837
|
**
|
12828
12838
|
** xInstCount:
|
12829
12839
|
** Set *pnInst to the total number of occurrences of all phrases within
|
@@ -12839,12 +12849,13 @@ struct Fts5PhraseIter {
|
|
12839
12849
|
** Query for the details of phrase match iIdx within the current row.
|
12840
12850
|
** Phrase matches are numbered starting from zero, so the iIdx argument
|
12841
12851
|
** should be greater than or equal to zero and smaller than the value
|
12842
|
-
** output by xInstCount().
|
12852
|
+
** output by xInstCount(). If iIdx is less than zero or greater than
|
12853
|
+
** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
|
12843
12854
|
**
|
12844
|
-
**
|
12855
|
+
** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
|
12845
12856
|
** to the column in which it occurs and *piOff the token offset of the
|
12846
|
-
** first token of the phrase.
|
12847
|
-
** code (i.e. SQLITE_NOMEM) if an error occurs.
|
12857
|
+
** first token of the phrase. SQLITE_OK is returned if successful, or an
|
12858
|
+
** error code (i.e. SQLITE_NOMEM) if an error occurs.
|
12848
12859
|
**
|
12849
12860
|
** This API can be quite slow if used with an FTS5 table created with the
|
12850
12861
|
** "detail=none" or "detail=column" option.
|
@@ -12870,6 +12881,10 @@ struct Fts5PhraseIter {
|
|
12870
12881
|
** Invoking Api.xUserData() returns a copy of the pointer passed as
|
12871
12882
|
** the third argument to pUserData.
|
12872
12883
|
**
|
12884
|
+
** If parameter iPhrase is less than zero, or greater than or equal to
|
12885
|
+
** the number of phrases in the query, as returned by xPhraseCount(),
|
12886
|
+
** this function returns SQLITE_RANGE.
|
12887
|
+
**
|
12873
12888
|
** If the callback function returns any value other than SQLITE_OK, the
|
12874
12889
|
** query is abandoned and the xQueryPhrase function returns immediately.
|
12875
12890
|
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
|
@@ -12984,9 +12999,42 @@ struct Fts5PhraseIter {
|
|
12984
12999
|
**
|
12985
13000
|
** xPhraseNextColumn()
|
12986
13001
|
** See xPhraseFirstColumn above.
|
13002
|
+
**
|
13003
|
+
** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
|
13004
|
+
** This is used to access token iToken of phrase iPhrase of the current
|
13005
|
+
** query. Before returning, output parameter *ppToken is set to point
|
13006
|
+
** to a buffer containing the requested token, and *pnToken to the
|
13007
|
+
** size of this buffer in bytes.
|
13008
|
+
**
|
13009
|
+
** If iPhrase or iToken are less than zero, or if iPhrase is greater than
|
13010
|
+
** or equal to the number of phrases in the query as reported by
|
13011
|
+
** xPhraseCount(), or if iToken is equal to or greater than the number of
|
13012
|
+
** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
|
13013
|
+
are both zeroed.
|
13014
|
+
**
|
13015
|
+
** The output text is not a copy of the query text that specified the
|
13016
|
+
** token. It is the output of the tokenizer module. For tokendata=1
|
13017
|
+
** tables, this includes any embedded 0x00 and trailing data.
|
13018
|
+
**
|
13019
|
+
** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
|
13020
|
+
** This is used to access token iToken of phrase hit iIdx within the
|
13021
|
+
** current row. If iIdx is less than zero or greater than or equal to the
|
13022
|
+
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
|
13023
|
+
** output variable (*ppToken) is set to point to a buffer containing the
|
13024
|
+
** matching document token, and (*pnToken) to the size of that buffer in
|
13025
|
+
** bytes. This API is not available if the specified token matches a
|
13026
|
+
** prefix query term. In that case both output variables are always set
|
13027
|
+
** to 0.
|
13028
|
+
**
|
13029
|
+
** The output text is not a copy of the document text that was tokenized.
|
13030
|
+
** It is the output of the tokenizer module. For tokendata=1 tables, this
|
13031
|
+
** includes any embedded 0x00 and trailing data.
|
13032
|
+
**
|
13033
|
+
** This API can be quite slow if used with an FTS5 table created with the
|
13034
|
+
** "detail=none" or "detail=column" option.
|
12987
13035
|
*/
|
12988
13036
|
struct Fts5ExtensionApi {
|
12989
|
-
int iVersion; /* Currently always set to
|
13037
|
+
int iVersion; /* Currently always set to 3 */
|
12990
13038
|
|
12991
13039
|
void *(*xUserData)(Fts5Context*);
|
12992
13040
|
|
@@ -13021,6 +13069,13 @@ struct Fts5ExtensionApi {
|
|
13021
13069
|
|
13022
13070
|
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
|
13023
13071
|
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
|
13072
|
+
|
13073
|
+
/* Below this point are iVersion>=3 only */
|
13074
|
+
int (*xQueryToken)(Fts5Context*,
|
13075
|
+
int iPhrase, int iToken,
|
13076
|
+
const char **ppToken, int *pnToken
|
13077
|
+
);
|
13078
|
+
int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
|
13024
13079
|
};
|
13025
13080
|
|
13026
13081
|
/*
|
data/gemspec.rb
CHANGED
@@ -16,7 +16,7 @@ def common_spec(s)
|
|
16
16
|
s.rdoc_options = ["--title", "extralite", "--main", "README.md"]
|
17
17
|
s.extra_rdoc_files = ["README.md"]
|
18
18
|
s.require_paths = ["lib"]
|
19
|
-
s.required_ruby_version = '>=
|
19
|
+
s.required_ruby_version = '>= 3.0'
|
20
20
|
|
21
21
|
s.add_development_dependency 'rake-compiler', '1.1.6'
|
22
22
|
s.add_development_dependency 'minitest', '5.15.0'
|
data/lib/extralite/version.rb
CHANGED
data/lib/extralite.rb
CHANGED
@@ -33,16 +33,20 @@ module Extralite
|
|
33
33
|
class Database
|
34
34
|
# @!visibility private
|
35
35
|
TABLES_SQL = <<~SQL
|
36
|
-
SELECT name FROM sqlite_master
|
36
|
+
SELECT name FROM %<db>s.sqlite_master
|
37
37
|
WHERE type ='table'
|
38
|
-
AND name NOT LIKE 'sqlite_
|
38
|
+
AND name NOT LIKE 'sqlite_%%';
|
39
39
|
SQL
|
40
40
|
|
41
|
-
|
41
|
+
alias_method :execute_multi, :batch_execute
|
42
|
+
|
43
|
+
# Returns the list of currently defined tables. If a database name is given,
|
44
|
+
# returns the list of tables for the relevant attached database.
|
42
45
|
#
|
46
|
+
# @param db [String] name of attached database
|
43
47
|
# @return [Array] list of tables
|
44
|
-
def tables
|
45
|
-
query_single_column(TABLES_SQL)
|
48
|
+
def tables(db = 'main')
|
49
|
+
query_single_column(format(TABLES_SQL, db: db))
|
46
50
|
end
|
47
51
|
|
48
52
|
# Gets or sets one or more pragmas:
|
@@ -87,7 +91,11 @@ module Extralite
|
|
87
91
|
end
|
88
92
|
|
89
93
|
def pragma_get(key)
|
90
|
-
|
94
|
+
query_single_value("pragma #{key}")
|
91
95
|
end
|
92
96
|
end
|
97
|
+
|
98
|
+
class Query
|
99
|
+
alias_method :execute_multi, :batch_execute
|
100
|
+
end
|
93
101
|
end
|
data/test/helper.rb
CHANGED
data/test/issue-54.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/extralite"
|
4
|
+
|
5
|
+
puts 'Connecting to database...'
|
6
|
+
|
7
|
+
connection_1 = Extralite::Database.new("test.sqlite3")
|
8
|
+
puts "#{connection_1} connected"
|
9
|
+
connection_2 = Extralite::Database.new("test.sqlite3")
|
10
|
+
connection_2.busy_timeout = 0
|
11
|
+
puts "#{connection_2} connected"
|
12
|
+
|
13
|
+
[connection_1, connection_2].each do |connection|
|
14
|
+
puts "#{connection} beginning transaction..."
|
15
|
+
connection.execute "begin immediate transaction"
|
16
|
+
end
|
17
|
+
|
18
|
+
[connection_1, connection_2].each do |connection|
|
19
|
+
puts "#{connection} rolling back transaction..."
|
20
|
+
connection.execute "rollback transaction"
|
21
|
+
end
|
data/test/issue-59.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/extralite"
|
4
|
+
require "benchmark"
|
5
|
+
require "tempfile"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
p sqlite_version: Extralite.sqlite3_version
|
9
|
+
|
10
|
+
N = (ENV['N'] || 1000).to_i
|
11
|
+
p N: N
|
12
|
+
|
13
|
+
fn1 = '/tmp/db1'
|
14
|
+
fn2 = '/tmp/db2'
|
15
|
+
|
16
|
+
FileUtils.rm(fn1) rescue nil
|
17
|
+
FileUtils.rm(fn2) rescue nil
|
18
|
+
|
19
|
+
p fn1: fn1
|
20
|
+
p fn2: fn2
|
21
|
+
|
22
|
+
db1 = Extralite::Database.new fn1
|
23
|
+
db1.execute "pragma journal_mode = wal;"
|
24
|
+
db1.transaction do
|
25
|
+
db1.execute "create table t1 ( a integer primary key, b text );"
|
26
|
+
values = N.times.map { |i| "#{i}-#{rand(1000)}" }
|
27
|
+
db1.execute_multi "insert into t1 ( b ) values ( ? );", values
|
28
|
+
|
29
|
+
p count: db1.query_single_value("select count(*) from t1")
|
30
|
+
p some_rows: db1.query("select * from t1 limit 5")
|
31
|
+
end
|
32
|
+
|
33
|
+
db2 = Extralite::Database.new fn2
|
34
|
+
db2.execute "pragma journal_mode = wal;"
|
35
|
+
db2.execute "attach '#{fn1}' as db1;"
|
36
|
+
db2.execute "create table t2 ( a integer primary key, b text );"
|
37
|
+
|
38
|
+
p main_tables: db2.tables
|
39
|
+
p db1_tables: db2.tables('db1')
|
40
|
+
|
41
|
+
overall = Benchmark.realtime do
|
42
|
+
t1 = Thread.new do
|
43
|
+
time1 = Benchmark.realtime do
|
44
|
+
db2.execute "create unique index db1.t1_b_unique on t1 (b);"
|
45
|
+
end
|
46
|
+
p({ indexing: time1 })
|
47
|
+
end
|
48
|
+
|
49
|
+
t2 = Thread.new do
|
50
|
+
time2 = Benchmark.realtime do
|
51
|
+
(N / 10000).times do |i|
|
52
|
+
values = 10000.times.map { |i| "#{i}-#{rand(1000)}" }
|
53
|
+
db2.transaction do
|
54
|
+
db2.execute_multi "insert into main.t2 ( b ) values ( ? );", values
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
p({ inserting: time2 })
|
59
|
+
p count_t2: db2.query_single_value("select count(*) from main.t2")
|
60
|
+
p some_rows_t2: db2.query("select * from main.t2 limit 5")
|
61
|
+
end
|
62
|
+
|
63
|
+
t1.join
|
64
|
+
t2.join
|
65
|
+
end
|
66
|
+
|
67
|
+
p({ overall: overall })
|
68
|
+
|
69
|
+
db1.close
|
70
|
+
db2.close
|