sqlite3 2.0.4-arm64-darwin → 2.1.0-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 +30 -0
 - data/CONTRIBUTING.md +12 -12
 - data/FAQ.md +43 -34
 - data/INSTALLATION.md +1 -3
 - data/README.md +17 -0
 - data/ext/sqlite3/database.c +98 -17
 - data/ext/sqlite3/database.h +6 -0
 - data/ext/sqlite3/extconf.rb +2 -0
 - data/ext/sqlite3/statement.c +38 -1
 - data/ext/sqlite3/statement.h +1 -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 +62 -0
 - data/lib/sqlite3/version.rb +1 -1
 - metadata +4 -4
 - 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: 58efd48fc68d6c46767c603de815022f41d35eb2aaafb187b8f18fa71ff4330e
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 3b904d8a115523e5a716ca2b1994fc66740181e40affddab7524c5c773a28576
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 0e059c53db3c06f362ba18d1e8e3d2c588156225b3616adbff17d07f86c67688585d66807c4aa227b174e8dc9e0ea0f6acc23bc67d6a2c3ddec7dcf04aab0401
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 2dc22242ff0d87c182c7b90dec3e14f85636d257d3576317142e30ad78b8ce7cc6d1638227d42ae968715d30e1ff17ecdbb07677fb713a2944e5f6fd4a5df415
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,35 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # sqlite3-ruby Changelog
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## 2.1.0 / 2024-09-24
         
     | 
