extralite 2.15 → 3.0.0

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.
@@ -2,6 +2,13 @@
2
2
  #include <stdlib.h>
3
3
  #include "extralite.h"
4
4
 
5
+ /*
6
+ * Document-class: Extralite::Database
7
+ *
8
+ * This class represents an SQLite database connection, and provides various
9
+ * methods for interacting with the database.
10
+ */
11
+
5
12
  VALUE cDatabase;
6
13
  VALUE cBlob;
7
14
  VALUE cError;
@@ -16,7 +23,7 @@ ID ID_call;
16
23
  ID ID_each;
17
24
  ID ID_keys;
18
25
  ID ID_new;
19
- ID ID_pragma;
26
+ ID ID_parse;
20
27
  ID ID_strip;
21
28
  ID ID_to_s;
22
29
  ID ID_track;
@@ -24,11 +31,11 @@ ID ID_track;
24
31
  VALUE SYM_at_least_once;
25
32
  VALUE SYM_full;
26
33
  VALUE SYM_gvl_release_threshold;
34
+ VALUE SYM_legacy;
27
35
  VALUE SYM_once;
28
36
  VALUE SYM_none;
29
37
  VALUE SYM_normal;
30
38
  VALUE SYM_passive;
31
- VALUE SYM_pragma;
32
39
  VALUE SYM_read_only;
33
40
  VALUE SYM_restart;
34
41
  VALUE SYM_truncate;
@@ -120,26 +127,26 @@ default_flags:
120
127
  }
121
128
 
122
129
  void Database_apply_opts(VALUE self, Database_t *db, VALUE opts) {
123
- VALUE value = Qnil;
130
+ if (NIL_P(opts)) goto modern_pragmas;
124
131
 
125
132
  // :gvl_release_threshold
126
- value = rb_hash_aref(opts, SYM_gvl_release_threshold);
133
+ VALUE value = rb_hash_aref(opts, SYM_gvl_release_threshold);
127
134
  if (!NIL_P(value)) db->gvl_release_threshold = NUM2INT(value);
128
135
 
129
- // :pragma
130
- value = rb_hash_aref(opts, SYM_pragma);
131
- if (!NIL_P(value)) rb_funcall(self, ID_pragma, 1, value);
132
-
133
- // :wal
134
- value = rb_hash_aref(opts, SYM_wal);
135
- if (RTEST(value)) {
136
- int rc = sqlite3_exec(db->sqlite3_db, "PRAGMA journal_mode=wal", NULL, NULL, NULL);
137
- if (rc != SQLITE_OK)
138
- rb_raise(cError, "Failed to set WAL journaling mode: %s", sqlite3_errstr(rc));
139
- rc = sqlite3_exec(db->sqlite3_db, "PRAGMA synchronous=1", NULL, NULL, NULL);
140
- if (rc != SQLITE_OK)
141
- rb_raise(cError, "Failed to set synchronous mode: %s", sqlite3_errstr(rc));
142
- }
136
+ value = rb_hash_aref(opts, SYM_legacy);
137
+ if (RTEST(value)) return;
138
+
139
+ modern_pragmas:
140
+ // :modern pragmas
141
+ int rc = sqlite3_exec(db->sqlite3_db, "PRAGMA journal_mode=wal", NULL, NULL, NULL);
142
+ if (rc != SQLITE_OK)
143
+ rb_raise(cError, "Failed to set WAL journaling mode: %s", sqlite3_errstr(rc));
144
+ rc = sqlite3_exec(db->sqlite3_db, "PRAGMA synchronous=1", NULL, NULL, NULL);
145
+ if (rc != SQLITE_OK)
146
+ rb_raise(cError, "Failed to set synchronous mode: %s", sqlite3_errstr(rc));
147
+ rc = sqlite3_exec(db->sqlite3_db, "PRAGMA foreign_keys=1", NULL, NULL, NULL);
148
+ if (rc != SQLITE_OK)
149
+ rb_raise(cError, "Failed to set foreign keys mode: %s", sqlite3_errstr(rc));
143
150
  }
144
151
 
