sequel 5.77.0 → 5.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +11 -1
- data/doc/release_notes/5.78.0.txt +67 -0
- data/doc/schema_modification.rdoc +2 -2
- data/lib/sequel/adapters/shared/mssql.rb +29 -1
- data/lib/sequel/adapters/shared/mysql.rb +35 -0
- data/lib/sequel/adapters/shared/postgres.rb +36 -1
- data/lib/sequel/database/misc.rb +1 -0
- data/lib/sequel/exceptions.rb +5 -0
- data/lib/sequel/extensions/async_thread_pool.rb +7 -0
- data/lib/sequel/extensions/migration.rb +12 -1
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/version.rb +1 -1
- metadata +4 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 2d821d7a1270874ced864c3a4bc66b519b3b5e3c050d2792e8dfc907a677e36a
         | 
| 4 | 
            +
              data.tar.gz: 67390870d998d266a1a07d44e72dc85e357226e7f5dd215ca695ab9037840638
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 50b737d3a944ec7d1df62dce2df68b6558e912c0d333d0cdf1d32bccd18ecb5dad8439e73f8c5497b4e627ea8caee34f131dee5a57217a479acfab95287f306d
         | 
| 7 | 
            +
              data.tar.gz: 3b9631235606432b44cc57aa624c6509dd14833fdc0e5e900945232c3593904a249dbe3106aef1e4e863cf939517cb1abbd16ca179eec6e5b1de90b815de941f
         | 
    
        data/CHANGELOG
    CHANGED
    
    | @@ -1,3 +1,13 @@ | |
