Module: Sparkql::ParserCompatibility

Included in:
Parser
Defined in:
lib/sparkql/parser_compatibility.rb

Overview

Required interface for existing parser implementations

Constant Summary collapse

MAXIMUM_MULTIPLE_VALUES =
200
MAXIMUM_EXPRESSIONS =
75
MAXIMUM_LEVEL_DEPTH =
2
MAXIMUM_FUNCTION_DEPTH =
5
FILTER_VALUES =

Ordered by precedence.

[
  {
    type: :datetime,
    operators: Sparkql::Token::OPERATORS + [Sparkql::Token::RANGE_OPERATOR]
  },
  {
    type: :date,
    operators: Sparkql::Token::OPERATORS + [Sparkql::Token::RANGE_OPERATOR]
  },
  {
    type: :time,
    operators: Sparkql::Token::OPERATORS + [Sparkql::Token::RANGE_OPERATOR]
  },
  {
    type: :character,
    multiple: /^'([^'\\]*(\\.[^'\\]*)*)'/,
    operators: Sparkql::Token::EQUALITY_OPERATORS
  },
  {
    type: :integer,
    multiple: /^-?[0-9]+/,
    operators: Sparkql::Token::OPERATORS + [Sparkql::Token::RANGE_OPERATOR]
  },
  {
    type: :decimal,
    multiple: /^-?[0-9]+\.[0-9]+/,
    operators: Sparkql::Token::OPERATORS + [Sparkql::Token::RANGE_OPERATOR]
  },
  {
    type: :shape,
    operators: Sparkql::Token::EQUALITY_OPERATORS
  },
  {
    type: :boolean,
    operators: Sparkql::Token::EQUALITY_OPERATORS
  },
  {
    type: :null,
    operators: Sparkql::Token::EQUALITY_OPERATORS
  },
  {
    type: :function,
    operators: Sparkql::Token::OPERATORS + [Sparkql::Token::RANGE_OPERATOR]
  }
].freeze
OPERATORS_SUPPORTING_MULTIPLES =
%w[Eq Ne].freeze

Instance Method Summary collapse

Instance Method Details

#boolean_escape(string) ⇒ Object



180
181
182
# File 'lib/sparkql/parser_compatibility.rb', line 180

def boolean_escape(string)
  string == "true"
end

#character_escape(string) ⇒ Object

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



147
148
149
# File 'lib/sparkql/parser_compatibility.rb', line 147

def character_escape(string)
  string.gsub(/^'/, '').gsub(/'$/, '').gsub(/\\'/, "'")
end

#compile(source, mapper) ⇒ Object

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.

Raises:

  • (NotImplementedError)


62
63
64
# File 'lib/sparkql/parser_compatibility.rb', line 62

def compile(source, mapper)
  raise NotImplementedError
end

#date_escape(string) ⇒ Object



159
160
161
# File 'lib/sparkql/parser_compatibility.rb', line 159

def date_escape(string)
  Date.parse(string)
end

#datetime_escape(string) ⇒ Object

datetime may have timezone info. Given that, we should honor it it when present or setting an appropriate default when not. Either way, we should convert to local appropriate for the parser when we’re done.

DateTime in ruby is deprecated as of ruby 3.0. We’ve switched to the Time class to be future compatible. The :time type in sparkql != a ruby Time instance



170
171
172
# File 'lib/sparkql/parser_compatibility.rb', line 170

def datetime_escape(string)
  Time.parse(string)
end

#decimal_escape(string) ⇒ Object



155
156
157
# File 'lib/sparkql/parser_compatibility.rb', line 155

def decimal_escape(string)
  string.to_f
end

#dropped_errors?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/sparkql/parser_compatibility.rb', line 99

def dropped_errors?
  process_errors.dropped_errors?
end

#errorsObject

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



79
80
81
82
# File 'lib/sparkql/parser_compatibility.rb', line 79

def errors
  @errors = [] unless defined?(@errors)
  @errors
end

#errors?Boolean

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

Returns:

  • (Boolean)


91
92
93
# File 'lib/sparkql/parser_compatibility.rb', line 91

def errors?
  process_errors.errors?
end

#escape_value(expression) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/sparkql/parser_compatibility.rb', line 119

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) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/sparkql/parser_compatibility.rb', line 107

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?Boolean

Returns:

  • (Boolean)


95
96
97
# File 'lib/sparkql/parser_compatibility.rb', line 95

def fatal_errors?
  process_errors.fatal_errors?
end

#integer_escape(string) ⇒ Object



151
152
153
# File 'lib/sparkql/parser_compatibility.rb', line 151

def integer_escape(string)
  string.to_i
end

#max_expressionsObject



202
203
204
# File 'lib/sparkql/parser_compatibility.rb', line 202

def max_expressions
  MAXIMUM_EXPRESSIONS
end

#max_function_depthObject



210
211
212
# File 'lib/sparkql/parser_compatibility.rb', line 210

def max_function_depth
  MAXIMUM_FUNCTION_DEPTH
end

#max_level_depthObject

Maximum supported nesting level for the parser filters



198
199
200
# File 'lib/sparkql/parser_compatibility.rb', line 198

def max_level_depth
  MAXIMUM_LEVEL_DEPTH
end

#max_valuesObject



206
207
208
# File 'lib/sparkql/parser_compatibility.rb', line 206

def max_values
  MAXIMUM_MULTIPLE_VALUES
end

#process_errorsObject

Delegator for methods to process the error list.



85
86
87
# File 'lib/sparkql/parser_compatibility.rb', line 85

def process_errors
  Sparkql::ErrorsProcessor.new(errors)
end

#recovered_errors?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/sparkql/parser_compatibility.rb', line 103

def recovered_errors?
  process_errors.recovered_errors?
end

#rules_for_type(type) ⇒ Object

Returns the rule hash for a given type



185
186
187
188
189
190
# File 'lib/sparkql/parser_compatibility.rb', line 185

def rules_for_type(type)
  FILTER_VALUES.each do |rule|
    return rule if rule[:type] == type
  end
  nil
end

#supports_multiple?(type) ⇒ Boolean

true if a given type supports multiple values

Returns:

  • (Boolean)


193
194
195
# File 'lib/sparkql/parser_compatibility.rb', line 193

def supports_multiple?(type)
  rules_for_type(type).include?(:multiple)
end

#time_escape(string) ⇒ Object

Per the lexer, times don’t have any timezone info. When parsing, pick the proper offset to set things at.



176
177
178
# File 'lib/sparkql/parser_compatibility.rb', line 176

def time_escape(string)
  Time.parse("#{string}#{offset}")
end

#tokenize(source) ⇒ Object

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.

Raises:

  • (ArgumentError)


69
70
71
72
73
74
75
76
# File 'lib/sparkql/parser_compatibility.rb', line 69

def tokenize(source)
  raise ArgumentError, "You must supply a source string to tokenize!" unless source.is_a?(String)

  # Reset the parser error stack
  @errors = []

  self.parse(source)
end