Class: Ferret::Expression

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/sql-ferret.rb

Constant Summary

Constants included from Constants

Constants::QSF_MULTICOL, Constants::QSF_MULTIROW

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeExpression

Returns a new instance of Expression.



929
930
931
932
933
934
935
936
937
# File 'lib/sql-ferret.rb', line 929

def initialize
  super()
  @stages = [Ferret::Stage.new(nil, nil, :left)]
  @selectees = []
  @exemplars = []
  @multicolumn = false
  @type = :select # the default
  return
end

Instance Attribute Details

#exemplarsObject (readonly)

Returns the value of attribute exemplars.



925
926
927
# File 'lib/sql-ferret.rb', line 925

def exemplars
  @exemplars
end

#multicolumnObject

Returns the value of attribute multicolumn.



926
927
928
# File 'lib/sql-ferret.rb', line 926

def multicolumn
  @multicolumn
end

#selecteesObject (readonly)

Returns the value of attribute selectees.



924
925
926
# File 'lib/sql-ferret.rb', line 924

def selectees
  @selectees
end

#stagesObject (readonly)

Returns the value of attribute stages.



923
924
925
# File 'lib/sql-ferret.rb', line 923

def stages
  @stages
end

#typeObject

Returns the value of attribute type.



927
928
929
# File 'lib/sql-ferret.rb', line 927

def type
  @type
end

Instance Method Details

#assign_stage_qualifiers(ag) ⇒ Object



939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
# File 'lib/sql-ferret.rb', line 939

def assign_stage_qualifiers ag
  raise 'type mismatch' \
      unless ag.is_a? Ferret::Alias_Generator
  table_visit_counts = Hash.new 0 # name => count
  @stages.each_with_index do |stage, i|
    table_visit_counts[stage.table.name] += 1
  end

  # The tables that we visited more than once need
  # distinguishing names.
  @stages.each do |stage|
    stage.qualifier =
        if table_visit_counts[stage.table.name] > 1 then
          ag.create stage.table.name[0]
        else
          stage.table.name
        end
  end
  return
end

#from_clauseObject



960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
# File 'lib/sql-ferret.rb', line 960

def from_clause
  clause = "from "
  @stages.each_with_index do |stage, i|
    # In case of a non-query expression -- a modification --,
    # the last stage is empty and mustn't be joined.  It then
    # serves only the purpose of holding the last stalk.
    break if i == @stages.length - 1 and modification?

    unless i.zero? then
      clause << " #{stage.join_type} join "
    end

    clause << stage.table.name << " as " << stage.qualifier

    unless i.zero? then
      clause << " on %s.%s = %s.%s" % [
          stage.parent.qualifier,
              (stage.stalk.haunt || stage.stalk).name,
          stage.qualifier, stage.stalk.ref.name,
      ]
    end
  end
  return clause
end

#modification?Boolean

Returns:

  • (Boolean)


1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
# File 'lib/sql-ferret.rb', line 1046

def modification?
  case @type
    when :select, :select_distinct then
      return false
    when :update, :insert, :delete then
      return true
    else
      raise 'assertion failed'
  end
end

#selectObject

Prepare a [[select]] statement as an [[Annotated_SQL_Template]]. If this expression represents a query statement, the result will cover the whole query. If it represents an update statement, the result will cover the subquery that determines key value(s) of records in the last table to update.



1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
# File 'lib/sql-ferret.rb', line 1008

def select
  qualifiers_needed =
      @stages.length != (modification? ? 2 : 1)
  sql_selectees = @selectees.map do |selectee|
    (qualifiers_needed ?
            selectee.stage.qualifier + "." : "") +
        (selectee.field.haunt || selectee.field).name
  end.join(', ')

  outputs = {}
  @selectees.each do |selectee|
    outputs[selectee.output_name.to_sym] =
        selectee.interpretation
  end

  sql = "select"
  sql << " distinct" if @type == :select_distinct
  sql << " " << sql_selectees << " " << from_clause

  sql << " " << where_clause unless @exemplars.empty?

  # Determine the shape of the table
  shape = 0
  shape |= QSF_MULTICOL if @multicolumn
  # If no [[unique]] exemplar field is specified or if any of
  # the joins is performed along a ghost field (i.e.,
  # possibly a 1->n reference), our result will have multiple
  # rows.
  shape |= QSF_MULTIROW \
      unless @exemplars.any?{|ex| ex.column.unique?} and
          !@stages[1 .. -1].any?{|stage| stage.stalk.ghost?}

  return Ferret::Annotated_SQL_Template.new(sql,
      outputs, shape)
end

#where_clauseObject



985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# File 'lib/sql-ferret.rb', line 985

def where_clause
  raise 'assertion failed' if @exemplars.empty?
  clause = "where "
  @exemplars.each_with_index do |exemplar, i|
    clause << " and " unless i.zero?
    # The qualifier is only necessary if the clause has more
    # than one stage.
    if @stages.length > 1 then
      # In the navigational model, the (primary) filter always
      # lives in the zeroth stage.
      clause << @stages[0].qualifier << "."
    end
    clause << exemplar.column.name << " [test #{i}]"
  end
  return clause
end