lib/veritas/sql/generator/relation.rb in veritas-sql-generator-0.0.3 vs lib/veritas/sql/generator/relation.rb in veritas-sql-generator-0.0.4
- old
+ new
@@ -9,11 +9,12 @@
extend Identifier
include Attribute
EMPTY_STRING = ''.freeze
SEPARATOR = ', '.freeze
- ALL_COLUMNS = '*'.freeze
+ STAR = '*'.freeze
+ EMPTY_HASH = {}.freeze
# Return the alias name
#
# @return [#to_s]
#
@@ -30,38 +31,25 @@
def self.visit(relation)
klass = case relation
when Veritas::Relation::Operation::Set then self::Set
when Veritas::Relation::Operation::Binary then self::Binary
when Veritas::Relation::Operation::Unary then self::Unary
- when Veritas::BaseRelation then self::Base
+ when Veritas::Relation::Base then self::Base
else
raise InvalidRelationError, "#{relation.class} is not a visitable relation"
end
klass.new.visit(relation)
end
- # Return the subquery for the relation and identifier
- #
- # @param [#to_subquery] relation
- #
- # @param [#to_s] identifier
- # optional identifier, defaults to relation.name
- #
- # @return [#to_s]
- #
- # @api private
- def self.subquery(relation, identifier = relation.name)
- "(#{relation.to_subquery}) AS #{visit_identifier(identifier)}"
- end
-
# Initialize a Generator
#
# @return [undefined]
#
# @api private
def initialize
- @sql = EMPTY_STRING
+ @sql = EMPTY_STRING
+ @extensions = {}
end
# Visit an object and generate SQL from each node
#
# @example
@@ -91,19 +79,158 @@
# @api public
def to_sql
@sql
end
+ # Return the SQL for the unary relation
+ #
+ # @example
+ # sql = unary_relation.to_s
+ #
+ # @return [#to_s]
+ #
+ # @api public
+ def to_s
+ return EMPTY_STRING unless visited?
+ generate_sql(query_columns)
+ end
+
+ # Return the SQL suitable for an subquery
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def to_subquery
+ return EMPTY_STRING unless visited?
+ Generator.parenthesize!(generate_sql(subquery_columns))
+ end
+
# Test if a visitable object has been visited
#
# @example
# visitor.visited? # true or false
#
# @return [Boolean]
#
# @api public
def visited?
!@name.nil?
+ end
+
+ private
+
+ # Return the columns to use in a query
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def query_columns
+ explicit_columns
+ end
+
+ # Return the columns to use in a subquery
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def subquery_columns
+ implicit_columns
+ end
+
+ # Return the implicit columns for the select list
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def implicit_columns
+ sql = [ STAR, column_list_for(@extensions) ]
+ sql.reject! { |fragment| fragment.empty? }
+ sql.join(SEPARATOR)
+ end
+
+ # Return the explicit columns for the select list
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def explicit_columns
+ @distinct.to_s + column_list_for(@extensions.merge(@columns || EMPTY_HASH))
+ end
+
+ # Return the list of columns
+ #
+ # @param [#map] columns
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def column_list_for(columns)
+ sql = columns.values_at(*@header)
+ sql.compact!
+ sql.join(SEPARATOR)
+ end
+
+ # Return a list of columns in a header
+ #
+ # @param [Veritas::Relation] relation
+ #
+ # @param [#[]] aliases
+ # optional aliases for the columns
+ #
+ # @return [Hash]
+ #
+ # @api private
+ def columns_for(relation, aliases = EMPTY_HASH)
+ columns = {}
+ relation.header.each do |attribute|
+ columns[aliases.fetch(attribute, attribute)] = column_for(attribute, aliases)
+ end
+ columns
+ end
+
+ # Return the column for an attribute
+ #
+ # @param [Attribute] attribute
+ #
+ # @param [#[]] aliases
+ # aliases for the columns
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def column_for(attribute, aliases)
+ if aliases.key?(attribute)
+ alias_for(attribute, aliases[attribute])
+ else
+ dispatch(attribute)
+ end
+ end
+
+ # Return the column alias for an attribute
+ #
+ # @param [#to_s] attribute
+ #
+ # @param [Attribute, nil] alias_attribute
+ # attribute to use for the alias
+ #
+ # @return [#to_s]
+ #
+ # @api private
+ def alias_for(attribute, alias_attribute)
+ "#{dispatch(attribute)} AS #{dispatch(alias_attribute)}"
+ end
+
+ # Add extensions for extension and summarize queries
+ #
+ # @param [#each] extensions
+ #
+ # @return [undefined]
+ #
+ # @api private
+ def add_extensions(extensions)
+ extensions.each do |attribute, function|
+ @extensions[attribute] = alias_for(function, attribute)
+ end
end
end # class Relation
end # module Generator
end # module SQL