sequel 5.54.0 → 5.57.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3baaadc192f621e371699d654e998661db242d11385b328e5298ebfa7ce97d53
4
- data.tar.gz: b6595670122d1ec5bbaccabef8d3af58a08f4d6edb6ffa782e34a1152d971143
3
+ metadata.gz: 5ab346b95d558c843b61291c10b7033a50fa3e68b1b6d76b16c36f35eea7669a
4
+ data.tar.gz: eb9acbdab39df252a1e1dd6e66c82a6b13a61544d04d709e46592310ada6f693
5
5
  SHA512:
6
- metadata.gz: ebf7ff5a43c42d769a1f35e7c90f32c24e7776cde36c17c1800a4603b8173066bb90864b0644dfb6402221e1ed37ea36a9b026e6ab07e3086ed208eb6fb58d67
7
- data.tar.gz: 82b525b77248038a08e2b247ae8de8028a1d4f67f332b267bd81f9d4087ebd900fcb58b1a7b9b82b20e3b93a00dd3a665d03dbe583ba94866c43663993c7436b
6
+ metadata.gz: 925cdbf7f82dcd9c98fadbaeba92e4b4f36a424e81bde6593a0dc9569d693dc16cc6cb901fe32d0839389348aa3870874589489db4ea4f70eb81e6b2d4e06de4
7
+ data.tar.gz: 270e046d9c90956dfb7e81d1e17c5baeba6feb563f8aaf66d86494697e5b59e027375b72e11b24d1eb23709b9e2272c2ed53430c4c4bfc1af2957bfba7e64416
data/CHANGELOG CHANGED
@@ -1,3 +1,29 @@
1
+ === 5.57.0 (2022-06-01)
2
+
3
+ * Make Database#create_function on PostgreSQL accept :parallel option (bananarne) (#1870)
4
+
5
+ * Add support for :on_update_current_timestamp column option on MySQL (jeremyevans)
6
+
7
+ * Add is_distinct_from extension with support for the IS DISTINCT FROM operator (jeremyevans)
8
+
9
+ === 5.56.0 (2022-05-01)
10
+
11
+ * Make alter_table add_column/add_foreign_key methods support :index option to create an index on the column (jeremyevans)
12
+
13
+ * Support creation of STRICT tables on SQLite 3.37.0+ via create_table :strict option (jeremyevans)
14
+
15
+ * Add sqlite_json_ops extension for DSL support for JSON functions and operators added in SQLite 3.38.0 (jeremyevans)
16
+
17
+ * Recognize "INTEGER" type same as "integer" type in the schema dumper, helpful on SQLite 3.37.0+ (jeremyevans)
18
+
19
+ === 5.55.0 (2022-04-01)
20
+
21
+ * Support :setup_regexp_function Database option in the sqlite adapter to allow the use of regexps when querying (jeremyevans)
22
+
23
+ * Add auto_restrict_eager_graph plugin for automatically disallow eager_graph with associations needing but lacking graph options (jeremyevans)
24
+
25
+ * Fix placeholder literalizer optimization for dataset aggregate methods on a model dataset (belousovAV) (#1847, #1848)
26
+
1
27
  === 5.54.0 (2022-03-01)
2
28
 
3
29
  * Add enum plugin for treating columns as enums in a model (jeremyevans) (#1839)
@@ -383,6 +383,9 @@ The following additional options are supported:
383
383
 
384
384
  :readonly :: open database in read-only mode
385
385
  :timeout :: the busy timeout to use in milliseconds (default: 5000).
386
+ :setup_regexp_function :: Whether to setup a REGEXP function in the underlying SQLite3::Database object. Doing so
387
+ allows you to use regexp support in dataset expressions. Note that this creates a new
388
+ Regexp object per call to the function, so it is not an efficient implementation.
386
389
 
387
390
  Note that SQLite memory databases are restricted to a single connection by
388
391
  default. This is because SQLite does not allow multiple connections to
data/doc/querying.rdoc CHANGED
@@ -357,7 +357,9 @@ For ranges, Sequel uses a pair of inequality statements:
357
357
  # SELECT * FROM artists WHERE ((id >= 1) AND (id < 5))
358
358
 
359
359
  Finally, for regexps, Sequel uses an SQL regular expression. Note that this
360
- is probably only supported on PostgreSQL and MySQL.
360
+ is only supported by default on PostgreSQL and MySQL. It can also be supported
361
+ on SQLite when using the sqlite adapter with the :setup_regexp_function
362
+ Database option.
361
363
 
362
364
  Artist.where(name: /JM$/)
363
365
  # SELECT * FROM artists WHERE (name ~ 'JM$')
@@ -0,0 +1,21 @@
1
+ = New Features
2
+
3
+ * An auto_restrict_eager_graph plugin has been added for automatically
4
+ disallowing the use of eager_graph with associations using blocks but
5
+ lacking graph_* options. This can prevent potentionally invalid usage,
6
+ as the restrictions added by the block are not used by eager_graph.
7
+
8
+ * The sqlite adapter now supports the :setup_regexp_function
9
+ Database option. This option will define a REGEXP function in the
10
+ database that will allow regexp support in queries, such as:
11
+
12
+ DB[:table].where(column: /(some|pattern)/)
13
+
14
+ Note that this creates a Ruby Regexp object per column value tested,
15
+ so it isn't the most optimal approach.
16
+
17
+ = Other Improvements
18
+
19
+ * Calling dataset aggregate methods such as #max on a model dataset now
20
+ works correctly. Previously, it could fail if called enough times to
21
+ optimize using a placeholder literalizer.
@@ -0,0 +1,51 @@
1
+ = New Features
2
+
3
+ * On SQLite, Database#create_table now supports a :strict option to
4
+ use the STRICT keyword when creating the table. When this option
5
+ is used, SQLite will enforce the types for each column. When using
6
+ this option, you are limited to using the following column types:
7
+ int, integer, real, text, blob, and any (any allows for dynamic
8
+ types).
9
+
10
+ * An sqlite_json_ops extension has been added, providing DSL support
11
+ for JSON functions and operators supported in SQLite 3.38.0. Usage
12
+ is similar to the pg_json_ops extension. First, you create an
13
+ appropriate object:
14
+
15
+ j = Sequel.sqlite_json_op(:json_column)
16
+ # or:
17
+ j = Sequel[:json_column].sqlite_json_op
18
+
19
+ Then, you call methods on that object to create expressions for the
20
+ JSON functions and operators:
21
+
22
+ j[1] # (json_column ->> 1)
23
+ j.get_text(1) # (json_column -> 1)
24
+ j.extract('$.a') # json_extract(json_column, '$.a')
25
+
26
+ j.array_length # json_array_length(json_column)
27
+ j.type # json_type(json_column)
28
+ j.valid # json_valid(json_column)
29
+ j.json # json(json_column)
30
+
31
+ j.insert('$.a', 1) # json_insert(json_column, '$.a', 1)
32
+ j.set('$.a', 1) # json_set(json_column, '$.a', 1)
33
+ j.replace('$.a', 1) # json_replace(json_column, '$.a', 1)
34
+ j.remove('$.a') # json_remove(json_column, '$.a')
35
+ j.patch('{"a":2}') # json_patch(json_column, '{"a":2}')
36
+
37
+ j.each # json_each(json_column)
38
+ j.tree # json_tree(json_column)
39
+
40
+ = Other Improvements
41
+
42
+ * The alter_table add_column and add_foreign_key methods now support
43
+ the :index option to create an index on the added column, for
44
+ compatibility with the :index option on the create_table column and
45
+ foreign_key methods.
46
+
47
+ * The schema_dumper extension now treats the "INTEGER" type the same
48
+ as the "integer" type. This fixes some behavior when using SQLite
49
+ 3.37.0+.
50
+
51
+ * Sequel's website has a much improved visual design.
@@ -0,0 +1,23 @@
1
+ = New Features
2
+
3
+ * An is_distinct_from extension has been added with support for the
4
+ SQL IS DISTINCT FROM operator. This operator is similar to the
5
+ not equals operator, except in terms of NULL handling. It returns
6
+ true if only one side is NULL, and false if both sides are NULL.
7
+ You can call is_distinct_from on Sequel itself or on Sequel objects:
8
+
9
+ Sequel.is_distinct_from(:column_a, :column_b)
10
+ Sequel[:column_a].is_distinct_from(:column_b)
11
+ # (column_a IS DISTINCT FROM column_b)
12
+
13
+ On databases not supporting IS DISTINCT FROM, support is emulated
14
+ using a CASE statement.
15
+
16
+ * Column definitions on MySQL can use the :on_update_current_timestamp
17
+ option for ON UPDATE CURRENT_TIMESTAMP, which creates a column that
18
+ will automatically have its value set to CURRENT_TIMESTAMP on every
19
+ update.
20
+
21
+ * Database#create_function on PostgreSQL now supports a :parallel
22
+ option to set the thread safety of the funciton. The value should
23
+ be :safe, :unsafe, or :restricted.
data/doc/sql.rdoc CHANGED
@@ -528,7 +528,7 @@ Inverting the LIKE operator works like other inversions:
528
528
 
529
529
  ~Sequel.like(:name, 'A%') # ("name" NOT LIKE 'A%' ESCAPE '\')
530
530
 
531
- Sequel also supports SQL regular expressions on MySQL and PostgreSQL. You can use these by passing a Ruby regular expression to +like+ or +ilike+, or by making the regular expression a hash value:
531
+ Sequel also supports SQL regular expressions on MySQL and PostgreSQL (and SQLite when using the sqlite adapter with the :setup_regexp_function Database option). You can use these by passing a Ruby regular expression to +like+ or +ilike+, or by making the regular expression a hash value:
532
532
 
533
533
  Sequel.like(:name, /^A/) # ("name" ~ '^A')
534
534
  ~Sequel.ilike(:name, /^A/) # ("name" !~* '^A')
@@ -356,6 +356,12 @@ module Sequel
356
356
  end
357
357
  end
358
358
 
359
+ # Support :on_update_current_timestamp option.
360
+ def column_definition_default_sql(sql, column)
361
+ super
362
+ sql << " ON UPDATE CURRENT_TIMESTAMP" if column[:on_update_current_timestamp]
363
+ end
364
+
359
365
  # Add generation clause SQL fragment to column creation SQL.
360
366
  def column_definition_generated_sql(sql, column)
361
367
  if (generated_expression = column[:generated_always_as])
@@ -414,6 +414,7 @@ module Sequel
414
414
  # 2 :: argument name
415
415
  # 3 :: argument mode (e.g. in, out, inout)
416
416
  # :behavior :: Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
417
+ # :parallel :: The thread safety attribute of the function. Should be SAFE, UNSAFE, RESTRICTED. PostgreSQL assumes UNSAFE by default.
417
418
  # :cost :: The estimated cost of the function, used by the query planner.
418
419
  # :language :: The language the function uses. SQL is the default.
419
420
  # :link_symbol :: For a dynamically loaded see function, the function's link symbol if different from the definition argument.
@@ -1117,6 +1118,7 @@ module Sequel
1117
1118
  #{opts[:behavior].to_s.upcase if opts[:behavior]}
1118
1119
  #{'STRICT' if opts[:strict]}
1119
1120
  #{'SECURITY DEFINER' if opts[:security_definer]}
1121
+ #{"PARALLEL #{opts[:parallel].to_s.upcase}" if opts[:parallel]}
1120
1122
  #{"COST #{opts[:cost]}" if opts[:cost]}
1121
1123
  #{"ROWS #{opts[:rows]}" if opts[:rows]}
1122
1124
  #{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
@@ -337,6 +337,11 @@ module Sequel
337
337
  ps
338
338
  end
339
339
 
340
+ # Support creating STRICT tables via :strict option
341
+ def create_table_sql(name, generator, options)
342
+ "#{super}#{' STRICT' if options[:strict]}"
343
+ end
344
+
340
345
  # SQLite support creating temporary views.
341
346
  def create_view_prefix_sql(name, options)
342
347
  create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
@@ -347,6 +352,7 @@ module Sequel
347
352
  /foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
348
353
  /\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
349
354
  /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
355
+ /\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
350
356
  /may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
351
357
  /\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
352
358
  }.freeze
@@ -98,6 +98,11 @@ module Sequel
98
98
  # The conversion procs to use for this database
99
99
  attr_reader :conversion_procs
100
100
 
101
+ def initialize(opts = OPTS)
102
+ super
103
+ @allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
104
+ end
105
+
101
106
  # Connect to the database. Since SQLite is a file based database,
102
107
  # available options are limited:
103
108
  #
@@ -119,6 +124,12 @@ module Sequel
119
124
  end
120
125
 
121
126
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
127
+
128
+ if typecast_value_boolean(opts[:setup_regexp_function])
129
+ db.create_function("regexp", 2) do |func, regexp_str, string|
130
+ func.result = Regexp.new(regexp_str).match(string) ? 1 : 0
131
+ end
132
+ end
122
133
 
123
134
  class << db
124
135
  attr_reader :prepared_statements
@@ -128,6 +139,12 @@ module Sequel
128
139
  db
129
140
  end
130
141
 
142
+ # Whether this Database instance is setup to allow regexp matching.
143
+ # True if the :setup_regexp_function option was passed when creating the Database.
144
+ def allow_regexp?
145
+ @allow_regexp
146
+ end
147
+
131
148
  # Disconnect given connections from the database.
132
149
  def disconnect_connection(c)
133
150
  c.prepared_statements.each_value{|v| v.first.close}
@@ -321,6 +338,28 @@ module Sequel
321
338
  BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
322
339
  PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
323
340
 
341
+ # Support regexp functions if using :setup_regexp_function Database option.
342
+ def complex_expression_sql_append(sql, op, args)
343
+ case op
344
+ when :~, :'!~', :'~*', :'!~*'
345
+ return super unless supports_regexp?
346
+
347
+ case_insensitive = [:'~*', :'!~*'].include?(op)
348
+ sql << 'NOT ' if [:'!~', :'!~*'].include?(op)
349
+ sql << '('
350
+ sql << 'LOWER(' if case_insensitive
351
+ literal_append(sql, args[0])
352
+ sql << ')' if case_insensitive
353
+ sql << ' REGEXP '
354
+ sql << 'LOWER(' if case_insensitive
355
+ literal_append(sql, args[1])
356
+ sql << ')' if case_insensitive
357
+ sql << ')'
358
+ else
359
+ super
360
+ end
361
+ end
362
+
324
363
  def fetch_rows(sql)
325
364
  execute(sql) do |result|
326
365
  cps = db.conversion_procs
@@ -344,6 +383,11 @@ module Sequel
344
383
  end
345
384
  end
346
385
  end
386
+
387
+ # Support regexp if using :setup_regexp_function Database option.
388
+ def supports_regexp?
389
+ db.allow_regexp?
390
+ end
347
391
 
348
392
  private
349
393
 
@@ -146,6 +146,9 @@ module Sequel
146
146
  #
147
147
  # :generated_type :: Set the type of column when using :generated_always_as,
148
148
  # should be :virtual or :stored to force a type.
149
+ # :on_update_current_timestamp :: Use ON UPDATE CURRENT TIMESTAMP when defining the column,
150
+ # which will update the column value to CURRENT_TIMESTAMP
151
+ # on every UPDATE.
149
152
  #
150
153
  # Microsoft SQL Server specific options:
151
154
  #
@@ -387,8 +390,7 @@ module Sequel
387
390
  end
388
391
 
389
392
  # Add a column with the given name, type, and opts.
390
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
391
- # separate +add_index+ call to add an index for the column).
393
+ # See CreateTableGenerator#column for the available options.
392
394
  #
393
395
  # add_column(:name, String) # ADD COLUMN name varchar(255)
394
396
  #
@@ -401,7 +403,10 @@ module Sequel
401
403
  # :after :: The name of an existing column that the new column should be positioned after
402
404
  # :first :: Create this new column before all other existing columns
403
405
  def add_column(name, type, opts = OPTS)
404
- @operations << {:op => :add_column, :name => name, :type => type}.merge!(opts)
406
+ op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
407
+ index_opts = op.delete(:index)
408
+ @operations << op
409
+ add_index(name, index_opts.is_a?(Hash) ? index_opts : OPTS) if index_opts
405
410
  nil
406
411
  end
407
412
 
@@ -430,8 +435,7 @@ module Sequel
430
435
  end
431
436
 
432
437
  # Add a foreign key with the given name and referencing the given table.
433
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
434
- # separate +add_index+ call to add an index for the column).
438
+ # See CreateTableGenerator#column for the available options.
435
439
  #
436
440
  # You can also pass an array of column names for creating composite foreign
437
441
  # keys. In this case, it will assume the columns exist and will only add
@@ -183,6 +183,15 @@ module Sequel
183
183
  # keys.
184
184
  # :tablespace :: The tablespace to use for the table.
185
185
  #
186
+ # SQLite specific options:
187
+ # :strict :: Create a STRICT table, which checks that the values for the columns
188
+ # are the correct type (similar to all other SQL databases). Note that
189
+ # when using this option, all column types used should be one of the
190
+ # following: +int+, +integer+, +real+, +text+, +blob+, and +any+.
191
+ # The +any+ type is treated like a SQLite column in a non-strict table,
192
+ # allowing any type of data to be stored. This option is supported on
193
+ # SQLite 3.37.0+.
194
+ #
186
195
  # See <tt>Schema::CreateTableGenerator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
187
196
  def create_table(name, options=OPTS, &block)
188
197
  remove_cached_schema(name)
@@ -894,9 +894,10 @@ module Sequel
894
894
  # Clone of this dataset usable in aggregate operations. Does
895
895
  # a from_self if dataset contains any parameters that would
896
896
  # affect normal aggregation, or just removes an existing
897
- # order if not.
897
+ # order if not. Also removes the row_proc, which isn't needed
898
+ # for aggregate calculations.
898
899
  def aggregate_dataset
899
- options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
900
+ (options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered).naked
900
901
  end
901
902
 
902
903
  # Append aliasing expression to SQL string.
@@ -0,0 +1,139 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The is_distinct_from extension adds the ability to use the
4
+ # SQL standard IS DISTINCT FROM operator, which is similar to the
5
+ # not equals operator, except that NULL values are considered
6
+ # equal. Only PostgreSQL and H2 currently support this operator. On
7
+ # other databases, support is emulated.
8
+ #
9
+ # First, you need to load the extension into the database:
10
+ #
11
+ # DB.extension :is_distinct_from
12
+ #
13
+ # Then you can use the Sequel.is_distinct_from to create the expression
14
+ # objects:
15
+ #
16
+ # expr = Sequel.is_distinct_from(:column_a, :column_b)
17
+ # # (column_a IS DISTINCT FROM column_b)
18
+ #
19
+ # You can also use the +is_distinct_from+ method on most Sequel expressions:
20
+ #
21
+ # expr = Sequel[:column_a].is_distinct_from(:column_b)
22
+ # # (column_a IS DISTINCT FROM column_b)
23
+ #
24
+ # These expressions can be used in your datasets, or anywhere else that
25
+ # Sequel expressions are allowed:
26
+ #
27
+ # DB[:table].where(expr)
28
+ #
29
+ # Related module: Sequel::SQL::IsDistinctFrom
30
+
31
+ #
32
+ module Sequel
33
+ module SQL
34
+ module Builders
35
+ # Return a IsDistinctFrom expression object, using the IS DISTINCT FROM operator
36
+ # with the given left hand side and right hand side.
37
+ def is_distinct_from(lhs, rhs)
38
+ BooleanExpression.new(:NOOP, IsDistinctFrom.new(lhs, rhs))
39
+ end
40
+ end
41
+
42
+ # Represents an SQL expression using the IS DISTINCT FROM operator.
43
+ class IsDistinctFrom < GenericExpression
44
+ # These methods are added to expressions, allowing them to return IS DISTINCT
45
+ # FROM expressions based on the receiving expression.
46
+ module Methods
47
+ # Return a IsDistinctFrom expression, using the IS DISTINCT FROM operator,
48
+ # with the receiver as the left hand side and the argument as the right hand side.
49
+ def is_distinct_from(rhs)
50
+ BooleanExpression.new(:NOOP, IsDistinctFrom.new(self, rhs))
51
+ end
52
+ end
53
+
54
+ # These methods are added to datasets using the is_distinct_from extension
55
+ # extension, for the purposes of correctly literalizing IsDistinctFrom
56
+ # expressions for the appropriate database type.
57
+ module DatasetMethods
58
+ # Append the SQL fragment for the IS DISTINCT FROM expression to the SQL query.
59
+ def is_distinct_from_sql_append(sql, idf)
60
+ lhs = idf.lhs
61
+ rhs = idf.rhs
62
+
63
+ if supports_is_distinct_from?
64
+ sql << "("
65
+ literal_append(sql, lhs)
66
+ sql << " IS DISTINCT FROM "
67
+ literal_append(sql, rhs)
68
+ sql << ")"
69
+ elsif db.database_type == :derby && (lhs == nil || rhs == nil)
70
+ if lhs == nil && rhs == nil
71
+ sql << literal_false
72
+ elsif lhs == nil
73
+ literal_append(sql, ~Sequel.expr(rhs=>nil))
74
+ else
75
+ literal_append(sql, ~Sequel.expr(lhs=>nil))
76
+ end
77
+ else
78
+ literal_append(sql, Sequel.case({(Sequel.expr(lhs=>rhs) | [[lhs, nil], [rhs, nil]]) => 0}, 1) => 1)
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ # Whether the database supports IS DISTINCT FROM.
85
+ def supports_is_distinct_from?
86
+ if defined?(super)
87
+ return super
88
+ end
89
+
90
+ case db.database_type
91
+ when :postgres, :h2
92
+ true
93
+ else
94
+ false
95
+ end
96
+ end
97
+ end
98
+
99
+ # The left hand side of the IS DISTINCT FROM expression.
100
+ attr_reader :lhs
101
+
102
+ # The right hand side of the IS DISTINCT FROM expression.
103
+ attr_reader :rhs
104
+
105
+ def initialize(lhs, rhs)
106
+ @lhs = lhs
107
+ @rhs = rhs
108
+ end
109
+
110
+ to_s_method :is_distinct_from_sql
111
+ end
112
+ end
113
+
114
+ class SQL::GenericExpression
115
+ include SQL::IsDistinctFrom::Methods
116
+ end
117
+
118
+ class LiteralString
119
+ include SQL::IsDistinctFrom::Methods
120
+ end
121
+
122
+ Dataset.register_extension(:is_distinct_from, SQL::IsDistinctFrom::DatasetMethods)
123
+ end
124
+
125
+ # :nocov:
126
+ if Sequel.core_extensions?
127
+ class Symbol
128
+ include Sequel::SQL::IsDistinctFrom::Methods
129
+ end
130
+ end
131
+
132
+ if defined?(Sequel::CoreRefinements)
133
+ module Sequel::CoreRefinements
134
+ refine Symbol do
135
+ send INCLUDE_METH, Sequel::SQL::IsDistinctFrom::Methods
136
+ end
137
+ end
138
+ end
139
+ # :nocov:
@@ -3,7 +3,8 @@
3
3
  # The pg_json_ops extension adds support to Sequel's DSL to make
4
4
  # it easier to call PostgreSQL JSON functions and operators (added
5
5
  # first in PostgreSQL 9.3). It also supports the JSONB functions
6
- # and operators added in PostgreSQL 9.4.
6
+ # and operators added in PostgreSQL 9.4, as well as additional
7
+ # functions and operators added in later versions.
7
8
  #
8
9
  # To load the extension:
9
10
  #
@@ -183,7 +183,7 @@ END_MIG
183
183
  if options[:single_pk] && schema_autoincrementing_primary_key?(schema)
184
184
  type_hash = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
185
185
  [:table, :key, :on_delete, :on_update, :deferrable].each{|f| type_hash[f] = schema[f] if schema[f]}
186
- if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"}
186
+ if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"} || type_hash == {:type=>"INTEGER"}
187
187
  type_hash.delete(:type)
188
188
  elsif options[:same_db] && type_hash == {:type=>type_literal_generic_bignum_symbol(type_hash).to_s}
189
189
  type_hash[:type] = :Bignum
@@ -225,7 +225,7 @@ END_MIG
225
225
  col_opts[:null] = false if schema[:allow_null] == false
226
226
  if table = schema[:table]
227
227
  [:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
228
- col_opts[:type] = type unless type == Integer || type == 'integer'
228
+ col_opts[:type] = type unless type == Integer || type == 'integer' || type == 'INTEGER'
229
229
  gen.foreign_key(name, table, col_opts)
230
230
  else
231
231
  gen.column(name, type, col_opts)
@@ -0,0 +1,255 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The sqlite_json_ops extension adds support to Sequel's DSL to make
4
+ # it easier to call SQLite JSON functions and operators (added
5
+ # first in SQLite 3.38.0).
6
+ #
7
+ # To load the extension:
8
+ #
9
+ # Sequel.extension :sqlite_json_ops
10
+ #
11
+ # This extension works by calling methods on Sequel::SQLite::JSONOp objects,
12
+ # which you can create via Sequel.sqlite_json_op:
13
+ #
14
+ # j = Sequel.sqlite_json_op(:json_column)
15
+ #
16
+ # Also, on most Sequel expression objects, you can call the sqlite_json_op method
17
+ # to create a Sequel::SQLite::JSONOp object:
18
+ #
19
+ # j = Sequel[:json_column].sqlite_json_op
20
+ #
21
+ # If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
22
+ # or you have loaded the core_refinements extension
23
+ # and have activated refinements for the file, you can also use Symbol#sqlite_json_op:
24
+ #
25
+ # j = :json_column.sqlite_json_op
26
+ #
27
+ # The following methods are available for Sequel::SQLite::JSONOp instances:
28
+ #
29
+ # j[1] # (json_column ->> 1)
30
+ # j.get(1) # (json_column ->> 1)
31
+ # j.get_text(1) # (json_column -> 1)
32
+ # j.extract('$.a') # json_extract(json_column, '$.a')
33
+ #
34
+ # j.array_length # json_array_length(json_column)
35
+ # j.type # json_type(json_column)
36
+ # j.valid # json_valid(json_column)
37
+ # j.json # json(json_column)
38
+ #
39
+ # j.insert('$.a', 1) # json_insert(json_column, '$.a', 1)
40
+ # j.set('$.a', 1) # json_set(json_column, '$.a', 1)
41
+ # j.replace('$.a', 1) # json_replace(json_column, '$.a', 1)
42
+ # j.remove('$.a') # json_remove(json_column, '$.a')
43
+ # j.patch('{"a":2}') # json_patch(json_column, '{"a":2}')
44
+ #
45
+ # j.each # json_each(json_column)
46
+ # j.tree # json_tree(json_column)
47
+ #
48
+ # Related modules: Sequel::SQLite::JSONOp
49
+
50
+ #
51
+ module Sequel
52
+ module SQLite
53
+ # The JSONOp class is a simple container for a single object that
54
+ # defines methods that yield Sequel expression objects representing
55
+ # SQLite json operators and functions.
56
+ #
57
+ # In the method documentation examples, assume that:
58
+ #
59
+ # json_op = Sequel.sqlite_json_op(:json)
60
+ class JSONOp < Sequel::SQL::Wrapper
61
+ GET = ["(".freeze, " ->> ".freeze, ")".freeze].freeze
62
+ private_constant :GET
63
+
64
+ GET_JSON = ["(".freeze, " -> ".freeze, ")".freeze].freeze
65
+ private_constant :GET_JSON
66
+
67
+ # Returns an expression for getting the JSON array element or object field
68
+ # at the specified path as a SQLite value.
69
+ #
70
+ # json_op[1] # (json ->> 1)
71
+ # json_op['a'] # (json ->> 'a')
72
+ # json_op['$.a.b'] # (json ->> '$.a.b')
73
+ # json_op['$[1][2]'] # (json ->> '$[1][2]')
74
+ def [](key)
75
+ json_op(GET, key)
76
+ end
77
+ alias get []
78
+
79
+ # Returns an expression for the length of the JSON array, or the JSON array at
80
+ # the given path.
81
+ #
82
+ # json_op.array_length # json_array_length(json)
83
+ # json_op.array_length('$[1]') # json_array_length(json, '$[1]')
84
+ def array_length(*args)
85
+ Sequel::SQL::NumericExpression.new(:NOOP, function(:array_length, *args))
86
+ end
87
+
88
+ # Returns an expression for a set of information extracted from the top-level
89
+ # members of the JSON array or object, or the top-level members of the JSON array
90
+ # or object at the given path.
91
+ #
92
+ # json_op.each # json_each(json)
93
+ # json_op.each('$.a') # json_each(json, '$.a')
94
+ def each(*args)
95
+ function(:each, *args)
96
+ end
97
+
98
+ # Returns an expression for the JSON array element or object field at the specified
99
+ # path as a SQLite value, but only accept paths as arguments, and allow the use of
100
+ # multiple paths.
101
+ #
102
+ # json_op.extract('$.a') # json_extract(json, '$.a')
103
+ # json_op.extract('$.a', '$.b') # json_extract(json, '$.a', '$.b')
104
+ def extract(*a)
105
+ function(:extract, *a)
106
+ end
107
+
108
+ # Returns an expression for getting the JSON array element or object field at the
109
+ # specified path as a JSON value.
110
+ #
111
+ # json_op.get_json(1) # (json -> 1)
112
+ # json_op.get_json('a') # (json -> 'a')
113
+ # json_op.get_json('$.a.b') # (json -> '$.a.b')
114
+ # json_op.get_json('$[1][2]') # (json -> '$[1][2]')
115
+ def get_json(key)
116
+ self.class.new(json_op(GET_JSON, key))
117
+ end
118
+
119
+ # Returns an expression for creating new entries at the given paths in the JSON array
120
+ # or object, but not overwriting existing entries.
121
+ #
122
+ # json_op.insert('$.a', 1) # json_insert(json, '$.a', 1)
123
+ # json_op.insert('$.a', 1, '$.b', 2) # json_insert(json, '$.a', 1, '$.b', 2)
124
+ def insert(path, value, *args)
125
+ wrapped_function(:insert, path, value, *args)
126
+ end
127
+
128
+ # Returns an expression for a minified version of the JSON.
129
+ #
130
+ # json_op.json # json(json)
131
+ def json
132
+ self.class.new(SQL::Function.new(:json, self))
133
+ end
134
+ alias minify json
135
+
136
+ # Returns an expression for updating the JSON object using the RFC 7396 MergePatch algorithm
137
+ #
138
+ # json_op.patch('{"a": 1, "b": null}') # json_patch(json, '{"a": 1, "b": null}')
139
+ def patch(json_patch)
140
+ wrapped_function(:patch, json_patch)
141
+ end
142
+
143
+ # Returns an expression for removing entries at the given paths from the JSON array or object.
144
+ #
145
+ # json_op.remove('$.a') # json_remove(json, '$.a')
146
+ # json_op.remove('$.a', '$.b') # json_remove(json, '$.a', '$.b')
147
+ def remove(path, *paths)
148
+ wrapped_function(:remove, path, *paths)
149
+ end
150
+
151
+ # Returns an expression for replacing entries at the given paths in the JSON array or object,
152
+ # but not creating new entries.
153
+ #
154
+ # json_op.replace('$.a', 1) # json_replace(json, '$.a', 1)
155
+ # json_op.replace('$.a', 1, '$.b', 2) # json_replace(json, '$.a', 1, '$.b', 2)
156
+ def replace(path, value, *args)
157
+ wrapped_function(:replace, path, value, *args)
158
+ end
159
+
160
+ # Returns an expression for creating or replacing entries at the given paths in the
161
+ # JSON array or object.
162
+ #
163
+ # json_op.set('$.a', 1) # json_set(json, '$.a', 1)
164
+ # json_op.set('$.a', 1, '$.b', 2) # json_set(json, '$.a', 1, '$.b', 2)
165
+ def set(path, value, *args)
166
+ wrapped_function(:set, path, value, *args)
167
+ end
168
+
169
+ # Returns an expression for a set of information extracted from the JSON array or object, or
170
+ # the JSON array or object at the given path.
171
+ #
172
+ # json_op.tree # json_tree(json)
173
+ # json_op.tree('$.a') # json_tree(json, '$.a')
174
+ def tree(*args)
175
+ function(:tree, *args)
176
+ end
177
+
178
+ # Returns an expression for the type of the JSON value or the JSON value at the given path.
179
+ #
180
+ # json_op.type # json_type(json)
181
+ # json_op.type('$[1]') # json_type(json, '$[1]')
182
+ def type(*args)
183
+ Sequel::SQL::StringExpression.new(:NOOP, function(:type, *args))
184
+ end
185
+ alias typeof type
186
+
187
+ # Returns a boolean expression for whether the JSON is valid or not.
188
+ def valid
189
+ Sequel::SQL::BooleanExpression.new(:NOOP, function(:valid))
190
+ end
191
+
192
+ private
193
+
194
+ # Internals of the [], get, get_json methods, using a placeholder literal string.
195
+ def json_op(str, args)
196
+ self.class.new(Sequel::SQL::PlaceholderLiteralString.new(str, [self, args]))
197
+ end
198
+
199
+ # Internals of the methods that return functions prefixed with +json_+.
200
+ def function(name, *args)
201
+ SQL::Function.new("json_#{name}", self, *args)
202
+ end
203
+
204
+ # Internals of the methods that return functions prefixed with +json_+, that
205
+ # return JSON values.
206
+ def wrapped_function(*args)
207
+ self.class.new(function(*args))
208
+ end
209
+ end
210
+
211
+ module JSONOpMethods
212
+ # Wrap the receiver in an JSONOp so you can easily use the SQLite
213
+ # json functions and operators with it.
214
+ def sqlite_json_op
215
+ JSONOp.new(self)
216
+ end
217
+ end
218
+ end
219
+
220
+ module SQL::Builders
221
+ # Return the object wrapped in an SQLite::JSONOp.
222
+ def sqlite_json_op(v)
223
+ case v
224
+ when SQLite::JSONOp
225
+ v
226
+ else
227
+ SQLite::JSONOp.new(v)
228
+ end
229
+ end
230
+ end
231
+
232
+ class SQL::GenericExpression
233
+ include Sequel::SQLite::JSONOpMethods
234
+ end
235
+
236
+ class LiteralString
237
+ include Sequel::SQLite::JSONOpMethods
238
+ end
239
+ end
240
+
241
+ # :nocov:
242
+ if Sequel.core_extensions?
243
+ class Symbol
244
+ include Sequel::SQLite::JSONOpMethods
245
+ end
246
+ end
247
+
248
+ if defined?(Sequel::CoreRefinements)
249
+ module Sequel::CoreRefinements
250
+ refine Symbol do
251
+ send INCLUDE_METH, Sequel::SQLite::JSONOpMethods
252
+ end
253
+ end
254
+ end
255
+ # :nocov:
@@ -0,0 +1,62 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The auto_restrict_eager_graph plugin will automatically disallow the use
6
+ # of eager_graph for associations that have associated blocks but no :graph_*
7
+ # association options. The reason for this is the block will have an effect
8
+ # during regular and eager loading, but not loading via eager_graph, and it
9
+ # is likely that whatever the block is doing should have an equivalent done
10
+ # when eager_graphing. Most likely, not including a :graph_* option was either
11
+ # an oversight (and one should be added), or use with eager_graph was never
12
+ # intended (and usage should be forbidden). Disallowing eager_graph in this
13
+ # case prevents likely unexpected behavior during eager_graph.
14
+ #
15
+ # As an example of this, consider the following code:
16
+ #
17
+ # Album.one_to_many :popular_tracks, class: :Track do |ds|
18
+ # ds = ds.where(popular: true)
19
+ # end
20
+ #
21
+ # Album.eager(:popular_tracks).all
22
+ # # SELECT * FROM albums
23
+ # # SELECT * FROM tracks WHERE ((popular IS TRUE) AND (album_id IN (...)))
24
+ #
25
+ # # Notice that no condition for tracks.popular is added.
26
+ # Album.eager_graph(:popular_tracks).all
27
+ # # SELECT ... FROM albums LEFT JOIN tracks ON (tracks.album_id = albums.id)
28
+ #
29
+ # With the auto_restrict_eager_graph plugin, the eager_graph call above will
30
+ # raise an error, alerting you to the fact that you either should not be
31
+ # using eager_graph with the association, or that you should be adding an
32
+ # appropriate :graph_* option, such as:
33
+ #
34
+ # Album.one_to_many :popular_tracks, class: :Track, graph_conditions: {popular: true} do |ds|
35
+ # ds = ds.where(popular: true)
36
+ # end
37
+ #
38
+ # Usage:
39
+ #
40
+ # # Automatically restrict eager_graph for associations if appropriate for all
41
+ # # model subclasses (called before loading subclasses)
42
+ # Sequel::Model.plugin :auto_restrict_eager_graph
43
+ #
44
+ # # Automatically restrict eager_graph for associations in Album class
45
+ # Album.plugin :auto_restrict_eager_graph
46
+ module AutoRestrictEagerGraph
47
+ module ClassMethods
48
+ # When defining an association, if a block is given for the association, but
49
+ # a :graph_* option is not used, disallow the use of eager_graph.
50
+ def associate(type, name, opts = OPTS, &block)
51
+ opts = super
52
+
53
+ if opts[:block] && !opts.has_key?(:allow_eager_graph) && !opts[:orig_opts].any?{|k,| /\Agraph_/ =~ k}
54
+ opts[:allow_eager_graph] = false
55
+ end
56
+
57
+ opts
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -22,6 +22,10 @@ module Sequel
22
22
  # for that album. See the PostgreSQL and SQLite adapter documention for
23
23
  # the options you can pass to the insert_conflict method.
24
24
  #
25
+ # You should not attempt to use this plugin to ignore conflicts when
26
+ # inserting, you should only use it to turn insert conflicts into updates.
27
+ # Any usage to ignore conflicts is not recommended or supported.
28
+ #
25
29
  # Usage:
26
30
  #
27
31
  # # Make all model subclasses support insert_conflict
@@ -29,7 +29,7 @@ module Sequel
29
29
  # end
30
30
  #
31
31
  # +first_track+ is not instance specific, but +last_track+ and +recent_tracks+ are.
32
- # +last_trac+ is because the +num_tracks+ call in the block is calling
32
+ # +last_track+ is because the +num_tracks+ call in the block is calling
33
33
  # <tt>Album#num_tracks</tt>. +recent_tracks+ is because the value will change over
34
34
  # time. This plugin allows you to find these cases, and set the :instance_specific
35
35
  # option appropriately for them:
@@ -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 = 54
9
+ MINOR = 57
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.54.0
4
+ version: 5.57.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: 2022-03-01 00:00:00.000000000 Z
11
+ date: 2022-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -199,6 +199,9 @@ extra_rdoc_files:
199
199
  - doc/release_notes/5.52.0.txt
200
200
  - doc/release_notes/5.53.0.txt
201
201
  - doc/release_notes/5.54.0.txt
202
+ - doc/release_notes/5.55.0.txt
203
+ - doc/release_notes/5.56.0.txt
204
+ - doc/release_notes/5.57.0.txt
202
205
  - doc/release_notes/5.6.0.txt
203
206
  - doc/release_notes/5.7.0.txt
204
207
  - doc/release_notes/5.8.0.txt
@@ -281,6 +284,9 @@ files:
281
284
  - doc/release_notes/5.52.0.txt
282
285
  - doc/release_notes/5.53.0.txt
283
286
  - doc/release_notes/5.54.0.txt
287
+ - doc/release_notes/5.55.0.txt
288
+ - doc/release_notes/5.56.0.txt
289
+ - doc/release_notes/5.57.0.txt
284
290
  - doc/release_notes/5.6.0.txt
285
291
  - doc/release_notes/5.7.0.txt
286
292
  - doc/release_notes/5.8.0.txt
@@ -410,6 +416,7 @@ files:
410
416
  - lib/sequel/extensions/index_caching.rb
411
417
  - lib/sequel/extensions/inflector.rb
412
418
  - lib/sequel/extensions/integer64.rb
419
+ - lib/sequel/extensions/is_distinct_from.rb
413
420
  - lib/sequel/extensions/looser_typecasting.rb
414
421
  - lib/sequel/extensions/migration.rb
415
422
  - lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb
@@ -451,6 +458,7 @@ files:
451
458
  - lib/sequel/extensions/sql_comments.rb
452
459
  - lib/sequel/extensions/sql_expr.rb
453
460
  - lib/sequel/extensions/sql_log_normalizer.rb
461
+ - lib/sequel/extensions/sqlite_json_ops.rb
454
462
  - lib/sequel/extensions/string_agg.rb
455
463
  - lib/sequel/extensions/string_date_time.rb
456
464
  - lib/sequel/extensions/symbol_aref.rb
@@ -479,6 +487,7 @@ files:
479
487
  - lib/sequel/plugins/association_pks.rb
480
488
  - lib/sequel/plugins/association_proxies.rb
481
489
  - lib/sequel/plugins/async_thread_pool.rb
490
+ - lib/sequel/plugins/auto_restrict_eager_graph.rb
482
491
  - lib/sequel/plugins/auto_validations.rb
483
492
  - lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb
484
493
  - lib/sequel/plugins/before_after_save.rb
@@ -566,13 +575,13 @@ files:
566
575
  - lib/sequel/sql.rb
567
576
  - lib/sequel/timezones.rb
568
577
  - lib/sequel/version.rb
569
- homepage: http://sequel.jeremyevans.net
578
+ homepage: https://sequel.jeremyevans.net
570
579
  licenses:
571
580
  - MIT
572
581
  metadata:
573
582
  bug_tracker_uri: https://github.com/jeremyevans/sequel/issues
574
- changelog_uri: http://sequel.jeremyevans.net/rdoc/files/CHANGELOG.html
575
- documentation_uri: http://sequel.jeremyevans.net/documentation.html
583
+ changelog_uri: https://sequel.jeremyevans.net/rdoc/files/CHANGELOG.html
584
+ documentation_uri: https://sequel.jeremyevans.net/documentation.html
576
585
  mailing_list_uri: https://github.com/jeremyevans/sequel/discussions
577
586
  source_code_uri: https://github.com/jeremyevans/sequel
578
587
  post_install_message: