sqlite3 2.0.3-arm64-darwin → 2.1.0.rc1-arm64-darwin
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 +32 -0
- data/CONTRIBUTING.md +5 -0
- data/FAQ.md +43 -34
- data/INSTALLATION.md +1 -3
- data/README.md +17 -0
- data/dependencies.yml +8 -8
- data/ext/sqlite3/database.c +96 -17
- data/ext/sqlite3/database.h +2 -0
- data/ext/sqlite3/extconf.rb +2 -0
- data/ext/sqlite3/statement.c +42 -0
- data/lib/sqlite3/3.1/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/3.2/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/3.3/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/database.rb +3 -1
- data/lib/sqlite3/fork_safety.rb +56 -0
- data/lib/sqlite3/version.rb +1 -1
- metadata +6 -6
- data/lib/sqlite3/3.0/sqlite3_native.bundle +0 -0
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 779c4b1da339eb05eb1118b043492459a4265d4fb46d695c98a64d2cc7e7f02d
         | 
| 4 | 
            +
              data.tar.gz: 43b4faebf9c7a1a5001c661ce068d5aaa1241d4e82d1132aa5676e786279d179
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b17d9bb19a5c6ff6b034643c08a97cee81d7e18a2d62a10a2f6f2e3a62cacd7f19a5152931de0c18bab375ffd192815064dc17dc5beeebeff0a9e6fac71f2ed1
         | 
| 7 | 
            +
              data.tar.gz: 8597575080e531c10a9862ef9cd0b2e384f0e94bf5c3b2455f1c0b0e79d8109cb71303e24ce622cc8374eb192be7b6ce66a211479fce68d88579e807093bd895
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,37 @@ | |
| 1 1 | 
             
            # sqlite3-ruby Changelog
         | 
