sequel-duckdb 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9ed53934b345068605d21e881ef98049be3dcbb07213c6d5238ce6566c15cdf
4
- data.tar.gz: 92a1cdd312058eb4459084f5eb9069b7780cf54f56ccfbc15ae7f7f931e0fbfd
3
+ metadata.gz: 7d728d2d4748d8ace682454b06155cea6ae570a8fbfbf888efa7d2a314077e5c
4
+ data.tar.gz: 525588b5baceefd4e7b0e3e61f429efb1c505d03250be98ca109bb9567bfce63
5
5
  SHA512:
6
- metadata.gz: c372ff00e47bb0680b7e2601fbc5ab61a281e0cd7c68c4764c6f2355d8701014b99d54ad364d77f6f8becba32ed9e9dc6b09d9ae73c9f53d179abbbc70bdbd96
7
- data.tar.gz: 5dce4902a7eb0f5b25ec86e129afc8f0c9962cdd2f960dea8d352827401303d9cc5bb189e1879369891cbaf0764facc9f8c726ef5d3b3ecac51ec9b7a21f0d71
6
+ metadata.gz: 63164b64d30f71edcb66edb7e3a01ed07074de9312673b51b823e264268f702609fd6fcf98481535c7fc9165f49fa6eb38aa28adfd4311f129ef10d3b9a6bb8b
7
+ data.tar.gz: 76f6fb5c6e9d2684a013176f3ff7b9414ba6e0a9e67e88d36312e5cb57df5ddda4805ad84f4506ce7c60576461748194e24c2727596493b735eb434bec00cfc7
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.1] - 2026-10-26
9
+
10
+ ### Added
11
+
12
+ - Support for the DuckDB C library 2.x and the duckdb gem 2.x, alongside 1.5.x. CI
13
+ tests library 1.5.6 and 2.0.0, and the duckdb gem 1.5.x and 2.x. Support for library
14
+ 1.5.x ends when DuckDB 1.5.x reaches end of life in March 2027. sequel-duckdb still
15
+ does not depend on the duckdb gem, so each project picks its own version.
16
+ - **database:** Add attach and detach for DuckDB catalogs ([f82169b](https://github.com/outcomesinsights/sequel-duckdb/commit/f82169b0ad4cd66abe588dfdad431053535151e1))
17
+
18
+ ### Changed
19
+
20
+ - **adapter:** Remove the unused primary key caches ([9a14254](https://github.com/outcomesinsights/sequel-duckdb/commit/9a14254abd8494e71af9e5478d081ff048b46701))
21
+
22
+ ### Fixed
23
+
24
+ - **connection:** Honor the readonly option ([e8e94c5](https://github.com/outcomesinsights/sequel-duckdb/commit/e8e94c5a5a210aacac0a53cc8190f3dc76b3bbdf))
25
+ - **schema:** Keep the transaction when a name lookup misses on DuckDB 2.x ([b3c02c6](https://github.com/outcomesinsights/sequel-duckdb/commit/b3c02c6029095e11fb048ef2b3c0e84a6c62e3ec))
26
+ - **schema:** Let a mock Database drop and create schemas ([f9aad74](https://github.com/outcomesinsights/sequel-duckdb/commit/f9aad74f94fc9d4ed024accd9ccb4bc6ecdb628e))
27
+
8
28
  ## [0.4.0] - 2026-10-25
9
29
 
10
30
  ### Breaking changes
@@ -348,3 +368,4 @@ tag-based release workflow.
348
368
  [0.3.0]: https://github.com/outcomesinsights/sequel-duckdb/compare/v0.2.1...v0.3.0
349
369
  [0.3.1]: https://github.com/outcomesinsights/sequel-duckdb/compare/v0.3.0...v0.3.1
350
370
  [0.4.0]: https://github.com/outcomesinsights/sequel-duckdb/compare/v0.3.1...v0.4.0
371
+ [0.4.1]: https://github.com/outcomesinsights/sequel-duckdb/compare/v0.4.0...v0.4.1
data/README.md CHANGED
@@ -286,6 +286,12 @@ db = Sequel.connect('duckdb://relative/path/to/database.duckdb')
286
286
  db = Sequel.connect('duckdb:///path/to/database.duckdb?readonly=true')
287
287
  ```
288
288
 
289
+ `readonly` opens the file with DuckDB's `access_mode` set to `READ_ONLY`, so writes
290
+ raise `Sequel::DatabaseError`. All `Sequel::Database` objects on one file in one
291
+ process share one DuckDB instance, so the first one to open the file sets its
292
+ settings. Opening the same file again with other settings, such as read-write after
293
+ read-only, raises `Sequel::DatabaseConnectionError`.
294
+
289
295
  ### Connection Hash
290
296
 
291
297
  ```ruby
@@ -539,6 +545,38 @@ db[:users].copy_to_sql("/data/users.csv", header: true, delimiter: "|")
539
545
  # => COPY (SELECT * FROM "users") TO '/data/users.csv' (FORMAT CSV, HEADER, DELIMITER '|')
540
546
  ```
541
547
 
548
+ ### Attaching database files
549
+
550
+ `Database#attach` runs a DuckDB `ATTACH`, and `Database#detach` runs `DETACH DATABASE`.
551
+ The catalog name `as:` is required. A name that DuckDB derives from the file name can
552
+ equal a schema name inside the file, and then `schema.table` is ambiguous. Other DuckDB
553
+ `ATTACH` options pass through, for example `type: :sqlite`.
554
+
555
+ ```ruby
556
+ db.attach("/data/dataset.duckdb", as: :dataset, read_only: true)
557
+ db[Sequel[:dataset][:people]].count
558
+ db.detach(:dataset, if_exists: true)
559
+
560
+ db.attach_sql("/data/dataset.duckdb", as: :dataset, read_only: true, if_not_exists: true)
561
+ # => ATTACH IF NOT EXISTS '/data/dataset.duckdb' AS "dataset" (READ_ONLY)
562
+ ```
563
+
564
+ An attached catalog belongs to the DuckDB database, which all pool connections of a
565
+ `Sequel::Database` share. It lasts until `Database#disconnect` closes the last
566
+ connection. After that, attach it again; `if_not_exists: true` makes a repeat safe.
567
+
568
+ `search_path` is different: DuckDB keeps it per connection. Set it on every new
569
+ connection with Sequel's `connect_sqls` option. Put the writable catalog first, because
570
+ `CREATE TABLE` without a schema goes to the first entry, and a read-only catalog there
571
+ makes it fail.
572
+
573
+ ```ruby
574
+ db = Sequel.connect("duckdb:///exports/42/work.duckdb",
575
+ connect_sqls: [ "SET search_path = 'work.main'" ])
576
+ db.attach("/data/dataset.duckdb", as: :dataset, read_only: true)
577
+ db.create_table(:summary) { Integer :n } # created in work.main
578
+ ```
579
+
542
580
  ### Lists
543
581
 
544
582
  Sequel writes a Ruby Array as a parenthesized list, such as `(1, 2, 3)`, for `IN`
@@ -765,10 +803,17 @@ db[:users].where(active: true).all
765
803
 
766
804
  ### Version Compatibility
767
805
 
768
- - **Ruby**: 3.1.0 or higher
806
+ - **Ruby**: 3.3 or higher (CI tests 3.3, 3.4 and 4.0)
769
807
  - **Sequel**: 5.0 or higher
770
- - **DuckDB**: 0.8.0 or higher
771
- - **ruby-duckdb**: 1.0.0 or higher
808
+ - **DuckDB C library**: 1.5.x and 2.x. Support for 1.5.x ends when DuckDB 1.5.x
809
+ reaches end of life in March 2027.
810
+ - **ruby-duckdb (the `duckdb` gem)**: 1.5.x and 2.x. sequel-duckdb does not depend on
811
+ it, so add `gem "duckdb"` to your own Gemfile. ruby-duckdb 2.x builds against
812
+ library 1.4 or newer, so it works with library 1.5.x.
813
+
814
+ DuckDB 2.x aborts an open transaction after any failed statement, including a query
815
+ on a missing table; 1.5.x does not. `table_exists?` and `schema` keep the transaction
816
+ on both versions.
772
817
 
773
818
  ### Getting Help
774
819
 
@@ -69,12 +69,10 @@ module Sequel
69
69
  # (InstanceRegistry), and #disconnect gives it back once no connection is open.
70
70
  def adapter_initialize
71
71
  @conversion_procs = {}
72
- @primary_keys = {}
73
- @primary_key_sequences = {}
74
72
  register_default_conversion_procs
75
73
  @duckdb_mutex = Mutex.new
76
74
  @duckdb_file = duckdb_file_path(opts[:database])
77
- @duckdb_settings = InstanceRegistry.normalize_settings(opts[:config])
75
+ @duckdb_settings = InstanceRegistry.normalize_settings(duckdb_config)
78
76
  @duckdb_state = ConnectionState.new(open_duckdb_database, 0)
79
77
  rescue Sequel::DatabaseConnectionError
80
78
  raise
@@ -96,6 +94,19 @@ module Sequel
96
94
  end
97
95
 
98
96
  # nil for an in-memory database, which is never shared.
97
+ # The DuckDB settings: the config: option, plus access_mode READ_ONLY when the
98
+ # readonly option (readonly: true, or ?readonly=true in the URL) is true.
99
+ def duckdb_config
100
+ config = opts[:config] || {}
101
+ return config unless typecast_value_boolean(opts[:readonly])
102
+
103
+ mode = config.find { |name, _| name.to_s == "access_mode" }&.last
104
+ return config.merge(access_mode: "READ_ONLY") if mode.nil?
105
+ return config if mode.to_s.casecmp?("READ_ONLY")
106
+
107
+ raise Sequel::DatabaseConnectionError, "readonly: true conflicts with config access_mode #{mode.inspect}"
108
+ end
109
+
99
110
  def duckdb_file_path(database_path)
100
111
  return nil if database_path.nil? || database_path == ":memory:"
101
112
  return "/#{database_path}" if database_path.match?(/^[a-zA-Z]/) && !database_path.start_with?(":")
@@ -199,6 +210,11 @@ module Sequel
199
210
 
200
211
  private
201
212
 
213
+ # DuckDB 2.0 made binder and catalog errors abort an open transaction.
214
+ def bind_errors_abort_transactions?
215
+ ::DuckDB::LIBRARY_VERSION.to_i >= 2
216
+ end
217
+
202
218
  def database_error_classes
203
219
  [ ::DuckDB::Error ]
204
220
  end
@@ -294,19 +294,21 @@ module Sequel
294
294
  #
295
295
  # DuckDB resolves the name itself, as it does for any query, so every Sequel
296
296
  # identifier form, the search_path, temporary tables and views all work. The query
297
- # reads no rows. A missing table is a catalog error, which does not abort an open
298
- # DuckDB transaction, so no savepoint is needed.
297
+ # reads no rows. A missing table is a catalog error. It must not abort an open
298
+ # transaction, and DuckDB has no savepoints, so see keep_transaction_on_bind_error.
299
299
  #
300
300
  # @param table_name [Symbol, String, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier] Name of the table
301
301
  # @param opts [Hash] Options (:schema qualifies an unqualified name)
302
302
  # @return [Boolean] true if table exists
303
303
  def table_exists?(table_name, opts = {})
304
- from(qualify_with_schema_option(table_name, opts)).where(false).select(Sequel::NULL).all
305
- true
306
- rescue Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError
307
- raise
308
- rescue Sequel::DatabaseError
309
- false
304
+ keep_transaction_on_bind_error do
305
+ from(qualify_with_schema_option(table_name, opts)).where(false).select(Sequel::NULL).all
306
+ true
307
+ rescue Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError
308
+ raise
309
+ rescue Sequel::DatabaseError
310
+ false
311
+ end
310
312
  end
311
313
 
312
314
  # Get list of tables
@@ -474,16 +476,18 @@ module Sequel
474
476
  # @return [Hash, nil] :catalog, :schema and :table, or nil if the name does not resolve
475
477
  def resolve_table(table_name, opts = {})
476
478
  table_name = qualify_with_schema_option(table_name, opts)
477
- target = explain_plan(table_name)
478
- return unless target
479
-
480
- candidates = fetch("SELECT table_catalog, table_schema, table_name FROM information_schema.tables " \
481
- "WHERE lower(table_name) = lower(?)", bare_table_name(table_name)).all
482
- matches = candidates.select do |c|
483
- explain_plan(qualified_name(c[:table_catalog], c[:table_schema], c[:table_name])) == target
479
+ keep_transaction_on_bind_error do
480
+ target = explain_plan(table_name)
481
+ next unless target
482
+
483
+ candidates = fetch("SELECT table_catalog, table_schema, table_name FROM information_schema.tables " \
484
+ "WHERE lower(table_name) = lower(?)", bare_table_name(table_name)).all
485
+ matches = candidates.select do |c|
486
+ explain_plan(qualified_name(c[:table_catalog], c[:table_schema], c[:table_name])) == target
487
+ end
488
+ match = matches.size > 1 ? first_in_search_order(matches) : matches.first
489
+ match && { catalog: match[:table_catalog], schema: match[:table_schema], table: match[:table_name] }
484
490
  end
485
- match = matches.size > 1 ? first_in_search_order(matches) : matches.first
486
- match && { catalog: match[:table_catalog], schema: match[:table_schema], table: match[:table_name] }
487
491
  end
488
492
 
489
493
  # resolve_table, but a name that does not resolve raises.
@@ -514,7 +518,7 @@ module Sequel
514
518
  end
515
519
 
516
520
  # The EXPLAIN plan of SELECT * FROM the name, or nil if DuckDB cannot bind the name.
517
- # A bind error does not abort an open DuckDB transaction.
521
+ # Call it inside keep_transaction_on_bind_error.
518
522
  def explain_plan(table_name)
519
523
  plan = []
520
524
  execute("EXPLAIN (FORMAT JSON) #{from(table_name).sql}") { |row| plan << row }
@@ -525,6 +529,32 @@ module Sequel
525
529
  nil
526
530
  end
527
531
 
532
+ # Runs the block so that a binder or catalog error in it, such as a missing table,
533
+ # does not abort an open transaction.
534
+ #
535
+ # DuckDB 1.x never aborts a transaction on those errors. DuckDB 2.x aborts it by
536
+ # default, and its current_transaction_invalidation_policy setting turns that off
537
+ # for the open transaction only. DuckDB 1.x does not know the value, and a failed
538
+ # SET aborts the transaction, so the setting is used only where
539
+ # bind_errors_abort_transactions? is true. The block must handle its own errors:
540
+ # if it raises, the policy is not restored, and the transaction ends anyway.
541
+ def keep_transaction_on_bind_error
542
+ return yield unless bind_errors_abort_transactions? && in_transaction?
543
+
544
+ setting = "current_transaction_invalidation_policy"
545
+ previous = get(Sequel.function(:current_setting, setting))
546
+ run("SET #{setting} = 'SYNTACTIC_ERRORS_DO_NOT_INVALIDATE'")
547
+ result = yield
548
+ run("SET #{setting} = #{literal(previous)}")
549
+ result
550
+ end
551
+
552
+ # True when a binder or catalog error aborts an open transaction. The native adapter
553
+ # answers from the loaded DuckDB library. Other adapters run no queries, so false.
554
+ def bind_errors_abort_transactions?
555
+ false
556
+ end
557
+
528
558
  # DuckDB looks in the temp catalog first, then in the search_path schemas, then in the
529
559
  # default catalog.
530
560
  def first_in_search_order(candidates)
@@ -758,15 +788,11 @@ module Sequel
758
788
  # Remove all cached schema information
759
789
  #
760
790
  # Called after schema modifications to ensure cache consistency. It empties the
761
- # caches in place, because a frozen Database cannot replace them.
791
+ # cache in place, because a frozen Database cannot replace it.
762
792
  #
763
793
  # @return [void]
764
794
  def remove_all_cached_schemas
765
- Sequel.synchronize do
766
- @schemas&.clear
767
- @primary_keys.clear
768
- @primary_key_sequences.clear
769
- end
795
+ Sequel.synchronize { @schemas&.clear }
770
796
  end
771
797
 
772
798
  # List all schemas in the database
@@ -864,6 +890,43 @@ module Sequel
864
890
  sql
865
891
  end
866
892
 
893
+ # Runs a DuckDB ATTACH, which opens the database file at +path+ as the catalog
894
+ # +as+. The catalog is part of the DuckDB database, so every pool connection sees
895
+ # it. It lasts until Database#disconnect closes the last connection; attach again
896
+ # after that. See attach_sql for the options.
897
+ def attach(path, as:, **options)
898
+ run(attach_sql(path, as:, **options))
899
+ end
900
+
901
+ # The ATTACH statement that #attach runs. The alias +as+ is required: an alias
902
+ # that DuckDB derives from the file name can equal a schema name in the file,
903
+ # which makes schema.table ambiguous.
904
+ #
905
+ # @param path [String] The database file; a string literal in the SQL
906
+ # @param as [Symbol, String] The catalog name
907
+ # @param if_not_exists [Boolean] Do nothing if the catalog is already attached
908
+ # @param options [Hash] DuckDB ATTACH options, for example read_only: true or
909
+ # type: :sqlite. A true value renders the bare keyword (READ_ONLY).
910
+ # @return [String]
911
+ def attach_sql(path, as:, if_not_exists: false, **options)
912
+ sql = +"ATTACH "
913
+ sql << "IF NOT EXISTS " if if_not_exists
914
+ sql << "#{literal(path.to_s)} AS #{Sequel::DuckDB::Helpers::SqlOptions.quoted_identifier(as)}"
915
+ list = Sequel::DuckDB::Helpers::SqlOptions.list(options)
916
+ sql << " #{list}" unless list.empty?
917
+ sql
918
+ end
919
+
920
+ # Runs a DuckDB DETACH of the catalog +name+.
921
+ def detach(name, if_exists: false)
922
+ run(detach_sql(name, if_exists:))
923
+ end
924
+
925
+ # The DETACH statement that #detach runs.
926
+ def detach_sql(name, if_exists: false)
927
+ "DETACH DATABASE #{"IF EXISTS " if if_exists}#{Sequel::DuckDB::Helpers::SqlOptions.quoted_identifier(name)}"
928
+ end
929
+
867
930
  # Runs a DuckDB COPY that writes +source+ (a Dataset or a SQL string) to
868
931
  # +path+. For a Dataset this is the SQL from Dataset#copy_to_sql.
869
932
  def copy_to(source, path, options = Sequel::OPTS)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "sql_options"
4
+
3
5
  module Sequel
4
6
  module DuckDB
5
7
  module Helpers
@@ -9,9 +11,6 @@ module Sequel
9
11
  # destination's file extension. A destination with neither (for example a
10
12
  # directory for a partitioned COPY) raises Sequel::Error.
11
13
  class Copier
12
- # Option values that DuckDB accepts as a bare keyword (FORMAT PARQUET).
13
- BARE_WORD = /\A[A-Za-z_][A-Za-z0-9_]*\z/
14
-
15
14
  def initialize(src, dst, options = Sequel::OPTS)
16
15
  @src = src
17
16
  @dst = dst
@@ -31,21 +30,11 @@ module Sequel
31
30
  end
32
31
 
33
32
  def options_str
34
- opts_str = { format: format }.merge(@options).map { |k, v| format_option(k, v) }.compact.join(", ").strip
35
- opts_str.empty? ? "" : "(#{opts_str})"
33
+ SqlOptions.list({ format: format }.merge(@options))
36
34
  end
37
35
 
38
36
  def format_option(key, value)
39
- return "PARTITION_BY (#{Array(value).map { |name| quoted_identifier(name) }.join(", ")})" if key.to_s == "partition_by"
40
-
41
- key = key.to_s.upcase
42
- case value
43
- when true then key
44
- when false then nil
45
- when Integer, Float then "#{key} #{value}"
46
- when BARE_WORD then "#{key} #{value.to_s.upcase}"
47
- else "#{key} #{string_literal(value)}"
48
- end
37
+ SqlOptions.option(key, value)
49
38
  end
50
39
 
51
40
  def format
@@ -61,11 +50,7 @@ module Sequel
61
50
  private
62
51
 
63
52
  def string_literal(value)
64
- "'#{value.to_s.gsub("'", "''")}'"
65
- end
66
-
67
- def quoted_identifier(name)
68
- "\"#{name.to_s.gsub('"', '""')}\""
53
+ SqlOptions.string_literal(value)
69
54
  end
70
55
  end
71
56
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sequel
4
+ module DuckDB
5
+ module Helpers
6
+ # Renders the option list of a DuckDB statement, as in COPY ... (FORMAT PARQUET)
7
+ # and ATTACH ... (READ_ONLY, TYPE SQLITE).
8
+ module SqlOptions
9
+ # Option values that DuckDB accepts as a bare keyword (FORMAT PARQUET).
10
+ BARE_WORD = /\A[A-Za-z_][A-Za-z0-9_]*\z/
11
+
12
+ module_function
13
+
14
+ # "(KEY value, ...)", or "" when no option renders. A true value renders the key
15
+ # alone, and a false value leaves the option out.
16
+ def list(options)
17
+ rendered = options.map { |k, v| option(k, v) }.compact
18
+ rendered.empty? ? "" : "(#{rendered.join(", ")})"
19
+ end
20
+
21
+ def option(key, value)
22
+ return "PARTITION_BY (#{Array(value).map { |name| quoted_identifier(name) }.join(", ")})" if key.to_s == "partition_by"
23
+
24
+ key = key.to_s.upcase
25
+ case value
26
+ when true then key
27
+ when false then nil
28
+ when Integer, Float then "#{key} #{value}"
29
+ when BARE_WORD then "#{key} #{value.to_s.upcase}"
30
+ else "#{key} #{string_literal(value)}"
31
+ end
32
+ end
33
+
34
+ def string_literal(value)
35
+ "'#{value.to_s.gsub("'", "''")}'"
36
+ end
37
+
38
+ def quoted_identifier(name)
39
+ "\"#{name.to_s.gsub('"', '""')}\""
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -11,6 +11,6 @@ module Sequel
11
11
  # puts Sequel::DuckDB::VERSION
12
12
  #
13
13
  # @since 0.1.0
14
- VERSION = "0.4.0"
14
+ VERSION = "0.4.1"
15
15
  end
16
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-duckdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Duryea
@@ -119,42 +119,42 @@ dependencies:
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: 0.40.0
122
+ version: '0.41'
123
123
  type: :development
124
124
  prerelease: false
125
125
  version_requirements: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: 0.40.0
129
+ version: '0.41'
130
130
  - !ruby/object:Gem::Dependency
131
131
  name: rubocop-rake
132
132
  requirement: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: 0.7.1
136
+ version: '0.7'
137
137
  type: :development
138
138
  prerelease: false
139
139
  version_requirements: !ruby/object:Gem::Requirement
140
140
  requirements:
141
141
  - - "~>"
142
142
  - !ruby/object:Gem::Version
143
- version: 0.7.1
143
+ version: '0.7'
144
144
  - !ruby/object:Gem::Dependency
145
145
  name: rubocop-sequel
146
146
  requirement: !ruby/object:Gem::Requirement
147
147
  requirements:
148
148
  - - "~>"
149
149
  - !ruby/object:Gem::Version
150
- version: 0.4.1
150
+ version: '0.4'
151
151
  type: :development
152
152
  prerelease: false
153
153
  version_requirements: !ruby/object:Gem::Requirement
154
154
  requirements:
155
155
  - - "~>"
156
156
  - !ruby/object:Gem::Version
157
- version: 0.4.1
157
+ version: '0.4'
158
158
  - !ruby/object:Gem::Dependency
159
159
  name: simplecov
160
160
  requirement: !ruby/object:Gem::Requirement
@@ -186,6 +186,7 @@ files:
186
186
  - lib/sequel/duckdb.rb
187
187
  - lib/sequel/duckdb/helpers/copier.rb
188
188
  - lib/sequel/duckdb/helpers/pathifier.rb
189
+ - lib/sequel/duckdb/helpers/sql_options.rb
189
190
  - lib/sequel/duckdb/instance_registry.rb
190
191
  - lib/sequel/duckdb/sql_time_tz.rb
191
192
  - lib/sequel/duckdb/version.rb