module Sparkql::ParserCompatibility

Required interface for existing parser implementations

Constants

FILTER_VALUES

Ordered by precedence.

MAXIMUM_EXPRESSIONS
MAXIMUM_FUNCTION_DEPTH
MAXIMUM_LEVEL_DEPTH
MAXIMUM_MULTIPLE_VALUES
OPERATORS_SUPPORTING_MULTIPLES

Public Instance Methods

boolean_escape(string) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 169
def boolean_escape(string)
  "true" == string
end
character_escape( string ) click to toggle source

processes escape characters for a given string. May be overridden by child classes.

# File lib/sparkql/parser_compatibility.rb, line 145
def character_escape( string )
  string.gsub(/^\'/,'').gsub(/\'$/,'').gsub(/\\'/, "'")
end
compile( source, mapper ) click to toggle source

To be implemented by child class. Shall return a valid query string for the respective database, or nil if the source could not be processed. It may be possible to return a valid SQL string AND have errors ( as checked by errors? ), but this will be left to the discretion of the child class.

# File lib/sparkql/parser_compatibility.rb, line 63
def compile( source, mapper )
 raise NotImplementedError
end
date_escape(string) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 157
def date_escape(string)
  Date.parse(string)
end
datetime_escape(string) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 161
def datetime_escape(string)
  DateTime.parse(string)
end
decimal_escape( string ) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 153
def decimal_escape( string )
  string.to_f
end
dropped_errors?() click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 99
def dropped_errors?
  process_errors.dropped_errors?
end
errors() click to toggle source

Returns an array of errors. This is an array of ParserError objects

# File lib/sparkql/parser_compatibility.rb, line 81
def errors
  @errors = [] unless defined?(@errors)
  @errors
end
errors?() click to toggle source

delegate :errors?, :fatal_errors?, :dropped_errors?, :recovered_errors?, :to => :process_errors Since I don't have rails delegate…

# File lib/sparkql/parser_compatibility.rb, line 93
def errors?
  process_errors.errors?
end
escape_value( expression ) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 118
def escape_value( expression )
  if expression[:value].is_a? Array
    return escape_value_list( expression )
  end
  case expression[:type]
  when :character
    return character_escape(expression[:value])
  when :integer
    return integer_escape(expression[:value])
  when :decimal
    return decimal_escape(expression[:value])
  when :date
    return date_escape(expression[:value])
  when :datetime
    return datetime_escape(expression[:value])
  when :time
    return time_escape(expression[:value])
  when :boolean
    return boolean_escape(expression[:value])
  when :null
    return nil
  end
  expression[:value]
end
escape_value_list( expression ) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 106
def escape_value_list( expression )
  final_list = []
  expression[:value].each do | value |
    new_exp = {
      :value => value,
      :type => expression[:type]
    }
    final_list << escape_value(new_exp)
  end
  expression[:value] = final_list
end
fatal_errors?() click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 96
def fatal_errors?
  process_errors.fatal_errors?
end
integer_escape( string ) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 149
def integer_escape( string )
  string.to_i
end
max_expressions() click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 191
def max_expressions
  MAXIMUM_EXPRESSIONS
end
max_function_depth() click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 199
def max_function_depth
  MAXIMUM_FUNCTION_DEPTH
end
max_level_depth() click to toggle source

Maximum supported nesting level for the parser filters

# File lib/sparkql/parser_compatibility.rb, line 187
def max_level_depth
  MAXIMUM_LEVEL_DEPTH
end
max_values() click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 195
def max_values
  MAXIMUM_MULTIPLE_VALUES
end
process_errors() click to toggle source

Delegator for methods to process the error list.

# File lib/sparkql/parser_compatibility.rb, line 87
def process_errors
  Sparkql::ErrorsProcessor.new(errors)
end
recovered_errors?() click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 102
def recovered_errors?
  process_errors.recovered_errors?
end
rules_for_type( type ) click to toggle source

Returns the rule hash for a given type

# File lib/sparkql/parser_compatibility.rb, line 174
def rules_for_type( type )
  FILTER_VALUES.each do |rule|
    return rule if rule[:type] == type
  end
  nil
end
supports_multiple?( type ) click to toggle source

true if a given type supports multiple values

# File lib/sparkql/parser_compatibility.rb, line 182
def supports_multiple?( type )
  rules_for_type(type).include?( :multiple )
end
time_escape(string) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 165
def time_escape(string)
  DateTime.parse(string)
end
tokenize( source ) click to toggle source

Returns a list of expressions tokenized in the following format:

{ :field => IdentifierName, :operator => “Eq”, :value => “'Fargo'”, :type => :character, :conjunction => “And” }

This step will set errors if source is not syntactically correct.

# File lib/sparkql/parser_compatibility.rb, line 70
def tokenize( source )
  raise ArgumentError, "You must supply a source string to tokenize!" unless source.is_a?(String)

  # Reset the parser error stack
  @errors = []

  expressions = self.parse(source)
  expressions