145
152
  int Database_progress_handler(void *ptr) {
@@ -166,16 +173,16 @@ int Database_busy_handler(void *ptr, int v) {
166
173
  *
167
174
  * - `:gvl_release_threshold` (`Integer`): sets the GVL release threshold (see
168
175
  * `#gvl_release_threshold=`).
169
- * - `:pragma` (`Hash`): one or more pragmas to set upon opening the database.
170
176
  * - `:read_only` (`true`/`false`): opens the database in read-only mode if true.
171
- * - `:wal` (`true`/`false`): sets up the database for [WAL journaling
172
- * mode](https://www.sqlite.org/wal.html) by setting `PRAGMA journal_mode=wal`
173
- * and `PRAGMA synchronous=1`.
177
+ * - `:legacy` (`true`/`false`): By default the database is set up for
178
+ * concurrent access with [WAL journaling
179
+ * mode](https://www.sqlite.org/wal.html). To prevent Extralite from setting up
180
+ * WAL journaling, set this option to true.
174
181
  *
175
182
  * @overload initialize(path)
176
183
  * @param path [String] file path (or ':memory:' for memory database)
177
184
  * @return [void]
178
- * @overload initialize(path, gvl_release_threshold: , on_progress: , read_only: , wal: )
185
+ * @overload initialize(path, gvl_release_threshold: , on_progress: , read_only: , legacy: )
179
186
  * @param path [String] file path (or ':memory:' for memory database)
180
187
  * @param options [Hash] options for opening the database
181
188
  * @return [void]
@@ -203,11 +210,9 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
203
210
  }
204
211
 
205
212
  #ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
206
- rc = sqlite3_enable_load_extension(db->sqlite3_db, 1);
207
- if (rc) {
208
- sqlite3_close_v2(db->sqlite3_db);
209
- rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
210
- }
213
+ // Allow loading extensions only using the C interface, i.e. Database#load_extension
214
+ // see: https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigenableloadextension
215
+ sqlite3_db_config(db->sqlite3_db ,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
211
216
  #endif
212
217
 
213
218
  db->trace_proc = Qnil;
@@ -223,7 +228,7 @@ VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
223
228
  sqlite3_busy_handler(db->sqlite3_db, &Database_busy_handler, db);
224
229
  }
225
230
 
226
- if (!NIL_P(opts)) Database_apply_opts(self, db, opts);
231
+ Database_apply_opts(self, db, opts);
227
232
  return Qnil;
228
233
  }
229
234
 
@@ -238,7 +243,7 @@ VALUE Database_read_only_p(VALUE self) {
238
243
  }
239
244
 
240
245
  /* Closes the database.
241
- *
246
+ *
242
247
  * @return [Extralite::Database] database
243
248
  */
244
249
  VALUE Database_close(VALUE self) {
@@ -276,9 +281,9 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
276
281
  VALUE sql = Qnil;
277
282
  VALUE transform = Qnil;
278
283
  // transform mode is set and the first parameter is not a string, so we expect
279
- // a transform.
284
+ // a transform.,
280
285
  int got_transform = (TYPE(argv[0]) != T_STRING);
281
-
286
+
282
287
  // extract query from args
283
288
  rb_check_arity(argc, got_transform ? 2 : 1, UNLIMITED_ARGUMENTS);
284
289
 
@@ -329,7 +334,7 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
329
334
  * specified using keyword arguments:
330
335
  *
331
336
  * db.query('select * from foo where x = :bar', bar: 42)
332
- *
337
+ *
333
338
  * @overload query(sql, ...)
334
339
  * @param sql [String] SQL statement
335
340
  * @return [Array<Hash>, Integer] rows or total changes
@@ -339,7 +344,11 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
339
344
  * @return [Array<Hash>, Integer] rows or total changes
340
345
  */
341
346
  VALUE Database_query(int argc, VALUE *argv, VALUE self) {
342
- return Database_perform_query(argc, argv, self, safe_query_hash, QUERY_HASH);
347
+ int got_transform = (argc > 1) && (TYPE(argv[0]) != T_STRING);
348
+ if (got_transform && rb_obj_is_instance_of(argv[0], cTransform))
349
+ return Database_perform_query(argc, argv, self, safe_query_transform, QUERY_HASH);
350
+ else
351
+ return Database_perform_query(argc, argv, self, safe_query_hash, QUERY_HASH);
343
352
  }
344
353
 
345
354
  /* Runs a query and transforms rows through the given transform poc. Each row is
@@ -382,7 +391,7 @@ VALUE Database_query_splat(int argc, VALUE *argv, VALUE self) {
382
391
  * db.query_array('select * from foo where x = :bar', bar: 42)
383
392
  * db.query_array('select * from foo where x = :bar', 'bar' => 42)
384
393
  * db.query_array('select * from foo where x = :bar', ':bar' => 42)
385
- *
394
+ *
386
395
  * @overload query_array(sql, ...)
387
396
  * @param sql [String] SQL statement
388
397
  * @return [Array<Array>, Integer] rows or total changes
@@ -418,7 +427,11 @@ VALUE Database_query_array(int argc, VALUE *argv, VALUE self) {
418
427
  * @return [Array, any] row
419
428
  */
420
429
  VALUE Database_query_single(int argc, VALUE *argv, VALUE self) {
421
- return Database_perform_query(argc, argv, self, safe_query_single_row_hash, QUERY_HASH);
430
+ int got_transform = (argc > 1) && (TYPE(argv[0]) != T_STRING);
431
+ if (got_transform && rb_obj_is_instance_of(argv[0], cTransform))
432
+ return Database_perform_query(argc, argv, self, safe_query_single_row_transform, QUERY_HASH);
433
+ else
434
+ return Database_perform_query(argc, argv, self, safe_query_single_row_hash, QUERY_HASH);
422
435
  }
423
436
 
424
437
  /* Runs a query returning a single row as an array or a single value.
@@ -434,7 +447,7 @@ VALUE Database_query_single(int argc, VALUE *argv, VALUE self) {
434
447
  * specified using keyword arguments:
435
448
  *
436
449
  * db.query_single_splat('select * from foo where x = :bar', bar: 42)
437
- *
450
+ *
438
451
  * @overload query_single_splat(sql, ...) -> row
439
452
  * @param sql [String] SQL statement
440
453
  * @return [Array, any] row
@@ -460,7 +473,7 @@ VALUE Database_query_single_splat(int argc, VALUE *argv, VALUE self) {
460
473
  * specified using keyword arguments:
461
474
  *
462
475
  * db.query_single_array('select * from foo where x = :bar', bar: 42)
463
- *
476
+ *
464
477
  * @overload query_single_array(sql, ...) -> row
465
478
  * @param sql [String] SQL statement
466
479
  * @return [Array, any] row
@@ -647,7 +660,7 @@ VALUE Database_batch_query_splat(VALUE self, VALUE sql, VALUE parameters) {
647
660
  }
648
661
 
649
662
  /* Returns the column names for the given query, without running it.
650
- *
663
+ *
651
664
  * @return [Array<String>] column names
652
665
  */
653
666
  VALUE Database_columns(VALUE self, VALUE sql) {
@@ -655,7 +668,7 @@ VALUE Database_columns(VALUE self, VALUE sql) {
655
668
  }
656
669
 
657
670
  /* Returns the rowid of the last inserted row.
658
- *
671
+ *
659
672
  * @return [Integer] last rowid
660
673
  */
661
674
  VALUE Database_last_insert_rowid(VALUE self) {
@@ -665,7 +678,7 @@ VALUE Database_last_insert_rowid(VALUE self) {
665
678
  }
666
679
 
667
680
  /* Returns the number of changes made to the database by the last operation.
668
- *
681
+ *
669
682
  * @return [Integer] number of changes
670
683
  */
671
684
  VALUE Database_changes(VALUE self) {
@@ -676,7 +689,7 @@ VALUE Database_changes(VALUE self) {
676
689
 
677
690
  /* Returns the database filename. If db_name is given, returns the filename for
678
691
  * the respective attached database.
679
- *
692
+ *
680
693
  * @overload filename()
681
694
  * @return [String] database filename
682
695
  * @overload filename(db_name)
@@ -695,7 +708,7 @@ VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
695
708
  }
696
709
 
697
710
  /* Returns true if a transaction is currently in progress.
698
- *
711
+ *
699
712
  * @return [bool] is transaction in progress
700
713
  */
701
714
  VALUE Database_transaction_active_p(VALUE self) {
@@ -706,7 +719,7 @@ VALUE Database_transaction_active_p(VALUE self) {
706
719
 
707
720
  #ifdef HAVE_SQLITE3_LOAD_EXTENSION
708
721
  /* Loads an extension with the given path.
709
- *
722
+ *
710
723
  * @param path [String] extension file path
711
724
  * @return [Extralite::Database] database
712
725
  */
@@ -728,8 +741,17 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
728
741
  static inline VALUE Database_prepare(int argc, VALUE *argv, VALUE self, VALUE mode) {
729
742
  rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
730
743
 
744
+ VALUE transform = Qnil;
745
+ if (argc > 1 && rb_obj_is_instance_of(argv[0], cTransform)) {
746
+ transform = argv[0];
747
+ argv++;
748
+ argc--;
749
+ }
750
+
731
751
  VALUE args[] = { self, argv[0], mode};
732
752
  VALUE query = rb_funcall_passing_block(cQuery, ID_new, 3, args);
753
+ if (!NIL_P(transform))
754
+ Query_transform_set(query, transform);
733
755
  if (argc > 1) rb_funcallv(query, ID_bind, argc - 1, argv + 1);
734
756
  RB_GC_GUARD(query);
735
757
  return query;
@@ -739,11 +761,14 @@ static inline VALUE Database_prepare(int argc, VALUE *argv, VALUE self, VALUE mo
739
761
  * db.prepare(sql) -> query
740
762
  * db.prepare(sql, *params) -> query
741
763
  * db.prepare(sql, *params) { ... } -> query
764
+ * db.prepare(transform, sql) -> query
765
+ * db.prepare(transform, sql, *params) -> query
742
766
  *
743
767
  * Creates a prepared query with the given SQL query in hash mode. If query
744
768
  * parameters are given, they are bound to the query. If a block is given, it is
745
769
  * used as a transform proc.
746
- *
770
+ *
771
+ * @param transform [Extralite::Transform] transform
747
772
  * @param sql [String] SQL statement
748
773
  * @param *params [Array<any>] parameters to bind
749
774
  * @return [Extralite::Query] prepared query
@@ -760,7 +785,7 @@ VALUE Database_prepare_hash(int argc, VALUE *argv, VALUE self) {
760
785
  * Creates a prepared query with the given SQL query in argv mode. If query
761
786
  * parameters are given, they are bound to the query. If a block is given, it is
762
787
  * used as a transform proc.
763
- *
788
+ *
764
789
  * @param sql [String] SQL statement
765
790
  * @param *params [Array<any>] parameters to bind
766
791
  * @return [Extralite::Query] prepared query
@@ -777,7 +802,7 @@ VALUE Database_prepare_splat(int argc, VALUE *argv, VALUE self) {
777
802
  * Creates a prepared query with the given SQL query in array mode. If query
778
803
  * parameters are given, they are bound to the query. If a block is given, it is
779
804
  * used as a transform proc.
780
- *
805
+ *
781
806
  * @param sql [String] SQL statement
782
807
  * @param *params [Array<any>] parameters to bind
783
808
  * @return [Extralite::Query] prepared query
@@ -793,7 +818,7 @@ VALUE Database_prepare_array(int argc, VALUE *argv, VALUE self) {
793
818
  * It is not safe to call `#interrupt` on a database that is about to be closed.
794
819
  * For more information, consult the [sqlite3 API
795
820
  * docs](https://sqlite.org/c3ref/interrupt.html).
796
- *
821
+ *
797
822
  * @return [Extralite::Database] database
798
823
  */
799
824
  VALUE Database_interrupt(VALUE self) {
@@ -874,11 +899,11 @@ VALUE backup_cleanup(VALUE ptr) {
874
899
  * method with two arguments: the remaining page count, and the total page
875
900
  * count, which can be used to display the progress to the user or to collect
876
901
  * statistics.
877
- *
902
+ *
878
903
  * db_src.backup(db_dest) do |remaining, total|
879
904
  * puts "Backing up #{remaining}/#{total}"
880
905
  * end
881
- *
906
+ *
882
907
  * @param dest [String, Extralite::Database] backup destination
883
908
  * @param src_db_name [String] source database name (default: "main")
884
909
  * @param dst_db_name [String] Destination database name (default: "main")
@@ -933,12 +958,12 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
933
958
  /* Returns runtime status values for the given op as an array containing the
934
959
  * current value and the high water mark value. To reset the high water mark,
935
960
  * pass true as reset.
936
- *
961
+ *
937
962
  * You can use the various `Extralite::SQLITE_STATUS_xxx` constants with this
938
963
  * method:
939
- *
964
+ *
940
965
  * Extralite.runtime_status(Extralite::SQLITE_STATUS_MEMORY_USED)
941
- *
966
+ *
942
967
  * For more information see the SQLite docs: https://sqlite.org/c3ref/c_status_malloc_count.html
943
968
  *
944
969
  * @overload runtime_status(op)
@@ -964,7 +989,7 @@ VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
964
989
  /* Returns database status values for the given op as an array containing the
965
990
  * current value and the high water mark value. To reset the high water mark,
966
991
  * pass true as reset.
967
- *
992
+ *
968
993
  * @overload status(op)
969
994
  * @param op [Integer] op
970
995
  * @return [Array<Integer>] array containing the value and high water mark
@@ -989,7 +1014,7 @@ VALUE Database_status(int argc, VALUE *argv, VALUE self) {
989
1014
 
990
1015
  /* Returns the current limit for the given category. If a new value is given,
991
1016
  * sets the limit to the new value and returns the previous value.
992
- *
1017
+ *
993
1018
  * @overload limit(category)
994
1019
  * @param category [Integer] category
995
1020
  * @return [Integer] limit value
@@ -1018,7 +1043,7 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
1018
1043
  * cause the program to wait for the database to become available. If the
1019
1044
  * database is still locked when the timeout period has elapsed, the query will
1020
1045
  * fail with a `Extralite::BusyError` exception.
1021
- *
1046
+ *
1022
1047
  * Setting the busy timeout allows other threads to run while waiting for the
1023
1048
  * database to become available. See also `#on_progress`.
1024
1049
  *
@@ -1036,7 +1061,7 @@ VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
1036
1061
  }
1037
1062
 
1038
1063
  /* Returns the total number of changes made to the database since opening it.
1039
- *
1064
+ *
1040
1065
  * @return [Integer] total changes
1041
1066
  */
1042
1067
  VALUE Database_total_changes(VALUE self) {
@@ -1048,7 +1073,7 @@ VALUE Database_total_changes(VALUE self) {
1048
1073
 
1049
1074
  /* Installs or removes a block that will be invoked for every SQL statement
1050
1075
  * executed. To stop tracing, call `#trace` without a block.
1051
- *
1076
+ *
1052
1077
  * @return [Extralite::Database] database
1053
1078
  */
1054
1079
  VALUE Database_trace(VALUE self) {
@@ -1066,13 +1091,13 @@ VALUE Database_trace(VALUE self) {
1066
1091
  * then be used to store the changes to a file, apply them to another database,
1067
1092
  * or undo the changes. The given table names specify which tables should be
1068
1093
  * tracked for changes. Passing a value of nil causes all tables to be tracked.
1069
- *
1094
+ *
1070
1095
  * changeset = db.track_changes(:foo, :bar) do
1071
1096
  * perform_a_bunch_of_queries
1072
1097
  * end
1073
- *
1098
+ *
1074
1099
  * File.open('my.changes', 'w+') { |f| f << changeset.to_blob }
1075
- *
1100
+ *
1076
1101
  * @param *tables [Array<String, Symbol>] table(s) to track
1077
1102
  * @return [Extralite::Changeset] changeset
1078
1103
  */
@@ -1195,10 +1220,10 @@ struct progress_handler parse_progress_handler_opts(VALUE opts) {
1195
1220
  * work correctly also when running simple queries that don't include many
1196
1221
  * VM instructions. If the `tick` value is greater than the period value it is
1197
1222
  * automatically capped to the period value.
1198
- *
1223
+ *
1199
1224
  * The `mode` parameter controls the progress handler mode, which is one of the
1200
1225
  * following:
1201
- *
1226
+ *
1202
1227
  * - `:normal` (default): the progress handler proc is invoked on query
1203
1228
  * progress.
1204
1229
  * - `:once`: the progress handler proc is invoked only once, when preparing the
@@ -1286,12 +1311,12 @@ VALUE Database_on_progress(int argc, VALUE *argv, VALUE self) {
1286
1311
 
1287
1312
  /* call-seq:
1288
1313
  * Extralite.on_progress(**opts) { ... }
1289
- *
1314
+ *
1290
1315
  * Installs or removes a global progress handler that will be executed
1291
1316
  * periodically while a query is running. This method can be used to support
1292
1317
  * switching between fibers and threads or implementing timeouts for running
1293
1318
  * queries.
1294
- *
1319
+ *
1295
1320
  * This method sets the progress handler settings and behaviour for all
1296
1321
  * subsequently created `Database` instances. Calling this method will have no
1297
1322
  * effect on already existing `Database` instances
@@ -1309,10 +1334,10 @@ VALUE Database_on_progress(int argc, VALUE *argv, VALUE self) {
1309
1334
  * work correctly also when running simple queries that don't include many
1310
1335
  * VM instructions. If the `tick` value is greater than the period value it is
1311
1336
  * automatically capped to the period value.
1312
- *
1337
+ *
1313
1338
  * The `mode` parameter controls the progress handler mode, which is one of the
1314
1339
  * following:
1315
- *
1340
+ *
1316
1341
  * - `:normal` (default): the progress handler proc is invoked on query
1317
1342
  * progress.
1318
1343
  * - `:once`: the progress handler proc is invoked only once, when preparing the
@@ -1353,7 +1378,7 @@ VALUE Extralite_on_progress(int argc, VALUE *argv, VALUE self) {
1353
1378
  }
1354
1379
 
1355
1380
  /* Returns the last error code for the database.
1356
- *
1381
+ *
1357
1382
  * @return [Integer] last error code
1358
1383
  */
1359
1384
  VALUE Database_errcode(VALUE self) {
@@ -1363,7 +1388,7 @@ VALUE Database_errcode(VALUE self) {
1363
1388
  }
1364
1389
 
1365
1390
  /* Returns the last error message for the database.
1366
- *
1391
+ *
1367
1392
  * @return [String] last error message
1368
1393
  */
1369
1394
  VALUE Database_errmsg(VALUE self) {
@@ -1375,7 +1400,7 @@ VALUE Database_errmsg(VALUE self) {
1375
1400
  #ifdef HAVE_SQLITE3_ERROR_OFFSET
1376
1401
  /* Returns the offset for the last error. This is useful for indicating where in
1377
1402
  * the SQL string an error was encountered.
1378
- *
1403
+ *
1379
1404
  * @return [Integer] offset in the last submitted SQL string
1380
1405
  */
1381
1406
  VALUE Database_error_offset(VALUE self) {
@@ -1414,7 +1439,7 @@ VALUE Database_gvl_release_threshold_get(VALUE self) {
1414
1439
 
1415
1440
  /* Sets the database's GVL release threshold. The release policy changes
1416
1441
  * according to the given value:
1417
- *
1442
+ *
1418
1443
  * - Less than 0: the GVL is never released while running queries. This is the
1419
1444
  * policy used when a progress handler is set. For more information see
1420
1445
  * `#on_progress`.
@@ -1499,10 +1524,40 @@ VALUE Database_wal_checkpoint(int argc, VALUE *argv, VALUE self) {
1499
1524
  );
1500
1525
  if (rc != SQLITE_OK)
1501
1526
  rb_raise(cError, "Failed to perform WAL checkpoint: %s", sqlite3_errstr(rc));
1502
-
1527
+
1503
1528
  return rb_ary_new3(2, INT2NUM(total_frames), INT2NUM(checkpointed_frames));
1504
1529
  }
1505
1530
 
1531
+ /* Flushes dirty pages in the pager-cache to the disk. For more information see:
1532
+ * https://sqlite.org/c3ref/db_cacheflush.html
1533
+ *
1534
+ * @return [Extralite::Database] Database
1535
+ */
1536
+ VALUE Database_cache_flush(VALUE self) {
1537
+ Database_t *db = self_to_open_database(self);
1538
+
1539
+ int rc = sqlite3_db_cacheflush(db->sqlite3_db);
1540
+ if (rc != SQLITE_OK)
1541
+ rb_raise(cError, "Failed to flush the database cache: %s", sqlite3_errstr(rc));
1542
+
1543
+ return self;
1544
+ }
1545
+
1546
+ /* Attempts to free up as much memory as possible. For more information see:
1547
+ * https://sqlite.org/c3ref/db_release_memory.html
1548
+ *
1549
+ * @return [Extralite::Database] Database
1550
+ */
1551
+ VALUE Database_release_memory(VALUE self) {
1552
+ Database_t *db = self_to_open_database(self);
1553
+
1554
+ int rc = sqlite3_db_release_memory(db->sqlite3_db);
1555
+ if (rc != SQLITE_OK)
1556
+ rb_raise(cError, "Failed to release memory: %s", sqlite3_errstr(rc));
1557
+
1558
+ return self;
1559
+ }
1560
+
1506
1561
  void Init_ExtraliteDatabase(void) {
1507
1562
  VALUE mExtralite = rb_define_module("Extralite");
1508
1563
  rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
@@ -1515,10 +1570,11 @@ void Init_ExtraliteDatabase(void) {
1515
1570
  rb_define_method(cDatabase, "backup", Database_backup, -1);
1516
1571
  rb_define_method(cDatabase, "batch_execute", Database_batch_execute, 2);
1517
1572
  rb_define_method(cDatabase, "batch_query", Database_batch_query, 2);
1518
- rb_define_method(cDatabase, "batch_query_array", Database_batch_query_array, 2);
1519
- rb_define_method(cDatabase, "batch_query_splat", Database_batch_query_splat, 2);
1573
+ rb_define_method(cDatabase, "batch_query_array", Database_batch_query_array, 2);
1574
+ rb_define_method(cDatabase, "batch_query_splat", Database_batch_query_splat, 2);
1520
1575
  rb_define_method(cDatabase, "batch_query_hash", Database_batch_query, 2);
1521
1576
  rb_define_method(cDatabase, "busy_timeout=", Database_busy_timeout_set, 1);
1577
+ rb_define_method(cDatabase, "cache_flush", Database_cache_flush, 0);
1522
1578
  rb_define_method(cDatabase, "changes", Database_changes, 0);
1523
1579
  rb_define_method(cDatabase, "close", Database_close, 0);
1524
1580
  rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
@@ -1558,6 +1614,7 @@ void Init_ExtraliteDatabase(void) {
1558
1614
  rb_define_method(cDatabase, "query_single_splat", Database_query_single_splat, -1);
1559
1615
  rb_define_method(cDatabase, "query_single_hash", Database_query_single, -1);
1560
1616
  rb_define_method(cDatabase, "read_only?", Database_read_only_p, 0);
1617
+ rb_define_method(cDatabase, "release_memory", Database_release_memory, 0);
1561
1618
  rb_define_method(cDatabase, "status", Database_status, -1);
1562
1619
  rb_define_method(cDatabase, "total_changes", Database_total_changes, 0);
1563
1620
  rb_define_method(cDatabase, "trace", Database_trace, 0);
@@ -1566,7 +1623,7 @@ void Init_ExtraliteDatabase(void) {
1566
1623
  #ifdef EXTRALITE_ENABLE_CHANGESET
1567
1624
  rb_define_method(cDatabase, "track_changes", Database_track_changes, -1);
1568
1625
  #endif
1569
-
1626
+
1570
1627
  rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
1571
1628
 
1572
1629
  cBlob = rb_define_class_under(mExtralite, "Blob", rb_cString);
@@ -1575,39 +1632,39 @@ void Init_ExtraliteDatabase(void) {
1575
1632
  cBusyError = rb_define_class_under(mExtralite, "BusyError", cError);
1576
1633
  cInterruptError = rb_define_class_under(mExtralite, "InterruptError", cError);
1577
1634
  cParameterError = rb_define_class_under(mExtralite, "ParameterError", cError);
1578
- eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
1579
-
1580
- ID_bind = rb_intern("bind");
1581
- ID_call = rb_intern("call");
1582
- ID_each = rb_intern("each");
1583
- ID_keys = rb_intern("keys");
1584
- ID_new = rb_intern("new");
1585
- ID_pragma = rb_intern("pragma");
1586
- ID_strip = rb_intern("strip");
1587
- ID_to_s = rb_intern("to_s");
1588
- ID_track = rb_intern("track");
1589
-
1590
- SYM_at_least_once = ID2SYM(rb_intern("at_least_once"));
1591
- SYM_full = ID2SYM(rb_intern("full"));
1592
- SYM_gvl_release_threshold = ID2SYM(rb_intern("gvl_release_threshold"));
1593
- SYM_once = ID2SYM(rb_intern("once"));
1594
- SYM_none = ID2SYM(rb_intern("none"));
1595
- SYM_normal = ID2SYM(rb_intern("normal"));
1596
- SYM_passive = ID2SYM(rb_intern("passive"));
1597
- SYM_pragma = ID2SYM(rb_intern("pragma"));
1598
- SYM_read_only = ID2SYM(rb_intern("read_only"));
1599
- SYM_restart = ID2SYM(rb_intern("restart"));
1600
- SYM_truncate = ID2SYM(rb_intern("truncate"));
1601
- SYM_wal = ID2SYM(rb_intern("wal"));
1635
+ eArgumentError = rb_const_get(rb_cObject, rb_intern_const("ArgumentError"));
1636
+
1637
+ ID_bind = rb_intern_const("bind");
1638
+ ID_call = rb_intern_const("call");
1639
+ ID_each = rb_intern_const("each");
1640
+ ID_keys = rb_intern_const("keys");
1641
+ ID_new = rb_intern_const("new");
1642
+ ID_parse = rb_intern_const("parse");
1643
+ ID_strip = rb_intern_const("strip");
1644
+ ID_to_s = rb_intern_const("to_s");
1645
+ ID_track = rb_intern_const("track");
1646
+
1647
+ SYM_at_least_once = ID2SYM(rb_intern_const("at_least_once"));
1648
+ SYM_full = ID2SYM(rb_intern_const("full"));
1649
+ SYM_gvl_release_threshold = ID2SYM(rb_intern_const("gvl_release_threshold"));
1650
+ SYM_legacy = ID2SYM(rb_intern_const("legacy"));
1651
+ SYM_once = ID2SYM(rb_intern_const("once"));
1652
+ SYM_none = ID2SYM(rb_intern_const("none"));
1653
+ SYM_normal = ID2SYM(rb_intern_const("normal"));
1654
+ SYM_passive = ID2SYM(rb_intern_const("passive"));
1655
+ SYM_read_only = ID2SYM(rb_intern_const("read_only"));
1656
+ SYM_restart = ID2SYM(rb_intern_const("restart"));
1657
+ SYM_truncate = ID2SYM(rb_intern_const("truncate"));
1658
+ SYM_wal = ID2SYM(rb_intern_const("wal"));
1602
1659
 
1603
1660
  rb_gc_register_mark_object(SYM_at_least_once);
1604
1661
  rb_gc_register_mark_object(SYM_full);
1605
1662
  rb_gc_register_mark_object(SYM_gvl_release_threshold);
1663
+ rb_gc_register_mark_object(SYM_legacy);
1606
1664
  rb_gc_register_mark_object(SYM_once);
1607
1665
  rb_gc_register_mark_object(SYM_none);
1608
1666
  rb_gc_register_mark_object(SYM_normal);
1609
1667
  rb_gc_register_mark_object(SYM_passive);
1610
- rb_gc_register_mark_object(SYM_pragma);
1611
1668
  rb_gc_register_mark_object(SYM_read_only);
1612
1669
  rb_gc_register_mark_object(SYM_restart);
1613
1670
  rb_gc_register_mark_object(SYM_truncate);