Class: Mxrb::Oql::ReverseTranslator

Inherits:
Object
  • Object
show all
Defined in:
lib/mxrb/oql/reverse_translator.rb

Overview

Converts a conservative, read-only SQL subset back into logical OQL. Physical Runtime names are accepted as Module$Entity. Supplying a project restores canonical entity and attribute casing. Parser transformations are intentionally kept together so table, alias, attribute and parameter state cannot diverge. rubocop:disable Metrics

Constant Summary collapse

DIALECTS =
Translator::DIALECTS
STOP_CLAUSES =
%w[GROUP HAVING LIMIT OFFSET ORDER UNION WHERE].freeze
ALIAS_STOP_WORDS =
(Translator::ALIAS_STOP_WORDS + %w[CROSS FETCH FOR]).freeze
REJECTED_WORDS =
%w[
  ALTER APPLY CALL COLLATE CREATE CROSS DELETE DROP EXEC EXECUTE FETCH
  FILTER FOR GRANT INSERT INTO LATERAL MERGE NATURAL NULLS OVER QUALIFY
  RETURNING REVOKE ROWS TOP TRUNCATE UNNEST UPDATE UPSERT VALUES
  WINDOW WITH
].freeze
FUNCTIONS =
%w[
  AVG CAST COALESCE COUNT DATEADD DATEDIFF DATEFORMAT DATEPARSE DATEPART
  DATETRUNC LENGTH LOCATE LOWER LPAD LTRIM MAX MIN RANGEBEGIN RANGEEND
  REPLACE ROUND RPAD RTRIM STRING_AGG SUBSTRING SUM TRIM UPPER
].freeze
FUNCTION_ALIASES =
{ 'CHAR_LENGTH' => 'LENGTH', 'LEN' => 'LENGTH' }.freeze
FUNCTION_LIKE_OPERATORS =
%w[
  AS BOOLEAN CASE DATETIME DECIMAL EXISTS FLOAT FROM HAVING IN INTEGER
  JOIN LONG ON SELECT STRING WHEN WHERE
].freeze
RESERVED =
%w[
  ALL AND AS ASC AVG BOOLEAN BY CASE CAST COUNT DATETIME DAY DECIMAL
  DESC DISTINCT ELSE END EXISTS FALSE FLOAT FROM FULL GROUP HAVING HOUR
  IN INNER INTEGER IS JOIN KEY LEFT LIKE LIMIT LONG MATCHED MAX MERGE
  MILLISECOND MIN MINUTE MONTH NOT NULL OFFSET ON OR ORDER OUTER QUARTER
  REPLACE RIGHT SECOND SELECT SET SOURCE STRING STRING_AGG SUM TARGET
  THEN TRUE UNION UPDATE UPSERT USING VALUES WEEK WEEKDAY WHEN WHERE WITH
  YEAR
].freeze
SCHEMAS =
%w[dbo public].freeze

Instance Method Summary collapse

Constructor Details

#initialize(dialect: :postgresql, project: nil) ⇒ ReverseTranslator

Returns a new instance of ReverseTranslator.

Raises:

  • (ArgumentError)


48
49
50
51
52
53
# File 'lib/mxrb/oql/reverse_translator.rb', line 48

def initialize(dialect: :postgresql, project: nil)
  @dialect = dialect.to_sym
  raise ArgumentError, "unsupported SQL dialect #{@dialect.inspect}" unless DIALECTS.include?(@dialect)

  @catalog = entity_catalog(project)
end

Instance Method Details

#translate(source) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/mxrb/oql/reverse_translator.rb', line 55

def translate(source)
  sql = source.to_s
  tokens = Translator.tokens(sql)
  translate_function_aliases!(tokens)
  warning = validation_warning(tokens)
  return unsupported(sql, warning) if warning

  warnings = []
  aliases = translate_tables!(tokens, warnings)
  return unsupported(sql, aliases) if aliases.is_a?(String)

  translate_attributes!(tokens, aliases)
  parameter_result = translate_parameters!(tokens)
  return unsupported(sql, parameter_result) if parameter_result.is_a?(String)

  translate_operators!(tokens)

  oql = tokens.map(&:text).join.strip
  confidence = warnings.empty? ? :logical : :inferred
  OqlProjection.new(
    sql.freeze, oql.freeze, @dialect, confidence,
    warnings.uniq.freeze, parameter_result.freeze
  )
end