extralite-bundle 2.3 → 2.5
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/.editorconfig +6 -0
- data/.github/workflows/test-bundle.yml +30 -0
- data/.github/workflows/test.yml +7 -3
- data/.gitignore +3 -0
- data/CHANGELOG.md +44 -3
- data/Gemfile-bundle +5 -0
- data/Gemfile.lock +3 -3
- data/README.md +106 -13
- data/TODO.md +0 -3
- data/bin/update_sqlite_source +10 -3
- data/ext/extralite/common.c +288 -37
- data/ext/extralite/database.c +256 -39
- data/ext/extralite/extralite.h +20 -9
- data/ext/extralite/extralite_ext.c +4 -0
- data/ext/extralite/iterator.c +5 -5
- data/ext/extralite/query.c +250 -41
- data/ext/sqlite3/sqlite3.c +5556 -2531
- data/ext/sqlite3/sqlite3.h +125 -27
- data/gemspec.rb +1 -1
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +41 -6
- data/test/fixtures/image.png +0 -0
- data/test/helper.rb +3 -0
- data/test/issue-38.rb +80 -0
- data/test/issue-54.rb +21 -0
- data/test/issue-59.rb +70 -0
- data/test/perf_ary.rb +6 -3
- data/test/perf_hash.rb +7 -4
- data/test/test_database.rb +726 -15
- data/test/test_iterator.rb +2 -1
- data/test/test_query.rb +402 -6
- metadata +10 -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
|
**
|
@@ -5573,13 +5575,27 @@ SQLITE_API int sqlite3_create_window_function(
|
|
5573
5575
|
** </dd>
|
5574
5576
|
**
|
5575
5577
|
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
|
5576
|
-
** The SQLITE_SUBTYPE flag indicates to SQLite that a function
|
5578
|
+
** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
|
5577
5579
|
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
|
5578
|
-
**
|
5579
|
-
**
|
5580
|
-
**
|
5581
|
-
**
|
5582
|
-
**
|
5580
|
+
** This flag instructs SQLite to omit some corner-case optimizations that
|
5581
|
+
** might disrupt the operation of the [sqlite3_value_subtype()] function,
|
5582
|
+
** causing it to return zero rather than the correct subtype().
|
5583
|
+
** SQL functions that invokes [sqlite3_value_subtype()] should have this
|
5584
|
+
** property. If the SQLITE_SUBTYPE property is omitted, then the return
|
5585
|
+
** value from [sqlite3_value_subtype()] might sometimes be zero even though
|
5586
|
+
** a non-zero subtype was specified by the function argument expression.
|
5587
|
+
**
|
5588
|
+
** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
|
5589
|
+
** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
|
5590
|
+
** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
|
5591
|
+
** result.
|
5592
|
+
** Every function that invokes [sqlite3_result_subtype()] should have this
|
5593
|
+
** property. If it does not, then the call to [sqlite3_result_subtype()]
|
5594
|
+
** might become a no-op if the function is used as term in an
|
5595
|
+
** [expression index]. On the other hand, SQL functions that never invoke
|
5596
|
+
** [sqlite3_result_subtype()] should avoid setting this property, as the
|
5597
|
+
** purpose of this property is to disable certain optimizations that are
|
5598
|
+
** incompatible with subtypes.
|
5583
5599
|
** </dd>
|
5584
5600
|
** </dl>
|
5585
5601
|
*/
|
@@ -5587,6 +5603,7 @@ SQLITE_API int sqlite3_create_window_function(
|
|
5587
5603
|
#define SQLITE_DIRECTONLY 0x000080000
|
5588
5604
|
#define SQLITE_SUBTYPE 0x000100000
|
5589
5605
|
#define SQLITE_INNOCUOUS 0x000200000
|
5606
|
+
#define SQLITE_RESULT_SUBTYPE 0x001000000
|
5590
5607
|
|
5591
5608
|
/*
|
5592
5609
|
** CAPI3REF: Deprecated Functions
|
@@ -5783,6 +5800,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
|
|
5783
5800
|
** information can be used to pass a limited amount of context from
|
5784
5801
|
** one SQL function to another. Use the [sqlite3_result_subtype()]
|
5785
5802
|
** routine to set the subtype for the return value of an SQL function.
|
5803
|
+
**
|
5804
|
+
** Every [application-defined SQL function] that invoke this interface
|
5805
|
+
** should include the [SQLITE_SUBTYPE] property in the text
|
5806
|
+
** encoding argument when the function is [sqlite3_create_function|registered].
|
5807
|
+
** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
|
5808
|
+
** might return zero instead of the upstream subtype in some corner cases.
|
5786
5809
|
*/
|
5787
5810
|
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
|
5788
5811
|
|
@@ -5913,14 +5936,22 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
|
|
5913
5936
|
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
|
5914
5937
|
** parameter)^, or
|
5915
5938
|
** <li> ^(during the original sqlite3_set_auxdata() call when a memory
|
5916
|
-
** allocation error occurs.)^
|
5939
|
+
** allocation error occurs.)^
|
5940
|
+
** <li> ^(during the original sqlite3_set_auxdata() call if the function
|
5941
|
+
** is evaluated during query planning instead of during query execution,
|
5942
|
+
** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
|
5917
5943
|
**
|
5918
|
-
** Note the last
|
5944
|
+
** Note the last two bullets in particular. The destructor X in
|
5919
5945
|
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
|
5920
5946
|
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
|
5921
5947
|
** should be called near the end of the function implementation and the
|
5922
5948
|
** function implementation should not make any use of P after
|
5923
|
-
** sqlite3_set_auxdata() has been called.
|
5949
|
+
** sqlite3_set_auxdata() has been called. Furthermore, a call to
|
5950
|
+
** sqlite3_get_auxdata() that occurs immediately after a corresponding call
|
5951
|
+
** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
|
5952
|
+
** condition occurred during the sqlite3_set_auxdata() call or if the
|
5953
|
+
** function is being evaluated during query planning rather than during
|
5954
|
+
** query execution.
|
5924
5955
|
**
|
5925
5956
|
** ^(In practice, auxiliary data is preserved between function calls for
|
5926
5957
|
** function parameters that are compile-time constants, including literal
|
@@ -6194,6 +6225,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
|
|
6194
6225
|
** higher order bits are discarded.
|
6195
6226
|
** The number of subtype bytes preserved by SQLite might increase
|
6196
6227
|
** in future releases of SQLite.
|
6228
|
+
**
|
6229
|
+
** Every [application-defined SQL function] that invokes this interface
|
6230
|
+
** should include the [SQLITE_RESULT_SUBTYPE] property in its
|
6231
|
+
** text encoding argument when the SQL function is
|
6232
|
+
** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
|
6233
|
+
** property is omitted from the function that invokes sqlite3_result_subtype(),
|
6234
|
+
** then in some cases the sqlite3_result_subtype() might fail to set
|
6235
|
+
** the result subtype.
|
6236
|
+
**
|
6237
|
+
** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
|
6238
|
+
** SQL function that invokes the sqlite3_result_subtype() interface
|
6239
|
+
** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
|
6240
|
+
** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
|
6241
|
+
** by default.
|
6197
6242
|
*/
|
6198
6243
|
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
|
6199
6244
|
|
@@ -7994,9 +8039,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
|
|
7994
8039
|
**
|
7995
8040
|
** ^(Some systems (for example, Windows 95) do not support the operation
|
7996
8041
|
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
|
7997
|
-
** will always return SQLITE_BUSY.
|
7998
|
-
** sqlite3_mutex_try() as an optimization so this is acceptable
|
7999
|
-
** 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.)^
|
8000
8047
|
**
|
8001
8048
|
** ^The sqlite3_mutex_leave() routine exits a mutex that was
|
8002
8049
|
** previously entered by the same thread. The behavior
|
@@ -8255,6 +8302,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
|
|
8255
8302
|
#define SQLITE_TESTCTRL_ASSERT 12
|
8256
8303
|
#define SQLITE_TESTCTRL_ALWAYS 13
|
8257
8304
|
#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
|
8305
|
+
#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
|
8258
8306
|
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
|
8259
8307
|
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
|
8260
8308
|
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
|
@@ -12768,8 +12816,11 @@ struct Fts5PhraseIter {
|
|
12768
12816
|
** created with the "columnsize=0" option.
|
12769
12817
|
**
|
12770
12818
|
** xColumnText:
|
12771
|
-
**
|
12772
|
-
**
|
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
|
12773
12824
|
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
|
12774
12825
|
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
|
12775
12826
|
** if an error occurs, an SQLite error code is returned and the final values
|
@@ -12779,8 +12830,10 @@ struct Fts5PhraseIter {
|
|
12779
12830
|
** Returns the number of phrases in the current query expression.
|
12780
12831
|
**
|
12781
12832
|
** xPhraseSize:
|
12782
|
-
**
|
12783
|
-
**
|
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.
|
12784
12837
|
**
|
12785
12838
|
** xInstCount:
|
12786
12839
|
** Set *pnInst to the total number of occurrences of all phrases within
|
@@ -12796,12 +12849,13 @@ struct Fts5PhraseIter {
|
|
12796
12849
|
** Query for the details of phrase match iIdx within the current row.
|
12797
12850
|
** Phrase matches are numbered starting from zero, so the iIdx argument
|
12798
12851
|
** should be greater than or equal to zero and smaller than the value
|
12799
|
-
** 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.
|
12800
12854
|
**
|
12801
|
-
**
|
12855
|
+
** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
|
12802
12856
|
** to the column in which it occurs and *piOff the token offset of the
|
12803
|
-
** first token of the phrase.
|
12804
|
-
** 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.
|
12805
12859
|
**
|
12806
12860
|
** This API can be quite slow if used with an FTS5 table created with the
|
12807
12861
|
** "detail=none" or "detail=column" option.
|
@@ -12827,6 +12881,10 @@ struct Fts5PhraseIter {
|
|
12827
12881
|
** Invoking Api.xUserData() returns a copy of the pointer passed as
|
12828
12882
|
** the third argument to pUserData.
|
12829
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
|
+
**
|
12830
12888
|
** If the callback function returns any value other than SQLITE_OK, the
|
12831
12889
|
** query is abandoned and the xQueryPhrase function returns immediately.
|
12832
12890
|
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
|
@@ -12941,9 +12999,42 @@ struct Fts5PhraseIter {
|
|
12941
12999
|
**
|
12942
13000
|
** xPhraseNextColumn()
|
12943
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.
|
12944
13035
|
*/
|
12945
13036
|
struct Fts5ExtensionApi {
|
12946
|
-
int iVersion; /* Currently always set to
|
13037
|
+
int iVersion; /* Currently always set to 3 */
|
12947
13038
|
|
12948
13039
|
void *(*xUserData)(Fts5Context*);
|
12949
13040
|
|
@@ -12978,6 +13069,13 @@ struct Fts5ExtensionApi {
|
|
12978
13069
|
|
12979
13070
|
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
|
12980
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*);
|
12981
13079
|
};
|
12982
13080
|
|
12983
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
@@ -25,20 +25,28 @@ module Extralite
|
|
25
25
|
class InterruptError < Error
|
26
26
|
end
|
27
27
|
|
28
|
+
# An exception raised when an Extralite doesn't know how to bind a parameter to a query
|
29
|
+
class ParameterError < Error
|
30
|
+
end
|
31
|
+
|
28
32
|
# An SQLite database
|
29
33
|
class Database
|
30
34
|
# @!visibility private
|
31
35
|
TABLES_SQL = <<~SQL
|
32
|
-
SELECT name FROM sqlite_master
|
36
|
+
SELECT name FROM %<db>s.sqlite_master
|
33
37
|
WHERE type ='table'
|
34
|
-
AND name NOT LIKE 'sqlite_
|
38
|
+
AND name NOT LIKE 'sqlite_%%';
|
35
39
|
SQL
|
36
40
|
|
37
|
-
|
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.
|
38
45
|
#
|
46
|
+
# @param db [String] name of attached database
|
39
47
|
# @return [Array] list of tables
|
40
|
-
def tables
|
41
|
-
query_single_column(TABLES_SQL)
|
48
|
+
def tables(db = 'main')
|
49
|
+
query_single_column(format(TABLES_SQL, db: db))
|
42
50
|
end
|
43
51
|
|
44
52
|
# Gets or sets one or more pragmas:
|
@@ -52,6 +60,29 @@ module Extralite
|
|
52
60
|
value.is_a?(Hash) ? pragma_set(value) : pragma_get(value)
|
53
61
|
end
|
54
62
|
|
63
|
+
# Starts a transaction and runs the given block. If an exception is raised
|
64
|
+
# in the block, the transaction is rolled back. Otherwise, the transaction
|
65
|
+
# is commited after running the block.
|
66
|
+
#
|
67
|
+
# db.transaction do
|
68
|
+
# db.execute('insert into foo values (1, 2, 3)')
|
69
|
+
# raise if db.query_single_value('select x from bar') > 42
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @param mode [Symbol, String] transaction mode (deferred, immediate or exclusive). Defaults to immediate.
|
73
|
+
# @return [Any] the given block's return value
|
74
|
+
def transaction(mode = :immediate)
|
75
|
+
execute "begin #{mode} transaction"
|
76
|
+
|
77
|
+
abort = false
|
78
|
+
yield self
|
79
|
+
rescue
|
80
|
+
abort = true
|
81
|
+
raise
|
82
|
+
ensure
|
83
|
+
execute(abort ? 'rollback' : 'commit')
|
84
|
+
end
|
85
|
+
|
55
86
|
private
|
56
87
|
|
57
88
|
def pragma_set(values)
|
@@ -60,7 +91,11 @@ module Extralite
|
|
60
91
|
end
|
61
92
|
|
62
93
|
def pragma_get(key)
|
63
|
-
|
94
|
+
query_single_value("pragma #{key}")
|
64
95
|
end
|
65
96
|
end
|
97
|
+
|
98
|
+
class Query
|
99
|
+
alias_method :execute_multi, :batch_execute
|
100
|
+
end
|
66
101
|
end
|
Binary file
|
data/test/helper.rb
CHANGED
data/test/issue-38.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sqlite3"
|
4
|
+
require "./lib/extralite"
|
5
|
+
require "benchmark"
|
6
|
+
|
7
|
+
# Setup
|
8
|
+
|
9
|
+
File.delete("benchmark.sqlite3") if File.exist?("benchmark.sqlite3")
|
10
|
+
|
11
|
+
POOL_SIZE = 10
|
12
|
+
|
13
|
+
EXTRALITE_CONNECTIONS = POOL_SIZE.times.map do
|
14
|
+
db = Extralite::Database.new("benchmark.sqlite3")
|
15
|
+
db.execute("PRAGMA journal_mode = WAL")
|
16
|
+
db.execute("PRAGMA synchronous = NORMAL")
|
17
|
+
db.execute("PRAGMA journal_size_limit = 64000000")
|
18
|
+
db.execute("PRAGMA mmap_size = 128000000")
|
19
|
+
db.execute("PRAGMA cache_size = 2000")
|
20
|
+
db.execute("PRAGMA busy_timeout = 5000")
|
21
|
+
db
|
22
|
+
end
|
23
|
+
|
24
|
+
SQLITE3_CONNECTIONS = POOL_SIZE.times.map do
|
25
|
+
db = SQLite3::Database.new("benchmark.sqlite3")
|
26
|
+
db.execute("PRAGMA journal_mode = WAL")
|
27
|
+
db.execute("PRAGMA synchronous = NORMAL")
|
28
|
+
db.execute("PRAGMA journal_size_limit = 64000000")
|
29
|
+
db.execute("PRAGMA mmap_size = 128000000")
|
30
|
+
db.execute("PRAGMA cache_size = 2000")
|
31
|
+
db.execute("PRAGMA busy_timeout = 5000")
|
32
|
+
db
|
33
|
+
end
|
34
|
+
|
35
|
+
EXTRALITE_CONNECTIONS[0].execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, created_at TEXT, updated_at TEXT) STRICT")
|
36
|
+
insert_statement = EXTRALITE_CONNECTIONS[0].prepare("INSERT INTO users (name, created_at, updated_at) VALUES (?, ?, ?)")
|
37
|
+
1000.times do
|
38
|
+
insert_statement.execute("John Doe", Time.now.iso8601, Time.now.iso8601)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Benchmark variations
|
42
|
+
|
43
|
+
THREAD_COUNTS = [1, 2, 4, 8]
|
44
|
+
LIMITS = [10, 100, 1000]
|
45
|
+
CLIENTS = %w[extralite sqlite3]
|
46
|
+
|
47
|
+
# Benchmark
|
48
|
+
|
49
|
+
GC.disable
|
50
|
+
Benchmark.bm do |x|
|
51
|
+
LIMITS.each do |limit|
|
52
|
+
THREAD_COUNTS.each do |thread_count|
|
53
|
+
CLIENTS.each do |client|
|
54
|
+
GC.start
|
55
|
+
|
56
|
+
x.report("#{client.rjust('extralite'.length)} - limit: #{limit}, threads: #{thread_count}") do
|
57
|
+
threads = thread_count.times.map do |thread_number|
|
58
|
+
Thread.new do
|
59
|
+
start = Time.now
|
60
|
+
if client == "extralite"
|
61
|
+
1_000.times do
|
62
|
+
records = EXTRALITE_CONNECTIONS[thread_number].query_ary("SELECT * FROM users LIMIT #{limit}")
|
63
|
+
raise "Expected #{limit} but got #{length}" unless records.length == limit
|
64
|
+
end
|
65
|
+
else
|
66
|
+
1_000.times do
|
67
|
+
records = SQLITE3_CONNECTIONS[thread_number].query("SELECT * FROM users LIMIT #{limit}").entries
|
68
|
+
raise "Expected #{limit} but got #{length}" unless records.length == limit
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
threads.each(&:join)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
puts
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
GC.enable
|
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
|
data/test/perf_ary.rb
CHANGED
@@ -12,15 +12,18 @@ end
|
|
12
12
|
require 'benchmark/ips'
|
13
13
|
require 'fileutils'
|
14
14
|
|
15
|
-
DB_PATH =
|
15
|
+
DB_PATH = "/tmp/extralite_sqlite3_perf-#{Time.now.to_i}-#{rand(10000)}.db"
|
16
|
+
puts "DB_PATH = #{DB_PATH.inspect}"
|
17
|
+
|
16
18
|
|
17
19
|
def prepare_database(count)
|
18
|
-
FileUtils.rm(DB_PATH) rescue nil
|
19
20
|
db = Extralite::Database.new(DB_PATH)
|
20
|
-
db.query('create table foo ( a integer primary key, b text )')
|
21
|
+
db.query('create table if not exists foo ( a integer primary key, b text )')
|
22
|
+
db.query('delete from foo')
|
21
23
|
db.query('begin')
|
22
24
|
count.times { db.query('insert into foo (b) values (?)', "hello#{rand(1000)}" )}
|
23
25
|
db.query('commit')
|
26
|
+
db.close
|
24
27
|
end
|
25
28
|
|
26
29
|
def sqlite3_run(count)
|
data/test/perf_hash.rb
CHANGED
@@ -12,15 +12,17 @@ end
|
|
12
12
|
require 'benchmark/ips'
|
13
13
|
require 'fileutils'
|
14
14
|
|
15
|
-
DB_PATH =
|
15
|
+
DB_PATH = "/tmp/extralite_sqlite3_perf-#{Time.now.to_i}-#{rand(10000)}.db"
|
16
|
+
puts "DB_PATH = #{DB_PATH.inspect}"
|
16
17
|
|
17
18
|
def prepare_database(count)
|
18
|
-
FileUtils.rm(DB_PATH) rescue nil
|
19
19
|
db = Extralite::Database.new(DB_PATH)
|
20
|
-
db.query('create table foo ( a integer primary key, b text )')
|
20
|
+
db.query('create table if not exists foo ( a integer primary key, b text )')
|
21
|
+
db.query('delete from foo')
|
21
22
|
db.query('begin')
|
22
23
|
count.times { db.query('insert into foo (b) values (?)', "hello#{rand(1000)}" )}
|
23
24
|
db.query('commit')
|
25
|
+
db.close
|
24
26
|
end
|
25
27
|
|
26
28
|
def sqlite3_run(count)
|
@@ -36,7 +38,7 @@ def extralite_run(count)
|
|
36
38
|
end
|
37
39
|
|
38
40
|
[10, 1000, 100000].each do |c|
|
39
|
-
puts
|
41
|
+
puts "Record count: #{c}"
|
40
42
|
|
41
43
|
prepare_database(c)
|
42
44
|
|
@@ -48,4 +50,5 @@ end
|
|
48
50
|
|
49
51
|
x.compare!
|
50
52
|
end
|
53
|
+
puts; puts;
|
51
54
|
end
|