end

Private Instance Methods

check_function_type?(expression, expected) click to toggle source

If a function is being applied to a field, we check that the return type of the function matches what is expected, and that the function supports the field type as the first argument.

# File lib/sparkql/parser_compatibility.rb, line 261
def check_function_type?(expression, expected)
  validate_manipulation_types(expression[:field_manipulations], expected)
end
check_type!(expression, expected, supports_nulls = true) click to toggle source

Checks the type of an expression with what is expected.

# File lib/sparkql/parser_compatibility.rb, line 216
def check_type!(expression, expected, supports_nulls = true)
  if (expected == expression[:type] && !expression.key?(:field_manipulations)) ||
      (expression.key?(:field_manipulations) && check_function_type?(expression, expected)) ||
    (supports_nulls && expression[:type] == :null)
    return true
  # If the field will be passed into a function,
  # check the type of the return value of the function
  # and coerce if necessary.
  elsif expression[:field_manipulations] &&
        expression[:type] == :integer &&
        expression[:field_manipulations][:return_type] == :decimal
    expression[:type] = :decimal
    expression[:cast] = :integer
    return true
  elsif expected == :datetime && expression[:type] == :date
    expression[:type] = :datetime
    expression[:cast] = :date
    return true
  elsif expected == :date && expression[:type] == :datetime
    expression[:type] = :date
    expression[:cast] = :datetime
    if multiple_values?(expression[:value])
      expression[:value].map!{ |val| coerce_datetime val }
    else
      expression[:value] = coerce_datetime expression[:value]
    end
    return true
  elsif expected == :decimal && expression[:type] == :integer
    expression[:type] = :decimal
    expression[:cast] = :integer
    return true
  end
  type_error(expression, expected)
  false
end
coerce_datetime(datetime) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 328
def coerce_datetime datetime
  if datestr = datetime.match(/^(\d{4}-\d{2}-\d{2})/)
    datestr[0]
  else
    datetime
  end
end
compile_error( error_hash )
Alias for: tokenizer_error
get_operator(expression, default ) click to toggle source

Builds the correct operator based on the type and the value. default should be the operator provided in the actual filter string

# File lib/sparkql/parser_compatibility.rb, line 299
def get_operator(expression, default )
  f = rules_for_type(expression[:type])
  if f[:operators].include?(default)
    if f[:multiple] && range?(expression[:value]) && default == 'Bt'
      return "Bt"
    elsif f[:multiple] && multiple_values?(expression[:value])
      return nil unless operator_supports_multiples?(default)
      return default == "Ne" ? "Not In" : "In"
    elsif default == "Ne"
      return "Not Eq"
    end
    return default
  else
    return nil
  end
end
multiple_values?(value) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 316
def multiple_values?(value)
  Array(value).size > 1
end
operator_supports_multiples?(operator) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 324
def operator_supports_multiples?(operator)
  OPERATORS_SUPPORTING_MULTIPLES.include?(operator)
end
range?(value) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 320
def range?(value)
  Array(value).size == 2
end
tokenizer_error( error_hash ) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 205
def tokenizer_error( error_hash )

  if @lexer
    error_hash[:token_index] = @lexer.token_index
  end

  self.errors << Sparkql::ParserError.new( error_hash )
end
Also aliased as: compile_error
type_error( expression, expected ) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 252
def type_error( expression, expected )
    compile_error(:token => expression[:field], :expression => expression,
          :message => "expected #{expected} but found #{expression[:type]}",
          :status => :fatal )
end
validate_manipulation_types(field_manipulations, expected) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 265
def validate_manipulation_types(field_manipulations, expected)
  if field_manipulations[:type] == :function
    function = Sparkql::FunctionResolver::SUPPORTED_FUNCTIONS[field_manipulations[:function_name].to_sym]
    return false if function.nil?
    field_manipulations[:args].each_with_index do |arg, index|
      if arg[:type] == :field
        return false unless function[:args][index].include?(:field)
      end
    end
  elsif field_manipulations[:type] == :arithmetic
    lhs = field_manipulations[:lhs]
    return false unless validate_side(lhs, expected)

    rhs = field_manipulations[:rhs]
    return false unless rhs.nil? || validate_side(rhs, expected)
  end
  true
end
validate_side(side, expected) click to toggle source
# File lib/sparkql/parser_compatibility.rb, line 284
def validate_side(side, expected)
  if side[:type] == :arithmetic
    return validate_manipulation_types(side, expected)
  elsif side[:type] == :field
    return false unless [:decimal, :integer].include?(expected)
  elsif side[:type] == :function
    return false unless [:decimal, :integer].include?(side[:return_type])
  elsif ![:decimal, :integer].include?(side[:type])
    return false
  end
  true
end