| 2 2 |  | 
| 3 | 
            +
            ## prerelease 2.1.0.rc1 / 2024-09-18
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ### Ruby
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            - This release drops support for Ruby 3.0. [#563] @flavorjones
         | 
| 8 | 
            +
             | 
| 9 | 
            +
             | 
| 10 | 
            +
            ### Fork safety improvements
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            Sqlite itself is [not fork-safe](https://www.sqlite.org/howtocorrupt.html#_carrying_an_open_database_connection_across_a_fork_). Specifically, writing in a child process to a database connection that was created in the parent process may corrupt the database file. To mitigate this risk, sqlite3-ruby has implemented the following changes:
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            - All open writable database connections carried across a `fork()` will immediately be closed in the child process to mitigate the risk of corrupting the database file.
         | 
| 15 | 
            +
            - These connections will be incompletely closed ("discarded") which will result in a one-time memory leak in the child process.
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            If it's at all possible, we strongly recommend that you close writable database connections in the parent before forking.
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            See the README "Fork Safety" section and `adr/2024-09-fork-safety.md` for more information. [#558] @flavorjones
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
            ### Improved
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            - Use `sqlite3_close_v2` to close databases in a deferred manner if there are unclosed prepared statements. Previously closing a database while statements were open resulted in a `BusyException`. See https://www.sqlite.org/c3ref/close.html for more context. [#557] @flavorjones
         | 
| 25 | 
            +
            - When setting a Database `busy_handler`, fire the write barrier to prevent potential crashes during the GC mark phase. [#556] @jhawthorn
         | 
| 26 | 
            +
             | 
| 27 | 
            +
             | 
| 28 | 
            +
            ## 2.0.4 / 2024-08-13
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            ### Dependencies
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            - Vendored sqlite is updated to [v3.46.1](https://sqlite.org/releaselog/3_46_1.html) @flavorjones
         | 
| 33 | 
            +
             | 
| 34 | 
            +
             | 
| 3 35 | 
             
            ## 2.0.3 / 2024-07-29
         | 
| 4 36 |  | 
| 5 37 | 
             
            ### Improved
         | 
    
        data/CONTRIBUTING.md
    CHANGED
    
    | @@ -7,6 +7,11 @@ This doc is a short introduction on how to modify and maintain the sqlite3-ruby | |
| 7 7 |  | 
| 8 8 | 
             
            ## Architecture notes
         | 
| 9 9 |  | 
| 10 | 
            +
            ### Decision record
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            As of 2024-09, we're starting to keep some architecture decisions in the subdirectory `/adr`, so
         | 
| 13 | 
            +
            please look there for additional information.
         | 
| 14 | 
            +
             | 
| 10 15 | 
             
            ### Garbage collection
         | 
| 11 16 |  | 
| 12 17 | 
             
            All statements keep pointers back to their respective database connections.
         | 
    
        data/FAQ.md
    CHANGED
    
    | @@ -207,48 +207,46 @@ Or do a `Database#prepare` to get the `Statement`, and then use either | |
| 207 207 | 
             
              stmt.bind_params( "value", "name" => "bob" )
         | 
| 208 208 | 
             
            ```
         | 
| 209 209 |  | 
| 210 | 
            -
            ## How do I discover metadata about a query?
         | 
| 210 | 
            +
            ## How do I discover metadata about a query result?
         | 
| 211 211 |  | 
| 212 | 
            -
             | 
| 213 | 
            -
             | 
| 212 | 
            +
            IMPORTANT: `Database#execute` returns an Array of Array of Strings
         | 
| 213 | 
            +
            which will have no metadata about the query or the result, such
         | 
| 214 | 
            +
            as column names.
         | 
| 214 215 |  | 
| 215 216 |  | 
| 216 | 
            -
             | 
| 217 | 
            -
            property "fields" that returns an array of the column names. The row
         | 
| 218 | 
            -
            will also have a property "types" that returns an array of the column
         | 
| 219 | 
            -
            types:
         | 
| 217 | 
            +
            There are 2 main sources of query metadata:
         | 
| 220 218 |  | 
| 221 | 
            -
             | 
| 222 | 
            -
             | 
| 223 | 
            -
              rows = db.execute( "select * from table" )
         | 
| 224 | 
            -
              p rows[0].fields
         | 
| 225 | 
            -
              p rows[0].types
         | 
| 226 | 
            -
            ```
         | 
| 219 | 
            +
            * `Statement`
         | 
| 220 | 
            +
            * `ResultSet`
         | 
| 227 221 |  | 
| 228 222 |  | 
| 229 | 
            -
             | 
| 230 | 
            -
             | 
| 231 | 
            -
            you still need the metadata, you can use `Database#query` and ask the
         | 
| 232 | 
            -
            `ResultSet` object itself:
         | 
| 223 | 
            +
            You can get a `Statement` via `Database#prepare`, and you can get
         | 
| 224 | 
            +
            a `ResultSet` via `Statement#execute` or `Database#query`.
         | 
| 233 225 |  | 
| 234 226 |  | 
| 235 227 | 
             
            ```ruby
         | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 241 | 
            -
             | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 244 | 
            -
             | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 248 | 
            -
             | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 251 | 
            -
             | 
| 228 | 
            +
            sql = 'select * from table'
         | 
| 229 | 
            +
             | 
| 230 | 
            +
            # No metadata
         | 
| 231 | 
            +
            rows = db.execute(sql)
         | 
| 232 | 
            +
            rows.class # => Array, no metadata
         | 
| 233 | 
            +
            rows.first.class # => Array, no metadata
         | 
| 234 | 
            +
            rows.first.first.class #=> String, no metadata
         | 
| 235 | 
            +
             | 
| 236 | 
            +
            # Statement has metadata
         | 
| 237 | 
            +
            stmt = db.prepare(sql)
         | 
| 238 | 
            +
            stmt.columns # => [ ... ]
         | 
| 239 | 
            +
            stmt.types # => [ ... ]
         | 
| 240 | 
            +
             | 
| 241 | 
            +
            # ResultSet has metadata
         | 
| 242 | 
            +
            results = stmt.execute
         | 
| 243 | 
            +
            results.columns # => [ ... ]
         | 
| 244 | 
            +
            results.types # => [ ... ]
         | 
| 245 | 
            +
             | 
| 246 | 
            +
            # ResultSet has metadata
         | 
| 247 | 
            +
            results = db.query(sql)
         | 
| 248 | 
            +
            results.columns # => [ ... ]
         | 
| 249 | 
            +
            results.types # => [ ... ]
         | 
| 252 250 | 
             
            ```
         | 
| 253 251 |  | 
| 254 252 | 
             
            ## I'd like the rows to be indexible by column name.
         | 
| @@ -273,7 +271,18 @@ is unavailable on the row, although the "types" property remains.) | |
| 273 271 | 
             
            ```
         | 
| 274 272 |  | 
| 275 273 |  | 
| 276 | 
            -
             | 
| 274 | 
            +
            A more granular way to do this is via `ResultSet#next_hash` or
         | 
| 275 | 
            +
            `ResultSet#each_hash`.
         | 
| 276 | 
            +
             | 
| 277 | 
            +
             | 
| 278 | 
            +
            ```ruby
         | 
| 279 | 
            +
              results = db.query( "select * from table" )
         | 
| 280 | 
            +
              row = results.next_hash
         | 
| 281 | 
            +
              p row['column1']
         | 
| 282 | 
            +
            ```
         | 
| 283 | 
            +
             | 
| 284 | 
            +
             | 
| 285 | 
            +
            Another way is to use Ara Howard's
         | 
| 277 286 | 
             
            [`ArrayFields`](http://rubyforge.org/projects/arrayfields)
         | 
| 278 287 | 
             
            module. Just `require "arrayfields"`, and all of your rows will be indexable
         | 
| 279 288 | 
             
            by column name, even though they are still arrays!
         | 
    
        data/INSTALLATION.md
    CHANGED
    
    | @@ -14,15 +14,13 @@ In v2.0.0 and later, native (precompiled) gems are available for recent Ruby ver | |
| 14 14 | 
             
            - `arm-linux-gnu` (requires: glibc >= 2.29)
         | 
| 15 15 | 
             
            - `arm-linux-musl`
         | 
| 16 16 | 
             
            - `arm64-darwin`
         | 
| 17 | 
            -
            - `x64- | 
| 17 | 
            +
            - `x64-mingw-ucrt`
         | 
| 18 18 | 
             
            - `x86-linux-gnu` (requires: glibc >= 2.17)
         | 
| 19 19 | 
             
            - `x86-linux-musl`
         | 
| 20 20 | 
             
            - `x86_64-darwin`
         | 
| 21 21 | 
             
            - `x86_64-linux-gnu` (requires: glibc >= 2.17)
         | 
| 22 22 | 
             
            - `x86_64-linux-musl`
         | 
| 23 23 |  | 
| 24 | 
            -
            ⚠ Ruby 3.0 linux users must use Rubygems >= 3.3.22 in order to use these gems.
         | 
| 25 | 
            -
             | 
| 26 24 | 
             
            ⚠ Musl linux users should update to Bundler >= 2.5.6 to avoid https://github.com/rubygems/rubygems/issues/7432
         | 
| 27 25 |  | 
| 28 26 | 
             
            If you are using one of these Ruby versions on one of these platforms, the native gem is the recommended way to install sqlite3-ruby.
         | 
    
        data/README.md
    CHANGED
    
    | @@ -148,6 +148,23 @@ It is generally recommended that if applications want to share a database among | |
| 148 148 | 
             
            threads, they _only_ share the database instance object.  Other objects are
         | 
| 149 149 | 
             
            fine to share, but may require manual locking for thread safety.
         | 
| 150 150 |  | 
| 151 | 
            +
             | 
| 152 | 
            +
            ## Fork Safety
         | 
| 153 | 
            +
             | 
| 154 | 
            +
            [Sqlite is not fork
         | 
| 155 | 
            +
            safe](https://www.sqlite.org/howtocorrupt.html#_carrying_an_open_database_connection_across_a_fork_)
         | 
| 156 | 
            +
            and instructs users to not carry an open writable database connection across a `fork()`. Using an inherited
         | 
| 157 | 
            +
            connection in the child may corrupt your database, leak memory, or cause other undefined behavior.
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            To help protect users of this gem from accidental corruption due to this lack of fork safety, the gem will immediately close any open writable databases in the child after a fork. Discarding writable
         | 
| 160 | 
            +
            connections in the child will incur a small one-time memory leak per connection, but that's
         | 
| 161 | 
            +
            preferable to potentially corrupting your database.
         | 
| 162 | 
            +
             | 
| 163 | 
            +
            Whenever possible, close writable connections in the parent before forking.
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            See [./adr/2024-09-fork-safety.md](./adr/2024-09-fork-safety.md) for more information and context.
         | 
| 166 | 
            +
             | 
| 167 | 
            +
             | 
| 151 168 | 
             
            ## Support
         | 
| 152 169 |  | 
| 153 170 | 
             
            ### Installation or database extensions
         | 
    
        data/dependencies.yml
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            sqlite3:
         | 
| 2 2 | 
             
              # checksum verified by first checking the published sha3(256) checksum against https://sqlite.org/download.html:
         | 
| 3 | 
            -
              #  | 
| 3 | 
            +
              # 923f68143dcd9fc0c38778dee253fd6540a91f578173a04ca5adff885d8a8fbb
         | 
| 4 4 | 
             
              #
         | 
| 5 | 
            -
              # $ sha3sum -a 256 ports/archives/sqlite-autoconf- | 
| 6 | 
            -
              #  | 
| 5 | 
            +
              # $ sha3sum -a 256 ports/archives/sqlite-autoconf-3460100.tar.gz
         | 
| 6 | 
            +
              # 923f68143dcd9fc0c38778dee253fd6540a91f578173a04ca5adff885d8a8fbb  ports/archives/sqlite-autoconf-3460100.tar.gz
         | 
| 7 7 | 
             
              #
         | 
| 8 | 
            -
              # $ sha256sum ports/archives/sqlite-autoconf- | 
| 9 | 
            -
              #  | 
| 10 | 
            -
              version: "3.46. | 
| 8 | 
            +
              # $ sha256sum ports/archives/sqlite-autoconf-3460100.tar.gz
         | 
| 9 | 
            +
              # 67d3fe6d268e6eaddcae3727fce58fcc8e9c53869bdd07a0c61e38ddf2965071  ports/archives/sqlite-autoconf-3460100.tar.gz
         | 
| 10 | 
            +
              version: "3.46.1"
         | 
| 11 11 | 
             
              files:
         | 
| 12 | 
            -
                - url: "https://sqlite.org/2024/sqlite-autoconf- | 
| 13 | 
            -
                  sha256: " | 
| 12 | 
            +
                - url: "https://sqlite.org/2024/sqlite-autoconf-3460100.tar.gz"
         | 
| 13 | 
            +
                  sha256: "67d3fe6d268e6eaddcae3727fce58fcc8e9c53869bdd07a0c61e38ddf2965071"
         | 
    
        data/ext/sqlite3/database.c
    CHANGED
    
    | @@ -12,6 +12,63 @@ | |
| 12 12 |  | 
| 13 13 | 
             
            VALUE cSqlite3Database;
         | 
| 14 14 |  | 
| 15 | 
            +
            /* See adr/2024-09-fork-safety.md */
         | 
| 16 | 
            +
            static void
         | 
| 17 | 
            +
            discard_db(sqlite3RubyPtr ctx)
         | 
| 18 | 
            +
            {
         | 
| 19 | 
            +
                sqlite3_file *sfile;
         | 
| 20 | 
            +
                int status;
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                // release as much heap memory as possible by deallocating non-essential memory
         | 
| 23 | 
            +
                // allocations held by the database library. Memory used to cache database pages to
         | 
| 24 | 
            +
                // improve performance is an example of non-essential memory.
         | 
| 25 | 
            +
                // on my development machine, this reduces the lost memory from 152k to 69k.
         | 
| 26 | 
            +
                sqlite3_db_release_memory(ctx->db);
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                // release file descriptors
         | 
| 29 | 
            +
            #ifdef HAVE_SQLITE3_DB_NAME
         | 
| 30 | 
            +
                const char *db_name;
         | 
| 31 | 
            +
                int j_db = 0;
         | 
| 32 | 
            +
                while ((db_name = sqlite3_db_name(ctx->db, j_db)) != NULL) {
         | 
| 33 | 
            +
                    status = sqlite3_file_control(ctx->db, db_name, SQLITE_FCNTL_FILE_POINTER, &sfile);
         | 
| 34 | 
            +
                    if (status == 0 && sfile->pMethods != NULL) {
         | 
| 35 | 
            +
                        sfile->pMethods->xClose(sfile);
         | 
| 36 | 
            +
                    }
         | 
| 37 | 
            +
                    j_db++;
         | 
| 38 | 
            +
                }
         | 
| 39 | 
            +
            #else
         | 
| 40 | 
            +
                status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_FILE_POINTER, &sfile);
         | 
| 41 | 
            +
                if (status == 0 && sfile->pMethods != NULL) {
         | 
| 42 | 
            +
                    sfile->pMethods->xClose(sfile);
         | 
| 43 | 
            +
                }
         | 
| 44 | 
            +
            #endif
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_JOURNAL_POINTER, &sfile);
         | 
| 47 | 
            +
                if (status == 0 && sfile->pMethods != NULL) {
         | 
| 48 | 
            +
                    sfile->pMethods->xClose(sfile);
         | 
| 49 | 
            +
                }
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                ctx->db = NULL;
         | 
| 52 | 
            +
            }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            static void
         | 
| 55 | 
            +
            close_or_discard_db(sqlite3RubyPtr ctx)
         | 
| 56 | 
            +
            {
         | 
| 57 | 
            +
                if (ctx->db) {
         | 
| 58 | 
            +
                    int isReadonly = (ctx->flags & SQLITE_OPEN_READONLY);
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    if (isReadonly || ctx->owner == getpid()) {
         | 
| 61 | 
            +
                        // Ordinary close.
         | 
| 62 | 
            +
                        sqlite3_close_v2(ctx->db);
         | 
| 63 | 
            +
                        ctx->db = NULL;
         | 
| 64 | 
            +
                    } else {
         | 
| 65 | 
            +
                        // This is an open connection carried across a fork(). "Discard" it.
         | 
| 66 | 
            +
                        discard_db(ctx);
         | 
| 67 | 
            +
                    }
         | 
| 68 | 
            +
                }
         | 
| 69 | 
            +
            }
         | 
| 70 | 
            +
             | 
| 71 | 
            +
             | 
| 15 72 | 
             
            static void
         | 
| 16 73 | 
             
            database_mark(void *ctx)
         | 
| 17 74 | 
             
            {
         | 
| @@ -22,11 +79,8 @@ database_mark(void *ctx) | |
| 22 79 | 
             
            static void
         | 
| 23 80 | 
             
            deallocate(void *ctx)
         | 
| 24 81 | 
             
            {
         | 
| 25 | 
            -
                 | 
| 26 | 
            -
                 | 
| 27 | 
            -
             | 
| 28 | 
            -
                if (db) { sqlite3_close(db); }
         | 
| 29 | 
            -
                xfree(c);
         | 
| 82 | 
            +
                close_or_discard_db((sqlite3RubyPtr)ctx);
         | 
| 83 | 
            +
                xfree(ctx);
         | 
| 30 84 | 
             
            }
         | 
| 31 85 |  | 
| 32 86 | 
             
            static size_t
         | 
| @@ -51,7 +105,9 @@ static VALUE | |
| 51 105 | 
             
            allocate(VALUE klass)
         | 
| 52 106 | 
             
            {
         | 
| 53 107 | 
             
                sqlite3RubyPtr ctx;
         | 
| 54 | 
            -
                 | 
| 108 | 
            +
                VALUE object = TypedData_Make_Struct(klass, sqlite3Ruby, &database_type, ctx);
         | 
| 109 | 
            +
                ctx->owner = getpid();
         | 
| 110 | 
            +
                return object;
         | 
| 55 111 | 
             
            }
         | 
| 56 112 |  | 
| 57 113 | 
             
            static char *
         | 
| @@ -62,8 +118,6 @@ utf16_string_value_ptr(VALUE str) | |
| 62 118 | 
             
                return RSTRING_PTR(str);
         | 
| 63 119 | 
             
            }
         | 
| 64 120 |  | 
| 65 | 
            -
            static VALUE sqlite3_rb_close(VALUE self);
         | 
| 66 | 
            -
             | 
| 67 121 | 
             
            sqlite3RubyPtr
         | 
| 68 122 | 
             
            sqlite3_database_unwrap(VALUE database)
         | 
| 69 123 | 
             
            {
         | 
| @@ -77,6 +131,7 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs) | |
| 77 131 | 
             
            {
         | 
| 78 132 | 
             
                sqlite3RubyPtr ctx;
         | 
| 79 133 | 
             
                int status;
         | 
| 134 | 
            +
                int flags;
         | 
| 80 135 |  | 
| 81 136 | 
             
                TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
         | 
| 82 137 |  | 
| @@ -89,14 +144,16 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs) | |
| 89 144 | 
             
            #  endif
         | 
| 90 145 | 
             
            #endif
         | 
| 91 146 |  | 
| 147 | 
            +
                flags = NUM2INT(mode);
         | 
| 92 148 | 
             
                status = sqlite3_open_v2(
         | 
| 93 149 | 
             
                             StringValuePtr(file),
         | 
| 94 150 | 
             
                             &ctx->db,
         | 
| 95 | 
            -
                              | 
| 151 | 
            +
                             flags,
         | 
| 96 152 | 
             
                             NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
         | 
| 97 153 | 
             
                         );
         | 
| 98 154 |  | 
| 99 | 
            -
                CHECK(ctx->db, status)
         | 
| 155 | 
            +
                CHECK(ctx->db, status);
         | 
| 156 | 
            +
                ctx->flags = flags;
         | 
| 100 157 |  | 
| 101 158 | 
             
                return self;
         | 
| 102 159 | 
             
            }
         | 
| @@ -119,21 +176,38 @@ rb_sqlite3_disable_quirk_mode(VALUE self) | |
| 119 176 | 
             
            #endif
         | 
| 120 177 | 
             
            }
         | 
| 121 178 |  | 
| 122 | 
            -
            /* | 
| 179 | 
            +
            /*
         | 
| 180 | 
            +
             *  Close the database and release all associated resources.
         | 
| 181 | 
            +
             *
         | 
| 182 | 
            +
             *  ⚠ Writable connections that are carried across a <tt>fork()</tt> are not completely
         | 
| 183 | 
            +
             *  closed. {Sqlite does not support forking}[https://www.sqlite.org/howtocorrupt.html],
         | 
| 184 | 
            +
             *  and fully closing a writable connection that has been carried across a fork may corrupt the
         | 
| 185 | 
            +
             *  database. Since it is an incomplete close, not all memory resources are freed, but this is safer
         | 
| 186 | 
            +
             *  than risking data loss.
         | 
| 123 187 | 
             
             *
         | 
| 124 | 
            -
             *  | 
| 188 | 
            +
             *  See rdoc-ref:adr/2024-09-fork-safety.md for more information on fork safety.
         | 
| 125 189 | 
             
             */
         | 
| 126 190 | 
             
            static VALUE
         | 
| 127 191 | 
             
            sqlite3_rb_close(VALUE self)
         | 
| 128 192 | 
             
            {
         | 
| 129 193 | 
             
                sqlite3RubyPtr ctx;
         | 
| 130 | 
            -
                sqlite3 *db;
         | 
| 131 194 | 
             
                TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
         | 
| 132 195 |  | 
| 133 | 
            -
                 | 
| 134 | 
            -
                CHECK(db, sqlite3_close(ctx->db));
         | 
| 196 | 
            +
                close_or_discard_db(ctx);
         | 
| 135 197 |  | 
| 136 | 
            -
                 | 
| 198 | 
            +
                rb_iv_set(self, "-aggregators", Qnil);
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                return self;
         | 
| 201 | 
            +
            }
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            /* private method, primarily for testing */
         | 
| 204 | 
            +
            static VALUE
         | 
| 205 | 
            +
            sqlite3_rb_discard(VALUE self)
         | 
| 206 | 
            +
            {
         | 
| 207 | 
            +
                sqlite3RubyPtr ctx;
         | 
| 208 | 
            +
                TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                discard_db(ctx);
         | 
| 137 211 |  | 
| 138 212 | 
             
                rb_iv_set(self, "-aggregators", Qnil);
         | 
| 139 213 |  | 
| @@ -246,7 +320,7 @@ busy_handler(int argc, VALUE *argv, VALUE self) | |
| 246 320 | 
             
                rb_scan_args(argc, argv, "01", &block);
         | 
| 247 321 |  | 
| 248 322 | 
             
                if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
         | 
| 249 | 
            -
                ctx->busy_handler  | 
| 323 | 
            +
                RB_OBJ_WRITE(self, &ctx->busy_handler, block);
         | 
| 250 324 |  | 
| 251 325 | 
             
                status = sqlite3_busy_handler(
         | 
| 252 326 | 
             
                             ctx->db,
         | 
| @@ -871,6 +945,10 @@ rb_sqlite3_open16(VALUE self, VALUE file) | |
| 871 945 |  | 
| 872 946 | 
             
                status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
         | 
| 873 947 |  | 
| 948 | 
            +
                // these are the perm flags used implicitly by sqlite3_open16,
         | 
| 949 | 
            +
                // see https://www.sqlite.org/capi3ref.html#sqlite3_open
         | 
| 950 | 
            +
                ctx->flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
         | 
| 951 | 
            +
             | 
| 874 952 | 
             
                CHECK(ctx->db, status)
         | 
| 875 953 |  | 
| 876 954 | 
             
                return INT2NUM(status);
         | 
| @@ -889,6 +967,7 @@ init_sqlite3_database(void) | |
| 889 967 | 
             
                rb_define_private_method(cSqlite3Database, "open16", rb_sqlite3_open16, 1);
         | 
| 890 968 | 
             
                rb_define_method(cSqlite3Database, "collation", collation, 2);
         | 
| 891 969 | 
             
                rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
         | 
| 970 | 
            +
                rb_define_private_method(cSqlite3Database, "discard", sqlite3_rb_discard, 0);
         | 
| 892 971 | 
             
                rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
         | 
| 893 972 | 
             
                rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
         | 
| 894 973 | 
             
                rb_define_method(cSqlite3Database, "trace", trace, -1);
         | 
    
        data/ext/sqlite3/database.h
    CHANGED
    
    
    
        data/ext/sqlite3/extconf.rb
    CHANGED
    
    
    
        data/ext/sqlite3/statement.c
    CHANGED
    
    | @@ -4,6 +4,20 @@ | |
| 4 4 | 
             
              if(!_ctxt->st) \
         | 
| 5 5 | 
             
                rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
         | 
| 6 6 |  | 
| 7 | 
            +
            static void
         | 
| 8 | 
            +
            require_open_db(VALUE stmt_rb)
         | 
| 9 | 
            +
            {
         | 
| 10 | 
            +
                VALUE closed_p = rb_funcall(
         | 
| 11 | 
            +
                                     rb_iv_get(stmt_rb, "@connection"),
         | 
| 12 | 
            +
                                     rb_intern("closed?"), 0);
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                if (RTEST(closed_p)) {
         | 
| 15 | 
            +
                    rb_raise(rb_path2class("SQLite3::Exception"),
         | 
| 16 | 
            +
                             "cannot use a statement associated with a closed database");
         | 
| 17 | 
            +
                }
         | 
| 18 | 
            +
            }
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 7 21 | 
             
            VALUE cSqlite3Statement;
         | 
| 8 22 |  | 
| 9 23 | 
             
            static void
         | 
| @@ -121,6 +135,7 @@ step(VALUE self) | |
| 121 135 |  | 
| 122 136 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 123 137 |  | 
| 138 | 
            +
                require_open_db(self);
         | 
| 124 139 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 125 140 |  | 
| 126 141 | 
             
                if (ctx->done_p) { return Qnil; }
         | 
| @@ -216,6 +231,8 @@ bind_param(VALUE self, VALUE key, VALUE value) | |
| 216 231 | 
             
                int index;
         | 
| 217 232 |  | 
| 218 233 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                require_open_db(self);
         | 
| 219 236 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 220 237 |  | 
| 221 238 | 
             
                switch (TYPE(key)) {
         | 
| @@ -308,6 +325,8 @@ reset_bang(VALUE self) | |
| 308 325 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 309 326 |  | 
| 310 327 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 328 | 
            +
             | 
| 329 | 
            +
                require_open_db(self);
         | 
| 311 330 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 312 331 |  | 
| 313 332 | 
             
                sqlite3_reset(ctx->st);
         | 
| @@ -328,6 +347,8 @@ clear_bindings_bang(VALUE self) | |
| 328 347 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 329 348 |  | 
| 330 349 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 350 | 
            +
             | 
| 351 | 
            +
                require_open_db(self);
         | 
| 331 352 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 332 353 |  | 
| 333 354 | 
             
                sqlite3_clear_bindings(ctx->st);
         | 
| @@ -360,6 +381,8 @@ column_count(VALUE self) | |
| 360 381 | 
             
            {
         | 
| 361 382 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 362 383 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 384 | 
            +
             | 
| 385 | 
            +
                require_open_db(self);
         | 
| 363 386 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 364 387 |  | 
| 365 388 | 
             
                return INT2NUM(sqlite3_column_count(ctx->st));
         | 
| @@ -391,6 +414,8 @@ column_name(VALUE self, VALUE index) | |
| 391 414 | 
             
                const char *name;
         | 
| 392 415 |  | 
| 393 416 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 417 | 
            +
             | 
| 418 | 
            +
                require_open_db(self);
         | 
| 394 419 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 395 420 |  | 
| 396 421 | 
             
                name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
         | 
| @@ -414,6 +439,8 @@ column_decltype(VALUE self, VALUE index) | |
| 414 439 | 
             
                const char *name;
         | 
| 415 440 |  | 
| 416 441 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 442 | 
            +
             | 
| 443 | 
            +
                require_open_db(self);
         | 
| 417 444 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 418 445 |  | 
| 419 446 | 
             
                name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
         | 
| @@ -431,6 +458,8 @@ bind_parameter_count(VALUE self) | |
| 431 458 | 
             
            {
         | 
| 432 459 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 433 460 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 461 | 
            +
             | 
| 462 | 
            +
                require_open_db(self);
         | 
| 434 463 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 435 464 |  | 
| 436 465 | 
             
                return INT2NUM(sqlite3_bind_parameter_count(ctx->st));
         | 
| @@ -538,7 +567,10 @@ stats_as_hash(VALUE self) | |
| 538 567 | 
             
            {
         | 
| 539 568 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 540 569 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 570 | 
            +
             | 
| 571 | 
            +
                require_open_db(self);
         | 
| 541 572 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 573 | 
            +
             | 
| 542 574 | 
             
                VALUE arg = rb_hash_new();
         | 
| 543 575 |  | 
| 544 576 | 
             
                stmt_stat_internal(arg, ctx->st);
         | 
| @@ -554,6 +586,8 @@ stat_for(VALUE self, VALUE key) | |
| 554 586 | 
             
            {
         | 
| 555 587 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 556 588 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 589 | 
            +
             | 
| 590 | 
            +
                require_open_db(self);
         | 
| 557 591 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 558 592 |  | 
| 559 593 | 
             
                if (SYMBOL_P(key)) {
         | 
| @@ -574,6 +608,8 @@ memused(VALUE self) | |
| 574 608 | 
             
            {
         | 
| 575 609 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 576 610 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 611 | 
            +
             | 
| 612 | 
            +
                require_open_db(self);
         | 
| 577 613 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 578 614 |  | 
| 579 615 | 
             
                return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_MEMUSED, 0));
         | 
| @@ -591,6 +627,8 @@ database_name(VALUE self, VALUE index) | |
| 591 627 | 
             
            {
         | 
| 592 628 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 593 629 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 630 | 
            +
             | 
| 631 | 
            +
                require_open_db(self);
         | 
| 594 632 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 595 633 |  | 
| 596 634 | 
             
                return SQLITE3_UTF8_STR_NEW2(
         | 
| @@ -608,6 +646,8 @@ get_sql(VALUE self) | |
| 608 646 | 
             
            {
         | 
| 609 647 | 
             
                sqlite3StmtRubyPtr ctx;
         | 
| 610 648 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 649 | 
            +
             | 
| 650 | 
            +
                require_open_db(self);
         | 
| 611 651 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 612 652 |  | 
| 613 653 | 
             
                return rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(sqlite3_sql(ctx->st)));
         | 
| @@ -626,6 +666,8 @@ get_expanded_sql(VALUE self) | |
| 626 666 | 
             
                VALUE rb_expanded_sql;
         | 
| 627 667 |  | 
| 628 668 | 
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         | 
| 669 | 
            +
             | 
| 670 | 
            +
                require_open_db(self);
         | 
| 629 671 | 
             
                REQUIRE_OPEN_STMT(ctx);
         | 
| 630 672 |  | 
| 631 673 | 
             
                expanded_sql = sqlite3_expanded_sql(ctx->st);
         | 
| Binary file | 
| Binary file | 
| Binary file | 
    
        data/lib/sqlite3/database.rb
    CHANGED
    
    | @@ -5,6 +5,7 @@ require "sqlite3/errors" | |
| 5 5 | 
             
            require "sqlite3/pragmas"
         | 
| 6 6 | 
             
            require "sqlite3/statement"
         | 
| 7 7 | 
             
            require "sqlite3/value"
         | 
| 8 | 
            +
            require "sqlite3/fork_safety"
         | 
| 8 9 |  | 
| 9 10 | 
             
            module SQLite3
         | 
| 10 11 | 
             
              # The Database class encapsulates a single connection to a SQLite3 database.
         | 
| @@ -127,7 +128,6 @@ module SQLite3 | |
| 127 128 |  | 
| 128 129 | 
             
                  @tracefunc = nil
         | 
| 129 130 | 
             
                  @authorizer = nil
         | 
| 130 | 
            -
                  @busy_handler = nil
         | 
| 131 131 | 
             
                  @progress_handler = nil
         | 
| 132 132 | 
             
                  @collations = {}
         | 
| 133 133 | 
             
                  @functions = {}
         | 
| @@ -135,6 +135,8 @@ module SQLite3 | |
| 135 135 | 
             
                  @readonly = mode & Constants::Open::READONLY != 0
         | 
| 136 136 | 
             
                  @default_transaction_mode = options[:default_transaction_mode] || :deferred
         | 
| 137 137 |  | 
| 138 | 
            +
                  ForkSafety.track(self)
         | 
| 139 | 
            +
             | 
| 138 140 | 
             
                  if block_given?
         | 
| 139 141 | 
             
                    begin
         | 
| 140 142 | 
             
                      yield self
         | 
| @@ -0,0 +1,56 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "weakref"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # based on Rails's active_support/fork_tracker.rb
         | 
| 6 | 
            +
            module SQLite3
         | 
| 7 | 
            +
              module ForkSafety
         | 
| 8 | 
            +
                module CoreExt
         | 
| 9 | 
            +
                  def _fork
         | 
| 10 | 
            +
                    pid = super
         | 
| 11 | 
            +
                    if pid == 0
         | 
| 12 | 
            +
                      ForkSafety.discard
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                    pid
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                @databases = []
         | 
| 19 | 
            +
                @mutex = Mutex.new
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                class << self
         | 
| 22 | 
            +
                  def hook!
         | 
| 23 | 
            +
                    ::Process.singleton_class.prepend(CoreExt)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def track(database)
         | 
| 27 | 
            +
                    @mutex.synchronize do
         | 
| 28 | 
            +
                      @databases << WeakRef.new(database)
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def discard
         | 
| 33 | 
            +
                    warned = false
         | 
| 34 | 
            +
                    @databases.each do |db|
         | 
| 35 | 
            +
                      next unless db.weakref_alive?
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      unless db.closed? || db.readonly?
         | 
| 38 | 
            +
                        unless warned
         | 
| 39 | 
            +
                          # If you are here, you may want to read
         | 
| 40 | 
            +
                          # https://github.com/sparklemotion/sqlite3-ruby/pull/558
         | 
| 41 | 
            +
                          warn("Writable sqlite database connection(s) were inherited from a forked process. " \
         | 
| 42 | 
            +
                               "This is unsafe and the connections are being closed to prevent possible data " \
         | 
| 43 | 
            +
                               "corruption. Please close writable sqlite database connections before forking.",
         | 
| 44 | 
            +
                            uplevel: 0)
         | 
| 45 | 
            +
                          warned = true
         | 
| 46 | 
            +
                        end
         | 
| 47 | 
            +
                        db.close
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                    @databases.clear
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
            end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            SQLite3::ForkSafety.hook!
         | 
    
        data/lib/sqlite3/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: sqlite3
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.0. | 
| 4 | 
            +
              version: 2.1.0.rc1
         | 
| 5 5 | 
             
            platform: arm64-darwin
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Jamis Buck
         | 
| @@ -11,7 +11,7 @@ authors: | |
| 11 11 | 
             
            autorequire: 
         | 
| 12 12 | 
             
            bindir: bin
         | 
| 13 13 | 
             
            cert_chain: []
         | 
| 14 | 
            -
            date: 2024- | 
| 14 | 
            +
            date: 2024-09-18 00:00:00.000000000 Z
         | 
| 15 15 | 
             
            dependencies: []
         | 
| 16 16 | 
             
            description: |
         | 
| 17 17 | 
             
              Ruby library to interface with the SQLite3 database engine (http://www.sqlite.org). Precompiled
         | 
| @@ -52,13 +52,13 @@ files: | |
| 52 52 | 
             
            - ext/sqlite3/statement.h
         | 
| 53 53 | 
             
            - ext/sqlite3/timespec.h
         | 
| 54 54 | 
             
            - lib/sqlite3.rb
         | 
| 55 | 
            -
            - lib/sqlite3/3.0/sqlite3_native.bundle
         | 
| 56 55 | 
             
            - lib/sqlite3/3.1/sqlite3_native.bundle
         | 
| 57 56 | 
             
            - lib/sqlite3/3.2/sqlite3_native.bundle
         | 
| 58 57 | 
             
            - lib/sqlite3/3.3/sqlite3_native.bundle
         | 
| 59 58 | 
             
            - lib/sqlite3/constants.rb
         | 
| 60 59 | 
             
            - lib/sqlite3/database.rb
         | 
| 61 60 | 
             
            - lib/sqlite3/errors.rb
         | 
| 61 | 
            +
            - lib/sqlite3/fork_safety.rb
         | 
| 62 62 | 
             
            - lib/sqlite3/pragmas.rb
         | 
| 63 63 | 
             
            - lib/sqlite3/resultset.rb
         | 
| 64 64 | 
             
            - lib/sqlite3/statement.rb
         | 
| @@ -84,15 +84,15 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 84 84 | 
             
              requirements:
         | 
| 85 85 | 
             
              - - ">="
         | 
| 86 86 | 
             
                - !ruby/object:Gem::Version
         | 
| 87 | 
            -
                  version: '3. | 
| 87 | 
            +
                  version: '3.1'
         | 
| 88 88 | 
             
              - - "<"
         | 
| 89 89 | 
             
                - !ruby/object:Gem::Version
         | 
| 90 90 | 
             
                  version: 3.4.dev
         | 
| 91 91 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 92 92 | 
             
              requirements:
         | 
| 93 | 
            -
              - - " | 
| 93 | 
            +
              - - ">"
         | 
| 94 94 | 
             
                - !ruby/object:Gem::Version
         | 
| 95 | 
            -
                  version:  | 
| 95 | 
            +
                  version: 1.3.1
         | 
| 96 96 | 
             
            requirements: []
         | 
| 97 97 | 
             
            rubygems_version: 3.3.26
         | 
| 98 98 | 
             
            signing_key: 
         | 
| Binary file |