File: exclude_or_null.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (68 lines) | stat: -rw-r--r-- 2,365 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# frozen-string-literal: true
#
# The exclude_or_null extension adds Dataset#exclude_or_null and
# Dataset#exclude_or_null_having.  These methods are similar to
# Dataset#exclude and Dataset#exclude_having, except that they
# will also exclude rows where the condition IS NULL.
#
#   DB[:table].exclude_or_null(foo: 1)
#   # SELECT * FROM table WHERE NOT coalesce((foo = 1), false)
#   
#   DB[:table].exclude_or_null{foo(bar) =~ 1}
#   # SELECT * FROM table HAVING NOT coalesce((foo(bar) = 1), false))
#   
# You can load this extension into specific datasets:
#
#   ds = DB[:table]
#   ds = ds.extension(:exclude_or_null)
#
# Or you can load it into all of a database's datasets, which
# is probably the desired behavior if you are using this extension:
#
#   DB.extension(:exclude_or_null)
#
# Note, this extension works correctly on PostgreSQL, SQLite, MySQL,
# H2, and HSQLDB.  However, it does not work correctly on Microsoft SQL Server,
# Oracle, DB2, SQLAnywhere, or Derby.
#
# Related module: Sequel::ExcludeOrNull

#
module Sequel
  module ExcludeOrNull
    # Performs the inverse of Dataset#where, but also excludes rows where the given
    # condition IS NULL.
    #
    #   DB[:items].exclude_or_null(category: 'software')
    #   # SELECT * FROM items WHERE NOT coalesce((category = 'software'), false)
    #   
    #   DB[:items].exclude_or_null(category: 'software', id: 3)
    #   # SELECT * FROM items WHERE NOT coalesce(((category = 'software') AND (id = 3)), false)
    def exclude_or_null(*cond, &block)
      add_filter(:where, cond, :or_null, &block)
    end

    # The same as exclude_or_null, but affecting the HAVING clause instead of the
    # WHERE clause.
    #
    #   DB[:items].select_group(:name).exclude_or_null_having{count(name) < 2}
    #   # SELECT name FROM items GROUP BY name HAVING NOT coalesce((count(name) < 2), true)
    def exclude_or_null_having(*cond, &block)
      add_filter(:having, cond, :or_null, &block)
    end

    private

    # Recognize :or_null value for invert, returning an expression for
    # the invert of the condition or the condition being null.
    def _invert_filter(cond, invert)
      if invert == :or_null
        ~SQL::Function.new(:coalesce, cond, SQL::Constants::SQLFALSE)
      else
        super
      end
    end
  end

  Dataset.register_extension(:exclude_or_null, ExcludeOrNull)
end