aboutsummaryrefslogtreecommitdiffstats
path: root/lib/arel/table.rb
blob: 16ae83c2849032764ec0a96d589f8bde51698458 (plain) (blame)
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
module Arel
  class Table
    include Arel::Crud
    include Arel::FactoryMethods

    @engine = nil
    class << self; attr_accessor :engine; end

    attr_accessor :name, :engine, :aliases, :table_alias

    # TableAlias and Table both have a #table_name which is the name of the underlying table
    alias :table_name :name

    def initialize name, engine = Table.engine
      @name    = name.to_s
      @engine  = engine
      @columns = nil
      @aliases = []
      @table_alias = nil
      @primary_key = nil

      if Hash === engine
        @engine  = engine[:engine] || Table.engine

        # Sometime AR sends an :as parameter to table, to let the table know
        # that it is an Alias.  We may want to override new, and return a
        # TableAlias node?
        @table_alias = engine[:as] unless engine[:as].to_s == @name
      end
    end

    def primary_key
      if $VERBOSE
        warn <<-eowarn
primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
        eowarn
      end
      @primary_key ||= begin
        primary_key_name = @engine.connection.primary_key(name)
        # some tables might be without primary key
        primary_key_name && self[primary_key_name]
      end
    end

    def alias name = "#{self.name}_2"
      Nodes::TableAlias.new(self, name).tap do |node|
        @aliases << node
      end
    end

    def from table
      SelectManager.new(@engine, table)
    end

    def join relation, klass = Nodes::InnerJoin
      return from(self) unless relation

      case relation
      when String, Nodes::SqlLiteral
        raise if relation.blank?
        klass = Nodes::StringJoin
      end

      from(self).join(relation, klass)
    end

    def group *columns
      from(self).group(*columns)
    end

    def order *expr
      from(self).order(*expr)
    end

    def where condition
      from(self).where condition
    end

    def project *things
      from(self).project(*things)
    end

    def take amount
      from(self).take amount
    end

    def skip amount
      from(self).skip amount
    end

    def having expr
      from(self).having expr
    end

    def [] name
      ::Arel::Attribute.new self, name
    end

    def select_manager
      SelectManager.new(@engine)
    end

    def insert_manager
      InsertManager.new(@engine)
    end

    def hash
      # Perf note: aliases, table alias and engine is excluded from the hash
      #  aliases can have a loop back to this table breaking hashes in parent
      #  relations, for the vast majority of cases @name is unique to a query
      @name.hash
    end

    def eql? other
      self.class == other.class &&
        self.name == other.name &&
        self.engine == other.engine &&
        self.aliases == other.aliases &&
        self.table_alias == other.table_alias
    end
    alias :== :eql?

    private

    def attributes_for columns
      return nil unless columns

      columns.map do |column|
        Attributes.for(column).new self, column.name.to_sym
      end
    end

  end
end