extralite 1.27 → 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 +14 -0
- data/Gemfile.lock +1 -1
- data/LICENSE +1 -1
- data/README.md +40 -14
- data/TODO.md +21 -0
- data/ext/extralite/common.c +80 -58
- data/ext/extralite/database.c +138 -78
- data/ext/extralite/extconf.rb +16 -16
- data/ext/extralite/extralite.h +63 -17
- data/ext/extralite/extralite_ext.c +4 -2
- data/ext/extralite/iterator.c +208 -0
- data/ext/extralite/query.c +534 -0
- data/lib/extralite/sqlite3_constants.rb +1 -1
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +0 -2
- data/lib/sequel/adapters/extralite.rb +104 -106
- data/test/perf_prepared.rb +2 -2
- data/test/test_database.rb +35 -9
- data/test/test_extralite.rb +1 -1
- data/test/test_iterator.rb +104 -0
- data/test/test_query.rb +519 -0
- data/test/test_sequel.rb +23 -4
- metadata +6 -4
- data/ext/extralite/prepared_statement.c +0 -333
- data/test/test_prepared_statement.rb +0 -225
    
        data/ext/extralite/database.c
    CHANGED
    
    | @@ -6,13 +6,17 @@ VALUE cError; | |
| 6 6 | 
             
            VALUE cSQLError;
         | 
| 7 7 | 
             
            VALUE cBusyError;
         | 
| 8 8 | 
             
            VALUE cInterruptError;
         | 
| 9 | 
            +
            VALUE eArgumentError;
         | 
| 9 10 |  | 
| 11 | 
            +
            ID ID_bind;
         | 
| 10 12 | 
             
            ID ID_call;
         | 
| 11 13 | 
             
            ID ID_keys;
         | 
| 12 14 | 
             
            ID ID_new;
         | 
| 13 15 | 
             
            ID ID_strip;
         | 
| 14 16 | 
             
            ID ID_to_s;
         | 
| 15 17 |  | 
| 18 | 
            +
            VALUE SYM_read_only;
         | 
| 19 | 
            +
             | 
| 16 20 | 
             
            static size_t Database_size(const void *ptr) {
         | 
| 17 21 | 
             
              return sizeof(Database_t);
         | 
| 18 22 | 
             
            }
         | 
| @@ -35,27 +39,21 @@ static VALUE Database_allocate(VALUE klass) { | |
| 35 39 | 
             
              return TypedData_Wrap_Struct(klass, &Database_type, db);
         | 
| 36 40 | 
             
            }
         | 
| 37 41 |  | 
| 38 | 
            -
             | 
| 39 | 
            -
               | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
            #define GetOpenDatabase(obj, database) { \
         | 
| 43 | 
            -
              TypedData_Get_Struct((obj), Database_t, &Database_type, (database)); \
         | 
| 44 | 
            -
              if (!(database)->sqlite3_db) { \
         | 
| 45 | 
            -
                rb_raise(cError, "Database is closed"); \
         | 
| 46 | 
            -
              } \
         | 
| 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;
         | 
| 47 46 | 
             
            }
         | 
| 48 47 |  | 
| 49 | 
            -
            Database_t * | 
| 50 | 
            -
              Database_t *db;
         | 
| 51 | 
            -
               | 
| 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 | 
            +
              
         | 
| 52 52 | 
             
              return db;
         | 
| 53 53 | 
             
            }
         | 
| 54 54 |  | 
| 55 | 
            -
            sqlite3 *Database_sqlite3_db(VALUE self) {
         | 
| 56 | 
            -
               | 
| 57 | 
            -
              GetDatabase(self, db);
         | 
| 58 | 
            -
              return db->sqlite3_db;
         | 
| 55 | 
            +
            inline sqlite3 *Database_sqlite3_db(VALUE self) {
         | 
| 56 | 
            +
              return self_to_database(self)->sqlite3_db;
         | 
| 59 57 | 
             
            }
         | 
| 60 58 |  | 
| 61 59 | 
             
            /* call-seq:
         | 
| @@ -68,18 +66,37 @@ VALUE Extralite_sqlite3_version(VALUE self) { | |
| 68 66 | 
             
              return rb_str_new_cstr(sqlite3_version);
         | 
| 69 67 | 
             
            }
         | 
| 70 68 |  | 
| 71 | 
            -
             | 
| 72 | 
            -
              | 
| 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.
         | 
| 73 82 | 
             
             *
         | 
| 74 | 
            -
             *  | 
| 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]
         | 
| 75 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;
         | 
| 76 95 |  | 
| 77 | 
            -
             | 
| 78 | 
            -
              int  | 
| 79 | 
            -
              Database_t *db;
         | 
| 80 | 
            -
              GetDatabase(self, db);
         | 
| 96 | 
            +
              rb_scan_args(argc, argv, "11", &path, &opts);
         | 
| 97 | 
            +
              int flags = db_open_flags_from_opts(opts);
         | 
| 81 98 |  | 
| 82 | 
            -
              rc =  | 
| 99 | 
            +
              int rc = sqlite3_open_v2(StringValueCStr(path), &db->sqlite3_db, flags, NULL);
         | 
| 83 100 | 
             
              if (rc) {
         | 
| 84 101 | 
             
                sqlite3_close_v2(db->sqlite3_db);
         | 
| 85 102 | 
             
                rb_raise(cError, "%s", sqlite3_errstr(rc));
         | 
| @@ -105,6 +122,16 @@ VALUE Database_initialize(VALUE self, VALUE path) { | |
| 105 122 | 
             
              return Qnil;
         | 
| 106 123 | 
             
            }
         | 
| 107 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 | 
            +
             | 
| 108 135 | 
             
            /* call-seq:
         | 
| 109 136 | 
             
             *   db.close -> db
         | 
| 110 137 | 
             
             *
         | 
| @@ -112,8 +139,7 @@ VALUE Database_initialize(VALUE self, VALUE path) { | |
| 112 139 | 
             
             */
         | 
| 113 140 | 
             
            VALUE Database_close(VALUE self) {
         | 
| 114 141 | 
             
              int rc;
         | 
| 115 | 
            -
              Database_t *db;
         | 
| 116 | 
            -
              GetDatabase(self, db);
         | 
| 142 | 
            +
              Database_t *db = self_to_database(self);
         | 
| 117 143 |  | 
| 118 144 | 
             
              rc = sqlite3_close_v2(db->sqlite3_db);
         | 
| 119 145 | 
             
              if (rc) {
         | 
| @@ -132,14 +158,12 @@ VALUE Database_close(VALUE self) { | |
| 132 158 | 
             
             * @return [bool] is database closed
         | 
| 133 159 | 
             
             */
         | 
| 134 160 | 
             
            VALUE Database_closed_p(VALUE self) {
         | 
| 135 | 
            -
              Database_t *db;
         | 
| 136 | 
            -
              GetDatabase(self, db);
         | 
| 137 | 
            -
             | 
| 161 | 
            +
              Database_t *db = self_to_database(self);
         | 
| 138 162 | 
             
              return db->sqlite3_db ? Qfalse : Qtrue;
         | 
| 139 163 | 
             
            }
         | 
| 140 164 |  | 
| 141 165 | 
             
            static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *)) {
         | 
| 142 | 
            -
              Database_t *db;
         | 
| 166 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 143 167 | 
             
              sqlite3_stmt *stmt;
         | 
| 144 168 | 
             
              VALUE sql;
         | 
| 145 169 |  | 
| @@ -149,13 +173,12 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA | |
| 149 173 | 
             
              if (RSTRING_LEN(sql) == 0) return Qnil;
         | 
| 150 174 |  | 
| 151 175 | 
             
              // prepare query ctx
         | 
| 152 | 
            -
              GetOpenDatabase(self, db);
         | 
| 153 176 | 
             
              if (db->trace_block != Qnil) rb_funcall(db->trace_block, ID_call, 1, sql);
         | 
| 154 177 | 
             
              prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
         | 
| 155 178 | 
             
              RB_GC_GUARD(sql);
         | 
| 156 179 |  | 
| 157 180 | 
             
              bind_all_parameters(stmt, argc - 1, argv + 1);
         | 
| 158 | 
            -
              query_ctx ctx = { self, db->sqlite3_db, stmt };
         | 
| 181 | 
            +
              query_ctx ctx = { self, db->sqlite3_db, stmt, Qnil, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
         | 
| 159 182 |  | 
| 160 183 | 
             
              return rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
         | 
| 161 184 | 
             
            }
         | 
| @@ -170,7 +193,7 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA | |
| 170 193 | 
             
             * 
         | 
| 171 194 | 
             
             * Query parameters to be bound to placeholders in the query can be specified as
         | 
| 172 195 | 
             
             * a list of values or as a hash mapping parameter names to values. When
         | 
| 173 | 
            -
             * parameters are given as  | 
| 196 | 
            +
             * parameters are given as an array, the query should specify parameters using
         | 
| 174 197 | 
             
             * `?`:
         | 
| 175 198 | 
             
             * 
         | 
| 176 199 | 
             
             *     db.query('select * from foo where x = ?', 42)
         | 
| @@ -195,7 +218,7 @@ VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) { | |
| 195 218 | 
             
             *
         | 
| 196 219 | 
             
             * Query parameters to be bound to placeholders in the query can be specified as
         | 
| 197 220 | 
             
             * a list of values or as a hash mapping parameter names to values. When
         | 
| 198 | 
            -
             * parameters are given as  | 
| 221 | 
            +
             * parameters are given as an array, the query should specify parameters using
         | 
| 199 222 | 
             
             * `?`:
         | 
| 200 223 | 
             
             *
         | 
| 201 224 | 
             
             *     db.query_ary('select * from foo where x = ?', 42)
         | 
| @@ -219,7 +242,7 @@ VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) { | |
| 219 242 | 
             
             *
         | 
| 220 243 | 
             
             * Query parameters to be bound to placeholders in the query can be specified as
         | 
| 221 244 | 
             
             * a list of values or as a hash mapping parameter names to values. When
         | 
| 222 | 
            -
             * parameters are given as  | 
| 245 | 
            +
             * parameters are given as an array, the query should specify parameters using
         | 
| 223 246 | 
             
             * `?`:
         | 
| 224 247 | 
             
             *
         | 
| 225 248 | 
             
             *     db.query_single_row('select * from foo where x = ?', 42)
         | 
| @@ -244,7 +267,7 @@ VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) { | |
| 244 267 | 
             
             *
         | 
| 245 268 | 
             
             * Query parameters to be bound to placeholders in the query can be specified as
         | 
| 246 269 | 
             
             * a list of values or as a hash mapping parameter names to values. When
         | 
| 247 | 
            -
             * parameters are given as  | 
| 270 | 
            +
             * parameters are given as an array, the query should specify parameters using
         | 
| 248 271 | 
             
             * `?`:
         | 
| 249 272 | 
             
             *
         | 
| 250 273 | 
             
             *     db.query_single_column('select x from foo where x = ?', 42)
         | 
| @@ -268,7 +291,7 @@ VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) { | |
| 268 291 | 
             
             *
         | 
| 269 292 | 
             
             * Query parameters to be bound to placeholders in the query can be specified as
         | 
| 270 293 | 
             
             * a list of values or as a hash mapping parameter names to values. When
         | 
| 271 | 
            -
             * parameters are given as  | 
| 294 | 
            +
             * parameters are given as an array, the query should specify parameters using
         | 
| 272 295 | 
             
             * `?`:
         | 
| 273 296 | 
             
             *
         | 
| 274 297 | 
             
             *     db.query_single_value('select x from foo where x = ?', 42)
         | 
| @@ -285,6 +308,31 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) { | |
| 285 308 | 
             
              return Database_perform_query(argc, argv, self, safe_query_single_value);
         | 
| 286 309 | 
             
            }
         | 
| 287 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 | 
            +
             | 
| 288 336 | 
             
            /* call-seq:
         | 
| 289 337 | 
             
             *   db.execute_multi(sql, params_array) -> changes
         | 
| 290 338 | 
             
             *
         | 
| @@ -296,19 +344,18 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) { | |
| 296 344 | 
             
             *       [1, 2, 3],
         | 
| 297 345 | 
             
             *       [4, 5, 6]
         | 
| 298 346 | 
             
             *     ]
         | 
| 299 | 
            -
             *     db. | 
| 347 | 
            +
             *     db.execute_multi('insert into foo values (?, ?, ?)', records)
         | 
| 300 348 | 
             
             *
         | 
| 301 349 | 
             
             */
         | 
| 302 350 | 
             
            VALUE Database_execute_multi(VALUE self, VALUE sql, VALUE params_array) {
         | 
| 303 | 
            -
              Database_t *db;
         | 
| 351 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 304 352 | 
             
              sqlite3_stmt *stmt;
         | 
| 305 353 |  | 
| 306 354 | 
             
              if (RSTRING_LEN(sql) == 0) return Qnil;
         | 
| 307 355 |  | 
| 308 356 | 
             
              // prepare query ctx
         | 
| 309 | 
            -
              GetOpenDatabase(self, db);
         | 
| 310 357 | 
             
              prepare_single_stmt(db->sqlite3_db, &stmt, sql);
         | 
| 311 | 
            -
              query_ctx ctx = { self, db->sqlite3_db, stmt, params_array };
         | 
| 358 | 
            +
              query_ctx ctx = { self, db->sqlite3_db, stmt, params_array, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
         | 
| 312 359 |  | 
| 313 360 | 
             
              return rb_ensure(SAFE(safe_execute_multi), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
         | 
| 314 361 | 
             
            }
         | 
| @@ -328,8 +375,7 @@ VALUE Database_columns(VALUE self, VALUE sql) { | |
| 328 375 | 
             
             * Returns the rowid of the last inserted row.
         | 
| 329 376 | 
             
             */
         | 
| 330 377 | 
             
            VALUE Database_last_insert_rowid(VALUE self) {
         | 
| 331 | 
            -
              Database_t *db;
         | 
| 332 | 
            -
              GetOpenDatabase(self, db);
         | 
| 378 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 333 379 |  | 
| 334 380 | 
             
              return INT2FIX(sqlite3_last_insert_rowid(db->sqlite3_db));
         | 
| 335 381 | 
             
            }
         | 
| @@ -340,8 +386,7 @@ VALUE Database_last_insert_rowid(VALUE self) { | |
| 340 386 | 
             
             * Returns the number of changes made to the database by the last operation.
         | 
| 341 387 | 
             
             */
         | 
| 342 388 | 
             
            VALUE Database_changes(VALUE self) {
         | 
| 343 | 
            -
              Database_t *db;
         | 
| 344 | 
            -
              GetOpenDatabase(self, db);
         | 
| 389 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 345 390 |  | 
| 346 391 | 
             
              return INT2FIX(sqlite3_changes(db->sqlite3_db));
         | 
| 347 392 | 
             
            }
         | 
| @@ -354,8 +399,7 @@ VALUE Database_changes(VALUE self) { | |
| 354 399 | 
             
            VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
         | 
| 355 400 | 
             
              const char *db_name;
         | 
| 356 401 | 
             
              const char *filename;
         | 
| 357 | 
            -
              Database_t *db;
         | 
| 358 | 
            -
              GetOpenDatabase(self, db);
         | 
| 402 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 359 403 |  | 
| 360 404 | 
             
              rb_check_arity(argc, 0, 1);
         | 
| 361 405 | 
             
              db_name = (argc == 1) ? StringValueCStr(argv[0]) : "main";
         | 
| @@ -369,8 +413,7 @@ VALUE Database_filename(int argc, VALUE *argv, VALUE self) { | |
| 369 413 | 
             
             * Returns true if a transaction is currently in progress.
         | 
| 370 414 | 
             
             */
         | 
| 371 415 | 
             
            VALUE Database_transaction_active_p(VALUE self) {
         | 
| 372 | 
            -
              Database_t *db;
         | 
| 373 | 
            -
              GetOpenDatabase(self, db);
         | 
| 416 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 374 417 |  | 
| 375 418 | 
             
              return sqlite3_get_autocommit(db->sqlite3_db) ? Qfalse : Qtrue;
         | 
| 376 419 | 
             
            }
         | 
| @@ -382,8 +425,7 @@ VALUE Database_transaction_active_p(VALUE self) { | |
| 382 425 | 
             
             * Loads an extension with the given path.
         | 
| 383 426 | 
             
             */
         | 
| 384 427 | 
             
            VALUE Database_load_extension(VALUE self, VALUE path) {
         | 
| 385 | 
            -
              Database_t *db;
         | 
| 386 | 
            -
              GetOpenDatabase(self, db);
         | 
| 428 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 387 429 | 
             
              char *err_msg;
         | 
| 388 430 |  | 
| 389 431 | 
             
              int rc = sqlite3_load_extension(db->sqlite3_db, RSTRING_PTR(path), 0, &err_msg);
         | 
| @@ -398,12 +440,16 @@ VALUE Database_load_extension(VALUE self, VALUE path) { | |
| 398 440 | 
             
            #endif
         | 
| 399 441 |  | 
| 400 442 | 
             
            /* call-seq:
         | 
| 401 | 
            -
             *   db.prepare(sql) -> Extralite:: | 
| 443 | 
            +
             *   db.prepare(sql) -> Extralite::Query
         | 
| 402 444 | 
             
             *
         | 
| 403 445 | 
             
             * Creates a prepared statement with the given SQL query.
         | 
| 404 446 | 
             
             */
         | 
| 405 | 
            -
            VALUE Database_prepare(VALUE  | 
| 406 | 
            -
               | 
| 447 | 
            +
            VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
         | 
| 448 | 
            +
              rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
         | 
| 449 | 
            +
              VALUE query = rb_funcall(cQuery, ID_new, 2, self, argv[0]);
         | 
| 450 | 
            +
              if (argc > 1) rb_funcallv(query, ID_bind, argc - 1, argv + 1);
         | 
| 451 | 
            +
              RB_GC_GUARD(query);
         | 
| 452 | 
            +
              return query;
         | 
| 407 453 | 
             
            }
         | 
| 408 454 |  | 
| 409 455 | 
             
            /* call-seq:
         | 
| @@ -417,8 +463,7 @@ VALUE Database_prepare(VALUE self, VALUE sql) { | |
| 417 463 | 
             
             * For more information, consult the [sqlite3 API docs](https://sqlite.org/c3ref/interrupt.html).
         | 
| 418 464 | 
             
             */
         | 
| 419 465 | 
             
            VALUE Database_interrupt(VALUE self) {
         | 
| 420 | 
            -
              Database_t *db;
         | 
| 421 | 
            -
              GetOpenDatabase(self, db);
         | 
| 466 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 422 467 |  | 
| 423 468 | 
             
              sqlite3_interrupt(db->sqlite3_db);
         | 
| 424 469 | 
             
              return self;
         | 
| @@ -510,8 +555,7 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) { | |
| 510 555 |  | 
| 511 556 | 
             
              int dst_is_fn = TYPE(dst) == T_STRING;
         | 
| 512 557 |  | 
| 513 | 
            -
              Database_t *src;
         | 
| 514 | 
            -
              GetOpenDatabase(self, src);
         | 
| 558 | 
            +
              Database_t *src = self_to_open_database(self);
         | 
| 515 559 | 
             
              sqlite3 *dst_db;
         | 
| 516 560 |  | 
| 517 561 | 
             
              if (dst_is_fn) {
         | 
| @@ -522,8 +566,7 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) { | |
| 522 566 | 
             
                }
         | 
| 523 567 | 
             
              }
         | 
| 524 568 | 
             
              else {
         | 
| 525 | 
            -
                Database_t *dst_struct;
         | 
| 526 | 
            -
                GetOpenDatabase(dst, dst_struct);
         | 
| 569 | 
            +
                Database_t *dst_struct = self_to_open_database(dst);
         | 
| 527 570 | 
             
                dst_db = dst_struct->sqlite3_db;
         | 
| 528 571 | 
             
              }
         | 
| 529 572 |  | 
| @@ -540,6 +583,9 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) { | |
| 540 583 | 
             
              backup_ctx ctx = { dst_db, dst_is_fn, backup, rb_block_given_p(), 0 };
         | 
| 541 584 | 
             
              rb_ensure(SAFE(backup_safe_iterate), (VALUE)&ctx, SAFE(backup_cleanup), (VALUE)&ctx);
         | 
| 542 585 |  | 
| 586 | 
            +
              RB_GC_GUARD(src_name);
         | 
| 587 | 
            +
              RB_GC_GUARD(dst_name);
         | 
| 588 | 
            +
             | 
| 543 589 | 
             
              return self;
         | 
| 544 590 | 
             
            }
         | 
| 545 591 |  | 
| @@ -575,8 +621,7 @@ VALUE Database_status(int argc, VALUE *argv, VALUE self) { | |
| 575 621 |  | 
| 576 622 | 
             
              rb_scan_args(argc, argv, "11", &op, &reset);
         | 
| 577 623 |  | 
| 578 | 
            -
              Database_t *db;
         | 
| 579 | 
            -
              GetOpenDatabase(self, db);
         | 
| 624 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 580 625 |  | 
| 581 626 | 
             
              int rc = sqlite3_db_status(db->sqlite3_db, NUM2INT(op), &cur, &hwm, RTEST(reset) ? 1 : 0);
         | 
| 582 627 | 
             
              if (rc != SQLITE_OK) rb_raise(cError, "%s", sqlite3_errstr(rc));
         | 
| @@ -596,8 +641,7 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) { | |
| 596 641 |  | 
| 597 642 | 
             
              rb_scan_args(argc, argv, "11", &category, &new_value);
         | 
| 598 643 |  | 
| 599 | 
            -
              Database_t *db;
         | 
| 600 | 
            -
              GetOpenDatabase(self, db);
         | 
| 644 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 601 645 |  | 
| 602 646 | 
             
              int value = sqlite3_limit(db->sqlite3_db, NUM2INT(category), RTEST(new_value) ? NUM2INT(new_value) : -1);
         | 
| 603 647 |  | 
| @@ -614,8 +658,7 @@ VALUE Database_limit(int argc, VALUE *argv, VALUE self) { | |
| 614 658 | 
             
             * disable the busy timeout, set it to 0 or nil.
         | 
| 615 659 | 
             
             */
         | 
| 616 660 | 
             
            VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
         | 
| 617 | 
            -
              Database_t *db;
         | 
| 618 | 
            -
              GetOpenDatabase(self, db);
         | 
| 661 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 619 662 |  | 
| 620 663 | 
             
              int ms = (sec == Qnil) ? 0 : (int)(NUM2DBL(sec) * 1000);
         | 
| 621 664 | 
             
              int rc = sqlite3_busy_timeout(db->sqlite3_db, ms);
         | 
| @@ -630,8 +673,7 @@ VALUE Database_busy_timeout_set(VALUE self, VALUE sec) { | |
| 630 673 | 
             
             * Returns the total number of changes made to the database since opening it.
         | 
| 631 674 | 
             
             */
         | 
| 632 675 | 
             
            VALUE Database_total_changes(VALUE self) {
         | 
| 633 | 
            -
              Database_t *db;
         | 
| 634 | 
            -
              GetOpenDatabase(self, db);
         | 
| 676 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 635 677 |  | 
| 636 678 | 
             
              int value = sqlite3_total_changes(db->sqlite3_db);
         | 
| 637 679 | 
             
              return INT2NUM(value);
         | 
| @@ -645,8 +687,7 @@ VALUE Database_total_changes(VALUE self) { | |
| 645 687 | 
             
             * executed.
         | 
| 646 688 | 
             
             */
         | 
| 647 689 | 
             
            VALUE Database_trace(VALUE self) {
         | 
| 648 | 
            -
              Database_t *db;
         | 
| 649 | 
            -
              GetOpenDatabase(self, db);
         | 
| 690 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 650 691 |  | 
| 651 692 | 
             
              db->trace_block = rb_block_given_p() ? rb_block_proc() : Qnil;
         | 
| 652 693 | 
             
              return self;
         | 
| @@ -658,8 +699,7 @@ VALUE Database_trace(VALUE self) { | |
| 658 699 | 
             
             * Returns the last error code for the database.
         | 
| 659 700 | 
             
             */
         | 
| 660 701 | 
             
            VALUE Database_errcode(VALUE self) {
         | 
| 661 | 
            -
              Database_t *db;
         | 
| 662 | 
            -
              GetOpenDatabase(self, db);
         | 
| 702 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 663 703 |  | 
| 664 704 | 
             
              return INT2NUM(sqlite3_errcode(db->sqlite3_db));
         | 
| 665 705 | 
             
            }
         | 
| @@ -670,8 +710,7 @@ VALUE Database_errcode(VALUE self) { | |
| 670 710 | 
             
             * Returns the last error message for the database.
         | 
| 671 711 | 
             
             */
         | 
| 672 712 | 
             
            VALUE Database_errmsg(VALUE self) {
         | 
| 673 | 
            -
              Database_t *db;
         | 
| 674 | 
            -
              GetOpenDatabase(self, db);
         | 
| 713 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 675 714 |  | 
| 676 715 | 
             
              return rb_str_new2(sqlite3_errmsg(db->sqlite3_db));
         | 
| 677 716 | 
             
            }
         | 
| @@ -683,13 +722,25 @@ VALUE Database_errmsg(VALUE self) { | |
| 683 722 | 
             
             * Returns the offset for the last error
         | 
| 684 723 | 
             
             */
         | 
| 685 724 | 
             
            VALUE Database_error_offset(VALUE self) {
         | 
| 686 | 
            -
              Database_t *db;
         | 
| 687 | 
            -
              GetOpenDatabase(self, db);
         | 
| 725 | 
            +
              Database_t *db = self_to_open_database(self);
         | 
| 688 726 |  | 
| 689 727 | 
             
              return INT2NUM(sqlite3_error_offset(db->sqlite3_db));
         | 
| 690 728 | 
             
            }
         | 
| 691 729 | 
             
            #endif
         | 
| 692 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 | 
            +
             | 
| 693 744 | 
             
            void Init_ExtraliteDatabase(void) {
         | 
| 694 745 | 
             
              VALUE mExtralite = rb_define_module("Extralite");
         | 
| 695 746 | 
             
              rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
         | 
| @@ -711,19 +762,22 @@ void Init_ExtraliteDatabase(void) { | |
| 711 762 | 
             
              rb_define_method(cDatabase, "error_offset", Database_error_offset, 0);
         | 
| 712 763 | 
             
              #endif
         | 
| 713 764 |  | 
| 765 | 
            +
              rb_define_method(cDatabase, "execute", Database_execute, -1);
         | 
| 714 766 | 
             
              rb_define_method(cDatabase, "execute_multi", Database_execute_multi, 2);
         | 
| 715 767 | 
             
              rb_define_method(cDatabase, "filename", Database_filename, -1);
         | 
| 716 | 
            -
              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);
         | 
| 717 770 | 
             
              rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
         | 
| 718 771 | 
             
              rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
         | 
| 719 772 | 
             
              rb_define_method(cDatabase, "limit", Database_limit, -1);
         | 
| 720 | 
            -
              rb_define_method(cDatabase, "prepare", Database_prepare, 1);
         | 
| 773 | 
            +
              rb_define_method(cDatabase, "prepare", Database_prepare, -1);
         | 
| 721 774 | 
             
              rb_define_method(cDatabase, "query", Database_query_hash, -1);
         | 
| 722 775 | 
             
              rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
         | 
| 723 776 | 
             
              rb_define_method(cDatabase, "query_hash", Database_query_hash, -1);
         | 
| 724 777 | 
             
              rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
         | 
| 725 778 | 
             
              rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
         | 
| 726 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);
         | 
| 727 781 | 
             
              rb_define_method(cDatabase, "status", Database_status, -1);
         | 
| 728 782 | 
             
              rb_define_method(cDatabase, "total_changes", Database_total_changes, 0);
         | 
| 729 783 | 
             
              rb_define_method(cDatabase, "trace", Database_trace, 0);
         | 
| @@ -742,9 +796,15 @@ void Init_ExtraliteDatabase(void) { | |
| 742 796 | 
             
              rb_gc_register_mark_object(cBusyError);
         | 
| 743 797 | 
             
              rb_gc_register_mark_object(cInterruptError);
         | 
| 744 798 |  | 
| 799 | 
            +
              eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
         | 
| 800 | 
            +
             | 
| 801 | 
            +
              ID_bind   = rb_intern("bind");
         | 
| 745 802 | 
             
              ID_call   = rb_intern("call");
         | 
| 746 803 | 
             
              ID_keys   = rb_intern("keys");
         | 
| 747 804 | 
             
              ID_new    = rb_intern("new");
         | 
| 748 805 | 
             
              ID_strip  = rb_intern("strip");
         | 
| 749 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);
         | 
| 750 810 | 
             
            }
         | 
    
        data/ext/extralite/extconf.rb
    CHANGED
    
    | @@ -4,13 +4,13 @@ if ENV['EXTRALITE_BUNDLE'] | |
| 4 4 | 
             
              require_relative('extconf-bundle')
         | 
| 5 5 | 
             
            else
         | 
| 6 6 | 
             
              ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
         | 
| 7 | 
            -
             | 
| 7 | 
            +
             | 
| 8 8 | 
             
              require 'mkmf'
         | 
| 9 | 
            -
             | 
| 9 | 
            +
             | 
| 10 10 | 
             
              # :stopdoc:
         | 
| 11 | 
            -
             | 
| 11 | 
            +
             | 
| 12 12 | 
             
              RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
         | 
| 13 | 
            -
             | 
| 13 | 
            +
             | 
| 14 14 | 
             
              ldflags = cppflags = nil
         | 
| 15 15 | 
             
              if RbConfig::CONFIG["host_os"] =~ /darwin/
         | 
| 16 16 | 
             
                begin
         | 
| @@ -25,7 +25,7 @@ else | |
| 25 25 | 
             
                    cppflags  = "#{brew_prefix}/include"
         | 
| 26 26 | 
             
                    pkg_conf  = "#{brew_prefix}/lib/pkgconfig"
         | 
| 27 27 | 
             
                  end
         | 
| 28 | 
            -
             | 
| 28 | 
            +
             | 
| 29 29 | 
             
                  # pkg_config should be less error prone than parsing compiler
         | 
| 30 30 | 
             
                  # commandline options, but we need to set default ldflags and cpp flags
         | 
| 31 31 | 
             
                  # in case the user doesn't have pkg-config installed
         | 
| @@ -33,13 +33,13 @@ else | |
| 33 33 | 
             
                rescue
         | 
| 34 34 | 
             
                end
         | 
| 35 35 | 
             
              end
         | 
| 36 | 
            -
             | 
| 36 | 
            +
             | 
| 37 37 | 
             
              if with_config('sqlcipher')
         | 
| 38 38 | 
             
                pkg_config("sqlcipher")
         | 
| 39 39 | 
             
              else
         | 
| 40 40 | 
             
                pkg_config("sqlite3")
         | 
| 41 41 | 
             
              end
         | 
| 42 | 
            -
             | 
| 42 | 
            +
             | 
| 43 43 | 
             
              # --with-sqlite3-{dir,include,lib}
         | 
| 44 44 | 
             
              if with_config('sqlcipher')
         | 
| 45 45 | 
             
                $CFLAGS << ' -DUSING_SQLCIPHER'
         | 
| @@ -47,15 +47,15 @@ else | |
| 47 47 | 
             
              else
         | 
| 48 48 | 
             
                dir_config("sqlite3", cppflags, ldflags)
         | 
| 49 49 | 
             
              end
         | 
| 50 | 
            -
             | 
| 50 | 
            +
             | 
| 51 51 | 
             
              if RbConfig::CONFIG["host_os"] =~ /mswin/
         | 
| 52 52 | 
             
                $CFLAGS << ' -W3'
         | 
| 53 53 | 
             
              end
         | 
| 54 | 
            -
             | 
| 54 | 
            +
             | 
| 55 55 | 
             
              if RUBY_VERSION < '2.7'
         | 
| 56 56 | 
             
                $CFLAGS << ' -DTAINTING_SUPPORT'
         | 
| 57 57 | 
             
              end
         | 
| 58 | 
            -
             | 
| 58 | 
            +
             | 
| 59 59 | 
             
              # @!visibility private
         | 
| 60 60 | 
             
              def asplode missing
         | 
| 61 61 | 
             
                if RUBY_PLATFORM =~ /mingw|mswin/
         | 
| @@ -70,25 +70,25 @@ else | |
| 70 70 | 
             
                    error
         | 
| 71 71 | 
             
                  end
         | 
| 72 72 | 
             
                end
         | 
| 73 | 
            -
             | 
| 73 | 
            +
             | 
| 74 74 | 
             
                asplode('sqlite3.h')  unless find_header  'sqlite3.h'
         | 
| 75 75 | 
             
                find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
         | 
| 76 | 
            -
             | 
| 76 | 
            +
             | 
| 77 77 | 
             
                have_library 'dl' # for static builds
         | 
| 78 | 
            -
             | 
| 78 | 
            +
             | 
| 79 79 | 
             
                if with_config('sqlcipher')
         | 
| 80 80 | 
             
                  asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
         | 
| 81 81 | 
             
                else
         | 
| 82 82 | 
             
                  asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
         | 
| 83 83 | 
             
                end
         | 
| 84 | 
            -
             | 
| 84 | 
            +
             | 
| 85 85 | 
             
                have_func('sqlite3_enable_load_extension')
         | 
| 86 86 | 
             
                have_func('sqlite3_load_extension')
         | 
| 87 87 | 
             
                have_func('sqlite3_prepare_v2')
         | 
| 88 88 | 
             
                have_func('sqlite3_error_offset')
         | 
| 89 | 
            -
             | 
| 89 | 
            +
             | 
| 90 90 | 
             
                $defs << "-DEXTRALITE_NO_BUNDLE"
         | 
| 91 | 
            -
             | 
| 91 | 
            +
             | 
| 92 92 | 
             
                dir_config('extralite_ext')
         | 
| 93 93 | 
             
                create_makefile('extralite_ext')
         | 
| 94 94 | 
             
              end
         |