extralite 2.0 → 2.1
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/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +20 -4
- data/ext/extralite/common.c +5 -0
- data/ext/extralite/database.c +117 -66
- data/ext/extralite/extralite.h +2 -1
- data/ext/extralite/iterator.c +33 -5
- data/ext/extralite/query.c +75 -20
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +0 -2
- data/test/test_database.rb +26 -0
- data/test/test_iterator.rb +5 -0
- data/test/test_query.rb +17 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd8cf56eff701ddf57586d6f7510d5959cdf81ff15e6a64c6ed8ff698f93b43b
|
4
|
+
data.tar.gz: 42000f8c83849577fd1672996969961357c5bbadb82d89d2168c74be7acfa303
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1159c965d8ce9bfade2c81de97b6ef311dcfdb84ce65eb85238aa6a23b29b786d31de2bb8f30f87a24a800b88d01475a7cb400b97245401d587bef365b140087
|
7
|
+
data.tar.gz: 74f975529a9efb7291524eaa4c285fadc6976377a607cd28e72cbd3010cbc9fd061264aabb9a5df1ce864f097ca330c9acd9dded62b8de7cc63c1217be88412a
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/TODO.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
|
-
- Add option to open database in readonly mode, use sqlite3_open_v2
|
2
|
-
https://sqlite.org/c3ref/open.html
|
3
|
-
|
4
1
|
- Improve tracing
|
5
|
-
-
|
2
|
+
- Transactions and savepoints:
|
3
|
+
|
4
|
+
- `DB#transaction {}` - does a `BEGIN..COMMIT` - non-reentrant!
|
5
|
+
- `DB#savepoint(name)` - creates a savepoint
|
6
|
+
- `DB#release(name)` - releases a savepoint
|
7
|
+
- `DB#rollback` - raises `Extralite::Rollback`, which is rescued by `DB#transaction`
|
8
|
+
- `DB#rollback_to(name)` - rolls back to a savepoint
|
9
|
+
|
10
|
+
- More database methods:
|
11
|
+
|
12
|
+
- `Database#quote`
|
13
|
+
- `Database#busy_timeout=` https://sqlite.org/c3ref/busy_timeout.html
|
14
|
+
- `Database#cache_flush` https://sqlite.org/c3ref/db_cacheflush.html
|
15
|
+
- `Database#release_memory` https://sqlite.org/c3ref/db_release_memory.html
|
16
|
+
|
17
|
+
- Security
|
18
|
+
|
19
|
+
- Enable extension loading by using
|
20
|
+
[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION](https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigenableloadextension)
|
21
|
+
in order to prevent usage of `load_extension()` SQL function.
|
data/ext/extralite/common.c
CHANGED
@@ -396,3 +396,8 @@ VALUE safe_execute_multi(query_ctx *ctx) {
|
|
396
396
|
VALUE safe_query_columns(query_ctx *ctx) {
|
397
397
|
return get_column_names(ctx->stmt, sqlite3_column_count(ctx->stmt));
|
398
398
|
}
|
399
|
+
|
400
|
+
VALUE safe_query_changes(query_ctx *ctx) {
|
401
|
+
while (stmt_iterate(ctx));
|
402
|
+
return INT2FIX(sqlite3_changes(ctx->sqlite3_db));
|
403
|
+
}
|
data/ext/extralite/database.c
CHANGED
@@ -6,6 +6,7 @@ VALUE cError;
|
|
6
6
|
VALUE cSQLError;
|
7
7
|
VALUE cBusyError;
|
8
8
|
VALUE cInterruptError;
|
9
|
+
VALUE eArgumentError;
|
9
10
|
|
10
11
|
ID ID_bind;
|
11
12
|
ID ID_call;
|
@@ -14,6 +15,8 @@ ID ID_new;
|
|
14
15
|
ID ID_strip;
|
15
16
|
ID ID_to_s;
|
16
17
|
|
18
|
+
VALUE SYM_read_only;
|
19
|
+
|
17
20
|
static size_t Database_size(const void *ptr) {
|
18
21
|
return sizeof(Database_t);
|
19
22
|
}
|
@@ -36,27 +39,21 @@ static VALUE Database_allocate(VALUE klass) {
|
|
36
39
|
return TypedData_Wrap_Struct(klass, &Database_type, db);
|
37
40
|
}
|
38
41
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
#define GetOpenDatabase(obj, database) { \
|
44
|
-
TypedData_Get_Struct((obj), Database_t, &Database_type, (database)); \
|
45
|
-
if (!(database)->sqlite3_db) { \
|
46
|
-
rb_raise(cError, "Database is closed"); \
|
47
|
-
} \
|
42
|
+
inline Database_t *self_to_database(VALUE self) {
|
43
|
+
Database_t *db;
|
44
|
+
TypedData_Get_Struct(self, Database_t, &Database_type, db);
|
45
|
+
return db;
|
48
46
|
}
|
49
47
|
|
50
|
-
Database_t *
|
51
|
-
Database_t *db;
|
52
|
-
|
48
|
+
inline Database_t *self_to_open_database(VALUE self) {
|
49
|
+
Database_t *db = self_to_database(self);
|
50
|
+
if (!(db)->sqlite3_db) rb_raise(cError, "Database is closed");
|
51
|
+
|
53
52
|
return db;
|
54
53
|
}
|
55
54
|
|
56
|
-
sqlite3 *Database_sqlite3_db(VALUE self) {
|
57
|
-
|
58
|
-
GetDatabase(self, db);
|
59
|
-
return db->sqlite3_db;
|
55
|
+
inline sqlite3 *Database_sqlite3_db(VALUE self) {
|
56
|
+
return self_to_database(self)->sqlite3_db;
|
60
57
|
}
|
61
58
|
|
62
59
|
/* call-seq:
|
@@ -69,18 +66,37 @@ VALUE Extralite_sqlite3_version(VALUE self) {
|
|
69
66
|
return rb_str_new_cstr(sqlite3_version);
|
70
67
|
}
|
71
68
|
|
72
|
-
|
73
|
-
|
69
|
+
static inline int db_open_flags_from_opts(VALUE opts) {
|
70
|
+
if (opts == Qnil) goto default_flags;
|
71
|
+
|
72
|
+
if (TYPE(opts) != T_HASH)
|
73
|
+
rb_raise(eArgumentError, "Expected hash as database initialization options");
|
74
|
+
|
75
|
+
VALUE read_only = rb_hash_aref(opts, SYM_read_only);
|
76
|
+
if (RTEST(read_only)) return SQLITE_OPEN_READONLY;
|
77
|
+
default_flags:
|
78
|
+
return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
79
|
+
}
|
80
|
+
|
81
|
+
/* Initializes a new SQLite database with the given path and options.
|
74
82
|
*
|
75
|
-
*
|
83
|
+
* @overload initialize(path)
|
84
|
+
* @param path [String] file path (or ':memory:' for memory database)
|
85
|
+
* @return [void]
|
86
|
+
* @overload initialize(path, read_only: false)
|
87
|
+
* @param path [String] file path (or ':memory:' for memory database)
|
88
|
+
* @param read_only [boolean] true for opening the database for reading only
|
89
|
+
* @return [void]
|
76
90
|
*/
|
91
|
+
VALUE Database_initialize(int argc, VALUE *argv, VALUE self) {
|
92
|
+
Database_t *db = self_to_database(self);
|
93
|
+
VALUE path;
|
94
|
+
VALUE opts = Qnil;
|
77
95
|
|
78
|
-
|
79
|
-
int
|
80
|
-
Database_t *db;
|
81
|
-
GetDatabase(self, db);
|
96
|
+
rb_scan_args(argc, argv, "11", &path, &opts);
|
97
|
+
int flags = db_open_flags_from_opts(opts);
|
82
98
|
|
83
|
-
rc =
|
99
|
+
int rc = sqlite3_open_v2(StringValueCStr(path), &db->sqlite3_db, flags, NULL);
|
84
100
|
if (rc) {
|
85
101
|
sqlite3_close_v2(db->sqlite3_db);
|
86
102
|
rb_raise(cError, "%s", sqlite3_errstr(rc));
|
@@ -106,6 +122,16 @@ VALUE Database_initialize(VALUE self, VALUE path) {
|
|
106
122
|
return Qnil;
|
107
123
|
}
|
108
124
|
|
125
|
+
/* Returns true if the database was open for read only access.
|
126
|
+
*
|
127
|
+
* @return [boolean] true if database is open for read only access
|
128
|
+
*/
|
129
|
+
VALUE Database_read_only_p(VALUE self) {
|
130
|
+
Database_t *db = self_to_database(self);
|
131
|
+
int open = sqlite3_db_readonly(db->sqlite3_db, "main");
|
132
|
+
return (open == 1) ? Qtrue : Qfalse;
|
133
|
+
}
|
134
|
+
|
109
135
|
/* call-seq:
|
110
136
|
* db.close -> db
|
111
137
|
*
|
@@ -113,8 +139,7 @@ VALUE Database_initialize(VALUE self, VALUE path) {
|
|
113
139
|
*/
|
114
140
|
VALUE Database_close(VALUE self) {
|
115
141
|
int rc;
|
116
|
-
Database_t *db;
|
117
|
-
GetDatabase(self, db);
|
142
|
+
Database_t *db = self_to_database(self);
|
118
143
|
|
119
144
|
rc = sqlite3_close_v2(db->sqlite3_db);
|
120
145
|
if (rc) {
|
@@ -133,14 +158,12 @@ VALUE Database_close(VALUE self) {
|
|
133
158
|
* @return [bool] is database closed
|
134
159
|
*/
|
135
160
|
VALUE Database_closed_p(VALUE self) {
|
136
|
-
Database_t *db;
|
137
|
-
GetDatabase(self, db);
|
138
|
-
|
161
|
+
Database_t *db = self_to_database(self);
|
139
162
|
return db->sqlite3_db ? Qfalse : Qtrue;
|
140
163
|
}
|
141
164
|
|
142
165
|
static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *)) {
|
143
|
-
Database_t *db;
|
166
|
+
Database_t *db = self_to_open_database(self);
|
144
167
|
sqlite3_stmt *stmt;
|
145
168
|
VALUE sql;
|
146
169
|
|
@@ -150,7 +173,6 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
|
|
150
173
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
151
174
|
|
152
175
|
// prepare query ctx
|
153
|
-
GetOpenDatabase(self, db);
|
154
176
|
if (db->trace_block != Qnil) rb_funcall(db->trace_block, ID_call, 1, sql);
|
155
177
|
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
156
178
|
RB_GC_GUARD(sql);
|
@@ -286,6 +308,31 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
286
308
|
return Database_perform_query(argc, argv, self, safe_query_single_value);
|
287
309
|
}
|
288
310
|
|
311
|
+
/* call-seq:
|
312
|
+
* db.execute(sql, *parameters) -> changes
|
313
|
+
*
|
314
|
+
* Runs a query returning the total changes effected. This method should be used
|
315
|
+
* for data- or schema-manipulation queries.
|
316
|
+
*
|
317
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
318
|
+
* a list of values or as a hash mapping parameter names to values. When
|
319
|
+
* parameters are given as an array, the query should specify parameters using
|
320
|
+
* `?`:
|
321
|
+
*
|
322
|
+
* db.execute('update foo set x = ? where y = ?', 42, 43)
|
323
|
+
*
|
324
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
325
|
+
* specified using a hash, where keys are either strings are symbols. String
|
326
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
327
|
+
*
|
328
|
+
* db.execute('update foo set x = :bar', bar: 42)
|
329
|
+
* db.execute('update foo set x = :bar', 'bar' => 42)
|
330
|
+
* db.execute('update foo set x = :bar', ':bar' => 42)
|
331
|
+
*/
|
332
|
+
VALUE Database_execute(int argc, VALUE *argv, VALUE self) {
|
333
|
+
return Database_perform_query(argc, argv, self, safe_query_changes);
|
334
|
+
}
|
335
|
+
|
289
336
|
/* call-seq:
|
290
337
|
* db.execute_multi(sql, params_array) -> changes
|
291
338
|
*
|
@@ -301,13 +348,12 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
301
348
|
*
|
302
349
|
*/
|
303
350
|
VALUE Database_execute_multi(VALUE self, VALUE sql, VALUE params_array) {
|
304
|
-
Database_t *db;
|
351
|
+
Database_t *db = self_to_open_database(self);
|
305
352
|
sqlite3_stmt *stmt;
|
306
353
|
|
307
354
|
if (RSTRING_LEN(sql) == 0) return Qnil;
|
308
355
|
|
309
356
|
// prepare query ctx
|
310
|
-
GetOpenDatabase(self, db);
|
311
357
|
prepare_single_stmt(db->sqlite3_db, &stmt, sql);
|
312
358
|
query_ctx ctx = { self, db->sqlite3_db, stmt, params_array, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
|
313
359
|
|
@@ -329,8 +375,7 @@ VALUE Database_columns(VALUE self, VALUE sql) {
|
|
329
375
|
* Returns the rowid of the last inserted row.
|
330
376
|
*/
|
331
377
|
VALUE Database_last_insert_rowid(VALUE self) {
|
332
|
-
Database_t *db;
|
333
|
-
GetOpenDatabase(self, db);
|
378
|
+
Database_t *db = self_to_open_database(self);
|
334
379
|
|
335
380
|
return INT2FIX(sqlite3_last_insert_rowid(db->sqlite3_db));
|
336
381
|
}
|
@@ -341,8 +386,7 @@ VALUE Database_last_insert_rowid(VALUE self) {
|
|
341
386
|
* Returns the number of changes made to the database by the last operation.
|
342
387
|
*/
|
343
388
|
VALUE Database_changes(VALUE self) {
|
344
|
-
Database_t *db;
|
345
|
-
GetOpenDatabase(self, db);
|
389
|
+
Database_t *db = self_to_open_database(self);
|
346
390
|
|
347
391
|
return INT2FIX(sqlite3_changes(db->sqlite3_db));
|
348
392
|
}
|
@@ -355,8 +399,7 @@ VALUE Database_changes(VALUE self) {
|
|
355
399
|
VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
|
356
400
|
const char *db_name;
|
357
401
|
const char *filename;
|
358
|
-
Database_t *db;
|
359
|
-
GetOpenDatabase(self, db);
|
402
|
+
Database_t *db = self_to_open_database(self);
|
360
403
|
|
361
404
|
rb_check_arity(argc, 0, 1);
|
362
405
|
db_name = (argc == 1) ? StringValueCStr(argv[0]) : "main";
|
@@ -370,8 +413,7 @@ VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
|
|
370
413
|
* Returns true if a transaction is currently in progress.
|
371
414
|
*/
|
372
415
|
VALUE Database_transaction_active_p(VALUE self) {
|
373
|
-
Database_t *db;
|
374
|
-
GetOpenDatabase(self, db);
|
416
|
+
Database_t *db = self_to_open_database(self);
|
375
417
|
|
376
418
|
return sqlite3_get_autocommit(db->sqlite3_db) ? Qfalse : Qtrue;
|
377
419
|
}
|
@@ -383,8 +425,7 @@ VALUE Database_transaction_active_p(VALUE self) {
|
|
383
425
|
* Loads an extension with the given path.
|
384
426
|
*/
|
385
427
|
VALUE Database_load_extension(VALUE self, VALUE path) {
|
386
|
-
Database_t *db;
|
387
|
-
GetOpenDatabase(self, db);
|
428
|
+
Database_t *db = self_to_open_database(self);
|
388
429
|
char *err_msg;
|
389
430
|
|
390
431
|
int rc = sqlite3_load_extension(db->sqlite3_db, RSTRING_PTR(path), 0, &err_msg);
|
@@ -422,8 +463,7 @@ VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
|
|
422
463
|
* For more information, consult the [sqlite3 API docs](https://sqlite.org/c3ref/interrupt.html).
|
423
464
|
*/
|
424
465
|
VALUE Database_interrupt(VALUE self) {
|
425
|
-
Database_t *db;
|
426
|
-
GetOpenDatabase(self, db);
|
466
|
+
Database_t *db = self_to_open_database(self);
|
427
467
|
|
428
468
|
sqlite3_interrupt(db->sqlite3_db);
|
429
469
|
return self;
|
@@ -515,8 +555,7 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
|
|
515
555
|
|
516
556
|
int dst_is_fn = TYPE(dst) == T_STRING;
|
517
557
|
|
518
|
-
Database_t *src;
|
519
|
-
GetOpenDatabase(self, src);
|
558
|
+
Database_t *src = self_to_open_database(self);
|
520
559
|
sqlite3 *dst_db;
|
521
560
|
|
522
561
|
if (dst_is_fn) {
|
@@ -527,8 +566,7 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
|
|
527
566
|
}
|
528
567
|
}
|
529
568
|
else {
|
530
|
-
Database_t *dst_struct;
|
531
|
-
GetOpenDatabase(dst, dst_struct);
|
569
|
+
Database_t *dst_struct = self_to_open_database(dst);
|
532
570
|
dst_db = dst_struct->sqlite3_db;
|
533
571
|
}
|
534
572
|
|
@@ -583,8 +621,7 @@ VALUE Database_status(int argc, VALUE *argv, VALUE self) {
|
|
583
621
|
|
584
622
|
rb_scan_args(argc, argv, "11", &op, &reset);
|
585
623
|
|
586
|
-
Database_t *db;
|
587
|
-
GetOpenDatabase(self, db);
|
624
|
+
Database_t *db = self_to_open_database(self);
|
588
625
|
|
589
626
|
int rc = sqlite3_db_status(db->sqlite3_db, NUM2INT(op), &cur, &hwm, RTEST(reset) ? 1 : 0);
|
590
627
|
if (rc != SQLITE_OK) rb_raise(cError, "%s", sqlite3_errstr(rc));
|
@@ -604,8 +641,7 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
|
|
604
641
|
|
605
642
|
rb_scan_args(argc, argv, "11", &category, &new_value);
|
606
643
|
|
607
|
-
Database_t *db;
|
608
|
-
GetOpenDatabase(self, db);
|
644
|
+
Database_t *db = self_to_open_database(self);
|
609
645
|
|
610
646
|
int value = sqlite3_limit(db->sqlite3_db, NUM2INT(category), RTEST(new_value) ? NUM2INT(new_value) : -1);
|
611
647
|
|
@@ -622,8 +658,7 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
|
|
622
658
|
* disable the busy timeout, set it to 0 or nil.
|
623
659
|
*/
|
624
660
|
VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
|
625
|
-
Database_t *db;
|
626
|
-
GetOpenDatabase(self, db);
|
661
|
+
Database_t *db = self_to_open_database(self);
|
627
662
|
|
628
663
|
int ms = (sec == Qnil) ? 0 : (int)(NUM2DBL(sec) * 1000);
|
629
664
|
int rc = sqlite3_busy_timeout(db->sqlite3_db, ms);
|
@@ -638,8 +673,7 @@ VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
|
|
638
673
|
* Returns the total number of changes made to the database since opening it.
|
639
674
|
*/
|
640
675
|
VALUE Database_total_changes(VALUE self) {
|
641
|
-
Database_t *db;
|
642
|
-
GetOpenDatabase(self, db);
|
676
|
+
Database_t *db = self_to_open_database(self);
|
643
677
|
|
644
678
|
int value = sqlite3_total_changes(db->sqlite3_db);
|
645
679
|
return INT2NUM(value);
|
@@ -653,8 +687,7 @@ VALUE Database_total_changes(VALUE self) {
|
|
653
687
|
* executed.
|
654
688
|
*/
|
655
689
|
VALUE Database_trace(VALUE self) {
|
656
|
-
Database_t *db;
|
657
|
-
GetOpenDatabase(self, db);
|
690
|
+
Database_t *db = self_to_open_database(self);
|
658
691
|
|
659
692
|
db->trace_block = rb_block_given_p() ? rb_block_proc() : Qnil;
|
660
693
|
return self;
|
@@ -666,8 +699,7 @@ VALUE Database_trace(VALUE self) {
|
|
666
699
|
* Returns the last error code for the database.
|
667
700
|
*/
|
668
701
|
VALUE Database_errcode(VALUE self) {
|
669
|
-
Database_t *db;
|
670
|
-
GetOpenDatabase(self, db);
|
702
|
+
Database_t *db = self_to_open_database(self);
|
671
703
|
|
672
704
|
return INT2NUM(sqlite3_errcode(db->sqlite3_db));
|
673
705
|
}
|
@@ -678,8 +710,7 @@ VALUE Database_errcode(VALUE self) {
|
|
678
710
|
* Returns the last error message for the database.
|
679
711
|
*/
|
680
712
|
VALUE Database_errmsg(VALUE self) {
|
681
|
-
Database_t *db;
|
682
|
-
GetOpenDatabase(self, db);
|
713
|
+
Database_t *db = self_to_open_database(self);
|
683
714
|
|
684
715
|
return rb_str_new2(sqlite3_errmsg(db->sqlite3_db));
|
685
716
|
}
|
@@ -691,13 +722,25 @@ VALUE Database_errmsg(VALUE self) {
|
|
691
722
|
* Returns the offset for the last error
|
692
723
|
*/
|
693
724
|
VALUE Database_error_offset(VALUE self) {
|
694
|
-
Database_t *db;
|
695
|
-
GetOpenDatabase(self, db);
|
725
|
+
Database_t *db = self_to_open_database(self);
|
696
726
|
|
697
727
|
return INT2NUM(sqlite3_error_offset(db->sqlite3_db));
|
698
728
|
}
|
699
729
|
#endif
|
700
730
|
|
731
|
+
/* Returns a short string representation of the database instance, including the
|
732
|
+
* database filename.
|
733
|
+
*
|
734
|
+
* @return [String] string representation
|
735
|
+
*/
|
736
|
+
VALUE Database_inspect(VALUE self) {
|
737
|
+
VALUE cname = rb_class_name(CLASS_OF(self));
|
738
|
+
VALUE filename = Database_filename(0, NULL, self);
|
739
|
+
if (RSTRING_LEN(filename) == 0) filename = rb_str_new_literal(":memory:");
|
740
|
+
|
741
|
+
return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, filename);
|
742
|
+
}
|
743
|
+
|
701
744
|
void Init_ExtraliteDatabase(void) {
|
702
745
|
VALUE mExtralite = rb_define_module("Extralite");
|
703
746
|
rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
|
@@ -719,9 +762,11 @@ void Init_ExtraliteDatabase(void) {
|
|
719
762
|
rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
|
720
763
|
#endif
|
721
764
|
|
765
|
+
rb_define_method(cDatabase, "execute", Database_execute, -1);
|
722
766
|
rb_define_method(cDatabase, "execute_multi", Database_execute_multi, 2);
|
723
767
|
rb_define_method(cDatabase, "filename", Database_filename, -1);
|
724
|
-
rb_define_method(cDatabase, "initialize", Database_initialize, 1);
|
768
|
+
rb_define_method(cDatabase, "initialize", Database_initialize, -1);
|
769
|
+
rb_define_method(cDatabase, "inspect", Database_inspect, 0);
|
725
770
|
rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
|
726
771
|
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
727
772
|
rb_define_method(cDatabase, "limit", Database_limit, -1);
|
@@ -732,6 +777,7 @@ void Init_ExtraliteDatabase(void) {
|
|
732
777
|
rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
|
733
778
|
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
734
779
|
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
780
|
+
rb_define_method(cDatabase, "read_only?", Database_read_only_p, 0);
|
735
781
|
rb_define_method(cDatabase, "status", Database_status, -1);
|
736
782
|
rb_define_method(cDatabase, "total_changes", Database_total_changes, 0);
|
737
783
|
rb_define_method(cDatabase, "trace", Database_trace, 0);
|
@@ -750,10 +796,15 @@ void Init_ExtraliteDatabase(void) {
|
|
750
796
|
rb_gc_register_mark_object(cBusyError);
|
751
797
|
rb_gc_register_mark_object(cInterruptError);
|
752
798
|
|
799
|
+
eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
|
800
|
+
|
753
801
|
ID_bind = rb_intern("bind");
|
754
802
|
ID_call = rb_intern("call");
|
755
803
|
ID_keys = rb_intern("keys");
|
756
804
|
ID_new = rb_intern("new");
|
757
805
|
ID_strip = rb_intern("strip");
|
758
806
|
ID_to_s = rb_intern("to_s");
|
807
|
+
|
808
|
+
SYM_read_only = ID2SYM(rb_intern("read_only"));
|
809
|
+
rb_gc_register_mark_object(SYM_read_only);
|
759
810
|
}
|
data/ext/extralite/extralite.h
CHANGED
@@ -94,6 +94,7 @@ typedef struct {
|
|
94
94
|
|
95
95
|
VALUE safe_execute_multi(query_ctx *ctx);
|
96
96
|
VALUE safe_query_ary(query_ctx *ctx);
|
97
|
+
VALUE safe_query_changes(query_ctx *ctx);
|
97
98
|
VALUE safe_query_columns(query_ctx *ctx);
|
98
99
|
VALUE safe_query_hash(query_ctx *ctx);
|
99
100
|
VALUE safe_query_single_column(query_ctx *ctx);
|
@@ -120,6 +121,6 @@ int stmt_iterate(query_ctx *ctx);
|
|
120
121
|
VALUE cleanup_stmt(query_ctx *ctx);
|
121
122
|
|
122
123
|
sqlite3 *Database_sqlite3_db(VALUE self);
|
123
|
-
Database_t *
|
124
|
+
Database_t *self_to_database(VALUE self);
|
124
125
|
|
125
126
|
#endif /* EXTRALITE_H */
|
data/ext/extralite/iterator.c
CHANGED
@@ -34,7 +34,7 @@ static VALUE Iterator_allocate(VALUE klass) {
|
|
34
34
|
return TypedData_Wrap_Struct(klass, &Iterator_type, iterator);
|
35
35
|
}
|
36
36
|
|
37
|
-
static inline Iterator_t *
|
37
|
+
static inline Iterator_t *self_to_iterator(VALUE obj) {
|
38
38
|
Iterator_t *iterator;
|
39
39
|
TypedData_Get_Struct((obj), Iterator_t, &Iterator_type, (iterator));
|
40
40
|
return iterator;
|
@@ -61,7 +61,7 @@ static inline enum iterator_mode symbol_to_mode(VALUE sym) {
|
|
61
61
|
* @return [void]
|
62
62
|
*/
|
63
63
|
VALUE Iterator_initialize(VALUE self, VALUE query, VALUE mode) {
|
64
|
-
Iterator_t *iterator =
|
64
|
+
Iterator_t *iterator = self_to_iterator(self);
|
65
65
|
|
66
66
|
iterator->query = query;
|
67
67
|
iterator->mode = symbol_to_mode(mode);
|
@@ -92,7 +92,7 @@ inline each_method mode_to_each_method(enum iterator_mode mode) {
|
|
92
92
|
*/
|
93
93
|
VALUE Iterator_each(VALUE self) {
|
94
94
|
if (rb_block_given_p()) {
|
95
|
-
Iterator_t *iterator =
|
95
|
+
Iterator_t *iterator = self_to_iterator(self);
|
96
96
|
each_method method = mode_to_each_method(iterator->mode);
|
97
97
|
method(iterator->query);
|
98
98
|
}
|
@@ -130,7 +130,7 @@ inline next_method mode_to_next_method(enum iterator_mode mode) {
|
|
130
130
|
* @return [Array, Extralite::Iterator] next rows or self if block is given
|
131
131
|
*/
|
132
132
|
VALUE Iterator_next(int argc, VALUE *argv, VALUE self) {
|
133
|
-
Iterator_t *iterator =
|
133
|
+
Iterator_t *iterator = self_to_iterator(self);
|
134
134
|
next_method method = mode_to_next_method(iterator->mode);
|
135
135
|
VALUE result = method(argc, argv, iterator->query);
|
136
136
|
|
@@ -156,11 +156,34 @@ inline to_a_method mode_to_to_a_method(enum iterator_mode mode) {
|
|
156
156
|
* @return [Array] array of query result set rows
|
157
157
|
*/
|
158
158
|
VALUE Iterator_to_a(VALUE self) {
|
159
|
-
Iterator_t *iterator =
|
159
|
+
Iterator_t *iterator = self_to_iterator(self);
|
160
160
|
to_a_method method = mode_to_to_a_method(iterator->mode);
|
161
161
|
return method(iterator->query);
|
162
162
|
}
|
163
163
|
|
164
|
+
inline VALUE mode_to_symbol(Iterator_t *iterator) {
|
165
|
+
switch (iterator->mode) {
|
166
|
+
case ITERATOR_ARY:
|
167
|
+
return SYM_ary;
|
168
|
+
case ITERATOR_SINGLE_COLUMN:
|
169
|
+
return SYM_single_column;
|
170
|
+
default:
|
171
|
+
return SYM_hash;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
/* Returns a short string representation of the iterator instance, including the
|
176
|
+
* SQL string.
|
177
|
+
*
|
178
|
+
* @return [String] string representation
|
179
|
+
*/
|
180
|
+
VALUE Iterator_inspect(VALUE self) {
|
181
|
+
VALUE cname = rb_class_name(CLASS_OF(self));
|
182
|
+
VALUE sym = mode_to_symbol(self_to_iterator(self));
|
183
|
+
|
184
|
+
return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, sym);
|
185
|
+
}
|
186
|
+
|
164
187
|
void Init_ExtraliteIterator(void) {
|
165
188
|
VALUE mExtralite = rb_define_module("Extralite");
|
166
189
|
|
@@ -171,10 +194,15 @@ void Init_ExtraliteIterator(void) {
|
|
171
194
|
|
172
195
|
rb_define_method(cIterator, "initialize", Iterator_initialize, 2);
|
173
196
|
rb_define_method(cIterator, "each", Iterator_each, 0);
|
197
|
+
rb_define_method(cIterator, "inspect", Iterator_inspect, 0);
|
174
198
|
rb_define_method(cIterator, "next", Iterator_next, -1);
|
175
199
|
rb_define_method(cIterator, "to_a", Iterator_to_a, 0);
|
176
200
|
|
177
201
|
SYM_hash = ID2SYM(rb_intern("hash"));
|
178
202
|
SYM_ary = ID2SYM(rb_intern("ary"));
|
179
203
|
SYM_single_column = ID2SYM(rb_intern("single_column"));
|
204
|
+
|
205
|
+
rb_gc_register_mark_object(SYM_hash);
|
206
|
+
rb_gc_register_mark_object(SYM_ary);
|
207
|
+
rb_gc_register_mark_object(SYM_single_column);
|
180
208
|
}
|
data/ext/extralite/query.c
CHANGED
@@ -11,6 +11,9 @@
|
|
11
11
|
|
12
12
|
VALUE cQuery;
|
13
13
|
|
14
|
+
ID ID_inspect;
|
15
|
+
ID ID_slice;
|
16
|
+
|
14
17
|
static size_t Query_size(const void *ptr) {
|
15
18
|
return sizeof(Query_t);
|
16
19
|
}
|
@@ -42,7 +45,7 @@ static VALUE Query_allocate(VALUE klass) {
|
|
42
45
|
return TypedData_Wrap_Struct(klass, &Query_type, query);
|
43
46
|
}
|
44
47
|
|
45
|
-
static inline Query_t *
|
48
|
+
static inline Query_t *self_to_query(VALUE obj) {
|
46
49
|
Query_t *query;
|
47
50
|
TypedData_Get_Struct((obj), Query_t, &Query_type, (query));
|
48
51
|
return query;
|
@@ -58,14 +61,14 @@ static inline Query_t *value_to_query(VALUE obj) {
|
|
58
61
|
* @return [void]
|
59
62
|
*/
|
60
63
|
VALUE Query_initialize(VALUE self, VALUE db, VALUE sql) {
|
61
|
-
Query_t *query =
|
64
|
+
Query_t *query = self_to_query(self);
|
62
65
|
|
63
66
|
sql = rb_funcall(sql, ID_strip, 0);
|
64
67
|
if (!RSTRING_LEN(sql))
|
65
68
|
rb_raise(cError, "Cannot prepare an empty SQL query");
|
66
69
|
|
67
70
|
query->db = db;
|
68
|
-
query->db_struct =
|
71
|
+
query->db_struct = self_to_database(db);
|
69
72
|
query->sqlite3_db = Database_sqlite3_db(db);
|
70
73
|
query->sql = sql;
|
71
74
|
query->stmt = NULL;
|
@@ -113,7 +116,7 @@ static inline void query_reset_and_bind(Query_t *query, int argc, VALUE * argv)
|
|
113
116
|
* @return [Extralite::Query] self
|
114
117
|
*/
|
115
118
|
VALUE Query_reset(VALUE self) {
|
116
|
-
Query_t *query =
|
119
|
+
Query_t *query = self_to_query(self);
|
117
120
|
if (query->closed) rb_raise(cError, "Query is closed");
|
118
121
|
|
119
122
|
query_reset(query);
|
@@ -145,7 +148,7 @@ VALUE Query_reset(VALUE self) {
|
|
145
148
|
* @return [Extralite::Query] self
|
146
149
|
*/
|
147
150
|
VALUE Query_bind(int argc, VALUE *argv, VALUE self) {
|
148
|
-
Query_t *query =
|
151
|
+
Query_t *query = self_to_query(self);
|
149
152
|
if (query->closed) rb_raise(cError, "Query is closed");
|
150
153
|
|
151
154
|
query_reset_and_bind(query, argc, argv);
|
@@ -157,7 +160,7 @@ VALUE Query_bind(int argc, VALUE *argv, VALUE self) {
|
|
157
160
|
* @return [boolean] true if iteration has reached the end of the result set
|
158
161
|
*/
|
159
162
|
VALUE Query_eof_p(VALUE self) {
|
160
|
-
Query_t *query =
|
163
|
+
Query_t *query = self_to_query(self);
|
161
164
|
if (query->closed) rb_raise(cError, "Query is closed");
|
162
165
|
|
163
166
|
return query->eof ? Qtrue : Qfalse;
|
@@ -166,7 +169,7 @@ VALUE Query_eof_p(VALUE self) {
|
|
166
169
|
#define MAX_ROWS(max_rows) (max_rows == SINGLE_ROW ? 1 : max_rows)
|
167
170
|
|
168
171
|
static inline VALUE Query_perform_next(VALUE self, int max_rows, VALUE (*call)(query_ctx *)) {
|
169
|
-
Query_t *query =
|
172
|
+
Query_t *query = self_to_query(self);
|
170
173
|
if (query->closed) rb_raise(cError, "Query is closed");
|
171
174
|
|
172
175
|
if (!query->stmt) query_reset(query);
|
@@ -265,7 +268,7 @@ VALUE Query_next_single_column(int argc, VALUE *argv, VALUE self) {
|
|
265
268
|
* @return [Array<Hash>] all rows
|
266
269
|
*/
|
267
270
|
VALUE Query_to_a_hash(VALUE self) {
|
268
|
-
Query_t *query =
|
271
|
+
Query_t *query = self_to_query(self);
|
269
272
|
query_reset(query);
|
270
273
|
return Query_perform_next(self, ALL_ROWS, safe_query_hash);
|
271
274
|
}
|
@@ -275,7 +278,7 @@ VALUE Query_to_a_hash(VALUE self) {
|
|
275
278
|
* @return [Array<Array>] all rows
|
276
279
|
*/
|
277
280
|
VALUE Query_to_a_ary(VALUE self) {
|
278
|
-
Query_t *query =
|
281
|
+
Query_t *query = self_to_query(self);
|
279
282
|
query_reset(query);
|
280
283
|
return Query_perform_next(self, ALL_ROWS, safe_query_ary);
|
281
284
|
}
|
@@ -286,7 +289,7 @@ VALUE Query_to_a_ary(VALUE self) {
|
|
286
289
|
* @return [Array<Object>] all rows
|
287
290
|
*/
|
288
291
|
VALUE Query_to_a_single_column(VALUE self) {
|
289
|
-
Query_t *query =
|
292
|
+
Query_t *query = self_to_query(self);
|
290
293
|
query_reset(query);
|
291
294
|
return Query_perform_next(self, ALL_ROWS, safe_query_single_column);
|
292
295
|
}
|
@@ -300,7 +303,7 @@ VALUE Query_to_a_single_column(VALUE self) {
|
|
300
303
|
VALUE Query_each_hash(VALUE self) {
|
301
304
|
if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_hash);
|
302
305
|
|
303
|
-
Query_t *query =
|
306
|
+
Query_t *query = self_to_query(self);
|
304
307
|
query_reset(query);
|
305
308
|
return Query_perform_next(self, ALL_ROWS, safe_query_hash);
|
306
309
|
}
|
@@ -314,7 +317,7 @@ VALUE Query_each_hash(VALUE self) {
|
|
314
317
|
VALUE Query_each_ary(VALUE self) {
|
315
318
|
if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_ary);
|
316
319
|
|
317
|
-
Query_t *query =
|
320
|
+
Query_t *query = self_to_query(self);
|
318
321
|
query_reset(query);
|
319
322
|
return Query_perform_next(self, ALL_ROWS, safe_query_ary);
|
320
323
|
}
|
@@ -329,11 +332,40 @@ VALUE Query_each_ary(VALUE self) {
|
|
329
332
|
VALUE Query_each_single_column(VALUE self) {
|
330
333
|
if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_single_column);
|
331
334
|
|
332
|
-
Query_t *query =
|
335
|
+
Query_t *query = self_to_query(self);
|
333
336
|
query_reset(query);
|
334
337
|
return Query_perform_next(self, ALL_ROWS, safe_query_single_column);
|
335
338
|
}
|
336
339
|
|
340
|
+
/* call-seq:
|
341
|
+
* query.execute(*parameters) -> changes
|
342
|
+
*
|
343
|
+
* Runs a query returning the total changes effected. This method should be used
|
344
|
+
* for data- or schema-manipulation queries.
|
345
|
+
*
|
346
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
347
|
+
* a list of values or as a hash mapping parameter names to values. When
|
348
|
+
* parameters are given as an array, the query should specify parameters using
|
349
|
+
* `?`:
|
350
|
+
*
|
351
|
+
* query = db.prepare('update foo set x = ? where y = ?')
|
352
|
+
* query.execute(42, 43)
|
353
|
+
*
|
354
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
355
|
+
* specified using a hash, where keys are either strings are symbols. String
|
356
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
357
|
+
*
|
358
|
+
* query = db.prepare('update foo set x = :bar')
|
359
|
+
* query.execute(bar: 42)
|
360
|
+
* query.execute('bar' => 42)
|
361
|
+
* query.execute(':bar' => 42)
|
362
|
+
*/
|
363
|
+
VALUE Query_execute(int argc, VALUE *argv, VALUE self) {
|
364
|
+
Query_t *query = self_to_query(self);
|
365
|
+
query_reset_and_bind(query, argc, argv);
|
366
|
+
return Query_perform_next(self, ALL_ROWS, safe_query_changes);
|
367
|
+
}
|
368
|
+
|
337
369
|
/* Executes the query for each set of parameters in the given array. Parameters
|
338
370
|
* can be specified as either an array (for unnamed parameters) or a hash (for
|
339
371
|
* named parameters). Returns the number of changes effected. This method is
|
@@ -350,7 +382,7 @@ VALUE Query_each_single_column(VALUE self) {
|
|
350
382
|
* @return [Integer] number of changes effected
|
351
383
|
*/
|
352
384
|
VALUE Query_execute_multi(VALUE self, VALUE parameters) {
|
353
|
-
Query_t *query =
|
385
|
+
Query_t *query = self_to_query(self);
|
354
386
|
if (query->closed) rb_raise(cError, "Query is closed");
|
355
387
|
|
356
388
|
if (!query->stmt)
|
@@ -368,7 +400,7 @@ VALUE Query_execute_multi(VALUE self, VALUE parameters) {
|
|
368
400
|
* @return [Extralite::Database] associated database
|
369
401
|
*/
|
370
402
|
VALUE Query_database(VALUE self) {
|
371
|
-
Query_t *query =
|
403
|
+
Query_t *query = self_to_query(self);
|
372
404
|
return query->db;
|
373
405
|
}
|
374
406
|
|
@@ -377,7 +409,7 @@ VALUE Query_database(VALUE self) {
|
|
377
409
|
* @return [String] SQL string
|
378
410
|
*/
|
379
411
|
VALUE Query_sql(VALUE self) {
|
380
|
-
Query_t *query =
|
412
|
+
Query_t *query = self_to_query(self);
|
381
413
|
return query->sql;
|
382
414
|
}
|
383
415
|
|
@@ -386,7 +418,7 @@ VALUE Query_sql(VALUE self) {
|
|
386
418
|
* @return [Array<Symbol>] column names
|
387
419
|
*/
|
388
420
|
VALUE Query_columns(VALUE self) {
|
389
|
-
Query_t *query =
|
421
|
+
Query_t *query = self_to_query(self);
|
390
422
|
query_reset(query);
|
391
423
|
return Query_perform_next(self, ALL_ROWS, safe_query_columns);
|
392
424
|
}
|
@@ -396,7 +428,7 @@ VALUE Query_columns(VALUE self) {
|
|
396
428
|
* @return [Extralite::Query] self
|
397
429
|
*/
|
398
430
|
VALUE Query_close(VALUE self) {
|
399
|
-
Query_t *query =
|
431
|
+
Query_t *query = self_to_query(self);
|
400
432
|
if (query->stmt) {
|
401
433
|
sqlite3_finalize(query->stmt);
|
402
434
|
query->stmt = NULL;
|
@@ -410,7 +442,7 @@ VALUE Query_close(VALUE self) {
|
|
410
442
|
* @return [boolean] true if query is closed
|
411
443
|
*/
|
412
444
|
VALUE Query_closed_p(VALUE self) {
|
413
|
-
Query_t *query =
|
445
|
+
Query_t *query = self_to_query(self);
|
414
446
|
return query->closed ? Qtrue : Qfalse;
|
415
447
|
}
|
416
448
|
|
@@ -431,7 +463,7 @@ VALUE Query_status(int argc, VALUE* argv, VALUE self) {
|
|
431
463
|
|
432
464
|
rb_scan_args(argc, argv, "11", &op, &reset);
|
433
465
|
|
434
|
-
Query_t *query =
|
466
|
+
Query_t *query = self_to_query(self);
|
435
467
|
if (query->closed) rb_raise(cError, "Query is closed");
|
436
468
|
|
437
469
|
if (!query->stmt)
|
@@ -441,6 +473,24 @@ VALUE Query_status(int argc, VALUE* argv, VALUE self) {
|
|
441
473
|
return INT2NUM(value);
|
442
474
|
}
|
443
475
|
|
476
|
+
/* Returns a short string representation of the query instance, including the
|
477
|
+
* SQL string.
|
478
|
+
*
|
479
|
+
* @return [String] string representation
|
480
|
+
*/
|
481
|
+
VALUE Query_inspect(VALUE self) {
|
482
|
+
VALUE cname = rb_class_name(CLASS_OF(self));
|
483
|
+
VALUE sql = self_to_query(self)->sql;
|
484
|
+
if (RSTRING_LEN(sql) > 48) {
|
485
|
+
sql = rb_funcall(sql, ID_slice, 2, INT2FIX(0), INT2FIX(45));
|
486
|
+
rb_str_cat2(sql, "...");
|
487
|
+
}
|
488
|
+
sql = rb_funcall(sql, ID_inspect, 0);
|
489
|
+
|
490
|
+
RB_GC_GUARD(sql);
|
491
|
+
return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, sql);
|
492
|
+
}
|
493
|
+
|
444
494
|
void Init_ExtraliteQuery(void) {
|
445
495
|
VALUE mExtralite = rb_define_module("Extralite");
|
446
496
|
|
@@ -460,8 +510,10 @@ void Init_ExtraliteQuery(void) {
|
|
460
510
|
rb_define_method(cQuery, "each_single_column", Query_each_single_column, 0);
|
461
511
|
|
462
512
|
rb_define_method(cQuery, "eof?", Query_eof_p, 0);
|
513
|
+
rb_define_method(cQuery, "execute", Query_execute, -1);
|
463
514
|
rb_define_method(cQuery, "execute_multi", Query_execute_multi, 1);
|
464
515
|
rb_define_method(cQuery, "initialize", Query_initialize, 2);
|
516
|
+
rb_define_method(cQuery, "inspect", Query_inspect, 0);
|
465
517
|
|
466
518
|
rb_define_method(cQuery, "next", Query_next_hash, -1);
|
467
519
|
rb_define_method(cQuery, "next_ary", Query_next_ary, -1);
|
@@ -476,4 +528,7 @@ void Init_ExtraliteQuery(void) {
|
|
476
528
|
rb_define_method(cQuery, "to_a_ary", Query_to_a_ary, 0);
|
477
529
|
rb_define_method(cQuery, "to_a_hash", Query_to_a_hash, 0);
|
478
530
|
rb_define_method(cQuery, "to_a_single_column", Query_to_a_single_column, 0);
|
531
|
+
|
532
|
+
ID_inspect = rb_intern("inspect");
|
533
|
+
ID_slice = rb_intern("slice");
|
479
534
|
}
|
data/lib/extralite/version.rb
CHANGED
data/lib/extralite.rb
CHANGED
data/test/test_database.rb
CHANGED
@@ -206,6 +206,17 @@ end
|
|
206
206
|
assert_equal [{recursive_triggers: 1}], @db.pragma(:recursive_triggers)
|
207
207
|
end
|
208
208
|
|
209
|
+
def test_execute
|
210
|
+
changes = @db.execute('update t set x = 42')
|
211
|
+
assert_equal 2, changes
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_execute_with_params
|
215
|
+
changes = @db.execute('update t set x = ? where z = ?', 42, 6)
|
216
|
+
assert_equal 1, changes
|
217
|
+
assert_equal [[1, 2, 3], [42, 5, 6]], @db.query_ary('select * from t order by x')
|
218
|
+
end
|
219
|
+
|
209
220
|
def test_execute_multi
|
210
221
|
@db.query('create table foo (a, b, c)')
|
211
222
|
assert_equal [], @db.query('select * from foo')
|
@@ -361,6 +372,21 @@ end
|
|
361
372
|
query.next
|
362
373
|
@db.close
|
363
374
|
end
|
375
|
+
|
376
|
+
def test_read_only_database
|
377
|
+
db = Extralite::Database.new(':memory:')
|
378
|
+
db.query('create table foo (bar)')
|
379
|
+
assert_equal false, db.read_only?
|
380
|
+
|
381
|
+
db = Extralite::Database.new(':memory:', read_only: true)
|
382
|
+
assert_raises(Extralite::Error) { db.query('create table foo (bar)') }
|
383
|
+
assert_equal true, db.read_only?
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_database_inspect
|
387
|
+
db = Extralite::Database.new(':memory:')
|
388
|
+
assert_match /^\#\<Extralite::Database:0x[0-9a-f]+ :memory:\>$/, db.inspect
|
389
|
+
end
|
364
390
|
end
|
365
391
|
|
366
392
|
class ScenarioTest < MiniTest::Test
|
data/test/test_iterator.rb
CHANGED
@@ -96,4 +96,9 @@ class IteratorTest < MiniTest::Test
|
|
96
96
|
mapped = query.each_single_column.map { |v| v * 10 }
|
97
97
|
assert_equal [30, 60, 90], mapped
|
98
98
|
end
|
99
|
+
|
100
|
+
def test_iterator_inspect
|
101
|
+
i = @query.each_ary
|
102
|
+
assert_match /^\#\<Extralite::Iterator:0x[0-9a-f]+ ary\>$/, i.inspect
|
103
|
+
end
|
99
104
|
end
|
data/test/test_query.rb
CHANGED
@@ -430,6 +430,18 @@ class QueryTest < MiniTest::Test
|
|
430
430
|
assert_raises(Extralite::Error) { p.next }
|
431
431
|
end
|
432
432
|
|
433
|
+
def test_query_execute
|
434
|
+
q = @db.prepare('update t set x = 42')
|
435
|
+
assert_equal 3, q.execute
|
436
|
+
assert_equal [[42, 2, 3], [42, 5, 6], [42, 8, 9]], @db.query_ary('select * from t order by z')
|
437
|
+
end
|
438
|
+
|
439
|
+
def test_query_execute_with_params
|
440
|
+
q = @db.prepare('update t set x = ? where z = ?')
|
441
|
+
assert_equal 1, q.execute(42, 9)
|
442
|
+
assert_equal [[1, 2, 3], [4, 5, 6], [42, 8, 9]], @db.query_ary('select * from t order by z')
|
443
|
+
end
|
444
|
+
|
433
445
|
def test_query_execute_multi
|
434
446
|
@db.query('create table foo (a, b, c)')
|
435
447
|
assert_equal [], @db.query('select * from foo')
|
@@ -499,4 +511,9 @@ class QueryTest < MiniTest::Test
|
|
499
511
|
assert_equal [1, 4, 7], query.to_a_single_column
|
500
512
|
assert_equal true, query.eof?
|
501
513
|
end
|
514
|
+
|
515
|
+
def test_query_inspect
|
516
|
+
q = @db.prepare('select x from t')
|
517
|
+
assert_match /^\#\<Extralite::Query:0x[0-9a-f]+ #{q.sql.inspect}\>$/, q.inspect
|
518
|
+
end
|
502
519
|
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: '2.
|
4
|
+
version: '2.1'
|
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-07-
|
11
|
+
date: 2023-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|