module Clear::SQL::Query::Select macro included getter columns : Array(SQL::Column) = [] of SQL::Column getter default_wildcard_table = nil def is_distinct? !!@distinct_value end end @columns : Array(SQL::Column) @forced_columns : Array(SQL::Column) getter distinct_value : String? # In some case you want you query to return `table.*` instead of `*` # if no select parameters has been set. This occurs in the case of joins # between models. def set_default_table_wildcard(table : String? = nil) @default_wildcard_table = table change! end # :nodoc: def select(c : Column) @columns << c change! end def force_select(c : Column) @forced_columns << c change! end # Add DISTINCT to the SELECT part of the query # # - If `on` is blank (empty string, default), will call a simple `SELECT DISTINCT ...` # - If `on` is nil, will remove the distinct (see `clear_distinct`) # - If `on` is a non empty string, will call `SELECT DISTINCT ON (on) ...` # def distinct(on : String? = "") @distinct_value = on change! end # Remove distinct def clear_distinct distinct nil end # Add columns in the SELECT query. # By default, a new SELECT query will select all using wildcard `*`. # # After a call to select is made, the query will select the given fields instead. # # ``` # select(user_id: "uid", updated_at: "updated_at") # # => Output "SELECT user_id as uid, updated_at as updated_at" # ``` def select(*__args) __args.each do |arg| case arg when NamedTuple arg.each { |k, v| @columns << Column.new(v, k.to_s) } else @columns << Column.new(arg) end end change! end # Act as `select` method, but is not cleared by `clear_select` # # This is useful for enriching a query which absolutely need some key colums, # for example this is used in relations caching under the hood. # # ``` # query.force_select("id") # query.select("a, b").to_sql # => Output "SELECT a, b, id" # query.clear_select.to_sql # => Output "SELECT *, id" # ``` def force_select(*__args) __args.each do |arg| case arg when NamedTuple arg.each { |k, v| @forced_columns << Column.new(v, k.to_s) } else @forced_columns << Column.new(arg) end end change! end def select(**__tuple) __tuple.each { |k, v| @columns << Column.new(v, k.to_s) } change! end def force_select(**__tuple) __tuple.each { |k, v| @forced_columns << Column.new(v, k.to_s) } change! end def clear_select @columns.clear change! end def clear_force_select @forced_columns.clear change! end protected def print_distinct case @distinct_value when nil "" when "" "DISTINCT " else {"DISTINCT ON (", @distinct_value, ") "}.join end end protected def print_wildcard if table = @default_wildcard_table {table, "*"}.join('.') else "*" end end protected def print_columns (@columns.any? ? @columns.map(&.to_sql.as(String)).join(", ") : print_wildcard) end protected def print_forced_columns if @forced_columns.any? ", " + @forced_columns.map(&.to_sql.as(String)).join(", ") else "" end end protected def print_select {"SELECT ", print_distinct, print_columns, print_forced_columns}.join end end