| 
      
 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. If absolutely necessary (and you know what you're doing), you may suppress the fork safety warnings by calling `SQLite3::ForkSafety.suppress_warnings!`.
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            See the README's "Fork Safety" section and `adr/2024-09-fork-safety.md` for more information. [#558, #565, #566] @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 
     | 
    
         
            +
            ### Documentation
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            - The `FAQ.md` has been updated to fix some inaccuracies. [#562] @rickhull
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       3 
33 
     | 
    
         
             
            ## 2.0.4 / 2024-08-13
         
     | 
| 
       4 
34 
     | 
    
         | 
| 
       5 
35 
     | 
    
         
             
            ### Dependencies
         
     | 
    
        data/CONTRIBUTING.md
    CHANGED
    
    | 
         @@ -7,24 +7,24 @@ 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.
         
     | 
| 
       13 
18 
     | 
    
         
             
            The `@connection` instance variable on the `Statement` handle keeps the database
         
     | 
| 
       14 
     | 
    
         
            -
            connection alive. 
     | 
| 
       15 
     | 
    
         
            -
            two cases:
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
            1. `#close` is called on the statement
         
     | 
| 
       18 
     | 
    
         
            -
            2. The `SQLite3::Database` object gets garbage collected
         
     | 
| 
      
 19 
     | 
    
         
            +
            connection alive.
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
            We  
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
            garbage collection.  If the database connection were to be free'd before the
         
     | 
| 
       25 
     | 
    
         
            -
            statement, then boom.  Instead we'll be conservative and free unclosed
         
     | 
| 
       26 
     | 
    
         
            -
            statements when the connection is terminated.
         
     | 
| 
      
 21 
     | 
    
         
            +
            We use `sqlite3_close_v2` in `Database#close` since v2.1.0 which defers _actually_ closing the
         
     | 
| 
      
 22 
     | 
    
         
            +
            connection and freeing the underlying memory until all open statments are closed; though the
         
     | 
| 
      
 23 
     | 
    
         
            +
            `Database` object will immediately behave as though it's been fully closed. If a Database is not
         
     | 
| 
      
 24 
     | 
    
         
            +
            explicitly closed, it will be closed when it is GCed.
         
     | 
| 
       27 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
            `Statement#close` finalizes the underlying statement. If a Statement is not explicitly closed, it
         
     | 
| 
      
 27 
     | 
    
         
            +
            will be closed/finalized when it is GCed.
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
            ## Building gems
         
     | 
    
        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. If absolutely necessary (and you know what you're doing), you may suppress the fork safety warnings by calling `SQLite3::ForkSafety.suppress_warnings!`.
         
     | 
| 
      
 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/ext/sqlite3/database.c
    CHANGED
    
    | 
         @@ -12,6 +12,64 @@ 
     | 
|
| 
       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 
     | 
    
         
            +
                ctx->flags |= SQLITE3_RB_DATABASE_DISCARDED;
         
     | 
| 
      
 53 
     | 
    
         
            +
            }
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            static void
         
     | 
| 
      
 56 
     | 
    
         
            +
            close_or_discard_db(sqlite3RubyPtr ctx)
         
     | 
| 
      
 57 
     | 
    
         
            +
            {
         
     | 
| 
      
 58 
     | 
    
         
            +
                if (ctx->db) {
         
     | 
| 
      
 59 
     | 
    
         
            +
                    int is_readonly = (ctx->flags & SQLITE3_RB_DATABASE_READONLY);
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    if (is_readonly || ctx->owner == getpid()) {
         
     | 
| 
      
 62 
     | 
    
         
            +
                        // Ordinary close.
         
     | 
| 
      
 63 
     | 
    
         
            +
                        sqlite3_close_v2(ctx->db);
         
     | 
| 
      
 64 
     | 
    
         
            +
                        ctx->db = NULL;
         
     | 
| 
      
 65 
     | 
    
         
            +
                    } else {
         
     | 
| 
      
 66 
     | 
    
         
            +
                        // This is an open connection carried across a fork(). "Discard" it.
         
     | 
| 
      
 67 
     | 
    
         
            +
                        discard_db(ctx);
         
     | 
| 
      
 68 
     | 
    
         
            +
                    }
         
     | 
| 
      
 69 
     | 
    
         
            +
                }
         
     | 
| 
      
 70 
     | 
    
         
            +
            }
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
       15 
73 
     | 
    
         
             
            static void
         
     | 
| 
       16 
74 
     | 
    
         
             
            database_mark(void *ctx)
         
     | 
| 
       17 
75 
     | 
    
         
             
            {
         
     | 
| 
         @@ -22,11 +80,8 @@ database_mark(void *ctx) 
     | 
|
| 
       22 
80 
     | 
    
         
             
            static void
         
     | 
| 
       23 
81 
     | 
    
         
             
            deallocate(void *ctx)
         
     | 
| 
       24 
82 
     | 
    
         
             
            {
         
     | 
| 
       25 
     | 
    
         
            -
                 
     | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                if (db) { sqlite3_close(db); }
         
     | 
| 
       29 
     | 
    
         
            -
                xfree(c);
         
     | 
| 
      
 83 
     | 
    
         
            +
                close_or_discard_db((sqlite3RubyPtr)ctx);
         
     | 
| 
      
 84 
     | 
    
         
            +
                xfree(ctx);
         
     | 
| 
       30 
85 
     | 
    
         
             
            }
         
     | 
| 
       31 
86 
     | 
    
         | 
| 
       32 
87 
     | 
    
         
             
            static size_t
         
     | 
| 
         @@ -51,7 +106,9 @@ static VALUE 
     | 
|
| 
       51 
106 
     | 
    
         
             
            allocate(VALUE klass)
         
     | 
| 
       52 
107 
     | 
    
         
             
            {
         
     | 
| 
       53 
108 
     | 
    
         
             
                sqlite3RubyPtr ctx;
         
     | 
| 
       54 
     | 
    
         
            -
                 
     | 
| 
      
 109 
     | 
    
         
            +
                VALUE object = TypedData_Make_Struct(klass, sqlite3Ruby, &database_type, ctx);
         
     | 
| 
      
 110 
     | 
    
         
            +
                ctx->owner = getpid();
         
     | 
| 
      
 111 
     | 
    
         
            +
                return object;
         
     | 
| 
       55 
112 
     | 
    
         
             
            }
         
     | 
| 
       56 
113 
     | 
    
         | 
| 
       57 
114 
     | 
    
         
             
            static char *
         
     | 
| 
         @@ -62,8 +119,6 @@ utf16_string_value_ptr(VALUE str) 
     | 
|
| 
       62 
119 
     | 
    
         
             
                return RSTRING_PTR(str);
         
     | 
| 
       63 
120 
     | 
    
         
             
            }
         
     | 
| 
       64 
121 
     | 
    
         | 
| 
       65 
     | 
    
         
            -
            static VALUE sqlite3_rb_close(VALUE self);
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
122 
     | 
    
         
             
            sqlite3RubyPtr
         
     | 
| 
       68 
123 
     | 
    
         
             
            sqlite3_database_unwrap(VALUE database)
         
     | 
| 
       69 
124 
     | 
    
         
             
            {
         
     | 
| 
         @@ -77,6 +132,7 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs) 
     | 
|
| 
       77 
132 
     | 
    
         
             
            {
         
     | 
| 
       78 
133 
     | 
    
         
             
                sqlite3RubyPtr ctx;
         
     | 
| 
       79 
134 
     | 
    
         
             
                int status;
         
     | 
| 
      
 135 
     | 
    
         
            +
                int flags;
         
     | 
| 
       80 
136 
     | 
    
         | 
| 
       81 
137 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
         
     | 
| 
       82 
138 
     | 
    
         | 
| 
         @@ -89,14 +145,18 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs) 
     | 
|
| 
       89 
145 
     | 
    
         
             
            #  endif
         
     | 
| 
       90 
146 
     | 
    
         
             
            #endif
         
     | 
| 
       91 
147 
     | 
    
         | 
| 
      
 148 
     | 
    
         
            +
                flags = NUM2INT(mode);
         
     | 
| 
       92 
149 
     | 
    
         
             
                status = sqlite3_open_v2(
         
     | 
| 
       93 
150 
     | 
    
         
             
                             StringValuePtr(file),
         
     | 
| 
       94 
151 
     | 
    
         
             
                             &ctx->db,
         
     | 
| 
       95 
     | 
    
         
            -
                              
     | 
| 
      
 152 
     | 
    
         
            +
                             flags,
         
     | 
| 
       96 
153 
     | 
    
         
             
                             NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
         
     | 
| 
       97 
154 
     | 
    
         
             
                         );
         
     | 
| 
       98 
155 
     | 
    
         | 
| 
       99 
     | 
    
         
            -
                CHECK(ctx->db, status)
         
     | 
| 
      
 156 
     | 
    
         
            +
                CHECK(ctx->db, status);
         
     | 
| 
      
 157 
     | 
    
         
            +
                if (flags & SQLITE_OPEN_READONLY) {
         
     | 
| 
      
 158 
     | 
    
         
            +
                    ctx->flags |= SQLITE3_RB_DATABASE_READONLY;
         
     | 
| 
      
 159 
     | 
    
         
            +
                }
         
     | 
| 
       100 
160 
     | 
    
         | 
| 
       101 
161 
     | 
    
         
             
                return self;
         
     | 
| 
       102 
162 
     | 
    
         
             
            }
         
     | 
| 
         @@ -119,21 +179,38 @@ rb_sqlite3_disable_quirk_mode(VALUE self) 
     | 
|
| 
       119 
179 
     | 
    
         
             
            #endif
         
     | 
| 
       120 
180 
     | 
    
         
             
            }
         
     | 
| 
       121 
181 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
            /* 
     | 
| 
      
 182 
     | 
    
         
            +
            /*
         
     | 
| 
      
 183 
     | 
    
         
            +
             *  Close the database and release all associated resources.
         
     | 
| 
       123 
184 
     | 
    
         
             
             *
         
     | 
| 
       124 
     | 
    
         
            -
             *  
     | 
| 
      
 185 
     | 
    
         
            +
             *  ⚠ Writable connections that are carried across a <tt>fork()</tt> are not completely
         
     | 
| 
      
 186 
     | 
    
         
            +
             *  closed. {Sqlite does not support forking}[https://www.sqlite.org/howtocorrupt.html],
         
     | 
| 
      
 187 
     | 
    
         
            +
             *  and fully closing a writable connection that has been carried across a fork may corrupt the
         
     | 
| 
      
 188 
     | 
    
         
            +
             *  database. Since it is an incomplete close, not all memory resources are freed, but this is safer
         
     | 
| 
      
 189 
     | 
    
         
            +
             *  than risking data loss.
         
     | 
| 
      
 190 
     | 
    
         
            +
             *
         
     | 
| 
      
 191 
     | 
    
         
            +
             *  See rdoc-ref:adr/2024-09-fork-safety.md for more information on fork safety.
         
     | 
| 
       125 
192 
     | 
    
         
             
             */
         
     | 
| 
       126 
193 
     | 
    
         
             
            static VALUE
         
     | 
| 
       127 
194 
     | 
    
         
             
            sqlite3_rb_close(VALUE self)
         
     | 
| 
       128 
195 
     | 
    
         
             
            {
         
     | 
| 
       129 
196 
     | 
    
         
             
                sqlite3RubyPtr ctx;
         
     | 
| 
       130 
     | 
    
         
            -
                sqlite3 *db;
         
     | 
| 
       131 
197 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
         
     | 
| 
       132 
198 
     | 
    
         | 
| 
       133 
     | 
    
         
            -
                 
     | 
| 
       134 
     | 
    
         
            -
                CHECK(db, sqlite3_close(ctx->db));
         
     | 
| 
      
 199 
     | 
    
         
            +
                close_or_discard_db(ctx);
         
     | 
| 
       135 
200 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                 
     | 
| 
      
 201 
     | 
    
         
            +
                rb_iv_set(self, "-aggregators", Qnil);
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                return self;
         
     | 
| 
      
 204 
     | 
    
         
            +
            }
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
            /* private method, primarily for testing */
         
     | 
| 
      
 207 
     | 
    
         
            +
            static VALUE
         
     | 
| 
      
 208 
     | 
    
         
            +
            sqlite3_rb_discard(VALUE self)
         
     | 
| 
      
 209 
     | 
    
         
            +
            {
         
     | 
| 
      
 210 
     | 
    
         
            +
                sqlite3RubyPtr ctx;
         
     | 
| 
      
 211 
     | 
    
         
            +
                TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                discard_db(ctx);
         
     | 
| 
       137 
214 
     | 
    
         | 
| 
       138 
215 
     | 
    
         
             
                rb_iv_set(self, "-aggregators", Qnil);
         
     | 
| 
       139 
216 
     | 
    
         | 
| 
         @@ -246,7 +323,7 @@ busy_handler(int argc, VALUE *argv, VALUE self) 
     | 
|
| 
       246 
323 
     | 
    
         
             
                rb_scan_args(argc, argv, "01", &block);
         
     | 
| 
       247 
324 
     | 
    
         | 
| 
       248 
325 
     | 
    
         
             
                if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
         
     | 
| 
       249 
     | 
    
         
            -
                ctx->busy_handler  
     | 
| 
      
 326 
     | 
    
         
            +
                RB_OBJ_WRITE(self, &ctx->busy_handler, block);
         
     | 
| 
       250 
327 
     | 
    
         | 
| 
       251 
328 
     | 
    
         
             
                status = sqlite3_busy_handler(
         
     | 
| 
       252 
329 
     | 
    
         
             
                             ctx->db,
         
     | 
| 
         @@ -869,6 +946,9 @@ rb_sqlite3_open16(VALUE self, VALUE file) 
     | 
|
| 
       869 
946 
     | 
    
         
             
            #endif
         
     | 
| 
       870 
947 
     | 
    
         
             
            #endif
         
     | 
| 
       871 
948 
     | 
    
         | 
| 
      
 949 
     | 
    
         
            +
                // sqlite3_open16 implicitly uses flags (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
         
     | 
| 
      
 950 
     | 
    
         
            +
                // see https://www.sqlite.org/capi3ref.html#sqlite3_open
         
     | 
| 
      
 951 
     | 
    
         
            +
                // so we do not ever set SQLITE3_RB_DATABASE_READONLY in ctx->flags
         
     | 
| 
       872 
952 
     | 
    
         
             
                status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
         
     | 
| 
       873 
953 
     | 
    
         | 
| 
       874 
954 
     | 
    
         
             
                CHECK(ctx->db, status)
         
     | 
| 
         @@ -889,6 +969,7 @@ init_sqlite3_database(void) 
     | 
|
| 
       889 
969 
     | 
    
         
             
                rb_define_private_method(cSqlite3Database, "open16", rb_sqlite3_open16, 1);
         
     | 
| 
       890 
970 
     | 
    
         
             
                rb_define_method(cSqlite3Database, "collation", collation, 2);
         
     | 
| 
       891 
971 
     | 
    
         
             
                rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
         
     | 
| 
      
 972 
     | 
    
         
            +
                rb_define_private_method(cSqlite3Database, "discard", sqlite3_rb_discard, 0);
         
     | 
| 
       892 
973 
     | 
    
         
             
                rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
         
     | 
| 
       893 
974 
     | 
    
         
             
                rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
         
     | 
| 
       894 
975 
     | 
    
         
             
                rb_define_method(cSqlite3Database, "trace", trace, -1);
         
     | 
    
        data/ext/sqlite3/database.h
    CHANGED
    
    | 
         @@ -3,11 +3,17 @@ 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            #include <sqlite3_ruby.h>
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
      
 6 
     | 
    
         
            +
            /* bits in the `flags` field */
         
     | 
| 
      
 7 
     | 
    
         
            +
            #define SQLITE3_RB_DATABASE_READONLY  0x01
         
     | 
| 
      
 8 
     | 
    
         
            +
            #define SQLITE3_RB_DATABASE_DISCARDED 0x02
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       6 
10 
     | 
    
         
             
            struct _sqlite3Ruby {
         
     | 
| 
       7 
11 
     | 
    
         
             
                sqlite3 *db;
         
     | 
| 
       8 
12 
     | 
    
         
             
                VALUE busy_handler;
         
     | 
| 
       9 
13 
     | 
    
         
             
                int stmt_timeout;
         
     | 
| 
       10 
14 
     | 
    
         
             
                struct timespec stmt_deadline;
         
     | 
| 
      
 15 
     | 
    
         
            +
                rb_pid_t owner;
         
     | 
| 
      
 16 
     | 
    
         
            +
                int flags;
         
     | 
| 
       11 
17 
     | 
    
         
             
            };
         
     | 
| 
       12 
18 
     | 
    
         | 
| 
       13 
19 
     | 
    
         
             
            typedef struct _sqlite3Ruby sqlite3Ruby;
         
     | 
    
        data/ext/sqlite3/extconf.rb
    CHANGED
    
    
    
        data/ext/sqlite3/statement.c
    CHANGED
    
    | 
         @@ -1,9 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            #include <sqlite3_ruby.h>
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            #define REQUIRE_OPEN_STMT(_ctxt) \
         
     | 
| 
       4 
     | 
    
         
            -
              if(!_ctxt->st) \
         
     | 
| 
      
 4 
     | 
    
         
            +
              if (!_ctxt->st) \
         
     | 
| 
       5 
5 
     | 
    
         
             
                rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
            #define REQUIRE_LIVE_DB(_ctxt) \
         
     | 
| 
      
 8 
     | 
    
         
            +
              if (_ctxt->db->flags & SQLITE3_RB_DATABASE_DISCARDED) \
         
     | 
| 
      
 9 
     | 
    
         
            +
                rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a statement associated with a discarded database");
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       7 
11 
     | 
    
         
             
            VALUE cSqlite3Statement;
         
     | 
| 
       8 
12 
     | 
    
         | 
| 
       9 
13 
     | 
    
         
             
            static void
         
     | 
| 
         @@ -57,6 +61,11 @@ prepare(VALUE self, VALUE db, VALUE sql) 
     | 
|
| 
       57 
61 
     | 
    
         | 
| 
       58 
62 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
       59 
63 
     | 
    
         | 
| 
      
 64 
     | 
    
         
            +
                /* Dereferencing a pointer to the database struct will be faster than accessing it through the
         
     | 
| 
      
 65 
     | 
    
         
            +
                 * instance variable @connection. The struct pointer is guaranteed to be live because instance
         
     | 
| 
      
 66 
     | 
    
         
            +
                 * variable will keep it from being GCed. */
         
     | 
| 
      
 67 
     | 
    
         
            +
                ctx->db = db_ctx;
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
       60 
69 
     | 
    
         
             
            #ifdef HAVE_SQLITE3_PREPARE_V2
         
     | 
| 
       61 
70 
     | 
    
         
             
                status = sqlite3_prepare_v2(
         
     | 
| 
       62 
71 
     | 
    
         
             
            #else
         
     | 
| 
         @@ -121,6 +130,7 @@ step(VALUE self) 
     | 
|
| 
       121 
130 
     | 
    
         | 
| 
       122 
131 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
       123 
132 
     | 
    
         | 
| 
      
 133 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       124 
134 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       125 
135 
     | 
    
         | 
| 
       126 
136 
     | 
    
         
             
                if (ctx->done_p) { return Qnil; }
         
     | 
| 
         @@ -216,6 +226,8 @@ bind_param(VALUE self, VALUE key, VALUE value) 
     | 
|
| 
       216 
226 
     | 
    
         
             
                int index;
         
     | 
| 
       217 
227 
     | 
    
         | 
| 
       218 
228 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       219 
231 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       220 
232 
     | 
    
         | 
| 
       221 
233 
     | 
    
         
             
                switch (TYPE(key)) {
         
     | 
| 
         @@ -308,6 +320,8 @@ reset_bang(VALUE self) 
     | 
|
| 
       308 
320 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       309 
321 
     | 
    
         | 
| 
       310 
322 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 323 
     | 
    
         
            +
             
     | 
| 
      
 324 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       311 
325 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       312 
326 
     | 
    
         | 
| 
       313 
327 
     | 
    
         
             
                sqlite3_reset(ctx->st);
         
     | 
| 
         @@ -328,6 +342,8 @@ clear_bindings_bang(VALUE self) 
     | 
|
| 
       328 
342 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       329 
343 
     | 
    
         | 
| 
       330 
344 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 345 
     | 
    
         
            +
             
     | 
| 
      
 346 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       331 
347 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       332 
348 
     | 
    
         | 
| 
       333 
349 
     | 
    
         
             
                sqlite3_clear_bindings(ctx->st);
         
     | 
| 
         @@ -360,6 +376,8 @@ column_count(VALUE self) 
     | 
|
| 
       360 
376 
     | 
    
         
             
            {
         
     | 
| 
       361 
377 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       362 
378 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 379 
     | 
    
         
            +
             
     | 
| 
      
 380 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       363 
381 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       364 
382 
     | 
    
         | 
| 
       365 
383 
     | 
    
         
             
                return INT2NUM(sqlite3_column_count(ctx->st));
         
     | 
| 
         @@ -391,6 +409,8 @@ column_name(VALUE self, VALUE index) 
     | 
|
| 
       391 
409 
     | 
    
         
             
                const char *name;
         
     | 
| 
       392 
410 
     | 
    
         | 
| 
       393 
411 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 412 
     | 
    
         
            +
             
     | 
| 
      
 413 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       394 
414 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       395 
415 
     | 
    
         | 
| 
       396 
416 
     | 
    
         
             
                name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
         
     | 
| 
         @@ -414,6 +434,8 @@ column_decltype(VALUE self, VALUE index) 
     | 
|
| 
       414 
434 
     | 
    
         
             
                const char *name;
         
     | 
| 
       415 
435 
     | 
    
         | 
| 
       416 
436 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 437 
     | 
    
         
            +
             
     | 
| 
      
 438 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       417 
439 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       418 
440 
     | 
    
         | 
| 
       419 
441 
     | 
    
         
             
                name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
         
     | 
| 
         @@ -431,6 +453,8 @@ bind_parameter_count(VALUE self) 
     | 
|
| 
       431 
453 
     | 
    
         
             
            {
         
     | 
| 
       432 
454 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       433 
455 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 456 
     | 
    
         
            +
             
     | 
| 
      
 457 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       434 
458 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       435 
459 
     | 
    
         | 
| 
       436 
460 
     | 
    
         
             
                return INT2NUM(sqlite3_bind_parameter_count(ctx->st));
         
     | 
| 
         @@ -538,7 +562,10 @@ stats_as_hash(VALUE self) 
     | 
|
| 
       538 
562 
     | 
    
         
             
            {
         
     | 
| 
       539 
563 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       540 
564 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 565 
     | 
    
         
            +
             
     | 
| 
      
 566 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       541 
567 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
      
 568 
     | 
    
         
            +
             
     | 
| 
       542 
569 
     | 
    
         
             
                VALUE arg = rb_hash_new();
         
     | 
| 
       543 
570 
     | 
    
         | 
| 
       544 
571 
     | 
    
         
             
                stmt_stat_internal(arg, ctx->st);
         
     | 
| 
         @@ -554,6 +581,8 @@ stat_for(VALUE self, VALUE key) 
     | 
|
| 
       554 
581 
     | 
    
         
             
            {
         
     | 
| 
       555 
582 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       556 
583 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 584 
     | 
    
         
            +
             
     | 
| 
      
 585 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       557 
586 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       558 
587 
     | 
    
         | 
| 
       559 
588 
     | 
    
         
             
                if (SYMBOL_P(key)) {
         
     | 
| 
         @@ -574,6 +603,8 @@ memused(VALUE self) 
     | 
|
| 
       574 
603 
     | 
    
         
             
            {
         
     | 
| 
       575 
604 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       576 
605 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 606 
     | 
    
         
            +
             
     | 
| 
      
 607 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       577 
608 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       578 
609 
     | 
    
         | 
| 
       579 
610 
     | 
    
         
             
                return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_MEMUSED, 0));
         
     | 
| 
         @@ -591,6 +622,8 @@ database_name(VALUE self, VALUE index) 
     | 
|
| 
       591 
622 
     | 
    
         
             
            {
         
     | 
| 
       592 
623 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       593 
624 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 625 
     | 
    
         
            +
             
     | 
| 
      
 626 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       594 
627 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       595 
628 
     | 
    
         | 
| 
       596 
629 
     | 
    
         
             
                return SQLITE3_UTF8_STR_NEW2(
         
     | 
| 
         @@ -608,6 +641,8 @@ get_sql(VALUE self) 
     | 
|
| 
       608 
641 
     | 
    
         
             
            {
         
     | 
| 
       609 
642 
     | 
    
         
             
                sqlite3StmtRubyPtr ctx;
         
     | 
| 
       610 
643 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 644 
     | 
    
         
            +
             
     | 
| 
      
 645 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       611 
646 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       612 
647 
     | 
    
         | 
| 
       613 
648 
     | 
    
         
             
                return rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(sqlite3_sql(ctx->st)));
         
     | 
| 
         @@ -626,6 +661,8 @@ get_expanded_sql(VALUE self) 
     | 
|
| 
       626 
661 
     | 
    
         
             
                VALUE rb_expanded_sql;
         
     | 
| 
       627 
662 
     | 
    
         | 
| 
       628 
663 
     | 
    
         
             
                TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
         
     | 
| 
      
 664 
     | 
    
         
            +
             
     | 
| 
      
 665 
     | 
    
         
            +
                REQUIRE_LIVE_DB(ctx);
         
     | 
| 
       629 
666 
     | 
    
         
             
                REQUIRE_OPEN_STMT(ctx);
         
     | 
| 
       630 
667 
     | 
    
         | 
| 
       631 
668 
     | 
    
         
             
                expanded_sql = sqlite3_expanded_sql(ctx->st);
         
     | 
    
        data/ext/sqlite3/statement.h
    CHANGED
    
    
| 
         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,62 @@ 
     | 
|
| 
      
 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 
     | 
    
         
            +
                @suppress = false
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 23 
     | 
    
         
            +
                  def hook!
         
     | 
| 
      
 24 
     | 
    
         
            +
                    ::Process.singleton_class.prepend(CoreExt)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def track(database)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @mutex.synchronize do
         
     | 
| 
      
 29 
     | 
    
         
            +
                      @databases << WeakRef.new(database)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def discard
         
     | 
| 
      
 34 
     | 
    
         
            +
                    warned = @suppress
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @databases.each do |db|
         
     | 
| 
      
 36 
     | 
    
         
            +
                      next unless db.weakref_alive?
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      unless db.closed? || db.readonly?
         
     | 
| 
      
 39 
     | 
    
         
            +
                        unless warned
         
     | 
| 
      
 40 
     | 
    
         
            +
                          # If you are here, you may want to read
         
     | 
| 
      
 41 
     | 
    
         
            +
                          # https://github.com/sparklemotion/sqlite3-ruby/pull/558
         
     | 
| 
      
 42 
     | 
    
         
            +
                          warn("Writable sqlite database connection(s) were inherited from a forked process. " \
         
     | 
| 
      
 43 
     | 
    
         
            +
                               "This is unsafe and the connections are being closed to prevent possible data " \
         
     | 
| 
      
 44 
     | 
    
         
            +
                               "corruption. Please close writable sqlite database connections before forking.",
         
     | 
| 
      
 45 
     | 
    
         
            +
                            uplevel: 0)
         
     | 
| 
      
 46 
     | 
    
         
            +
                          warned = true
         
     | 
| 
      
 47 
     | 
    
         
            +
                        end
         
     | 
| 
      
 48 
     | 
    
         
            +
                        db.close
         
     | 
| 
      
 49 
     | 
    
         
            +
                      end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @databases.clear
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  # Call to suppress the fork-related warnings.
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def suppress_warnings!
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @suppress = true
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
            end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            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
         
     | 
| 
       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-24 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,7 +84,7 @@ 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
         
     | 
| 
         Binary file 
     |