| 1 | 
            +
            === 5.78.0 (2024-03-01)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            * Support SQLite 3.45+ jsonb functions in the sqlite_json_ops extension (jeremyevans) (#2133)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            * Support compounds (e.g. UNION) in conjunction with Database#values on PostgreSQL (jeremyevans) (#2137)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            * Support :use_advisory_lock option to Migrator.run to use advisory locks when running migrations (jeremyevans) (#2089)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            * Support Database#with_advisory_lock on PostgreSQL, MySQL, and Microsoft SQL Server (jeremyevans) (#2089)
         | 
| 10 | 
            +
             | 
| 1 11 | 
             
            === 5.77.0 (2024-02-01)
         | 
| 2 12 |  | 
| 3 13 | 
             
            * Support create_table :without_rowid option on SQLite (loranger32) (#2126)
         | 
| @@ -16,7 +26,7 @@ | |
| 16 26 |  | 
| 17 27 | 
             
            * Support on_duplicate_columns={raise,warn} parameter in connection URL when using duplicate_columns_handler extension (jeremyevans)
         | 
| 18 28 |  | 
| 19 | 
            -
            * Add transaction_connection_validator extension for retrying transactions on new connection if  | 
| 29 | 
            +
            * Add transaction_connection_validator extension for retrying transactions on new connection if there is a disconnect error when starting transaction (jeremyevans)
         | 
| 20 30 |  | 
| 21 31 | 
             
            === 5.76.0 (2024-01-01)
         | 
| 22 32 |  | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            = New Features
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            * SQLite 3.45+ jsonb functions are now supported in the sqlite_json_ops
         | 
| 4 | 
            +
              extension.  Similar to the postgres_json_ops extension, there are
         | 
| 5 | 
            +
              now separate methods for dealing with json and jsonb types:
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                Sequel.sqlite_json_op(:column)  # json
         | 
| 8 | 
            +
                Sequel.sqlite_jsonb_op(:column) # jsonb
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              Some methods that use json_* functions for json ops use jsonb_*
         | 
| 11 | 
            +
              functions for jsonb ops:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                jb = Sequel.sqlite_jsonb_op(:column)
         | 
| 14 | 
            +
                jb.extract('$.a')    # jsonb_extract(column, '$.a')
         | 
| 15 | 
            +
                jb.insert('$.a', 1)  # jsonb_insert(column, '$.a', 1)
         | 
| 16 | 
            +
                jb.set('$.a', 1)     # jsonb_set(column, '$.a', 1)
         | 
| 17 | 
            +
                jb.replace('$.a', 1) # jsonb_replace(column, '$.a', 1)
         | 
| 18 | 
            +
                jb.remove('$.a')     # jsonb_remove(column, '$.a')
         | 
| 19 | 
            +
                jb.patch('{"a":2}')  # jsonb_patch(column, '{"a":2}')
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              You can use the json and jsonb methods to convert jsonb to json
         | 
| 22 | 
            +
              and json to jsonb, respectively.
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                jb.json              # json(column)
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              Use of the json method on jsonb types is important, because if you
         | 
| 27 | 
            +
              want to be able to deal with the values in Ruby, you must convert
         | 
| 28 | 
            +
              the jsonb value to json in the database before the database returns
         | 
| 29 | 
            +
              the value.  Unlike PostgreSQL, SQLite will not convert the value
         | 
| 30 | 
            +
              from jsonb to json on retrieval, and direct use of SQLite's jsonb
         | 
| 31 | 
            +
              format is unsupported by SQLite as it is subject to change.
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            * Database#with_advisory_lock is now supported on PostgreSQL, MySQL,
         | 
| 34 | 
            +
              and Microsoft SQL Server. This supports advisory (explicit)
         | 
| 35 | 
            +
              locking, using the database-specific APIs.  To work on all three
         | 
| 36 | 
            +
              servers, lock ids should be integers in the signed 64-bit range.
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                DB.with_advisory_lock(1234) do
         | 
| 39 | 
            +
                  # do something
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              By default, an AdvisoryLockError is raised if the lock cannot be
         | 
| 43 | 
            +
              immediately acquired.  You can use the :wait option to wait until
         | 
| 44 | 
            +
              the lock can be acquired, instead of raising.
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                DB.with_advisory_lock(1234, wait: true) do
         | 
| 47 | 
            +
                  # do something
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            * Migrator.run now supports a :use_advisory_lock option to use
         | 
| 51 | 
            +
              advisory locks when running migrations, so that it does not
         | 
| 52 | 
            +
              attempt to run the same migration more than once in the case
         | 
| 53 | 
            +
              where multiple processes are running the migrator simultaneously.
         | 
| 54 | 
            +
              It's probably best to avoid running the migrator in multiple
         | 
| 55 | 
            +
              processes simultaneously instead of relying on this option.
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            = Other Improvements
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            * Database#values now supports chaining with compounds on
         | 
| 60 | 
            +
              PostgreSQL.
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                DB.values([[1, 2]]).union(DB.values([[3, 4]]))
         | 
| 63 | 
            +
                # SELECT * FROM (VALUES (1, 2) UNION (VALUES (3, 4))) AS t1
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            * The internal hash used to store transaction metadata now uses
         | 
| 66 | 
            +
              compare_by_identity, which is faster and avoids potential
         | 
| 67 | 
            +
              issues if a driver implements connection object equality.
         | 
| @@ -81,8 +81,8 @@ appropriate 64-bit integer type for the database you are using. | |
| 81 81 |  | 
| 82 82 | 
             
            === Column options
         | 
| 83 83 |  | 
| 84 | 
            -
            When using the type name as method, the  | 
| 85 | 
            -
            method, the  | 
| 84 | 
            +
            When using the type name as method, the second argument is an options hash, and when using the +column+
         | 
| 85 | 
            +
            method, the third argument is the options hash.  The following options are supported:
         | 
| 86 86 |  | 
| 87 87 | 
             
            :default :: The default value for the column.
         | 
| 88 88 | 
             
            :index :: Create an index on this column. If given a hash, use the hash as the
         | 
| @@ -32,7 +32,7 @@ module Sequel | |
| 32 32 | 
             
                  #
         | 
| 33 33 | 
             
                  # Options:
         | 
| 34 34 | 
             
                  # :args :: Arguments to stored procedure.  For named arguments, this should be a
         | 
| 35 | 
            -
                  #          hash keyed by argument  | 
| 35 | 
            +
                  #          hash keyed by argument name.  For unnamed arguments, this should be an
         | 
| 36 36 | 
             
                  #          array.  Output parameters to the function are specified using :output.
         | 
| 37 37 | 
             
                  #          You can also name output parameters and provide a type by using an
         | 
| 38 38 | 
             
                  #          array containing :output, the type name, and the parameter name.
         | 
| @@ -246,6 +246,34 @@ module Sequel | |
| 246 246 | 
             
                    information_schema_tables('VIEW', opts)
         | 
| 247 247 | 
             
                  end
         | 
| 248 248 |  | 
| 249 | 
            +
                  # Attempt to acquire an exclusive advisory lock with the given lock_id (which will
         | 
| 250 | 
            +
                  # be converted to a string). If successful, yield to the block, then release the advisory lock
         | 
| 251 | 
            +
                  # when the block exits.  If unsuccessful, raise a Sequel::AdvisoryLockError.
         | 
| 252 | 
            +
                  #
         | 
| 253 | 
            +
                  # Options:
         | 
| 254 | 
            +
                  # :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
         | 
| 255 | 
            +
                  def with_advisory_lock(lock_id, opts=OPTS)
         | 
| 256 | 
            +
                    lock_id = lock_id.to_s
         | 
| 257 | 
            +
                    timeout = opts[:wait] ? -1 : 0
         | 
| 258 | 
            +
                    server = opts[:server]
         | 
| 259 | 
            +
                  
         | 
| 260 | 
            +
                    synchronize(server) do
         | 
| 261 | 
            +
                      begin
         | 
| 262 | 
            +
                        res = call_mssql_sproc(:sp_getapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockTimeout'=>timeout, 'LockMode'=>'Exclusive', 'LockOwner'=>'Session'})
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                        unless locked = res[:result] >= 0
         | 
| 265 | 
            +
                            raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
         | 
| 266 | 
            +
                        end
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                        yield
         | 
| 269 | 
            +
                      ensure
         | 
| 270 | 
            +
                        if locked
         | 
| 271 | 
            +
                          call_mssql_sproc(:sp_releaseapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockOwner'=>'Session'})
         | 
| 272 | 
            +
                        end
         | 
| 273 | 
            +
                      end
         | 
| 274 | 
            +
                    end
         | 
| 275 | 
            +
                  end
         | 
| 276 | 
            +
             | 
| 249 277 | 
             
                  private
         | 
| 250 278 |  | 
| 251 279 | 
             
                  # Add CLUSTERED or NONCLUSTERED as needed
         | 
| @@ -197,6 +197,41 @@ module Sequel | |
| 197 197 | 
             
                    renames.each{|from,| remove_cached_schema(from)}
         | 
| 198 198 | 
             
                  end
         | 
| 199 199 |  | 
| 200 | 
            +
                  # Attempt to acquire an exclusive advisory lock with the given lock_id (which will be
         | 
| 201 | 
            +
                  # converted to a string).  If successful, yield to the block, then release the advisory lock
         | 
| 202 | 
            +
                  # when the block exits.  If unsuccessful, raise a Sequel::AdvisoryLockError.
         | 
| 203 | 
            +
                  #
         | 
| 204 | 
            +
                  #   DB.with_advisory_lock(1357){DB.get(1)}
         | 
| 205 | 
            +
                  #   # SELECT GET_LOCK('1357', 0) LIMIT 1
         | 
| 206 | 
            +
                  #   # SELECT 1 AS v LIMIT 1
         | 
| 207 | 
            +
                  #   # SELECT RELEASE_LOCK('1357') LIMIT 1
         | 
| 208 | 
            +
                  #
         | 
| 209 | 
            +
                  # Options:
         | 
| 210 | 
            +
                  # :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
         | 
| 211 | 
            +
                  def with_advisory_lock(lock_id, opts=OPTS)
         | 
| 212 | 
            +
                    lock_id = lock_id.to_s
         | 
| 213 | 
            +
                    ds = dataset
         | 
| 214 | 
            +
                    if server = opts[:server]
         | 
| 215 | 
            +
                      ds = ds.server(server)
         | 
| 216 | 
            +
                    end
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                    # MariaDB doesn't support negative values for infinite wait.  A wait of 34 years
         | 
| 219 | 
            +
                    # should be reasonably similar to infinity for this case.
         | 
| 220 | 
            +
                    timeout = opts[:wait] ? 1073741823 : 0
         | 
| 221 | 
            +
                  
         | 
| 222 | 
            +
                    synchronize(server) do |c|
         | 
| 223 | 
            +
                      begin
         | 
| 224 | 
            +
                        unless locked = ds.get{GET_LOCK(lock_id, timeout)} == 1
         | 
| 225 | 
            +
                          raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
         | 
| 226 | 
            +
                        end
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                        yield
         | 
| 229 | 
            +
                      ensure
         | 
| 230 | 
            +
                        ds.get{RELEASE_LOCK(lock_id)} if locked
         | 
| 231 | 
            +
                      end
         | 
| 232 | 
            +
                    end
         | 
| 233 | 
            +
                  end
         | 
| 234 | 
            +
             | 
| 200 235 | 
             
                  private
         | 
| 201 236 |  | 
| 202 237 | 
             
                  def alter_table_add_column_sql(table, op)
         | 
| @@ -859,6 +859,41 @@ module Sequel | |
| 859 859 | 
             
                    pg_class_relname(relkind, opts)
         | 
| 860 860 | 
             
                  end
         | 
| 861 861 |  | 
| 862 | 
            +
                  # Attempt to acquire an exclusive advisory lock with the given lock_id (which should be
         | 
| 863 | 
            +
                  # a 64-bit integer).  If successful, yield to the block, then release the advisory lock
         | 
| 864 | 
            +
                  # when the block exits.  If unsuccessful, raise a Sequel::AdvisoryLockError.
         | 
| 865 | 
            +
                  #
         | 
| 866 | 
            +
                  #   DB.with_advisory_lock(1347){DB.get(1)}
         | 
| 867 | 
            +
                  #   # SELECT pg_try_advisory_lock(1357) LIMIT 1
         | 
| 868 | 
            +
                  #   # SELECT 1 AS v LIMIT 1
         | 
| 869 | 
            +
                  #   # SELECT pg_advisory_unlock(1357) LIMIT 1
         | 
| 870 | 
            +
                  #
         | 
| 871 | 
            +
                  # Options:
         | 
| 872 | 
            +
                  # :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
         | 
| 873 | 
            +
                  def with_advisory_lock(lock_id, opts=OPTS)
         | 
| 874 | 
            +
                    ds = dataset
         | 
| 875 | 
            +
                    if server = opts[:server]
         | 
| 876 | 
            +
                      ds = ds.server(server)
         | 
| 877 | 
            +
                    end
         | 
| 878 | 
            +
                  
         | 
| 879 | 
            +
                    synchronize(server) do |c|
         | 
| 880 | 
            +
                      begin
         | 
| 881 | 
            +
                        if opts[:wait]
         | 
| 882 | 
            +
                          ds.get{pg_advisory_lock(lock_id)}
         | 
| 883 | 
            +
                          locked = true
         | 
| 884 | 
            +
                        else
         | 
| 885 | 
            +
                          unless locked = ds.get{pg_try_advisory_lock(lock_id)}
         | 
| 886 | 
            +
                            raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
         | 
| 887 | 
            +
                          end
         | 
| 888 | 
            +
                        end
         | 
| 889 | 
            +
             | 
| 890 | 
            +
                        yield
         | 
| 891 | 
            +
                      ensure
         | 
| 892 | 
            +
                        ds.get{pg_advisory_unlock(lock_id)} if locked
         | 
| 893 | 
            +
                      end
         | 
| 894 | 
            +
                    end
         | 
| 895 | 
            +
                  end
         | 
| 896 | 
            +
             | 
| 862 897 | 
             
                  private
         | 
| 863 898 |  | 
| 864 899 | 
             
                  # Dataset used to retrieve CHECK constraint information
         | 
| @@ -1785,7 +1820,7 @@ module Sequel | |
| 1785 1820 |  | 
| 1786 1821 | 
             
                  Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
         | 
| 1787 1822 | 
             
                  Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns override values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
         | 
| 1788 | 
            -
                  Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
         | 
| 1823 | 
            +
                  Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values compounds order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
         | 
| 1789 1824 | 
             
                  Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
         | 
| 1790 1825 |  | 
| 1791 1826 | 
             
                  # Return the results of an EXPLAIN ANALYZE query as a string
         | 
    
        data/lib/sequel/database/misc.rb
    CHANGED
    
    
    
        data/lib/sequel/exceptions.rb
    CHANGED
    
    | @@ -18,6 +18,11 @@ module Sequel | |
| 18 18 | 
             
                  end
         | 
| 19 19 | 
             
                end
         | 
| 20 20 | 
             
              end  
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              (
         | 
| 23 | 
            +
              # Error raised when there is a failed attempt to acquire an advisory lock.
         | 
| 24 | 
            +
              AdvisoryLockError = Class.new(Error)
         | 
| 25 | 
            +
              ).name
         | 
| 21 26 |  | 
| 22 27 | 
             
              (
         | 
| 23 28 | 
             
              # Error raised when the adapter requested doesn't exist or can't be loaded.
         | 
| @@ -176,6 +176,13 @@ | |
| 176 176 | 
             
            # +:preempt_async_thread+ Database option before loading the
         | 
| 177 177 | 
             
            # async_thread_pool extension.
         | 
| 178 178 | 
             
            #
         | 
| 179 | 
            +
            # Note that the async_thread_pool extension creates the thread pool
         | 
| 180 | 
            +
            # when it is loaded into the Database.  If you fork after loading
         | 
| 181 | 
            +
            # the extension, the extension will not work, as fork does not
         | 
| 182 | 
            +
            # copy the thread pools.  If you are using a forking webserver
         | 
| 183 | 
            +
            # (or any other system that forks worker processes), load this
         | 
| 184 | 
            +
            # extension in each child process, do not load it before forking.
         | 
| 185 | 
            +
            #
         | 
| 179 186 | 
             
            # Related module: Sequel::Database::AsyncThreadPool::DatasetMethods
         | 
| 180 187 |  | 
| 181 188 |  | 
| @@ -403,6 +403,11 @@ module Sequel | |
| 403 403 | 
             
                  migrator_class(directory).new(db, directory, opts).is_current?
         | 
| 404 404 | 
             
                end
         | 
| 405 405 |  | 
| 406 | 
            +
                # Lock ID to use for advisory locks when running migrations
         | 
| 407 | 
            +
                # "sequel-migration".codepoints.reduce(:*) % (2**63)
         | 
| 408 | 
            +
                MIGRATION_ADVISORY_LOCK_ID = 4966325471869609408
         | 
| 409 | 
            +
                private_constant :MIGRATION_ADVISORY_LOCK_ID
         | 
| 410 | 
            +
             | 
| 406 411 | 
             
                # Migrates the supplied database using the migration files in the specified directory. Options:
         | 
| 407 412 | 
             
                # :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
         | 
| 408 413 | 
             
                #                                   It is very risky to use this option, since it can result in
         | 
| @@ -416,6 +421,8 @@ module Sequel | |
| 416 421 | 
             
                # :table :: The table containing the schema version (default: :schema_info for integer migrations and
         | 
| 417 422 | 
             
                #           :schema_migrations for timestamped migrations).
         | 
| 418 423 | 
             
                # :target :: The target version to which to migrate.  If not given, migrates to the maximum version.
         | 
| 424 | 
            +
                # :use_advisory_lock :: Use advisory locks in migrations (only use this if Sequel supports advisory
         | 
| 425 | 
            +
                #                       locks for the database).
         | 
| 419 426 | 
             
                #
         | 
| 420 427 | 
             
                # Examples: 
         | 
| 421 428 | 
             
                #   Sequel::Migrator.run(DB, "migrations")
         | 
| @@ -423,7 +430,11 @@ module Sequel | |
| 423 430 | 
             
                #   Sequel::Migrator.run(DB, "app1/migrations", column: :app2_version)
         | 
| 424 431 | 
             
                #   Sequel::Migrator.run(DB, "app2/migrations", column: :app2_version, table: :schema_info2)
         | 
| 425 432 | 
             
                def self.run(db, directory, opts=OPTS)
         | 
| 426 | 
            -
                   | 
| 433 | 
            +
                  if opts[:use_advisory_lock]
         | 
| 434 | 
            +
                    db.with_advisory_lock(MIGRATION_ADVISORY_LOCK_ID){run(db, directory, opts.merge(:use_advisory_lock=>false))}
         | 
| 435 | 
            +
                  else
         | 
| 436 | 
            +
                    migrator_class(directory).new(db, directory, opts).run
         | 
| 437 | 
            +
                  end
         | 
| 427 438 | 
             
                end
         | 
| 428 439 |  | 
| 429 440 | 
             
                # Choose the Migrator subclass to use.  Uses the TimestampMigrator
         | 
| @@ -2,27 +2,34 @@ | |
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # The sqlite_json_ops extension adds support to Sequel's DSL to make
         | 
| 4 4 | 
             
            # it easier to call SQLite JSON functions and operators (added
         | 
| 5 | 
            -
            # first in SQLite 3.38.0).
         | 
| 5 | 
            +
            # first in SQLite 3.38.0).  It also supports the SQLite JSONB functions
         | 
| 6 | 
            +
            # added in SQLite 3.45.0.
         | 
| 6 7 | 
             
            #
         | 
| 7 8 | 
             
            # To load the extension:
         | 
| 8 9 | 
             
            #
         | 
| 9 10 | 
             
            #   Sequel.extension :sqlite_json_ops
         | 
| 10 11 | 
             
            #
         | 
| 11 | 
            -
            # This extension works by calling methods on Sequel::SQLite::JSONOp  | 
| 12 | 
            -
            # which you can create  | 
| 12 | 
            +
            # This extension works by calling methods on Sequel::SQLite::JSONOp and
         | 
| 13 | 
            +
            # Sequel::SQLite::JSONBOp objects, which you can create using
         | 
| 14 | 
            +
            # Sequel.sqlite_json_op and Sequel.sqlite_jsonb_op:
         | 
| 13 15 | 
             
            #
         | 
| 14 16 | 
             
            #   j = Sequel.sqlite_json_op(:json_column)
         | 
| 17 | 
            +
            #   jb = Sequel.sqlite_jsonb_op(:jsonb_column)
         | 
| 15 18 | 
             
            #
         | 
| 16 | 
            -
            # Also, on most Sequel expression objects, you can call the sqlite_json_op  | 
| 17 | 
            -
            # to create a Sequel::SQLite::JSONOp  | 
| 19 | 
            +
            # Also, on most Sequel expression objects, you can call the sqlite_json_op or
         | 
| 20 | 
            +
            # sqlite_jsonb_op method to create a Sequel::SQLite::JSONOp or 
         | 
| 21 | 
            +
            # Sequel::SQLite::JSONBOp object:
         | 
| 18 22 | 
             
            #
         | 
| 19 23 | 
             
            #   j = Sequel[:json_column].sqlite_json_op
         | 
| 24 | 
            +
            #   jb = Sequel[:jsonb_column].sqlite_jsonb_op
         | 
| 20 25 | 
             
            #
         | 
| 21 26 | 
             
            # If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
         | 
| 22 27 | 
             
            # or you have loaded the core_refinements extension
         | 
| 23 28 | 
             
            # and have activated refinements for the file, you can also use Symbol#sqlite_json_op:
         | 
| 29 | 
            +
            # or Symbol#sqlite_jsonb_op:
         | 
| 24 30 | 
             
            #
         | 
| 25 31 | 
             
            #   j = :json_column.sqlite_json_op
         | 
| 32 | 
            +
            #   jb = :json_column.sqlite_jsonb_op
         | 
| 26 33 | 
             
            #
         | 
| 27 34 | 
             
            # The following methods are available for Sequel::SQLite::JSONOp instances:
         | 
| 28 35 | 
             
            #
         | 
| @@ -30,11 +37,13 @@ | |
| 30 37 | 
             
            #   j.get(1)                 # (json_column ->> 1)
         | 
| 31 38 | 
             
            #   j.get_text(1)            # (json_column -> 1)
         | 
| 32 39 | 
             
            #   j.extract('$.a')         # json_extract(json_column, '$.a')
         | 
| 40 | 
            +
            #   jb.extract('$.a')        # jsonb_extract(jsonb_column, '$.a')
         | 
| 33 41 | 
             
            #
         | 
| 34 42 | 
             
            #   j.array_length           # json_array_length(json_column)
         | 
| 35 43 | 
             
            #   j.type                   # json_type(json_column)
         | 
| 36 44 | 
             
            #   j.valid                  # json_valid(json_column)
         | 
| 37 | 
            -
            #    | 
| 45 | 
            +
            #   jb.json                  # json(jsonb_column)
         | 
| 46 | 
            +
            #   j.jsonb                  # jsonb(json_column)
         | 
| 38 47 | 
             
            #
         | 
| 39 48 | 
             
            #   j.insert('$.a', 1)       # json_insert(json_column, '$.a', 1)
         | 
| 40 49 | 
             
            #   j.set('$.a', 1)          # json_set(json_column, '$.a', 1)
         | 
| @@ -42,22 +51,30 @@ | |
| 42 51 | 
             
            #   j.remove('$.a')          # json_remove(json_column, '$.a')
         | 
| 43 52 | 
             
            #   j.patch('{"a":2}')       # json_patch(json_column, '{"a":2}')
         | 
| 44 53 | 
             
            #
         | 
| 54 | 
            +
            #   jb.insert('$.a', 1)      # jsonb_insert(jsonb_column, '$.a', 1)
         | 
| 55 | 
            +
            #   jb.set('$.a', 1)         # jsonb_set(jsonb_column, '$.a', 1)
         | 
| 56 | 
            +
            #   jb.replace('$.a', 1)     # jsonb_replace(jsonb_column, '$.a', 1)
         | 
| 57 | 
            +
            #   jb.remove('$.a')         # jsonb_remove(jsonb_column, '$.a')
         | 
| 58 | 
            +
            #   jb.patch('{"a":2}')      # jsonb_patch(jsonb_column, '{"a":2}')
         | 
| 59 | 
            +
            #
         | 
| 45 60 | 
             
            #   j.each                   # json_each(json_column)
         | 
| 46 61 | 
             
            #   j.tree                   # json_tree(json_column)
         | 
| 47 62 | 
             
            #
         | 
| 48 | 
            -
            # Related modules: Sequel::SQLite::JSONOp
         | 
| 63 | 
            +
            # Related modules: Sequel::SQLite::JSONBaseOp, Sequel::SQLite::JSONOp,
         | 
| 64 | 
            +
            # Sequel::SQLite::JSONBOp
         | 
| 49 65 |  | 
| 50 66 | 
             
            #
         | 
| 51 67 | 
             
            module Sequel
         | 
| 52 68 | 
             
              module SQLite
         | 
| 53 | 
            -
                #  | 
| 54 | 
            -
                # defines methods that  | 
| 55 | 
            -
                # SQLite json operators and functions.
         | 
| 69 | 
            +
                # JSONBaseOp is an abstract base wrapper class for a object that
         | 
| 70 | 
            +
                # defines methods that return Sequel expression objects representing
         | 
| 71 | 
            +
                # SQLite json operators and functions. It is subclassed by both
         | 
| 72 | 
            +
                # JSONOp and JSONBOp for json and jsonb specific behavior.
         | 
| 56 73 | 
             
                #
         | 
| 57 74 | 
             
                # In the method documentation examples, assume that:
         | 
| 58 75 | 
             
                #
         | 
| 59 76 | 
             
                #   json_op = Sequel.sqlite_json_op(:json)
         | 
| 60 | 
            -
                class  | 
| 77 | 
            +
                class JSONBaseOp < Sequel::SQL::Wrapper
         | 
| 61 78 | 
             
                  GET = ["(".freeze, " ->> ".freeze, ")".freeze].freeze
         | 
| 62 79 | 
             
                  private_constant :GET
         | 
| 63 80 |  | 
| @@ -82,7 +99,7 @@ module Sequel | |
| 82 99 | 
             
                  #   json_op.array_length         # json_array_length(json)
         | 
| 83 100 | 
             
                  #   json_op.array_length('$[1]') # json_array_length(json, '$[1]')
         | 
| 84 101 | 
             
                  def array_length(*args)
         | 
| 85 | 
            -
                    Sequel::SQL::NumericExpression.new(:NOOP,  | 
| 102 | 
            +
                    Sequel::SQL::NumericExpression.new(:NOOP, SQL::Function.new(:json_array_length, self, *args))
         | 
| 86 103 | 
             
                  end
         | 
| 87 104 |  | 
| 88 105 | 
             
                  # Returns an expression for a set of information extracted from the top-level
         | 
| @@ -92,7 +109,7 @@ module Sequel | |
| 92 109 | 
             
                  #   json_op.each        # json_each(json)
         | 
| 93 110 | 
             
                  #   json_op.each('$.a') # json_each(json, '$.a')
         | 
| 94 111 | 
             
                  def each(*args)
         | 
| 95 | 
            -
                     | 
| 112 | 
            +
                    SQL::Function.new(:json_each, self, *args)
         | 
| 96 113 | 
             
                  end
         | 
| 97 114 |  | 
| 98 115 | 
             
                  # Returns an expression for the JSON array element or object field at the specified
         | 
| @@ -129,10 +146,17 @@ module Sequel | |
| 129 146 | 
             
                  #
         | 
| 130 147 | 
             
                  #   json_op.json   # json(json)
         | 
| 131 148 | 
             
                  def json
         | 
| 132 | 
            -
                     | 
| 149 | 
            +
                    JSONOp.new(SQL::Function.new(:json, self))
         | 
| 133 150 | 
             
                  end
         | 
| 134 151 | 
             
                  alias minify json
         | 
| 135 152 |  | 
| 153 | 
            +
                  # Returns the JSONB format of the JSON.
         | 
| 154 | 
            +
                  #
         | 
| 155 | 
            +
                  #   json_op.jsonb   # jsonb(json)
         | 
| 156 | 
            +
                  def jsonb
         | 
| 157 | 
            +
                    JSONBOp.new(SQL::Function.new(:jsonb, self))
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
             | 
| 136 160 | 
             
                  # Returns an expression for updating the JSON object using the RFC 7396 MergePatch algorithm
         | 
| 137 161 | 
             
                  #
         | 
| 138 162 | 
             
                  #   json_op.patch('{"a": 1, "b": null}') # json_patch(json, '{"a": 1, "b": null}')
         | 
| @@ -172,7 +196,7 @@ module Sequel | |
| 172 196 | 
             
                  #   json_op.tree        # json_tree(json)
         | 
| 173 197 | 
             
                  #   json_op.tree('$.a') # json_tree(json, '$.a')
         | 
| 174 198 | 
             
                  def tree(*args)
         | 
| 175 | 
            -
                     | 
| 199 | 
            +
                    SQL::Function.new(:json_tree, self, *args)
         | 
| 176 200 | 
             
                  end
         | 
| 177 201 |  | 
| 178 202 | 
             
                  # Returns an expression for the type of the JSON value or the JSON value at the given path.
         | 
| @@ -180,13 +204,13 @@ module Sequel | |
| 180 204 | 
             
                  #   json_op.type         # json_type(json)
         | 
| 181 205 | 
             
                  #   json_op.type('$[1]') # json_type(json, '$[1]')
         | 
| 182 206 | 
             
                  def type(*args)
         | 
| 183 | 
            -
                    Sequel::SQL::StringExpression.new(:NOOP,  | 
| 207 | 
            +
                    Sequel::SQL::StringExpression.new(:NOOP, SQL::Function.new(:json_type, self, *args))
         | 
| 184 208 | 
             
                  end
         | 
| 185 209 | 
             
                  alias typeof type
         | 
| 186 210 |  | 
| 187 211 | 
             
                  # Returns a boolean expression for whether the JSON is valid or not.
         | 
| 188 212 | 
             
                  def valid
         | 
| 189 | 
            -
                    Sequel::SQL::BooleanExpression.new(:NOOP,  | 
| 213 | 
            +
                    Sequel::SQL::BooleanExpression.new(:NOOP, SQL::Function.new(:json_valid, self))
         | 
| 190 214 | 
             
                  end
         | 
| 191 215 |  | 
| 192 216 | 
             
                  private
         | 
| @@ -198,7 +222,7 @@ module Sequel | |
| 198 222 |  | 
| 199 223 | 
             
                  # Internals of the methods that return functions prefixed with +json_+.
         | 
| 200 224 | 
             
                  def function(name, *args)
         | 
| 201 | 
            -
                    SQL::Function.new(" | 
| 225 | 
            +
                    SQL::Function.new("#{function_prefix}_#{name}", self, *args)
         | 
| 202 226 | 
             
                  end
         | 
| 203 227 |  | 
| 204 228 | 
             
                  # Internals of the methods that return functions prefixed with +json_+, that
         | 
| @@ -208,12 +232,36 @@ module Sequel | |
| 208 232 | 
             
                  end
         | 
| 209 233 | 
             
                end
         | 
| 210 234 |  | 
| 235 | 
            +
                # JSONOp is used for SQLite json-specific functions and operators.
         | 
| 236 | 
            +
                class JSONOp < JSONBaseOp
         | 
| 237 | 
            +
                  private
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                  def function_prefix
         | 
| 240 | 
            +
                    "json"
         | 
| 241 | 
            +
                  end
         | 
| 242 | 
            +
                end
         | 
| 243 | 
            +
             | 
| 244 | 
            +
                # JSONOp is used for SQLite jsonb-specific functions and operators.
         | 
| 245 | 
            +
                class JSONBOp < JSONBaseOp
         | 
| 246 | 
            +
                  private
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                  def function_prefix
         | 
| 249 | 
            +
                    "jsonb"
         | 
| 250 | 
            +
                  end
         | 
| 251 | 
            +
                end
         | 
| 252 | 
            +
             | 
| 211 253 | 
             
                module JSONOpMethods
         | 
| 212 254 | 
             
                  # Wrap the receiver in an JSONOp so you can easily use the SQLite
         | 
| 213 255 | 
             
                  # json functions and operators with it.
         | 
| 214 256 | 
             
                  def sqlite_json_op
         | 
| 215 257 | 
             
                    JSONOp.new(self)
         | 
| 216 258 | 
             
                  end
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                  # Wrap the receiver in an JSONBOp so you can easily use the SQLite
         | 
| 261 | 
            +
                  # jsonb functions and operators with it.
         | 
| 262 | 
            +
                  def sqlite_jsonb_op
         | 
| 263 | 
            +
                    JSONBOp.new(self)
         | 
| 264 | 
            +
                  end
         | 
| 217 265 | 
             
                end
         | 
| 218 266 | 
             
              end
         | 
| 219 267 |  | 
| @@ -227,6 +275,16 @@ module Sequel | |
| 227 275 | 
             
                    SQLite::JSONOp.new(v)
         | 
| 228 276 | 
             
                  end
         | 
| 229 277 | 
             
                end
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                # Return the object wrapped in an SQLite::JSONBOp.
         | 
| 280 | 
            +
                def sqlite_jsonb_op(v)
         | 
| 281 | 
            +
                  case v
         | 
| 282 | 
            +
                  when SQLite::JSONBOp
         | 
| 283 | 
            +
                    v
         | 
| 284 | 
            +
                  else
         | 
| 285 | 
            +
                    SQLite::JSONBOp.new(v)
         | 
| 286 | 
            +
                  end
         | 
| 287 | 
            +
                end
         | 
| 230 288 | 
             
              end
         | 
| 231 289 |  | 
| 232 290 | 
             
              class SQL::GenericExpression
         | 
    
        data/lib/sequel/version.rb
    CHANGED
    
    | @@ -6,7 +6,7 @@ module Sequel | |
| 6 6 |  | 
| 7 7 | 
             
              # The minor version of Sequel.  Bumped for every non-patch level
         | 
| 8 8 | 
             
              # release, generally around once a month.
         | 
| 9 | 
            -
              MINOR =  | 
| 9 | 
            +
              MINOR = 78
         | 
| 10 10 |  | 
| 11 11 | 
             
              # The tiny version of Sequel.  Usually 0, only bumped for bugfix
         | 
| 12 12 | 
             
              # releases that fix regressions from previous versions.
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: sequel
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 5. | 
| 4 | 
            +
              version: 5.78.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Jeremy Evans
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-03-01 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bigdecimal
         | 
| @@ -224,6 +224,7 @@ extra_rdoc_files: | |
| 224 224 | 
             
            - doc/release_notes/5.75.0.txt
         | 
| 225 225 | 
             
            - doc/release_notes/5.76.0.txt
         | 
| 226 226 | 
             
            - doc/release_notes/5.77.0.txt
         | 
| 227 | 
            +
            - doc/release_notes/5.78.0.txt
         | 
| 227 228 | 
             
            - doc/release_notes/5.8.0.txt
         | 
| 228 229 | 
             
            - doc/release_notes/5.9.0.txt
         | 
| 229 230 | 
             
            files:
         | 
| @@ -329,6 +330,7 @@ files: | |
| 329 330 | 
             
            - doc/release_notes/5.75.0.txt
         | 
| 330 331 | 
             
            - doc/release_notes/5.76.0.txt
         | 
| 331 332 | 
             
            - doc/release_notes/5.77.0.txt
         | 
| 333 | 
            +
            - doc/release_notes/5.78.0.txt
         | 
| 332 334 | 
             
            - doc/release_notes/5.8.0.txt
         | 
| 333 335 | 
             
            - doc/release_notes/5.9.0.txt
         | 
| 334 336 | 
             
            - doc/schema_modification.rdoc
         |