sequel 5.54.0 → 5.57.0

Sign up to get free protection for your applications and to get access to all the features.
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: