From 3294f81fe36f515e23ef50eef3fa2bc6515d951e Mon Sep 17 00:00:00 2001 From: Jamie Gilbert Date: Fri, 10 Jul 2026 17:39:53 -0700 Subject: [PATCH] Added sqlrender functionality back in with improved tests --- circe/sqlrender/__init__.py | 19 + circe/sqlrender/bigquery_spark.py | 331 ++++ circe/sqlrender/patterns.py | 117 ++ circe/sqlrender/renderer.py | 314 ++++ circe/sqlrender/replacementPatterns.csv | 1447 ++++++++++++++++++ circe/sqlrender/splitter.py | 48 + circe/sqlrender/tokenizer.py | 105 ++ circe/sqlrender/translator.py | 431 ++++++ pyproject.toml | 1 + tests/sqlrender_utils.py | 26 + tests/test_sqlrender_csv_format.py | 54 + tests/test_sqlrender_render.py | 150 ++ tests/test_sqlrender_split.py | 62 + tests/test_sqlrender_translate.py | 31 + tests/test_sqlrender_translate_duckdb.py | 64 + tests/test_sqlrender_translate_oracle.py | 644 ++++++++ tests/test_sqlrender_translate_postgresql.py | 102 ++ 17 files changed, 3946 insertions(+) create mode 100644 circe/sqlrender/__init__.py create mode 100644 circe/sqlrender/bigquery_spark.py create mode 100644 circe/sqlrender/patterns.py create mode 100644 circe/sqlrender/renderer.py create mode 100644 circe/sqlrender/replacementPatterns.csv create mode 100644 circe/sqlrender/splitter.py create mode 100644 circe/sqlrender/tokenizer.py create mode 100644 circe/sqlrender/translator.py create mode 100644 tests/sqlrender_utils.py create mode 100644 tests/test_sqlrender_csv_format.py create mode 100644 tests/test_sqlrender_render.py create mode 100644 tests/test_sqlrender_split.py create mode 100644 tests/test_sqlrender_translate.py create mode 100644 tests/test_sqlrender_translate_duckdb.py create mode 100644 tests/test_sqlrender_translate_oracle.py create mode 100644 tests/test_sqlrender_translate_postgresql.py diff --git a/circe/sqlrender/__init__.py b/circe/sqlrender/__init__.py new file mode 100644 index 0000000..4107dcf --- /dev/null +++ b/circe/sqlrender/__init__.py @@ -0,0 +1,19 @@ +from .bigquery_spark import translate_bigquery, translate_spark +from .patterns import generate_session_id, get_supported_dialects, set_replacement_patterns_path +from .renderer import SqlRenderError, render +from .splitter import split_sql +from .translator import SqlTranslateError, check, translate + +__all__ = [ + "render", + "split_sql", + "translate", + "translate_bigquery", + "translate_spark", + "generate_session_id", + "set_replacement_patterns_path", + "get_supported_dialects", + "check", + "SqlRenderError", + "SqlTranslateError", +] diff --git a/circe/sqlrender/bigquery_spark.py b/circe/sqlrender/bigquery_spark.py new file mode 100644 index 0000000..a50ce96 --- /dev/null +++ b/circe/sqlrender/bigquery_spark.py @@ -0,0 +1,331 @@ +from __future__ import annotations + +import re +from enum import Enum + +from .tokenizer import tokenize_sql +from .translator import _MatchedPattern, parse_search_pattern, search + + +class _ListType(Enum): + SELECT = "SELECT" + GROUP_BY = "GROUP_BY" + ORDER_BY = "ORDER_BY" + WITH_COLUMNS = "WITH_COLUMNS" + IN = "IN" + + +class _CommaListIterator: + """Iterates elements of a comma-separated list of expressions.""" + + def __init__(self, expression_list: str, list_type: _ListType) -> None: + self._list_type = list_type + self._expression_list = expression_list + self._list_prefix = "" + self._list_suffix = "" + self._expression_prefix = "" + self._expression_suffix = "" + self._current_match: _MatchedPattern | None = None + + self._split_list() + self._expression_list = "," + self._expression_list + "," + self._list_element_pattern = parse_search_pattern(", @@a ,") + self._current_match = search(self._expression_list, self._list_element_pattern, 0) + if self._current_match.start != -1: + self._split_expression() + + def is_done(self) -> bool: + return self._current_match is None or self._current_match.start == -1 + + def next(self) -> None: + if self._current_match is None or self._current_match.start == -1: + return + + expr_length = len( + tokenize_sql(self._expression_list[self._current_match.start : self._current_match.end]) + ) + start_token = self._current_match.start_token + expr_length - 1 + self._current_match = search(self._expression_list, self._list_element_pattern, start_token) + if self._current_match.start != -1: + self._split_expression() + + def get_expression_prefix(self) -> str: + return self._expression_prefix + + def get_expression_suffix(self) -> str: + return self._expression_suffix + + def get_full_expression(self) -> str: + return self._expression_prefix + self._expression_suffix + + def get_list_prefix(self) -> str: + return self._list_prefix + + def get_list_suffix(self) -> str: + return self._list_suffix + + def is_single_column_reference(self) -> bool: + tokens = tokenize_sql(self.get_expression_prefix()) + return ( + len(tokens) == 3 + and tokens[0].is_identifier() + and tokens[1].text == "." + and tokens[2].is_identifier() + ) + + def _split_list(self) -> None: + self._list_prefix = "" + self._list_suffix = "" + + if self._list_type == _ListType.SELECT: + self._split_select() + elif self._list_type == _ListType.GROUP_BY: + self._split_group_by() + + def _split_select(self) -> None: + match = search( + "^" + self._expression_list + "$", + parse_search_pattern("^ distinct @@a $"), + 0, + ) + if match.start != -1: + self._list_prefix = "distinct " + self._expression_list = match.variable_to_value["@@a"] + + match = search( + "^" + self._expression_list + "$", + parse_search_pattern("^ @@a into @@b $"), + 0, + ) + if match.start != -1: + self._expression_list = match.variable_to_value["@@a"] + self._list_suffix = " into " + match.variable_to_value["@@b"] + + def _split_group_by(self) -> None: + match = search( + "^" + self._expression_list + "$", + parse_search_pattern("^ @@a order by @@b $"), + 0, + ) + if match.start != -1: + self._expression_list = match.variable_to_value["@@a"] + self._list_suffix = " order by " + match.variable_to_value["@@b"] + + def _split_expression(self) -> None: + assert self._current_match is not None + self._expression_prefix = self._current_match.variable_to_value["@@a"] + self._expression_suffix = "" + + if self._list_type == _ListType.SELECT: + self._split_alias() + elif self._list_type == _ListType.ORDER_BY: + self._split_order_element() + + def _split_alias(self) -> None: + tokens = tokenize_sql(self._expression_prefix) + alias_pattern = parse_search_pattern("^ @@a as @@b $") + alias_match = search("^" + self._expression_prefix + "$", alias_pattern, 0) + if alias_match.start == -1: + if len(tokens) >= 2: + possible_alias = tokens[-1] + preceding_token = tokens[-2].text + if ( + possible_alias.is_identifier() + and preceding_token.lower() != "." + and preceding_token.lower() != "+" + ): + self._expression_prefix = self._expression_prefix[: possible_alias.start] + self._expression_suffix = possible_alias.text + else: + self._expression_prefix = alias_match.variable_to_value["@@a"] + self._expression_suffix = alias_match.variable_to_value["@@b"] + + def _split_order_element(self) -> None: + tokens = tokenize_sql(self.get_full_expression()) + last_token = tokens[-1] + if last_token.text.lower() in ("asc", "desc"): + self._expression_prefix = self.get_full_expression()[: last_token.start - 1] + self._expression_suffix = " " + last_token.text + + +def _bigquery_alias_common_table_expressions(sql: str, pattern: str) -> str: + cte_pattern = parse_search_pattern(pattern) + cte_match = search(sql, cte_pattern, 0) + while cte_match.start != -1: + with_list_iter = _CommaListIterator(cte_match.variable_to_value["@@b"], _ListType.WITH_COLUMNS) + select_list_iter = _CommaListIterator(cte_match.variable_to_value["@@c"], _ListType.SELECT) + replacement_select_list = "" + + while not with_list_iter.is_done(): + if select_list_iter.is_done(): + break + with_expr = with_list_iter.get_full_expression() + select_expr = select_list_iter.get_expression_prefix() + " as " + with_expr + if replacement_select_list: + replacement_select_list = replacement_select_list + "," + replacement_select_list = replacement_select_list + select_expr + with_list_iter.next() + select_list_iter.next() + + replacement_select_list = ( + select_list_iter.get_list_prefix() + replacement_select_list + select_list_iter.get_list_suffix() + ) + + d_val = cte_match.variable_to_value.get("@@d", "") + if d_val is None: + d_val = "" + + replacement = ( + pattern.replace("@@a", cte_match.variable_to_value["@@a"]) + .replace("(@@b)", "") + .replace("@@c", replacement_select_list) + .replace("@@d", d_val) + ) + + sql = sql[: cte_match.start] + replacement + sql[cte_match.end :] + cte_match = search(sql, cte_pattern, cte_match.start_token + 1) + + return sql + + +def _bigquery_convert_select_list_references(sql: str, select_pattern: str, list_type: _ListType) -> str: + select_statement_pattern = parse_search_pattern(select_pattern) + select_statement_match = search(sql, select_statement_pattern, 0) + + while select_statement_match.start != -1: + select_list = select_statement_match.variable_to_value["@@s"] + list_to_replace = select_statement_match.variable_to_value["@@r"] + replacement_list = "" + + list_to_replace_iter = _CommaListIterator(list_to_replace, list_type) + while not list_to_replace_iter.is_done(): + list_expr = list_to_replace_iter.get_expression_prefix() + list_expr_suffix = list_to_replace_iter.get_expression_suffix() + list_expr_pattern = parse_search_pattern(list_expr) + + if list_to_replace_iter.is_single_column_reference(): + replacement_list = replacement_list + ", " + list_expr + list_expr_suffix + else: + select_list_iter = _CommaListIterator(select_list, _ListType.SELECT) + found = False + position = 1 + while not select_list_iter.is_done(): + select_expr = select_list_iter.get_expression_prefix() + test_match = search(select_expr, list_expr_pattern, 0) + if test_match.start != -1: + found = True + replacement_list = replacement_list + ", " + str(position) + list_expr_suffix + break + select_list_iter.next() + position += 1 + if not found: + replacement_list = replacement_list + ", " + list_expr + list_expr_suffix + + list_to_replace_iter.next() + + if replacement_list.startswith(", "): + replacement_list = replacement_list[2:] + replacement_list = ( + list_to_replace_iter.get_list_prefix() + replacement_list + list_to_replace_iter.get_list_suffix() + ) + + suffix = sql[select_statement_match.end :] + sql = sql[: select_statement_match.start] + for i, block in enumerate(select_statement_pattern): + if sql and i > 0: + sql += " " + if block.is_variable: + if block.text == "@@r": + sql += replacement_list + else: + sql += select_statement_match.variable_to_value.get(block.text, "") + else: + sql += block.text + sql += suffix + + select_statement_match = search(sql, select_statement_pattern, select_statement_match.start_token + 1) + + return sql + + +def _bigquery_lower_case(sql: str) -> str: + tokens = tokenize_sql(sql) + result = list(sql) + for token in tokens: + if not token.in_quotes and not token.text.startswith("@"): + lowered = token.text.lower() + if lowered != token.text: + result[token.start : token.end] = lowered + return "".join(result) + + +def translate_bigquery(sql: str) -> str: + sql = _bigquery_lower_case(sql) + + sql = _bigquery_alias_common_table_expressions(sql, "with @@a (@@b) as (select @@c from @@d)") + sql = _bigquery_alias_common_table_expressions(sql, "with @@a (@@b) as (select @@c union @@d)") + sql = _bigquery_alias_common_table_expressions(sql, "with @@a (@@b) as (select @@c)") + sql = _bigquery_alias_common_table_expressions(sql, ", @@a (@@b) as (select @@c from @@d)") + + group_by_references = "select @@s from @@b group by @@r" + sql = _bigquery_convert_select_list_references(sql, group_by_references + ";", _ListType.GROUP_BY) + sql = _bigquery_convert_select_list_references(sql, group_by_references + ")", _ListType.GROUP_BY) + sql = _bigquery_convert_select_list_references(sql, group_by_references + " having", _ListType.GROUP_BY) + sql = _bigquery_convert_select_list_references(sql, group_by_references + " order by", _ListType.GROUP_BY) + + order_by = "select @@s from @@b order by @@r" + sql = _bigquery_convert_select_list_references(sql, order_by + ";", _ListType.ORDER_BY) + sql = _bigquery_convert_select_list_references(sql, order_by + ")", _ListType.ORDER_BY) + + return sql + + +def _spark_create_table(sql: str) -> str: + if not sql.endswith(";"): + sql += ";" + + pattern_str = "create table @@table (@@definition)" + create_table_pattern = parse_search_pattern(pattern_str) + sql_normalized = re.sub(r"\t", " ", sql.strip()) + sql_normalized = re.sub(r" +", " ", sql_normalized) + + create_table_match = search(sql_normalized, create_table_pattern, 0) + if create_table_match.start == -1: + return sql.replace(";", "") + + table_name = create_table_match.variable_to_value.get("@@table", "") + definition_list = create_table_match.variable_to_value.get("@@definition", "") + + if not table_name or not definition_list: + return sql.replace(";", "") + + table_name = re.sub(r"\r\n", "", table_name) + definition_list = definition_list.lower().replace("\r\n", "").replace(" as ", " ") + + column_names: list[str] = [] + for f in definition_list.split(","): + parts = f.strip().split(" ") + if len(parts) >= 2: + col_name = parts[0] + col_type = parts[1] + column_names.append(f"\tCAST(NULL AS {col_type}) AS {col_name}") + + prefix = sql[: create_table_match.start] + sql = prefix + "SELECT " + ",\r\n".join(column_names) + " INTO " + table_name + " WHERE 1 = 0" + + return sql.replace(";", "") + + +def translate_spark(sql: str) -> str: + from .splitter import split_sql + + splits = split_sql(sql) + for i in range(len(splits)): + splits[i] = _spark_create_table(splits[i]) + + if len(splits) > 1 or sql.strip().endswith(";"): + sql = ";\r\n".join(splits).strip() + ";" + else: + sql = ";\r\n".join(splits).strip() + + return sql diff --git a/circe/sqlrender/patterns.py b/circe/sqlrender/patterns.py new file mode 100644 index 0000000..f8e7e80 --- /dev/null +++ b/circe/sqlrender/patterns.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +import random +import string + +_target_to_patterns: dict[str, list[tuple[str, str]]] | None = None +_global_session_id: str | None = None +_PATTERNS_PATH: str | None = None + +SESSION_ID_LENGTH = 8 +MAX_TABLE_NAME_LENGTH = 63 + + +def generate_session_id() -> str: + """Generates a random string that can be used as a unique session identifier.""" + chars = string.ascii_lowercase + "0123456789" + sb: list[str] = [] + sb.append(random.choice(string.ascii_lowercase)) + for _ in range(1, SESSION_ID_LENGTH): + sb.append(random.choice(chars)) + return "".join(sb) + + +def get_global_session_id() -> str: + """Get the global session ID. This ID is prefixed to all table names in the + temp emulation schema to avoid collisions between sessions.""" + global _global_session_id + if _global_session_id is None: + _global_session_id = generate_session_id() + return _global_session_id + + +def set_replacement_patterns_path(path: str | None) -> None: + """Forces the replacement patterns to be loaded from the specified path.""" + global _target_to_patterns, _PATTERNS_PATH + _target_to_patterns = None + _PATTERNS_PATH = path + + +def _safe_split(line: str, delimiter: str = ",") -> list[str]: + """Split that takes escapes into account (port of Java StringUtils.safeSplit).""" + result: list[str] = [] + if len(line) == 0: + result.append("") + return result + literal = False + escape = False + startpos = 0 + i = 0 + while i < len(line): + ch = line[i] + if ch == '"' and not escape: + literal = not literal + if not literal and ch == delimiter and not escape: + result.append(line[startpos:i]) + startpos = i + 1 + escape = not escape if ch == "\\" else False + i += 1 + result.append(line[startpos:i]) + return result + + +def _clean_column(col: str) -> str: + if col.startswith('"') and col.endswith('"') and len(col) > 1: + col = col[1:-1] + col = col.replace('\\"', '"') + col = col.replace("\\n", "\n") + return col + + +def load_patterns() -> dict[str, list[tuple[str, str]]]: + """Loads replacement patterns from the CSV file.""" + global _target_to_patterns + if _target_to_patterns is not None: + return _target_to_patterns + + _target_to_patterns = {} + + if _PATTERNS_PATH is not None: + import pathlib + + path = pathlib.Path(_PATTERNS_PATH) + f = path.open("r", encoding="utf-8") + else: + from importlib.resources import files + + f = files("circe.sqlrender").joinpath("replacementPatterns.csv").open("r", encoding="utf-8") + + try: + first = True + for line in f: + line = line.rstrip("\n").rstrip("\r") + if first: + first = False + continue + if not line: + continue + columns = _safe_split(line, ",") + if len(columns) < 3: + continue + target = _clean_column(columns[0]).strip() + pattern = _clean_column(columns[1]) + replacement = _clean_column(columns[2]) + + pattern = pattern.replace("@", "@@") + replacement = replacement.replace("@", "@@") + + _target_to_patterns.setdefault(target, []).append((pattern, replacement)) + finally: + f.close() + + return _target_to_patterns + + +def get_supported_dialects() -> list[str]: + patterns = load_patterns() + return sorted(patterns.keys()) diff --git a/circe/sqlrender/renderer.py b/circe/sqlrender/renderer.py new file mode 100644 index 0000000..be7707b --- /dev/null +++ b/circe/sqlrender/renderer.py @@ -0,0 +1,314 @@ +from __future__ import annotations + +import re +from dataclasses import dataclass +from typing import Any + + +class SqlRenderError(RuntimeError): + pass + + +@dataclass +class _Span: + start: int + end: int + valid: bool = True + + +@dataclass +class _IfThenElse: + condition: _Span + if_true: _Span + if_false: _Span | None = None + has_if_false: bool = False + + def spans_end(self) -> int: + if self.has_if_false and self.if_false is not None: + return self.if_false.end + return self.if_true.end + + +def _find_curly_bracket_spans(s: str) -> list[_Span]: + starts: list[int] = [] + spans: list[_Span] = [] + for i, ch in enumerate(s): + if ch == "{": + starts.append(i) + elif ch == "}" and starts: + spans.append(_Span(start=starts.pop(), end=i + 1)) + return spans + + +def _find_parentheses(s: str) -> list[_Span]: + starts: list[int] = [] + spans: list[_Span] = [] + for i, ch in enumerate(s): + if ch == "(": + starts.append(i) + elif ch == ")" and starts: + spans.append(_Span(start=starts.pop(), end=i + 1)) + return spans + + +def _link_if_then_elses(s: str, spans: list[_Span]) -> list[_IfThenElse]: + result: list[_IfThenElse] = [] + if len(spans) <= 1: + return result + + for i in range(len(spans) - 1): + for j in range(i + 1, len(spans)): + if spans[j].start > spans[i].end: + in_between = s[spans[i].end : spans[j].start].strip() + if in_between == "?": + ite = _IfThenElse(condition=spans[i], if_true=spans[j]) + if j < len(spans): + for k in range(j + 1, len(spans)): + if spans[k].start > spans[j].end: + in_between2 = s[spans[j].end : spans[k].start].strip() + if in_between2 == ":": + ite.if_false = spans[k] + ite.has_if_false = True + result.append(ite) + return result + + +def _preceded_by_in(start: int, s: str) -> bool: + s = s.lower() + matched = 0 + for i in range(start - 1, -1, -1): + ch = s[i] + if not ch.isspace(): + if matched == 0 and ch == "n" or matched == 1 and ch == "i": + matched += 1 + else: + return False + elif matched == 2: + return True + return False + + +def _remove_parentheses(s: str) -> str: + if len(s) > 1 and ((s[0] == "'" and s[-1] == "'") or (s[0] == '"' and s[-1] == '"')): + return s[1:-1] + return s + + +def _evaluate_primitive_condition(s: str) -> bool: + s = s.strip() + s_lc = s.lower() + if s_lc in ("false", "0", "!true", "!1"): + return False + if s_lc in ("true", "1", "!false", "!0"): + return True + + found = s.find("==") + if found != -1: + left = s[:found].strip() + left = _remove_parentheses(left) + right = s[found + 2 :].strip() + right = _remove_parentheses(right) + return left == right + + found = s.find("!=") + if found == -1: + found = s.find("<>") + if found != -1: + left = s[:found].strip() + left = _remove_parentheses(left) + right = s[found + 2 :].strip() + right = _remove_parentheses(right) + return left != right + + found = s_lc.find(" in ") + if found != -1: + left = s[:found].strip() + left = _remove_parentheses(left) + right = s[found + 4 :].strip() + if len(right) > 2 and right[0] == "(" and right[-1] == ")": + right = right[1:-1] + parts = right.split(",") + return any(left == _remove_parentheses(part.strip()) for part in parts) + + raise SqlRenderError(f'Error parsing boolean condition: "{s}"') + + +def _evaluate_boolean_condition(s: str) -> bool: + s = s.strip() + + found = s.find("&") + if found != -1: + parts = s.split("&") + return all(_evaluate_primitive_condition(part) for part in parts) + + found = s.find("|") + if found != -1: + parts = s.split("|") + return any(_evaluate_primitive_condition(part) for part in parts) + + return _evaluate_primitive_condition(s) + + +def _evaluate_condition(s: str) -> bool: + s = s.strip() + spans = _find_parentheses(s) + for span in spans: + if not _preceded_by_in(span.start, s): + evaluation = _evaluate_boolean_condition(s[span.start + 1 : span.end - 1]) + # Replace opening paren with '1'/'0' + s = s[: span.start] + ("1" if evaluation else "0") + s[span.start + 1 :] + # Collapse the entire (...) to just the single digit, adjusting other spans + s = _replace_spans(s, spans, span.start, span.end, span.start, span.start) + return _evaluate_boolean_condition(s) + + +def _replace_spans( + s: str, + spans: list[_Span], + to_replace_start: int, + to_replace_end: int, + replace_with_start: int, + replace_with_end: int, +) -> str: + replace_with_str = "" + if replace_with_end >= 0: + replace_with_str = s[replace_with_start : replace_with_end + 1] + + s = s[:to_replace_start] + replace_with_str + s[to_replace_end:] + + for span in spans: + if not span.valid: + continue + if span.start > to_replace_start: + if span.start >= replace_with_start and span.start < replace_with_end: + delta = to_replace_start - replace_with_start + span.start += delta + span.end += delta + elif span.start > to_replace_end: + delta = to_replace_start - to_replace_end + len(replace_with_str) + span.start += delta + span.end += delta + else: + span.valid = False + elif span.end > to_replace_end: + delta = to_replace_start - to_replace_end + len(replace_with_str) + span.end += delta + + return s + + +def _extract_defaults(s: str) -> dict[str, str]: + defaults: dict[str, str] = {} + default_start = 0 + default_end = 0 + pre = "{DEFAULT " + post = "}" + while default_start != -1 and default_end != -1: + default_start = s.find(pre, default_end) + if default_start != -1: + default_end = s.find(post, default_start + len(pre)) + if default_end != -1: + span = s[default_start + len(pre) : default_end] + found = span.find("=") + if found != -1: + parameter = span[:found].strip() + if len(parameter) > 0 and parameter[0] == "@": + parameter = parameter[1:] + default_value = span[found + 2 :].strip() + default_value = _remove_parentheses(default_value) + defaults[parameter] = default_value + return defaults + + +def _remove_defaults(s: str) -> str: + return re.sub(r"\{DEFAULT[^}]*\}\s*\n?", "", s) + + +def _escape_dollar_sign(s: str) -> str: + if "$" not in s: + return s + result: list[str] = [] + for ch in s: + if ch == "$": + result.append("\\") + result.append(ch) + return "".join(result) + + +def _substitute_parameters(s: str, parameter_to_value: dict[str, str]) -> str: + defaults = _extract_defaults(s) + s = _remove_defaults(s) + for key, val in defaults.items(): + if key not in parameter_to_value: + parameter_to_value[key] = val + + # Sort parameters longest to shortest so substring matches don't interfere + sorted_params = sorted(parameter_to_value.items(), key=lambda x: len(x[0]), reverse=True) + for key, value in sorted_params: + value = value.replace("\\", "\\\\") + s = re.sub(r"@" + re.escape(key), _escape_dollar_sign(value), s) + + return s + + +def _parse_if_then_else(s: str) -> str: + spans = _find_curly_bracket_spans(s) + if_then_elses = _link_if_then_elses(s, spans) + + result = s + for ite in if_then_elses: + if ite.condition.valid: + cond_text = result[ite.condition.start + 1 : ite.condition.end - 1] + if _evaluate_condition(cond_text): + result = _replace_spans( + result, + spans, + ite.condition.start, + ite.spans_end(), + ite.if_true.start + 1, + ite.if_true.end - 2, + ) + else: + if ite.has_if_false and ite.if_false is not None: + result = _replace_spans( + result, + spans, + ite.condition.start, + ite.spans_end(), + ite.if_false.start + 1, + ite.if_false.end - 2, + ) + else: + result = _replace_spans( + result, + spans, + ite.condition.start, + ite.spans_end(), + 0, + -1, + ) + return result + + +def render(sql: str, **params: Any) -> str: + parameter_to_value: dict[str, str] = {} + for key, val in params.items(): + if isinstance(val, bool): + parameter_to_value[key] = str(val).lower() + elif isinstance(val, list): + parameter_to_value[key] = ", ".join(str(v) for v in val) + else: + parameter_to_value[key] = str(val) + + result = _substitute_parameters(sql, parameter_to_value) + result = _parse_if_then_else(result) + return result + + +def check(sql: str, parameters: list[str] | None = None, values: list[str] | None = None) -> list[str]: + warnings_list: list[str] = [] + if parameters is not None: + for param in parameters: + if "@" + param not in sql: + warnings_list.append(f"Parameter '{param}' not found in SQL") + return warnings_list diff --git a/circe/sqlrender/replacementPatterns.csv b/circe/sqlrender/replacementPatterns.csv new file mode 100644 index 0000000..fc733bc --- /dev/null +++ b/circe/sqlrender/replacementPatterns.csv @@ -0,0 +1,1447 @@ +To,Pattern,Replacement +oracle,...@([0-9]+|y)a,xxx@a +oracle,"AS drvd(@a)","AS drvd(@a)" +oracle,"@a, @b)","@a, @b)" +oracle,"","NULL AS " +oracle,"FROM (VALUES @a) AS drvd","FROM (@a) AS drvd" +oracle,"@a, @b)","@a UNION ALL @b)" +oracle,"(@a)","SELECT @a" +oracle,"FROM (SELECT @a) AS drvd(@b)","FROM (SELECT @b WHERE (0 = 1) UNION ALL SELECT @a) AS values_table" +oracle,TRY_CAST(@a),CAST(@a) +oracle,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +oracle,"CAST('@a' AS DATE)","TO_DATE('@a', 'YYYYMMDD')" +oracle,"CAST('@a' + @b AS DATE)","TO_DATE('@a' + @b, 'YYYYMMDD')" +oracle,"CAST(@a + '@b' AS DATE)","TO_DATE(@a + '@b', 'YYYYMMDD')" +oracle,"CAST(CONCAT(@a) AS DATE)","TO_DATE(CONCAT(@a), 'YYYYMMDD')" +oracle,"INSERT INTO @table (@columns) VALUES (@values1),(@values2)", INSERT INTO @table (@columns) VALUES (INTO @table @columns VALUES @values1\n INTO @table @columns VALUES @values2\n) +oracle,INTO @table @columns VALUES INTO @table @columns VALUES,INTO @table @columns VALUES +oracle, @a INSERT INTO @table (@columns) VALUES (@b),INSERT ALL\n@bSELECT * FROM dual +oracle,,( +oracle,,) +oracle,EXCEPT,MINUS +oracle,"CONCAT(@a, @b, @c)","CONCAT(@a, CONCAT(@b, @c))" +oracle,"DATEADD(second,@seconds,@datetime)","(@date + NUMTODSINTERVAL(@seconds, 'second'))" +oracle,"DATEADD(minute,@minutes,@datetime)","(@date + NUMTODSINTERVAL(@minutes, 'minute'))" +oracle,"DATEADD(hour,@hours,@datetime)","(@date + NUMTODSINTERVAL(@hours, 'hour'))" +oracle,"DATEADD(d,@days,@date)","(@date + NUMTODSINTERVAL(@days, 'day'))" +oracle,"DATEADD(dd,@days,@date)","(@date + NUMTODSINTERVAL(@days, 'day'))" +oracle,"DATEADD(day,@days,@date)","(@date + NUMTODSINTERVAL(@days, 'day'))" +oracle,"DATEADD(month,@months,@date)","ADD_MONTHS(@date, @months)" +oracle,"DATEADD(mm,@months,@date)","ADD_MONTHS(@date, @months)" +oracle,"DATEADD(m,@months,@date)","ADD_MONTHS(@date, @months)" +oracle,"DATEADD(year,@years,@date)","ADD_MONTHS(@date, 12 * @years)" +oracle,"DATEADD(yyyy,@years,@date)","ADD_MONTHS(@date, 12 * @years)" +oracle,"DATEADD(yy,@years,@date)","ADD_MONTHS(@date, 12 * @years)" +oracle,"DATEDIFF(second,@start, @end)","EXTRACT(SECOND FROM (@end - @start))" +oracle,"DATEDIFF(minute,@start, @end)","EXTRACT(MINUTE FROM (@end - @start))" +oracle,"DATEDIFF(hour,@start, @end)","EXTRACT(HOUR FROM (@end - @start))" +oracle,"DATEDIFF(day,@start, @end)",CEIL(CAST(@end AS DATE) - CAST(@start AS DATE)) +oracle,"DATEDIFF(dd,@start, @end)",CEIL(CAST(@end AS DATE) - CAST(@start AS DATE)) +oracle,"DATEDIFF(d,@start, @end)",CEIL(CAST(@end AS DATE) - CAST(@start AS DATE)) +oracle,"DATEDIFF(year,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +oracle,"DATEDIFF(yyyy,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +oracle,"DATEDIFF(yy,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +oracle,"DATEDIFF(month,@start, @end)","MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +oracle,"DATEDIFF(mm,@start, @end)","MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +oracle,"DATEDIFF(m,@start, @end)","MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +oracle,"CONVERT(VARCHAR,@date,112)","TO_CHAR(@date, 'YYYYMMDD')" +oracle,GETDATE(),SYSDATE +oracle,+ '@a',|| '@a' +oracle,'@a' +,'@a' || +oracle,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +oracle,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +oracle,CAST(@a AS varchar) +,CAST(@a AS varchar) || +oracle,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +oracle,"CONVERT(DATE, @a)","TO_DATE(@a, 'YYYYMMDD')" +oracle,CAST(@a AS VARCHAR),TO_CHAR(@a) +oracle,"DATEFROMPARTS(@year,@month,@day)","TO_DATE(TO_CHAR(@year,'0000')||'-'||TO_CHAR(@month,'00')||'-'||TO_CHAR(@day,'00'), 'YYYY-MM-DD')" +oracle,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","TO_DATE(TO_CHAR(@year,'0000')||'-'||TO_CHAR(@month,'00')||'-'||TO_CHAR(@day,'00')||' '||TO_CHAR(@hour,'00')||':'||TO_CHAR(@minute,'00')||':'||TO_CHAR(@second,'00'), 'YYYY-MM-DD HH24:MI:SS')" +oracle,EOMONTH(@date),"TO_DATE(to_char(last_day(@date),'YYYY-MM-DD')||' 23:59:59','YYYY-MM-DD HH24:MI:SS')" +oracle,STDEV(@a),STDDEV(@a) +oracle,VAR(@a),VARIANCE(@a) +oracle,RAND(),DBMS_RANDOM.VALUE +oracle,CEILING(@a),CEIL(@a) +oracle,"HASHBYTES('MD5',@a)","DBMS_CRYPTO.HASH(@a,2)" +oracle,LEN(@a),LENGTH(@a) +oracle,"LEFT(@str,@chars)","SUBSTR(@str,0,@chars)" +oracle,"RIGHT(@str,@chars)","SUBSTR(@str,-@chars)" +oracle,"LOG(@expression,@base)","(@base,@expression)" +oracle,LOG(@expression),"LOG(2.718281828459,@expression)" +oracle,,LOG +oracle,LOG10(@expression),"LOG(10,@expression)" +oracle,"ISNULL(@a,@b)","NVL(@a,@b)" +oracle,ISNUMERIC(@a),"CASE WHEN (LENGTH(TRIM(TRANSLATE(@a, ' +-.0123456789',' '))) IS NULL) THEN 1 ELSE 0 END" +oracle,COUNT_BIG(@a),COUNT(@a) +oracle,SQUARE(@a),((@a)*(@a)) +oracle,PI(),3.141592654 +oracle,NEWID(),SYS_GUID() +oracle,"CHARINDEX(@a,@b)","INSTR(@b,@a)" +oracle,SELECT @a WHERE @b;,SELECT @a FROM DUAL WHERE @b; +oracle,(SELECT @a WHERE @b),(SELECT @a FROM DUAL WHERE @b) +oracle,SELECT @a WHERE @b UNION,SELECT @a FROM DUAL WHERE @b UNION +oracle,SELECT @a;,SELECT @a FROM DUAL; +oracle,(SELECT @a),(SELECT @a FROM DUAL) +oracle,SELECT @a UNION,SELECT @a FROM DUAL UNION +oracle,FROM DUAL FROM DUAL, FROM DUAL +oracle,FROM @a UNION @b FROM DUAL UNION,FROM @a UNION @b FROM DUAL FROM DUAL UNION +oracle,FROM @a FROM DUAL UNION,FROM @a UNION +oracle,FROM @a UNION @b FROM DUAL;,FROM @a UNION @b FROM DUAL FROM DUAL; +oracle,FROM @a FROM DUAL;,FROM @a; +oracle,FROM @a UNION @b FROM DUAL WHERE,FROM @a UNION @b FROM DUAL FROM DUAL WHERE +oracle,FROM @a FROM DUAL WHERE,FROM @a WHERE +oracle,FROM @a UNION @b FROM DUAL),FROM @a UNION @b FROM DUAL FROM DUAL) +oracle,FROM @b FROM DUAL),FROM @b) +oracle,, +oracle,SELECT @a CASE @b COUNT(@c) @d END @e;,SELECT @a CASE @b COUNT(@c) @d END @e GROUP BY 1; +oracle,(SELECT @a CASE @b COUNT(@c) @d END @e),(SELECT @a CASE @b COUNT(@c) @d END @e GROUP BY 1) +oracle,GROUP BY @a GROUP BY 1,GROUP BY @a +oracle,GROUP BY @a GROUP BY 1,GROUP BY @a +oracle,YEAR(@date),EXTRACT(YEAR FROM @date) +oracle,MONTH(@date),EXTRACT(MONTH FROM @date) +oracle,DAY(@date),EXTRACT(DAY FROM @date) +oracle,"DATEPART(YEAR, @date)",EXTRACT(YEAR FROM @date) +oracle,"DATEPART(MONTH, @date)",EXTRACT(MONTH FROM @date) +oracle,"DATEPART(DAY, @date)",EXTRACT(DAY FROM @date) +oracle,USE @schema;,ALTER SESSION SET current_schema = @schema; +oracle,.dbo.,. +oracle,CREATE CLUSTERED INDEX,CREATE INDEX +oracle,CREATE UNIQUE INDEX @name ON @table (@variable);,BEGIN\n EXECUTE IMMEDIATE 'CREATE UNIQUE INDEX @name ON @table (@variable)';\nEXCEPTION\n WHEN OTHERS THEN\n IF SQLCODE != -1408 THEN\n RAISE;\n END IF;\nEND; +oracle,CREATE UNIQUE CLUSTERED INDEX @name ON @table (@variable);,BEGIN\n EXECUTE IMMEDIATE 'CREATE UNIQUE INDEX @name ON @table (@variable)';\nEXCEPTION\n WHEN OTHERS THEN\n IF SQLCODE != -1408 THEN\n RAISE;\n END IF;\nEND; +oracle,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +oracle,DATETIME,TIMESTAMP +oracle,DATETIME2,TIMESTAMP +oracle,BIGINT,NUMBER(19) +oracle,VARCHAR(MAX),VARCHAR2(1024) +oracle,"NOT NULL DEFAULT @a,","DEFAULT @a NOT NULL," +oracle,"(@x NOT NULL DEFAULT @a)","(@x DEFAULT @a NOT NULL)" +oracle,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +oracle,WITH @a AS @b INSERT INTO @c SELECT @d;,INSERT INTO @c WITH @a AS @b SELECT @d; +oracle,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS\nSELECT\n@a\nFROM\n@c; +oracle,SELECT @a INTO @b;,CREATE TABLE @b AS\nSELECT\n@a; +oracle,##, +oracle,CREATE TABLE #@([^\s]+)table,DROP TABLE IF EXISTS %temp_prefix%%session_id%@table;\nCREATE TABLE %temp_prefix%%session_id%@table +oracle,#@([^\s]+)table.@([^\s]+)field,%session_id%@table.@field +oracle,"DROP TABLE IF EXISTS #@table;",BEGIN\n EXECUTE IMMEDIATE 'TRUNCATE TABLE %temp_prefix%%session_id%@table';\n EXECUTE IMMEDIATE 'DROP TABLE %temp_prefix%%session_id%@table';\nEXCEPTION\n WHEN OTHERS THEN\n IF SQLCODE != -942 THEN\n RAISE;\n END IF;\nEND; +oracle,#,%temp_prefix%%session_id% +oracle,"CREATE INDEX @a ON @b (@c,@d) WHERE @e;","CREATE INDEX @a ON @b (CASE WHEN @e THEN @c END, CASE WHEN @e THEN @d END);" +oracle,,## +oracle,SELECT TOP @([0-9]+)rows @a;,SELECT @a FETCH FIRST @rows ROWS ONLY; +oracle,(SELECT TOP @([0-9]+)rows @a),(SELECT @a FETCH FIRST @rows ROWS ONLY) +oracle,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a FETCH FIRST @rows ROWS ONLY; +oracle,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a FETCH FIRST @rows ROWS ONLY) +oracle,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",BEGIN\n EXECUTE IMMEDIATE 'CREATE TABLE @table (@definition)';\nEXCEPTION\n WHEN OTHERS THEN\n IF SQLCODE != -955 THEN\n RAISE;\n END IF;\nEND; +oracle,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE #@table;",BEGIN\n EXECUTE IMMEDIATE 'TRUNCATE TABLE %temp_prefix%%session_id%@table';\n EXECUTE IMMEDIATE 'DROP TABLE %temp_prefix%%session_id%@table';\nEXCEPTION\n WHEN OTHERS THEN\n IF SQLCODE != -942 THEN\n RAISE;\n END IF;\nEND; +oracle,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",BEGIN\n EXECUTE IMMEDIATE 'TRUNCATE TABLE @table';\n EXECUTE IMMEDIATE 'DROP TABLE @table';\nEXCEPTION\n WHEN OTHERS THEN\n IF SQLCODE != -942 THEN\n RAISE;\n END IF;\nEND; +oracle,"DROP TABLE IF EXISTS @table;",BEGIN\n EXECUTE IMMEDIATE 'TRUNCATE TABLE @table';\n EXECUTE IMMEDIATE 'DROP TABLE @table';\nEXCEPTION\n WHEN OTHERS THEN\n IF SQLCODE != -942 THEN\n RAISE;\n END IF;\nEND; +oracle,"FROM @a AS @b;","FROM @a NESTED @b;" +oracle,"NESTED @a AS @b;","@a @b;" +oracle,"NESTED","" +oracle,"FROM @a AS @b WHERE","FROM @a @b WHERE" +oracle,"FROM @a AS @b)","FROM @a @b)" +oracle,"JOIN @a AS @b ON","JOIN @a @b ON" +oracle,UPDATE STATISTICS @a;,-- ANALYZE should not be used to collect optimizer statistics +oracle,"CONVERT(VARBINARY, @a, 1)","TO_NUMBER(@a, RPAD('X', LENGTH(@a), 'X'))" +oracle,"SELECT *, @a FROM (@c) @b WHERE","SELECT @b.*, @a FROM (@c) @b WHERE" +oracle,"SELECT *, @a FROM (@c) @b ORDER BY","SELECT @b.*, @a FROM (@c) @b ORDER BY" +oracle,"SELECT *, @a FROM (@c) @b FETCH FIRST","SELECT @b.*, @a FROM (@c) @b FETCH FIRST" +oracle,"(SELECT *, @a FROM (@c) @b)","(SELECT @b.*, @a FROM (@c) @b)" +oracle,"SELECT *, @a FROM (@c) @b;","SELECT @b.*, @a FROM (@c) @b;" +oracle,"SELECT *, @a FROM @b WHERE","SELECT @b.*, @a FROM @b WHERE" +oracle,"SELECT *, @a FROM @b ORDER BY","SELECT @b.*, @a FROM @b ORDER BY" +oracle,"SELECT *, @a FROM @b FETCH FIRST","SELECT @b.*, @a FROM @b FETCH FIRST" +oracle,"(SELECT *, @a FROM @b)","(SELECT @b.*, @a FROM @b)" +oracle,"SELECT *, @a FROM @b;","SELECT @b.*, @a FROM @b;" +oracle,"SELECT @a, * FROM @b WHERE","SELECT @a, @b.* FROM @b WHERE" +oracle,"SELECT @a, * FROM @b ORDER BY","SELECT @a, @b.* FROM @b ORDER BY" +oracle,"SELECT @a, * FROM @b FETCH FIRST","SELECT @a, @b.* FROM @b FETCH FIRST" +oracle,"(SELECT @a, * FROM @b)","(SELECT @a, @b.* FROM @b)" +oracle,"SELECT @a, * FROM @b;","SELECT @a, @b.* FROM @b;" +oracle,(@a & @b),"BITAND(@a, @b)" +postgresql,...@([0-9]+|y)a,xxx@a +postgresql,TRY_CAST(@a),CAST(@a) +postgresql,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +postgresql,"ROUND(@a,@b)","ROUND(CAST(@a AS NUMERIC),@b)" +postgresql,"HASHBYTES('MD5',@a)","MD5(@a)" +postgresql,"CONVERT(VARBINARY, @a, 1)","CAST(CONCAT('x', @a) AS BIT(32))" +postgresql,"CONVERT(DATE, @a)","TO_DATE(@a, 'yyyymmdd')" +postgresql,"DATEADD(second,@seconds,@datetime)",(@datetime + @seconds*INTERVAL'1 second') +postgresql,"DATEADD(minute,@minutes,@datetime)",(@datetime + @minutes*INTERVAL'1 minute') +postgresql,"DATEADD(hour,@hours,@datetime)",(@datetime + @hours*INTERVAL'1 hour') +postgresql,"DATEADD(d,@days,@date)",(@date + @days*INTERVAL'1 day') +postgresql,"DATEADD(dd,@days,@date)",(@date + @days*INTERVAL'1 day') +postgresql,"DATEADD(day,@days,@date)",(@date + @days*INTERVAL'1 day') +postgresql,"DATEADD(m,@months,@date)",(@date + @months*INTERVAL'1 month') +postgresql,"DATEADD(mm,@months,@date)",(@date + @months*INTERVAL'1 month') +postgresql,"DATEADD(month,@months,@date)",(@date + @months*INTERVAL'1 month') +postgresql,"DATEADD(yy,@years,@date)",(@date + @years*INTERVAL'1 year') +postgresql,"DATEADD(yyyy,@years,@date)",(@date + @years*INTERVAL'1 year') +postgresql,"DATEADD(year,@years,@date)",(@date + @years*INTERVAL'1 year') +postgresql,"DATEDIFF(second,@start, @end)",EXTRACT(EPOCH FROM (@end - @start)) +postgresql,"DATEDIFF(minute,@start, @end)",(EXTRACT(EPOCH FROM (@end - @start)) / 60) +postgresql,"DATEDIFF(hour,@start, @end)",(EXTRACT(EPOCH FROM (@end - @start)) / 3600) +postgresql,"DATEDIFF(d,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +postgresql,"DATEDIFF(dd,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +postgresql,"DATEDIFF(day,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +postgresql,"DATEDIFF(year,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +postgresql,"DATEDIFF(yyyy,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +postgresql,"DATEDIFF(yy,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +postgresql,"DATEDIFF(month,@start, @end)","(extract(year from age(CAST(@end AS DATE), CAST(@start AS DATE)))*12 + extract(month from age(CAST(@end AS DATE), CAST(@start AS DATE))))" +postgresql,"DATEDIFF(mm,@start, @end)","(extract(year from age(CAST(@end AS DATE), CAST(@start AS DATE)))*12 + extract(month from age(CAST(@end AS DATE), CAST(@start AS DATE))))" +postgresql,"DATEDIFF(m,@start, @end)","(extract(year from age(CAST(@end AS DATE), CAST(@start AS DATE)))*12 + extract(month from age(CAST(@end AS DATE), CAST(@start AS DATE))))" +postgresql,"CONVERT(VARCHAR,@date,112)","TO_CHAR(@date, 'YYYYMMDD')" +postgresql,GETDATE(),CURRENT_DATE +postgresql,+ '@a',|| '@a' +postgresql,'@a' +,'@a' || +postgresql,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +postgresql,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +postgresql,CAST(@a AS varchar) +,CAST(@a AS varchar) || +postgresql,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +postgresql,"DATEFROMPARTS(@year,@month,@day)","TO_DATE(TO_CHAR(@year,'0000')||'-'||TO_CHAR(@month,'00')||'-'||TO_CHAR(@day,'00'), 'YYYY-MM-DD')" +postgresql,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","TO_DATE(TO_CHAR(@year,'0000')||'-'||TO_CHAR(@month,'00')||'-'||TO_CHAR(@day,'00')||' '||TO_CHAR(@hour,'00')||':'||TO_CHAR(@minute,'00')||':'||TO_CHAR(@second,'00'), 'YYYY-MM-DD HH24:MI:SS')" +postgresql,YEAR(@date),EXTRACT(YEAR FROM @date) +postgresql,MONTH(@date),EXTRACT(MONTH FROM @date) +postgresql,DAY(@date),EXTRACT(DAY FROM @date) +postgresql,"DATEPART(YEAR, @date)",EXTRACT(YEAR FROM @date) +postgresql,"DATEPART(MONTH, @date)",EXTRACT(MONTH FROM @date) +postgresql,"DATEPART(DAY, @date)",EXTRACT(DAY FROM @date) +postgresql,EOMONTH(@date),"(DATE_TRUNC('MONTH', @date) + INTERVAL '1 MONTH - 1 day')::DATE" +postgresql,STDEV(@a),STDDEV(@a) +postgresql,VAR(@a),VARIANCE(@a) +postgresql,RAND(),RANDOM() +postgresql,LEN(@a),CHAR_LENGTH(@a) +postgresql,"CHARINDEX(@a,@b)","STRPOS(@b,@a)" +postgresql,"LOG(@expression,@base)","(CAST((@base) AS NUMERIC),CAST((@expression) AS NUMERIC))" +postgresql,LOG(@expression),LN(CAST((@expression) AS REAL)) +postgresql,,LOG +postgresql,LOG10(@expression),"LOG(10,CAST((@expression) AS NUMERIC))" +postgresql,"ISNULL(@a,@b)","COALESCE(@a,@b)" +postgresql,"ISNUMERIC(@a)","CASE WHEN (CAST(@a AS VARCHAR) ~ '^([0-9]+\.?[0-9]*|\.[0-9]+)$') THEN 1 ELSE 0 END" +postgresql,COUNT_BIG(@a),COUNT(@a) +postgresql,SQUARE(@a),((@a)*(@a)) +postgresql,NEWID(),MD5(RANDOM()::TEXT || CLOCK_TIMESTAMP()::TEXT) +postgresql,USE @schema;,SET search_path TO @schema; +postgresql,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +postgresql,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +postgresql,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +postgresql,.dbo.,. +postgresql,CREATE TABLE #@table (@definition),CREATE TEMP TABLE @table (@definition) +postgresql,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE INDEX @index_name ON @table (@variable);\nCLUSTER @table USING @index_name; +postgresql,CREATE UNIQUE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE UNIQUE INDEX @index_name ON @table (@variable);\nCLUSTER @table USING @index_name; +postgresql,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +postgresql,DATETIME,TIMESTAMP +postgresql,DATETIME2,TIMESTAMP +postgresql,VARCHAR(MAX),TEXT +postgresql,FLOAT,NUMERIC +postgresql,WITH @a AS @b SELECT @c INTO #@d FROM @e;,CREATE TEMP TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e;\nANALYZE @d; +postgresql,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +postgresql,SELECT @a INTO #@b FROM @c;,CREATE TEMP TABLE @b\nAS\nSELECT\n@a\nFROM\n@c;\nANALYZE @b; +postgresql,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS\nSELECT\n@a\nFROM\n@c; +postgresql,SELECT @a INTO @b;,CREATE TABLE @b AS\nSELECT\n@a; +postgresql,#, +postgresql,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +postgresql,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +postgresql,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +postgresql,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +postgresql,"WITH @cte AS (SELECT @s1 '@literal' @s2)","WITH @cte AS (SELECT @s1 CAST('@literal' as TEXT) @s2)" +postgresql,UPDATE STATISTICS @a;,ANALYZE @a; +postgresql,"ALTER TABLE @table ALTER COLUMN @([^ ]+)a @b;","ALTER TABLE @table ALTER COLUMN @a TYPE @b;" +postgresql,"ALTER TABLE @table ADD @a, @b;","ALTER TABLE @table @a, @b;" +postgresql," @b, @c;"," @b, @c;" +postgresql,"ALTER TABLE @table ADD @a;","ALTER TABLE @table @a;" +postgresql,"",ADD COLUMN +postgresql,"",ADD COLUMN +postgresql,ADD COLUMN COLUMN,ADD COLUMN +postgresql,ADD COLUMN CONSTRAINT,ADD CONSTRAINT +redshift,...@([0-9]+|y)a,xxx@a +redshift,"AS drvd(@a)","AS drvd(@a)" +redshift,"@a, @b)","@a, @b)" +redshift,"","NULL AS " +redshift,"FROM (VALUES @a) AS drvd","FROM (@a) AS drvd" +redshift,"@a, @b)","@a UNION ALL @b)" +redshift,"(@a)","SELECT @a" +redshift,"FROM (SELECT @a) AS drvd(@b)","FROM (SELECT @b WHERE (0 = 1) UNION ALL SELECT @a) AS values_table" +redshift,TRY_CAST(@a),CAST(@a) +redshift,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +redshift,CREATE INDEX @index_name ON @table (@variable);,-- redshift does not support indexes +redshift,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,-- redshift does not support indexes +redshift,"OVER (@a ORDER BY @b DESC)","OVER (@a O*D*R B* @b DESC ROWS UNBOUNDED PRECEDING)" +redshift,"OVER (@a ORDER BY @b ASC)","OVER (@a O*D*R B* @b ASC ROWS UNBOUNDED PRECEDING)" +redshift,"OVER (@a ORDER BY @((?!.*ROWS).*)b)","OVER (@a O*D*R B* @b ROWS UNBOUNDED PRECEDING)" +redshift,"ROW_NUMBER() OVER (@a ROWS UNBOUNDED PRECEDING)","ROW_NUMBER() OVER (@a)" +redshift,"CUME_DIST() OVER (@a ROWS UNBOUNDED PRECEDING)","CUME_DIST() OVER (@a)" +redshift,"DENSE_RANK() OVER (@a ROWS UNBOUNDED PRECEDING)","DENSE_RANK() OVER (@a)" +redshift,"PERCENT_RANK() OVER (@a ROWS UNBOUNDED PRECEDING)","PERCENT_RANK() OVER (@a)" +redshift,"RANK() OVER (@a ROWS UNBOUNDED PRECEDING)","RANK() OVER (@a)" +redshift,"LAG(@x) OVER (@a ROWS UNBOUNDED PRECEDING)","LAG(@x) OVER (@a)" +redshift,"LEAD(@x) OVER (@a ROWS UNBOUNDED PRECEDING)","LEAD(@x) OVER (@a)" +redshift,"NTILE(@x) OVER (@a ROWS UNBOUNDED PRECEDING)","NTILE(@x) OVER (@a)" +redshift,"O*D*R B*","ORDER BY" +redshift,"ROUND(@a,@b)","ROUND(CAST(@a AS FLOAT),@b)" +redshift,"ROUND(@expression,@length,@trunc)","case when @trunc = 0 then ROUND(@expression,@length) else TRUNC(@expression,@length) end" +redshift,"DATEADD(dd,@days,@date)","DATEADD(day,@days,@date)" +redshift,"DATEADD(m,@months,@date)","DATEADD(month,@months,@date)" +redshift,"DATEADD(mm,@months,@date)","DATEADD(month,@months,@date)" +redshift,"DATEADD(yyyy,@years,@date)","DATEADD(year,@years,@date)" +redshift,"DATEADD(yy,@years,@date)","DATEADD(year,@years,@date)" +redshift,"DATEADD(qq,@n,@date)","DATEADD(quarter,@n,@date)" +redshift,"DATEADD(q,@n,@date)","DATEADD(quarter,@n,@date)" +redshift,"DATEADD(wk,@n,@date)","DATEADD(week,@n,@date)" +redshift,"DATEADD(ww,@n,@date)","DATEADD(week,@n,@date)" +redshift,"DATEADD(hh,@n,@date)","DATEADD(hour,@n,@date)" +redshift,"DATEADD(mi,@n,@date)","DATEADD(minute,@n,@date)" +redshift,"DATEADD(n,@n,@date)","DATEADD(minute,@n,@date)" +redshift,"DATEADD(ss,@n,@date)","DATEADD(second,@n,@date)" +redshift,"DATEADD(mcs,@n,@date)","DATEADD(microsecond,@n,@date)" +redshift,"DATEADD(@part,@n,@date)","DATEADD(@part,CAST(@n as int),@date)" +redshift,"DATEDIFF(dd,@start,@end)","DATEDIFF(day,@start,@end)" +redshift,"DATEDIFF(m,@start,@end)","DATEDIFF(month,@start,@end)" +redshift,"DATEDIFF(mm,@start,@end)","DATEDIFF(month,@start,@end)" +redshift,"DATEDIFF(yyyy,@start,@end)","DATEDIFF(year,@start,@end)" +redshift,"DATEDIFF(yy,@start,@end)","DATEDIFF(year,@start,@end)" +redshift,"DATEDIFF(qq,@start,@end)","DATEDIFF(quarter,@start,@end)" +redshift,"DATEDIFF(q,@start,@end)","DATEDIFF(quarter,@start,@end)" +redshift,"DATEDIFF(wk,@start,@end)","DATEDIFF(week,@start,@end)" +redshift,"DATEDIFF(ww,@start,@end)","DATEDIFF(week,@start,@end)" +redshift,"DATEDIFF(hh,@start,@end)","DATEDIFF(hour,@start,@end)" +redshift,"DATEDIFF(mi,@start,@end)","DATEDIFF(minute,@start,@end)" +redshift,"DATEDIFF(n,@start,@end)","DATEDIFF(minute,@start,@end)" +redshift,"DATEDIFF(ss,@start,@end)","DATEDIFF(second,@start,@end)" +redshift,"DATEDIFF(mcs,@start,@end)","DATEDIFF(microsecond,@start,@end)" +redshift,"DATEDIFF_BIG(dd,@start,@end)","DATEDIFF(day,@start,@end)" +redshift,"DATEDIFF_BIG(day,@start,@end)","DATEDIFF(day,@start,@end)" +redshift,"DATEDIFF_BIG(m,@start,@end)","DATEDIFF(month,@start,@end)" +redshift,"DATEDIFF_BIG(mm,@start,@end)","DATEDIFF(month,@start,@end)" +redshift,"DATEDIFF_BIG(yyyy,@start,@end)","DATEDIFF(year,@start,@end)" +redshift,"DATEDIFF_BIG(yy,@start,@end)","DATEDIFF(year,@start,@end)" +redshift,"DATEDIFF_BIG(qq,@start,@end)","DATEDIFF(quarter,@start,@end)" +redshift,"DATEDIFF_BIG(q,@start,@end)","DATEDIFF(quarter,@start,@end)" +redshift,"DATEDIFF_BIG(wk,@start,@end)","DATEDIFF(week,@start,@end)" +redshift,"DATEDIFF_BIG(ww,@start,@end)","DATEDIFF(week,@start,@end)" +redshift,"DATEDIFF_BIG(hh,@start,@end)","DATEDIFF(hour,@start,@end)" +redshift,"DATEDIFF_BIG(hour,@start,@end)","DATEDIFF(hour,@start,@end)" +redshift,"DATEDIFF_BIG(mi,@start,@end)","DATEDIFF(minute,@start,@end)" +redshift,"DATEDIFF_BIG(minute,@start,@end)","DATEDIFF(minute,@start,@end)" +redshift,"DATEDIFF_BIG(n,@start,@end)","DATEDIFF(minute,@start,@end)" +redshift,"DATEDIFF_BIG(ss,@start,@end)","DATEDIFF(second,@start,@end)" +redshift,"DATEDIFF_BIG(second,@start,@end)","DATEDIFF(second,@start,@end)" +redshift,"DATEDIFF_BIG(mcs,@start,@end)","DATEDIFF(microsecond,@start,@end)" +redshift,"DATEPART(dd,@date)","DATEPART(day,@date)" +redshift,"DATEPART(m,@date)","DATEPART(month,@date)" +redshift,"DATEPART(mm,@date)","DATEPART(month,@date)" +redshift,"DATEPART(yyyy,@date)","DATEPART(year,@date)" +redshift,"DATEPART(yy,@date)","DATEPART(year,@date)" +redshift,"DATEPART(qq,@date)","DATEPART(quarter,@date)" +redshift,"DATEPART(q,@date)","DATEPART(quarter,@date)" +redshift,"DATEPART(wk,@date)","DATEPART(week,@date)" +redshift,"DATEPART(ww,@date)","DATEPART(week,@date)" +redshift,"DATEPART(hh,@date)","DATEPART(hour,@date)" +redshift,"DATEPART(mi,@date)","DATEPART(minute,@date)" +redshift,"DATEPART(n,@date)","DATEPART(minute,@date)" +redshift,"DATEPART(ss,@date)","DATEPART(second,@date)" +redshift,"DATEPART(mcs,@date)","DATEPART(microsecond,@date)" +redshift,"CONVERT(VARCHAR,@date,112)","TO_CHAR(@date, 'YYYYMMDD')" +redshift,GETDATE(),CURRENT_DATE +redshift,+ '@a',|| '@a' +redshift,'@a' +,'@a' || +redshift,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +redshift,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +redshift,CAST(@a AS varchar) +,CAST(@a AS varchar) || +redshift,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +redshift,"DATEFROMPARTS(@year,@month,@day)","TO_DATE(TO_CHAR(@year,'0000FM')||'-'||TO_CHAR(@month,'00FM')||'-'||TO_CHAR(@day,'00FM'), 'YYYY-MM-DD')" +redshift,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","CAST(TO_CHAR(@year,'0000FM')||'-'||TO_CHAR(@month,'00FM')||'-'||TO_CHAR(@day,'00FM')||' '||TO_CHAR(@hour,'00FM')||':'||TO_CHAR(@minute,'00FM')||':'||TO_CHAR(@second,'00FM')||'.'||TO_CHAR(@ms,'000FM') as TIMESTAMP)" +redshift,YEAR(@date),EXTRACT(YEAR FROM @date) +redshift,MONTH(@date),EXTRACT(MONTH FROM @date) +redshift,DAY(@date),EXTRACT(DAY FROM @date) +redshift,EOMONTH(@date),LAST_DAY(@date) +redshift,VAR(@a),VARIANCE(@a) +redshift,STDEV(@a),STDDEV(@a) +redshift,RAND(),RANDOM() +redshift,"HASHBYTES('MD5',@a)",MD5(@a) +redshift,"CONVERT(VARBINARY, @a, 1)","STRTOL(LEFT(@a, 15), 16)" +redshift,LEN(@a),CHAR_LENGTH(@a) +redshift,"LOG(@expression,@base)",(LN(CAST((@expression) AS REAL))/LN(CAST((@base) AS REAL))) +redshift,LOG(@expression),LN(CAST((@expression) AS REAL)) +redshift,LOG10(@expression),LOG(CAST((@expression) AS REAL)) +redshift,"ISNULL(@a,@b)","COALESCE(@a,@b)" +redshift,COUNT_BIG(@a),COUNT(@a) +redshift,SQUARE(@a),((@a) * (@a)) +redshift,TEXT,VARCHAR(max) +redshift,NTEXT,VARCHAR(max) +redshift,NEWID(),MD5(RANDOM()::TEXT || GETDATE()::TEXT) +redshift,USE @schema;,SET search_path TO @schema; +redshift,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +redshift,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +redshift,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE @table;","DROP TABLE IF EXISTS #@table;" +redshift,.dbo.,. +redshift,"HINT DISTRIBUTE_ON_KEY(@key) @hint CREATE TABLE @table (@definition);",HINT DISTRIBUTE_ON_KEY(@key) @hint\nCREATE TABLE @table (@definition)\nDISTKEY(@key); +redshift,"HINT DISTRIBUTE_ON_RANDOM @hint CREATE TABLE @table (@definition);",HINT DISTRIBUTE_ON_RANDOM @hint\nCREATE TABLE @table (@definition)\nDISTSTYLE EVEN; +redshift,"HINT @hint SORT_ON_KEY(@type:@key) CREATE TABLE @table (@definition) @options;",HINT @hint SORT_ON_KEY(@type:@key)\nCREATE TABLE @table (@definition)\n@options\n@type SORTKEY(@key); +redshift,"CREATE TABLE @table (@a1 person_id @a2);",CREATE TABLE @table (@a1 person_id @a2)\nDISTKEY(person_id); +redshift,"CREATE TABLE @table (@a1 subject_id @a2);",CREATE TABLE @table (@a1 subject_id @a2)\nDISTKEY(subject_id); +redshift,"CREATE TABLE @table (@a1 analysis_id @a2);",CREATE TABLE @table (@a1 analysis_id @a2)\nDISTKEY(analysis_id); +redshift,"CREATE TABLE @table (@definition);",CREATE TABLE @table (@definition)\nDISTSTYLE ALL; +redshift,HINT DISTRIBUTE_ON_KEY(@key) WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT DISTRIBUTE_ON_KEY(@key)\nCREATE TABLE @d\nDISTKEY(@key)\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +redshift,HINT DISTRIBUTE_ON_RANDOM WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT DISTRIBUTE_ON_RANDOM\nCREATE TABLE @d\nDISTSTYLE EVEN\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +redshift,HINT SORT_ON_KEY(@type:@sortkey) WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT SORT_ON_KEY(@type:@sortkey)\nCREATE TABLE @d\n@type SORTKEY(@sortkey)\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +redshift,HINT DISTRIBUTE_ON_KEY(@key) SORT_ON_KEY(@type:@sortkey) WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT DISTRIBUTE_ON_KEY(@key) SORT_ON_KEY(@type:@sortkey)\nCREATE TABLE @d\nDISTKEY(@key)\n@type SORTKEY(@sortkey)\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +redshift,HINT DISTRIBUTE_ON_RANDOM SORT_ON_KEY(@type:@sortkey) WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT DISTRIBUTE_ON_RANDOM SORT_ON_KEY(@type:@sortkey)\nCREATE TABLE @d\nDISTSTYLE EVEN\n@type SORTKEY(@sortkey)\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +redshift,WITH @a INSERT INTO @b SELECT @c;,INSERT INTO @b WITH @a SELECT @c; +redshift,WITH @a AS @b SELECT @c1 person_id as @(\w+\b)key @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(@key)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 person_id as @key @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 person_id @(\w+\b)key @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(@key)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 person_id @key @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 person_id @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(person_id)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 person_id @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 subject_id as @(\w+\b)key @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(@key)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 subject_id as @key @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 subject_id @(\w+\b)key @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(@key)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 subject_id @key @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 subject_id @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(subject_id)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 subject_id @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 analysis_id as @(\w+\b)key @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(@key)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 analysis_id as @key @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 analysis_id @(\w+\b)key @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(@key)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 analysis_id @key @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c1 analysis_id @c2 INTO @d FROM @e;,CREATE TABLE @d\nDISTKEY(analysis_id)\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c1 analysis_id @c2\nFROM\n@e; +redshift,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d DISTSTYLE ALL\nAS\nWITH\n@a\nAS\n@b\nSELECT\n@c\nFROM\n@e; +redshift,HINT DISTRIBUTE_ON_KEY(@key) SELECT @a INTO @b FROM @c;,HINT DISTRIBUTE_ON_KEY(@key) \nCREATE TABLE @b\nDISTKEY(@key)\nAS\nSELECT\n@a\nFROM\n@c; +redshift,HINT DISTRIBUTE_ON_RANDOM SELECT @a INTO @b FROM @c;,HINT DISTRIBUTE_ON_RANDOM \nCREATE TABLE @b\nDISTSTYLE EVEN\nAS\nSELECT\n@a\nFROM\n@c; +redshift,HINT SORT_ON_KEY(@type:@sortkey) SELECT @a INTO @b FROM @c;,HINT SORT_ON_KEY(@type:@sortkey)\nCREATE TABLE @b\n@type SORTKEY(@sortkey)\nAS\nSELECT\n@a\nFROM\n@c; +redshift,HINT DISTRIBUTE_ON_KEY(@key) SORT_ON_KEY(@type:@sortkey) SELECT @a INTO @b FROM @c;,HINT DISTRIBUTE_ON_KEY(@key) SORT_ON_KEY(@type:@sortkey)\nCREATE TABLE @b\nDISTKEY(@key)\n@type SORTKEY(@sortkey)\nAS\nSELECT\n@a\nFROM\n@c; +redshift,HINT DISTRIBUTE_ON_RANDOM SORT_ON_KEY(@type:@sortkey) SELECT @a INTO @b FROM @c;,HINT DISTRIBUTE_ON_RANDOM SORT_ON_KEY(@type:@sortkey)\nCREATE TABLE @b\nDISTSTYLE EVEN\n@type SORTKEY(@sortkey)\nAS\nSELECT\n@a\nFROM\n@c; +redshift,SELECT @a1 person_id as @(\w+\b)key @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(@key)\nAS\nSELECT\n@a1 person_id as @key @a2\nFROM\n@c; +redshift,SELECT @a1 person_id @(\w+\b)key @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(@key)\nAS\nSELECT\n@a1 person_id @key @a2\nFROM\n@c; +redshift,SELECT @a1 person_id @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(person_id)\nAS\nSELECT\n@a1 person_id @a2\nFROM\n@c; +redshift,SELECT @a1 subject_id as @(\w+\b)key @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(@key)\nAS\nSELECT\n@a1 subject_id as @key @a2\nFROM\n@c; +redshift,SELECT @a1 subject_id @(\w+\b)key @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(@key)\nAS\nSELECT\n@a1 subject_id @key @a2\nFROM\n@c; +redshift,SELECT @a1 subject_id @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(subject_id)\nAS\nSELECT\n@a1 subject_id @a2\nFROM\n@c; +redshift,SELECT @a1 analysis_id as @(\w+\b)key @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(@key)\nAS\nSELECT\n@a1 analysis_id as @key @a2\nFROM\n@c; +redshift,SELECT @a1 analysis_id @(\w+\b)key @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(@key)\nAS\nSELECT\n@a1 analysis_id @key @a2\nFROM\n@c; +redshift,SELECT @a1 analysis_id @a2 INTO @b FROM @c;,CREATE TABLE @b\nDISTKEY(analysis_id)\nAS\nSELECT\n@a1 analysis_id @a2\nFROM\n@c; +redshift,SELECT @a INTO @b FROM @c;,CREATE TABLE @b DISTSTYLE ALL\nAS\nSELECT\n@a\nFROM\n@c; +redshift,SELECT @a INTO @b;,CREATE TABLE @b DISTSTYLE ALL\nAS\nSELECT\n@a; +redshift,[ person_id ],[person_id] +redshift,[ subject_id ],[subject_id] +redshift,[ analysis_id ],[analysis_id] +redshift,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +redshift,DATETIME,TIMESTAMP +redshift,SELECT DISTINCT TOP @([0-9]+)rows,SELECT TOP @rows DISTINCT +redshift,BIT,BOOLEAN +redshift,MONEY,"DECIMAL(19, 4)" +redshift,SMALLMONEY,"DECIMAL(10, 4)" +redshift,TINYINT,SMALLINT +redshift,FLOAT(@s),FLOAT +redshift,DATETIME2(@p),TIMESTAMP +redshift,DATETIME2,TIMESTAMP +redshift,DATETIME,TIMESTAMP +redshift,DATETIMEOFFSET(@p),TIMESTAMPTZ +redshift,DATETIMEOFFSET,TIMESTAMPTZ +redshift,SMALLDATETIME,TIMESTAMP +redshift,UNIQUEIDENTIFIER,CHAR(36) +redshift,STDEVP(@a),STDDEV_POP(@a) +redshift,VARP(@a),VAR_POP(@a) +redshift,"DATETIME2FROMPARTS(@year,@month,@day,@hour,@minute,@seconds,0,0)","CAST(TO_CHAR(@year,'0000FM')||'-'||TO_CHAR(@month,'00FM')||'-'||TO_CHAR(@day,'00FM')||' '||TO_CHAR(@hour,'00FM')||':'||TO_CHAR(@minute,'00FM')||':'||TO_CHAR(@seconds,'00FM') as TIMESTAMP)" +redshift,"DATETIME2FROMPARTS(@year,@month,@day,@hour,@minute,@seconds,@fractions,@precision)","CAST(TO_CHAR(@year,'0000FM')||'-'||TO_CHAR(@month,'00FM')||'-'||TO_CHAR(@day,'00FM')||' '||TO_CHAR(@hour,'00FM')||':'||TO_CHAR(@minute,'00FM')||':'||TO_CHAR(@seconds,'00FM')||'.'||TO_CHAR(@fractions,repeat('0', @precision) || 'FM') as TIMESTAMP)" +redshift,"DATETIMEOFFSETFROMPARTS (@year,@month,@day,@hour,@minute,@seconds,0,@h_offset,@m_offset,0 )","CAST(TO_CHAR(@year,'0000FM')||'-'||TO_CHAR(@month,'00FM')||'-'||TO_CHAR(@day,'00FM')||' '||TO_CHAR(@hour,'00FM')||':'||TO_CHAR(@minute,'00FM')||':'||TO_CHAR(@seconds,'00FM')||case when @h_offset >= 0 then '+' else '-' end ||TO_CHAR(ABS(@h_offset),'00FM')||':'||TO_CHAR(ABS(@m_offset),'00FM') as TIMESTAMPTZ)" +redshift,"DATETIMEOFFSETFROMPARTS (@year,@month,@day,@hour,@minute,@seconds,@fractions,@h_offset,@m_offset,@precision )","CAST(TO_CHAR(@year,'0000FM')||'-'||TO_CHAR(@month,'00FM')||'-'||TO_CHAR(@day,'00FM')||' '||TO_CHAR(@hour,'00FM')||':'||TO_CHAR(@minute,'00FM')||':'||TO_CHAR(@seconds,'00FM')||'.'||TO_CHAR(@fractions,repeat('0',@precision) || 'FM')||case when @h_offset >= 0 then '+' else '-' end ||TO_CHAR(ABS(@h_offset),'00FM')||':'||TO_CHAR(ABS(@m_offset),'00FM') as TIMESTAMPTZ)" +redshift,GETUTCDATE(),CURRENT_TIMESTAMP +redshift,"SMALLDATETIMEFROMPARTS(@year,@month,@day,@hour,@minute )","CAST(TO_CHAR(@year,'0000FM')||'-'||TO_CHAR(@month,'00FM')||'-'||TO_CHAR(@day,'00FM')||' '||TO_CHAR(@hour,'00FM')||':'||TO_CHAR(@minute,'00FM') as TIMESTAMP)" +redshift,SYSUTCDATETIME(),CURRENT_TIMESTAMP +redshift,"TODATETIMEOFFSET(@expression,@timezone)","CAST(TO_CHAR(CAST(@expression as TIMESTAMP), 'YYYY-MM-DD HH24:MI:SS.US') ||@timezone as TIMESTAMPTZ)" +redshift,"ATN2(@a,@b)","ATAN2(@a,@b)" +redshift,"CHARINDEX(@expression,@in,@start)","case when CHARINDEX(@expression, SUBSTRING(@in,@start)) > 0 then (CHARINDEX(@expression, SUBSTRING(@in,@start)) +@start - 1) else 0 end" +redshift,QUOTENAME(@a),QUOTE_IDENT(@a) +redshift,"SPACE(@n)","REPEAT(' ',@n)" +redshift,"STUFF(@expression,@start,@length,@replace)","SUBSTRING(@expression, 0,@start)||@replace||SUBSTRING(@expression,@start +@length)" +redshift,"CONCAT(@a,@b,@tail)","CONCAT(@a,CONCAT(@b,@tail))" +redshift,"ISDATE(@s)","REGEXP_INSTR(@s, '^(\\d{4}[/\-]?[01]\\d[/\-]?[0123]\\d)([ T]([0-1][0-9]|[2][0-3]):([0-5][0-9])(:[0-5][0-9](.\\d+)?)?)?$')" +redshift,"ISNUMERIC(@s)","REGEXP_INSTR(@s, '^[\-\+]?(\\d*\\.)?\\d+([Ee][\-\+]?\\d+)?$')" +redshift,"PATINDEX(@pattern,@expression)","REGEXP_INSTR(@expression, case when LEFT(@pattern,1)<>'%' and RIGHT(@pattern,1)='%' then '^' else '' end||TRIM('%' FROM REPLACE(@pattern,'_','.'))||case when LEFT(@pattern,1)='%' and RIGHT(@pattern,1)<>'%' then '$' else '' end)" +redshift,^,# +redshift,"CONVERT(DATE, @a)","CAST(@a as DATE)" +redshift,"CONVERT(TIMESTAMPTZ, @a)","CONVERT(TIMESTAMP WITH TIME ZONE, @a)" +redshift,UPDATE STATISTICS @a;,ANALYZE @a; +pdw,...@([0-9]+|y)a,xxx@a +pdw,CREATE INDEX @index_name ON #@table (@variable);,-- PDW does not support non-clustered index on temp tables. +pdw,VARCHAR(MAX),VARCHAR(1000) +pdw,HINT DISTRIBUTE_ON_KEY(@key) @hint WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT DISTRIBUTE_ON_KEY(@key) @hint\nCREATE TABLE @d WITH (DISTRIBUTION = HASH(@key))\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +pdw,HINT DISTRIBUTE_ON_RANDOM @hint WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT DISTRIBUTE_ON_RANDOM @hint\nCREATE TABLE @d WITH (DISTRIBUTION = ROUND_ROBIN)\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +pdw,"WITH @a AS @b SELECT @c1 subject_id, @c2 INTO @d FROM @e;","CREATE TABLE @d WITH (DISTRIBUTION = HASH(subject_id))\nAS\nWITH @a AS @b SELECT\n@c1 subject_id, @c2\nFROM\n@e;" +pdw,"WITH @a AS @b SELECT @c1 person_id, @c2 INTO @d FROM @e;","CREATE TABLE @d WITH (DISTRIBUTION = HASH(person_id))\nAS\nWITH @a AS @b SELECT\n@c1 person_id, @c2\nFROM\n@e;" +pdw,"WITH @a AS @b SELECT @c1 analysis_id, @c2 INTO @d FROM @e;","CREATE TABLE @d WITH (DISTRIBUTION = HASH(analysis_id))\nAS\nWITH @a AS @b SELECT\n@c1 analysis_id, @c2\nFROM\n@e;" +pdw,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d WITH (DISTRIBUTION = REPLICATE)\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +pdw,HINT DISTRIBUTE_ON_KEY(@key) @hint SELECT @a INTO @b FROM @c;,HINT DISTRIBUTE_ON_KEY(@key) @hint\nCREATE TABLE @b WITH (DISTRIBUTION = HASH(@key))\nAS\nSELECT\n@a\nFROM\n@c; +pdw,HINT DISTRIBUTE_ON_RANDOM @hint SELECT @a INTO @b FROM @c;,HINT DISTRIBUTE_ON_RANDOM @hint\nCREATE TABLE @b WITH (DISTRIBUTION = ROUND_ROBIN)\nAS\nSELECT\n@a\nFROM\n@c; +pdw,"SELECT @a1 subject_id, @a2 INTO @b FROM @c;","CREATE TABLE @b WITH (DISTRIBUTION = HASH(subject_id))\nAS\nSELECT\n@a1 subject_id, @a2\nFROM\n@c;" +pdw,"SELECT @a1 person_id, @a2 INTO @b FROM @c;","CREATE TABLE @b WITH (DISTRIBUTION = HASH(person_id))\nAS\nSELECT\n@a1 person_id, @a2\nFROM\n@c;" +pdw,"SELECT @a1 analysis_id, @a2 INTO @b FROM @c;","CREATE TABLE @b WITH (DISTRIBUTION = HASH(analysis_id))\nAS\nSELECT\n@a1 analysis_id, @a2\nFROM\n@c;" +pdw,SELECT @a INTO @b FROM @c;,CREATE TABLE @b WITH (DISTRIBUTION = REPLICATE)\nAS\nSELECT\n@a\nFROM\n@c; +pdw,SELECT @a INTO @b;,CREATE TABLE @b WITH (DISTRIBUTION = REPLICATE)\nAS\nSELECT\n@a; +pdw,CREATE TABLE #@a WITH (DISTRIBUTION = @b) AS,"CREATE TABLE #@a WITH (LOCATION = USER_DB, DISTRIBUTION = @b) AS" +pdw,HINT DISTRIBUTE_ON_KEY(@key) @hint CREATE TABLE @table (@definition);,HINT DISTRIBUTE_ON_KEY(@key) @hint\nCREATE TABLE @table (@definition)\nWITH (DISTRIBUTION = HASH(@key)); +pdw,HINT DISTRIBUTE_ON_RANDOM @hint CREATE TABLE @table (@definition);,HINT DISTRIBUTE_ON_RANDOM @hint\nCREATE TABLE @table (@definition)\nWITH (DISTRIBUTION = ROUND_ROBIN); +pdw,CREATE TABLE @table (@definition_part1 subject_id @definition_part2);,CREATE TABLE @table (@definition_part1 subject_id @definition_part2)\nWITH (DISTRIBUTION = HASH(subject_id)); +pdw,CREATE TABLE @table (@definition_part1 person_id @definition_part2);,CREATE TABLE @table (@definition_part1 person_id @definition_part2)\nWITH (DISTRIBUTION = HASH(person_id)); +pdw,CREATE TABLE @table (@definition_part1 analysis_id @definition_part2);,CREATE TABLE @table (@definition_part1 analysis_id @definition_part2)\nWITH (DISTRIBUTION = HASH(analysis_id)); +pdw,CREATE TABLE @table (@definition);,CREATE TABLE @table (@definition)\nWITH (DISTRIBUTION = REPLICATE); +pdw,CREATE TABLE #@table (@definition) WITH (DISTRIBUTION = @distribution);,"CREATE TABLE #@table (@definition)\nWITH (LOCATION = USER_DB, DISTRIBUTION = @distribution);" +pdw,[ person_id ],[person_id] +pdw,[ subject_id ],[subject_id] +pdw,[ analysis_id ],[analysis_id] +pdw,TRUNCATE TABLE ,IF XACT_STATE() = 1 COMMIT; TR*NC*T* TABLE +pdw,DROP TABLE ,IF XACT_STATE() = 1 COMMIT; DR*P TABLE +pdw,CREATE TABLE ,IF XACT_STATE() = 1 COMMIT; CR**T* TABLE +pdw,TR*NC*T*,TRUNCATE +pdw,DR*P,DROP +pdw,CR**T*,CREATE +pdw,IF OBJECT_ID(@a) IS NOT NULL IF XACT_STATE() = 1 COMMIT;,IF XACT_STATE() = 1 COMMIT; IF OBJECT_ID(@a) IS NOT NULL +pdw,IF OBJECT_ID(@a) IS NULL IF XACT_STATE() = 1 COMMIT;,IF XACT_STATE() = 1 COMMIT; IF OBJECT_ID(@a) IS NULL +pdw,"CONSTRAINT @a DEFAULT GETDATE()","" +pdw,"DEFAULT GETDATE()","" +pdw,CREATE INDEX @index_name ON @table (@variable) WHERE @b;,CREATE INDEX @index_name ON @table (@variable); +impala,...@([0-9]+|y)a,xxx@a +impala,TRY_CAST(@a),CAST(@a) +impala,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +impala,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,-- impala does not support indexes +impala,CREATE INDEX @index_name ON @table (@variable);,-- impala does not support indexes +impala,"CHARINDEX(@a,@b)","INSTR(@b,@a)" +impala,COUNT_BIG(@a),COUNT(@a) +impala,"LEFT(@str,@chars)","SUBSTR(@str,1,@chars)" +impala,LEN(@a),LENGTH(@a) +impala,LOG(@expression),LN(@expression) +impala,NEWID(),UUID() +impala,"RIGHT(@str,@chars)","SUBSTR(@str,-@chars)" +impala,"ROUND(@a,@b)","ROUND(CAST(@a AS DOUBLE),@b)" +impala,SQUARE(@a),((@a)*(@a)) +impala,STDEV(@a),STDDEV(@a) +impala,VAR(@a),VARIANCE(@a) +impala,"DATEADD(d,@days,CAST(@date AS DATE))","DATE_ADD(@date, @days)" +impala,"DATEADD(dd,@days,CAST(@date AS DATE))","DATE_ADD(@date, @days)" +impala,"DATEADD(day,@days,CAST(@date AS DATE))","DATE_ADD(@date, @days)" +impala,"DATEADD(month,@months,CAST(@date AS DATE))","ADD_MONTHS(@date, @months)" +impala,"DATEADD(mm,@months,CAST(@date AS DATE))","ADD_MONTHS(@date, @months)" +impala,"DATEADD(m,@months,CAST(@date AS DATE))","ADD_MONTHS(@date, @months)" +impala,"DATEADD(year,@years,CAST(@date AS DATE))","ADD_MONTHS(@date, 12 * @years)" +impala,"DATEADD(yyyy,@years,CAST(@date AS DATE))","ADD_MONTHS(@date, 12 * @years)" +impala,"DATEADD(yy,@years,CAST(@date AS DATE))","ADD_MONTHS(@date, 12 * @years)" +impala,"DATEADD(d,@days,@date)","DATE_ADD(CAST(@date AS DATE), @days)" +impala,"DATEADD(dd,@days,@date)","DATE_ADD(CAST(@date AS DATE), @days)" +impala,"DATEADD(day,@days,@date)","DATE_ADD(CAST(@date AS DATE), @days)" +impala,"DATEADD(month,@months,@date)","ADD_MONTHS(CAST(@date AS DATE), @months)" +impala,"DATEADD(mm,@months,@date)","ADD_MONTHS(CAST(@date AS DATE), @months)" +impala,"DATEADD(m,@months,@date)","ADD_MONTHS(CAST(@date AS DATE), @months)" +impala,"DATEADD(year,@years,@date)","ADD_MONTHS(CAST(@date AS DATE), 12 * @years)" +impala,"DATEADD(yyyy,@years,@date)","ADD_MONTHS(CAST(@date AS DATE), 12 * @years)" +impala,"DATEADD(yy,@years,@date)","ADD_MONTHS(CAST(@date AS DATE), 12 * @years)" +impala,"DATEDIFF(d,@start, @end)","DATEDIFF(CAST(@end AS DATE), CAST(@start AS DATE))" +impala,"DATEDIFF(dd,@start, @end)","DATEDIFF(CAST(@end AS DATE), CAST(@start AS DATE))" +impala,"DATEDIFF(day,@start, @end)","DATEDIFF(CAST(@end AS DATE), CAST(@start AS DATE))" +impala,"DATEDIFF(year,@start, @end)",(YEAR(CAST(@end AS DATE)) - YEAR(CAST(@start AS DATE))) +impala,"DATEDIFF(yyyy,@start, @end)",(YEAR(CAST(@end AS DATE)) - YEAR(CAST(@start AS DATE))) +impala,"DATEDIFF(yy,@start, @end)",(YEAR(CAST(@end AS DATE)) - YEAR(CAST(@start AS DATE))) +impala,"DATEDIFF(month,@start, @end)","INT_MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +impala,"DATEDIFF(mm,@start, @end)","INT_MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +impala,"DATEDIFF(m,@start, @end)","INT_MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +impala,"DATEFROMPARTS(@year,@month,@day)","to_timestamp(CONCAT(CAST(@year AS VARCHAR),'-',CAST(@month AS VARCHAR),'-',CAST(@day AS VARCHAR)), 'yyyy-M-d')" +impala,"eomonth(@date)","days_sub(add_months(trunc(CAST(@date AS TIMESTAMP), 'MM'),1),1)" +impala,DAY(@date),DAY(CAST(@date AS DATE)) +impala,GETDATE(),NOW() +impala,MONTH(@date),MONTH(CAST(@date AS DATE)) +impala,YEAR(@date),YEAR(CAST(@date AS DATE)) +impala,"DATEPART(YEAR, @date)",YEAR(CAST(@date AS DATE)) +impala,"DATEPART(MONTH, @date)",MONTH(CAST(@date AS DATE)) +impala,"DATEPART(DAY, @date)",DAY(CAST(@date AS DATE)) +impala,CAST(@a AS DATE),"CASE TYPEOF(@a) WHEN 'TIMESTAMP' THEN CAST(@a AS TIMESTAMP) ELSE TO_UTC_TIMESTAMP(CONCAT_WS('-', SUBSTR(CAST(@a AS STRING), 1, 4), SUBSTR(CAST(@a AS STRING), 5, 2), SUBSTR(CAST(@a AS STRING), 7, 2)), 'UTC') END" +impala,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +impala,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +impala,(SELECT @a UNION SELECT @b) ORDER BY,SELECT * FROM\n(SELECT @a\nUNION\nSELECT @b)\nAS t1 ORDER BY +impala,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d STORED AS PARQUET\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e;\n UPDATE STATISTICS @d; +impala,SELECT @a INTO @b FROM @c;,CREATE TABLE @b STORED AS PARQUET AS\nSELECT\n@a\nFROM\n@c;\n UPDATE STATISTICS @b; +impala,SELECT @a INTO @b;,CREATE TABLE @b STORED AS PARQUET AS\nSELECT\n@a;\n UPDATE STATISTICS @b; +impala,SELECT DISTINCT @a FROM @b INTERSECT SELECT DISTINCT @a FROM @c;,SELECT t1.@a FROM (SELECT DISTINCT @a FROM @b UNION ALL SELECT DISTINCT @a FROM @c) AS t1 GROUP BY @a HAVING COUNT(*) >= 2; +impala,(SELECT DISTINCT @a FROM @b INTERSECT SELECT DISTINCT @a FROM @c),(SELECT t1.@a FROM (SELECT DISTINCT @a FROM @b UNION ALL SELECT DISTINCT @a FROM @c) AS t1 GROUP BY @a HAVING COUNT(*) >= 2) +impala,DELETE FROM @a WHERE @b;,INSERT OVERWRITE TABLE @a SELECT * FROM @a WHERE NOT(@b); +impala,DELETE FROM @a;,TRUNCATE TABLE @a; +impala,.dbo.,. +impala,##, +impala,#@([^\s]+)table.@([^\s]+)field,%session_id%@table.@field +impala,#,%temp_prefix%%session_id% +impala,,## +impala,.location,.`location` +impala,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +impala,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +impala,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +impala,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +impala,DATE,TIMESTAMP +impala,DATETIME,TIMESTAMP +impala,DATETIME2,TIMESTAMP +impala,BIGINT NOT NULL,BIGINT +impala,BOOLEAN NOT NULL,BOOLEAN +impala,CHAR NOT NULL,CHAR +impala,DECIMAL NOT NULL,DECIMAL +impala,DOUBLE PRECISION,DOUBLE +impala,DOUBLE NOT NULL,DOUBLE +impala,FLOAT NOT NULL,FLOAT +impala,INT NOT NULL,INT +impala,INTEGER NOT NULL,INT +impala,REAL NOT NULL,REAL +impala,SMALLINT NOT NULL,SMALLINT +impala,STRING NOT NULL,STRING +impala,TIMESTAMP NOT NULL,TIMESTAMP +impala,TINYINT NOT NULL,TINYINT +impala,VARCHAR(@a) NOT NULL,VARCHAR(@a) +impala,BIGINT NULL,BIGINT +impala,BOOLEAN NULL,BOOLEAN +impala,CHAR NULL,CHAR +impala,DECIMAL NULL,DECIMAL +impala,DOUBLE NULL,DOUBLE +impala,FLOAT NULL,FLOAT +impala,INT NULL,INT +impala,REAL NULL,REAL +impala,SMALLINT NULL,SMALLINT +impala,STRING NULL,STRING +impala,TIMESTAMP NULL,TIMESTAMP +impala,TINYINT NULL,TINYINT +impala,VARCHAR(@a) NULL,VARCHAR(@a) +impala,"CHAR,","CHAR(1)," +impala,"CHAR\n+","CHAR(1)\n" +impala,"CHAR)","CHAR(1))" +impala,"CONSTRAINT @a DEFAULT NOW()","" +impala,"DEFAULT NOW()","" +impala,stats,_stats +impala,UPDATE STATISTICS @a;,COMPUTE STATS @a; +impala,"ISNUMERIC(@a)","case when regexp_like(@a,'^([0-9]+\.?[0-9]*|\.[0-9]+)$') then 1 else 0 end" +impala,"HASHBYTES('MD5',@a)","fnv_hash(@a)" +impala,"CONVERT(VARBINARY, @a, 1)","cast(conv(@a, 16, 10) as int)" +netezza,...@([0-9]+|y)a,xxx@a +netezza,TRY_CAST(@a),CAST(@a) +netezza,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +netezza,HINT DISTRIBUTE_ON_KEY(@key)\n@statement;,HINT DISTRIBUTE_ON_KEY(@key)\n@statement\nDISTRIBUTE ON (@key); +netezza,HINT DISTRIBUTE_ON_RANDOM\n@statement;,HINT DISTRIBUTE_ON_RANDOM\n@statement\nDISTRIBUTE ON RANDOM; +netezza,CREATE TABLE #@table (@definition);,CREATE TEMP TABLE @table (@definition); +netezza,WITH @a AS @b SELECT @c INTO #@d FROM @e;,CREATE TEMP TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +netezza,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +netezza,SELECT @a INTO #@b FROM @c;,CREATE TEMP TABLE @b\nAS\nSELECT\n@a\nFROM\n@c; +netezza,SELECT @a INTO @b FROM @c;,CREATE TABLE @b\nAS\nSELECT\n@a\nFROM\n@c; +netezza,SELECT @a INTO #@b;,CREATE TEMP TABLE @b\nAS\nSELECT\n@a; +netezza,SELECT @a INTO @b;,CREATE TABLE @b\nAS\nSELECT @a; +netezza,"ROUND(@a,@b)","ROUND(CAST(@a AS NUMERIC),@b)" +netezza,"CAST('@a' AS DATE)","TO_DATE('@a', 'yyyymmdd')" +netezza,"CAST('@a' + @b AS DATE)","TO_DATE('@a' + @b, 'yyyymmdd')" +netezza,"CAST(@a + '@b' AS DATE)","TO_DATE(@a + '@b', 'yyyymmdd')" +netezza,"CAST(CONCAT(@a) AS DATE)","TO_DATE(CONCAT(@a), 'yyyymmdd')" +netezza,CAST(@a AS INT),CAST(@a AS INTEGER) +netezza,CAST(@a AS VARCHAR),CAST(@a AS VARCHAR(1000)) +netezza,"CONVERT(VARCHAR,@date,112)","TO_CHAR(@date, 'YYYYMMDD')" +netezza,"DATEADD(d,@days,@date)",(@date + @days) +netezza,"DATEADD(dd,@days,@date)",(@date + @days) +netezza,"DATEADD(day,@days,@date)",(@date + @days) +netezza,"DATEADD(m,@months,@date)",CAST((@date + @months*INTERVAL'1 month') AS DATE) +netezza,"DATEADD(mm,@months,@date)",CAST((@date + @months*INTERVAL'1 month') AS DATE) +netezza,"DATEADD(month,@months,@date)",CAST((@date + @months*INTERVAL'1 month') AS DATE) +netezza,"DATEADD(yy,@years,@date)",CAST((@date + @years*INTERVAL'1 year') AS DATE) +netezza,"DATEADD(yyyy,@years,@date)",CAST((@date + @years*INTERVAL'1 year') AS DATE) +netezza,"DATEADD(year,@years,@date)",CAST((@date + @years*INTERVAL'1 year') AS DATE) +netezza,"DATEDIFF(d,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +netezza,"DATEDIFF(dd,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +netezza,"DATEDIFF(day,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +netezza,"DATEDIFF(year,@start, @end)","(DATE_PART('YEAR', CAST(@end AS DATE)) - DATE_PART('YEAR', CAST(@start AS DATE)))" +netezza,"DATEDIFF(yyyy,@start, @end)","(DATE_PART('YEAR', CAST(@end AS DATE)) - DATE_PART('YEAR', CAST(@start AS DATE)))" +netezza,"DATEDIFF(yy,@start, @end)","(DATE_PART('YEAR', CAST(@end AS DATE)) - DATE_PART('YEAR', CAST(@start AS DATE)))" +netezza,"DATEDIFF(month,@start, @end)","MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +netezza,"DATEDIFF(mm,@start, @end)","MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +netezza,"DATEDIFF(m,@start, @end)","MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE))" +netezza,GETDATE(),CURRENT_DATE +netezza,+ '@a',|| '@a' +netezza,'@a' +,'@a' || +netezza,YEAR(@variable),"DATE_PART('YEAR', @variable)" +netezza,MONTH(@variable),"DATE_PART('MONTH', @variable)" +netezza,DAY(@variable),"DATE_PART('DAY', @variable)" +netezza,"DATEPART(YEAR, @date)","DATEPART('YEAR', @date)" +netezza,"DATEPART(MONTH, @date)","DATEPART('MONTH', @date)" +netezza,"DATEPART(DAY, @date)","DATEPART('DAY', @date)" +netezza,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +netezza,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +netezza,STDEV(@a),STDDEV(@a) +netezza,LEN(@a),CHAR_LENGTH(@a) +netezza,"LOG(@expression,@base)",(LN(@expression)/LN(@base)) +netezza,LOG(@expression),LN(@expression) +netezza,LOG10(@expression),LOG(@expression) +netezza,"ISNULL(@a,@b)","COALESCE(@a,@b)" +netezza,COUNT_BIG(@a),COUNT(@a) +netezza,USE @schema;,SET search_path TO @schema; +netezza,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition) DISTRIBUTE ON RANDOM; +netezza,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE @table IF EXISTS; +netezza,.dbo.,. +netezza,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,-- netezza does not support indexes +netezza,CREATE INDEX @index_name ON @table (@variable);,-- netezza does not support indexes +netezza,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +netezza,VARCHAR(MAX),VARCHAR(1000) +netezza,FLOAT,"FLOAT(6)" +netezza,#, +netezza,"LEFT(@variable,@b)","SUBSTR(@variable, 1, @b)" +netezza,"RIGHT(@variable,@b)","SUBSTR(@variable, LENGTH(@variable)-@b+1, @b)" +netezza,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +netezza,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +netezza,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +netezza,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +netezza,"CONCAT(@a, @b, @c)","CONCAT(@a, CONCAT(@b, @c))" +netezza,"CONCAT(@a,@b)","@a || @b" +netezza,"POWER(@a,@b)","POW(@a,@b)" +netezza,EOMONTH(@date),LAST_DAY(@date) +netezza,ROWCOUNT(), ROW_NUMBER() +netezza,"DATEFROMPARTS(@year,@month,@day)","TO_DATE(TO_CHAR(@year,'0000')||'-'||TO_CHAR(@month,'00')||'-'||TO_CHAR(@day,'00'), 'YYYY-MM-DD')" +netezza,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","TO_DATE(TO_CHAR(@year,'0000')||'-'||TO_CHAR(@month,'00')||'-'||TO_CHAR(@day,'00')||' '||TO_CHAR(@hour,'00')||':'||TO_CHAR(@minute,'00')||':'||TO_CHAR(@second,'00'), 'YYYY-MM-DD HH24:MI:SS')" +netezza,WITH @a INSERT INTO @b SELECT @c;,INSERT INTO @b WITH @a SELECT @c; +netezza,UPDATE STATISTICS @a;,GENERATE STATISTICS ON @a; +netezza,DATETIME,TIMESTAMP +netezza,DATETIME2,TIMESTAMP +netezza,"ISNUMERIC(@a)","CASE WHEN translate(@a,'0123456789','') in ('','.','-','-.') THEN 1 ELSE 0 END" +netezza,"HASHBYTES('MD5',@a)","hash(@a)" +netezza,"CONVERT(VARBINARY, @a, 1)","hex_to_binary(@a)" +netezza,RAND(),RANDOM() +netezza,DROP TABLE IF EXISTS #@table;,DROP TABLE @table IF EXISTS; +netezza,DROP TABLE IF EXISTS @table;,DROP TABLE @table IF EXISTS; +bigquery,...@([0-9]+|y)a,xxx@a +bigquery,"AS drvd(@a)","AS drvd(@a)" +bigquery,"@a, @b)","@a, @b)" +bigquery,"","NULL AS " +bigquery,"FROM (VALUES @a) AS drvd","FROM (@a) AS drvd" +bigquery,"@a, @b)","@a UNION ALL @b)" +bigquery,"(@a)","SELECT @a" +bigquery,"FROM (SELECT @a) AS drvd(@b)","FROM (SELECT @b UNION ALL SELECT @a LIMIT 999999 OFFSET 1) AS values_table" +bigquery,TRY_CAST(@a),CAST(@a) +bigquery,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +bigquery,"CONVERT(VARBINARY, @a, 1)","safe_cast(concat('0x', @a) as int64)" +bigquery,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +bigquery,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +bigquery,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +bigquery,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +bigquery,"DATEDIFF(d,@start, @end)","DATE_DIFF(cast(@end as date), cast(@start as date), DAY)" +bigquery,"DATEDIFF(dd,@start, @end)","DATE_DIFF(cast(@end as date), cast(@start as date), DAY)" +bigquery,"DATEDIFF(day,@start, @end)","DATE_DIFF(cast(@end as date), cast(@start as date), DAY)" +bigquery,"DATEDIFF(year,@start, @end)",(EXTRACT(YEAR from CAST(@end AS DATE)) - EXTRACT(YEAR from CAST(@start AS DATE))) +bigquery,"DATEDIFF(yyyy,@start, @end)",(EXTRACT(YEAR from CAST(@end AS DATE)) - EXTRACT(YEAR from CAST(@start AS DATE))) +bigquery,"DATEDIFF(yy,@start, @end)",(EXTRACT(YEAR from CAST(@end AS DATE)) - EXTRACT(YEAR from CAST(@start AS DATE))) +bigquery,"DATEDIFF(month,@start, @end)","((12 * EXTRACT(YEAR FROM CAST(@end AS DATE)) + EXTRACT(MONTH FROM CAST(@end AS DATE))) - (12 * EXTRACT(YEAR FROM CAST(@start AS DATE)) + EXTRACT(MONTH FROM CAST(@start AS DATE))) + IF(EXTRACT(DAY FROM CAST(@end AS DATE)) >= EXTRACT(DAY FROM CAST(@start AS DATE)), 0, -1))" +bigquery,"DATEDIFF(mm,@start, @end)","((12 * EXTRACT(YEAR FROM CAST(@end AS DATE)) + EXTRACT(MONTH FROM CAST(@end AS DATE))) - (12 * EXTRACT(YEAR FROM CAST(@start AS DATE)) + EXTRACT(MONTH FROM CAST(@start AS DATE))) + IF(EXTRACT(DAY FROM CAST(@end AS DATE)) >= EXTRACT(DAY FROM CAST(@start AS DATE)), 0, -1))" +bigquery,"DATEDIFF(m,@start, @end)","((12 * EXTRACT(YEAR FROM CAST(@end AS DATE)) + EXTRACT(MONTH FROM CAST(@end AS DATE))) - (12 * EXTRACT(YEAR FROM CAST(@start AS DATE)) + EXTRACT(MONTH FROM CAST(@start AS DATE))) + IF(EXTRACT(DAY FROM CAST(@end AS DATE)) >= EXTRACT(DAY FROM CAST(@start AS DATE)), 0, -1))" +bigquery,"DATEADD(d,@days,@date)","DATE_ADD(cast(@date as date), INTERVAL @days DAY)" +bigquery,"DATEADD(dd,@days,@date)","DATE_ADD(cast(@date as date), INTERVAL @days DAY)" +bigquery,"DATEADD(day,@days,@date)","DATE_ADD(cast(@date as date), INTERVAL @days DAY)" +bigquery,"DATEADD(m,@months,@date)","DATE_ADD(cast(@date as date), INTERVAL @months MONTH)" +bigquery,"DATEADD(mm,@months,@date)","DATE_ADD(cast(@date as date), INTERVAL @months MONTH)" +bigquery,"DATEADD(month,@months,@date)","DATE_ADD(cast(@date as date), INTERVAL @months MONTH)" +bigquery,"DATEADD(yy,@years,@date)","DATE_ADD(@date, INTERVAL @years YEAR)" +bigquery,"DATEADD(yyyy,@years,@date)","DATE_ADD(@date, INTERVAL @years YEAR)" +bigquery,"DATEADD(year,@years,@date)","DATE_ADD(@date, INTERVAL @years YEAR)" +bigquery,INTERVAL @(-?[0-9]+)a.0,INTERVAL @a +bigquery,CAST(@a AS VARCHAR) + @b(@c),"CONCAT(CAST(@a AS VARCHAR), @b(@c))" +bigquery,@([a-z]+)a(@b) + CAST(@c AS VARCHAR),"CONCAT(@a(@b), CAST(@c AS VARCHAR))" +bigquery,CAST(@a AS VARCHAR(@n)) + @b(@c),"CONCAT(CAST(@a AS VARCHAR(@n)), @b(@c))" +bigquery,@([a-z]+)a(@b) + CAST(@c AS VARCHAR(@n)),"CONCAT(@a(@b), CAST(@c AS VARCHAR(@n)))" +bigquery,'@a' + @b(@c),"CONCAT('@a', @b(@c))" +bigquery,@([a-z]+)a(@b) + '@c',"CONCAT(@a(@b), '@c')" +bigquery,'@a' + @b FROM,"CONCAT('@a', @b) FROM" +bigquery,@([a-z0-9_]+)a + '@b',"CONCAT(@a, '@b')" +bigquery,CAST(@a AS VARCHAR) + @b FROM,"CONCAT(CAST(@a AS VARCHAR), @b) FROM" +bigquery,@([a-z0-9_]+)a + CAST(@b AS VARCHAR),"CONCAT(@a, CAST(@b AS VARCHAR))" +bigquery,CAST(@a AS VARCHAR(@n)) + @b FROM,"CONCAT(CAST(@a AS VARCHAR(@n)), @b) FROM" +bigquery,@([a-z0-9_]+)a + CAST(@b AS VARCHAR(@n)),"CONCAT(@a, CAST(@b AS VARCHAR(@n)))" +bigquery,CONCAT(@a) + @b(@c),"CONCAT(@a, @b(@c))" +bigquery,@([a-z]+)a(@b) + CONCAT(@c),"CONCAT(@a(@b), @c)" +bigquery,CONCAT(@a) + @b FROM,"CONCAT(@a, @b) FROM" +bigquery,@([a-z0-9_]+)a + CONCAT(@b),"CONCAT(@a, @b)" +bigquery,"CONCAT(@a, CONCAT(@b, @c))","CONCAT(@a, @b, @c)" +bigquery,"CONCAT(CONCAT(@a, @b), @c)","CONCAT(@a, @b, @c)" +bigquery,"CONCAT(CONCAT(@a, @b, @c))","CONCAT(@a, @b, @c)" +bigquery,"STDEV(@a)","STDDEV(@a)" +bigquery,"HASHBYTES('MD5',@a)","md5(@a)" +bigquery,"LEN(@a)","LENGTH(@a)" +bigquery,"COUNT_BIG(@a)","COUNT(@a)" +bigquery,"cast(@a % @b as int)","CAST(MOD(@a, @b) AS INT64)" +bigquery,"cast((@a % @b) as int)","CAST(MOD(@a, @b) AS INT64)" +bigquery,"cast(@a) % @([0-9]+)b","MOD(CAST(@a), @b)" +bigquery,"cast(@a) % cast(@b)","MOD(CAST(@a), CAST(@b))" +bigquery,"CAST(@a as:string)","CAST(@a as string)" +bigquery,"CAST(@a as:integer)","CAST(@a as int64)" +bigquery,"CAST(@a as:float)","CAST(@a as float64)" +bigquery,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +bigquery,WITH @a SELECT @b INTO @c FROM @d;,CREATE TABLE @c AS WITH @a SELECT @b FROM @d; +bigquery,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS\nSELECT\n@a\nFROM\n@c; +bigquery,SELECT @a INTO @b;,CREATE TABLE @b\nAS\nSELECT\n@a; +bigquery,WITH @a INSERT INTO @b SELECT @c;,INSERT INTO @b WITH @a SELECT @c; +bigquery,"LEFT(@str,@chars)","SUBSTR(@str,0,@chars)" +bigquery,"RIGHT(@str,@chars)","SUBSTR(@str,-@chars)" +bigquery,"cast(@a as float)","cast(@a as float64)" +bigquery,"cast(@a as bigint)","cast(@a as int64)" +bigquery,"cast(@a as int)","cast(@a as int64)" +bigquery,date(@a),cast(@a as date) +bigquery,"cast(concat(@a) as date)","parse_date('%Y%m%d', concat(@a))" +bigquery,"cast(@a as date)","IF(SAFE_CAST(@a AS DATE) IS NULL,PARSE_DATE('%Y%m%d', cast(@a AS STRING)),SAFE_CAST(@a AS DATE))" +bigquery,"YEAR(@date)","EXTRACT(YEAR from @date)" +bigquery,"MONTH(@date)","EXTRACT(MONTH from @date)" +bigquery,"DAY(@date)","EXTRACT(DAY from @date)" +bigquery,"DATEPART(YEAR, @date)",EXTRACT(YEAR FROM @date) +bigquery,"DATEPART(MONTH, @date)",EXTRACT(MONTH FROM @date) +bigquery,"DATEPART(DAY, @date)",EXTRACT(DAY FROM @date) +bigquery,"union select","union distinct select" +bigquery,INTERSECT,INTERSECT DISTINCT +bigquery,"ISNULL(@a,@b)","IFNULL(@a,@b)" +bigquery,as \" @a \",as @a +bigquery,"coalesce(@([0-9]+)a, @b)","coalesce(@a, cast(@b as int64))" +bigquery,"coalesce(@a, @([0-9]+)b)","coalesce(cast(@a as int64), @b)" +bigquery,"cast(@a as decimal(@b))","cast(@a as float64)" +bigquery,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +bigquery,"int","INT64" +bigquery,"DATETIME2","datetime" +bigquery,"INTEGER","INT64" +bigquery,"bigint","INT64" +bigquery,"float","FLOAT64" +bigquery,VARCHAR(@a),STRING +bigquery,VARCHAR,STRING +bigquery,CHAR(@a),STRING +bigquery,CHAR,STRING +bigquery,STRING NULL,STRING +bigquery,DATE NULL,DATE +bigquery,DATETIME NULL,DATETIME +bigquery,INT64 NULL,INT64 +bigquery,FLOAT64 NULL,FLOAT64 +bigquery,NUMERIC NULL,NUMERIC +bigquery,"DOUBLE PRECISION","FLOAT64" +bigquery,"GETDATE()","CURRENT_DATE()" +bigquery,"CONSTRAINT @a DEFAULT CURRENT_DATE()","" +bigquery,DEFAULT @([0-9]+)a,"" +bigquery,DEFAULT \"@a\","" +bigquery,"DEFAULT CURRENT_DATE()","" +bigquery,TRUNCATE TABLE @a;,DELETE FROM @a WHERE True; +bigquery,CREATE TABLE #@([^\s]+)table,DROP TABLE IF EXISTS %temp_prefix%%session_id%@table;\nCREATE TABLE %temp_prefix%%session_id%@table +bigquery,#@([^\s]+)table.@([^\s]+)field,%session_id%@table.@field +bigquery,#,%temp_prefix%%session_id% +bigquery,CREATE INDEX @index_name ON @table_col_cond;,-- bigquery does not support indexes +bigquery,DROP INDEX @index_name;,-- bigquery does not support indexes +bigquery,"DATEFROMPARTS(@year,@month,@day)","DATE(@year, @month, @day)" +bigquery,EOMONTH(@date),"DATE_SUB(DATE_TRUNC(DATE_ADD(@date, INTERVAL 1 MONTH), MONTH), INTERVAL 1 DAY)" +bigquery,"ISNUMERIC(@a)","CASE WHEN SAFE_CAST(@a AS FLOAT64) IS NULL THEN 0 ELSE 1 END" +bigquery,UPDATE STATISTICS @a;,-- big query does not support such functionality +bigquery,NEWID(),GENERATE_UUID() +bigquery,"AS @(q[0-9]+)a","AS val_@a" +bigquery,"(@(q[0-9]+)a","(val_@a" +bigquery,"CHARINDEX(@a,@b)","STRPOS(@b,@a)" +bigquery,"\"","`" +sqlite,...@([0-9]+|y)a,xxx@a +sqlite,"AS drvd(@a)","AS drvd(@a)" +sqlite,"@a, @b)","@a, @b)" +sqlite,"","NULL AS " +sqlite,"FROM (VALUES @a) AS drvd(@b)","FROM (SELECT @b WHERE (0 = 1) UNION ALL VALUES @a) AS values_table" +sqlite,TRY_CAST(@a),CAST(@a) +sqlite,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +sqlite,"ROUND(@a,@b)","ROUND(CAST(@a AS REAL),@b)" +sqlite,DATETIME,REAL +sqlite,DATETIME2,REAL +sqlite,"CONVERT(DATE, @a)","CAST(STRFTIME('%s', SUBSTR(CAST(@a AS TEXT), 1, 4) || '-' || SUBSTR(CAST(@a AS TEXT), 5, 2) || '-' || SUBSTR(CAST(@a AS TEXT), 7)) AS REAL)" +sqlite,CAST(@a AS DATE),"CAST(STRFTIME('%s', SUBSTR(CAST(@a AS TEXT), 1, 4) || '-' || SUBSTR(CAST(@a AS TEXT), 5, 2) || '-' || SUBSTR(CAST(@a AS TEXT), 7)) AS REAL)" +sqlite,"DATEADD(second,@seconds,@datetime)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@seconds)||' seconds')) AS REAL)" +sqlite,"DATEADD(minute,@minutes,@datetime)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@minutes)||' minutes')) AS REAL)" +sqlite,"DATEADD(hour,@hours,@datetime)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@hours)||' hours')) AS REAL)" +sqlite,"DATEADD(d,@days,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@days)||' days')) AS REAL)" +sqlite,"DATEADD(dd,@days,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@days)||' days')) AS REAL)" +sqlite,"DATEADD(day,@days,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@days)||' days')) AS REAL)" +sqlite,"DATEADD(m,@months,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@months)||' months')) AS REAL)" +sqlite,"DATEADD(mm,@months,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@months)||' months')) AS REAL)" +sqlite,"DATEADD(month,@months,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@months)||' months')) AS REAL)" +sqlite,"DATEADD(yy,@years,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@years)||' years')) AS REAL)" +sqlite,"DATEADD(yyyy,@years,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@years)||' years')) AS REAL)" +sqlite,"DATEADD(year,@years,@date)","CAST(STRFTIME('%s', DATETIME(@date, 'unixepoch', (@years)||' years')) AS REAL)" +sqlite,"DATEDIFF(d,@start,@end)","(JULIANDAY(@end, 'unixepoch') - JULIANDAY(@start, 'unixepoch'))" +sqlite,"DATEDIFF(dd,@start,@end)","(JULIANDAY(@end, 'unixepoch') - JULIANDAY(@start, 'unixepoch'))" +sqlite,"DATEDIFF(day,@start,@end)","(JULIANDAY(@end, 'unixepoch') - JULIANDAY(@start, 'unixepoch'))" +sqlite,"DATEDIFF(year,@start, @end)","(STRFTIME('%Y', @end, 'unixepoch') - STRFTIME('%Y', @start, 'unixepoch'))" +sqlite,"DATEDIFF(yyyy,@start, @end)","(STRFTIME('%Y', @end, 'unixepoch') - STRFTIME('%Y', @start, 'unixepoch'))" +sqlite,"DATEDIFF(yy,@start, @end)","(STRFTIME('%Y', @end, 'unixepoch') - STRFTIME('%Y', @start, 'unixepoch'))" +sqlite,"DATEDIFF(MONTH,@start, @end)","((STRFTIME('%Y', @end, 'unixepoch')*12 + STRFTIME('%m', @end, 'unixepoch')) - (STRFTIME('%Y', @start, 'unixepoch')*12 + STRFTIME('%m', @start, 'unixepoch')) + (CASE WHEN STRFTIME('%d', @end, 'unixepoch') >= STRFTIME('%d', @start, 'unixepoch') then 0 else -1 end))" +sqlite,"JULIANDAY('@literal', 'unixepoch')","JULIANDAY(CAST(STRFTIME('%s', SUBSTR(CAST('@literal' AS TEXT), 1, 4) || '-' || SUBSTR(CAST('@literal' AS TEXT), 5, 2) || '-' || SUBSTR(CAST('@literal' AS TEXT), 7)) AS REAL), 'unixepoch')" +sqlite,"STRFTIME('%Y', '@literal', 'unixepoch')","CAST(SUBSTR('@literal', 1, 4) AS REAL)" +sqlite,"STRFTIME('%m', '@literal', 'unixepoch')","CAST(SUBSTR('@literal', 5, 2) AS REAL)" +sqlite,"STRFTIME('%d', '@literal', 'unixepoch')","CAST(SUBSTR('@literal', 7, 2) AS REAL)" +sqlite,"CONVERT(VARCHAR,@date,112)","CAST(STRFTIME('%Y%m%d', @date) AS REAL)" +sqlite,GETDATE(),"STRFTIME('%s','now')" +sqlite,+ '@a',|| '@a' +sqlite,'@a' +,'@a' || +sqlite,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +sqlite,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +sqlite,CAST(@a AS varchar) +,CAST(@a AS varchar) || +sqlite,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +sqlite,"DATEFROMPARTS(@year,@month,@day)","STRFTIME('%s', SUBSTR(CAST('0000'||CAST(@year AS INT) AS TEXT),-4) || '-' || SUBSTR(CAST('00'||CAST(@month AS INT) AS TEXT),-2) || '-' || SUBSTR(CAST('00'||CAST(@day AS INT) AS TEXT),-2))" +sqlite,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","STRFTIME('%s', SUBSTR(CAST('0000'||CAST(@year AS INT) AS TEXT),-4) || '-' || SUBSTR(CAST('00'||CAST(@month AS INT) AS TEXT),-2) || '-' || SUBSTR(CAST('00'||CAST(@day AS INT) AS TEXT),-2) || ' ' || SUBSTR(CAST('00'||CAST(@hour AS INT) AS TEXT),-2) || ':' || SUBSTR(CAST('00'||CAST(@minute AS INT) AS TEXT),-2) || ':' || SUBSTR(CAST('00'||CAST(@second AS INT) AS TEXT),-2) || '.' || SUBSTR(CAST('000'||CAST(@ms AS INT) AS TEXT),-3))" +sqlite,YEAR(@date),"CAST(STRFTIME('%Y', @date, 'unixepoch') AS INT)" +sqlite,MONTH(@date),"CAST(STRFTIME('%m', @date, 'unixepoch') AS INT)" +sqlite,DAY(@date),"CAST(STRFTIME('%d', @date, 'unixepoch') AS INT)" +sqlite,"DATEPART(YEAR, @date)","CAST(STRFTIME('%Y', @date, 'unixepoch') AS INT)" +sqlite,"DATEPART(MONTH, @date)","CAST(STRFTIME('%m', @date, 'unixepoch') AS INT)" +sqlite,"DATEPART(DAY, @date)","CAST(STRFTIME('%d', @date, 'unixepoch') AS INT)" +sqlite,EOMONTH(@date),"STRFTIME('%s', DATETIME(@date, 'unixepoch', 'start of month', '+1 month', '-1 day'))" +sqlite,VAR(@a),VARIANCE(@a) +sqlite,RAND(),((RANDOM()+9223372036854775808) / 18446744073709551615) +sqlite,LEN(@a),LENGTH(@a) +sqlite,"LOG(@expression,@base)","(LOG(@expression)/LOG(@base))" +sqlite,"ISNULL(@a,@b)","COALESCE(@a,@b)" +sqlite,"ISNUMERIC(@a)","CASE WHEN @a GLOB '[0-9]*' OR @a GLOB '[0-9]*.[0-9]*' OR @a GLOB '.[0-9]*' THEN 1 ELSE 0 END" +sqlite,COUNT_BIG(@a),COUNT(@a) +sqlite,NEWID(),RANDOM() +sqlite,"RIGHT(@a,@b)","SUBSTR(CAST(@a AS TEXT),-@b)" +sqlite,"LEFT(@str,@chars)","SUBSTR(CAST(@str AS TEXT),1,@chars)" +sqlite,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +sqlite,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +sqlite,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +sqlite,.dbo.,. +sqlite,CREATE TABLE #@table (@definition),CREATE TEMP TABLE @table (@definition) +sqlite,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE INDEX @index_name ON @table (@variable); +sqlite,CREATE UNIQUE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE UNIQUE INDEX @index_name ON @table (@variable); +sqlite,CREATE INDEX @index_name ON @schema.@table (@variable);,CREATE INDEX @index_name ON @table (@variable); +sqlite,CREATE UNIQUE INDEX @index_name ON @schema.@table (@variable);,CREATE UNIQUE INDEX @index_name ON @table (@variable); +sqlite,DROP INDEX @schema.@index_name;,DROP INDEX @index_name; +sqlite,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +sqlite,VARCHAR(@a),TEXT +sqlite,VARCHAR,TEXT +sqlite,FLOAT,REAL +sqlite,WITH @a AS @b SELECT @c INTO #@d FROM @e;,CREATE TEMP TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e;\nANALYZE @d; +sqlite,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +sqlite,SELECT @a INTO #@b FROM @c;,CREATE TEMP TABLE @b\nAS\nSELECT\n@a\nFROM\n@c;\nANALYZE @b; +sqlite,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS\nSELECT\n@a\nFROM\n@c; +sqlite,SELECT @a INTO @b;,CREATE TABLE @b AS\nSELECT\n@a; +sqlite,#@([^\s]+)table.@([^\s]+)field,@table.@field +sqlite,#,temp. +sqlite,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +sqlite,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +sqlite,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +sqlite,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +sqlite,"WITH @cte AS (SELECT @s1 '@literal' @s2)","WITH @cte AS (SELECT @s1 CAST('@literal' as TEXT) @s2)" +sqlite,UPDATE STATISTICS @a;,ANALYZE @a; +sqlite,TRUNCATE TABLE @a;,DELETE FROM @a; +sqlite,"CONCAT(@a, @b, @c)","CONCAT(@a, CONCAT(@b, @c))" +sqlite,"CONCAT(@a, @b)","@a || @b" +sqlite,CEILING(@a),CEIL(@a) +sqlite,IN (SELECT @a) UNION,IN ((SELECT @a)) UNION +sqlite,(SELECT @a) UNION,SELECT @a UNION +sqlite,UNION (@a),UNION @a +sqlite,UNION ALL (@a),UNION ALL @a +sqlite,"ALTER TABLE @table ALTER COLUMN @a BIGINT;","SELECT 0;" +sqlite,"ALTER TABLE @table ADD @a, @b;","ALTER TABLE @table ADD @a; ALTER TABLE @table ADD @b;" +hive,...@([0-9]+|y)a,xxx@a +hive,TRY_CAST(@a),CAST(@a) +hive,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +hive,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,-- hive does not support indexes +hive,CREATE INDEX @index_name ON @table (@variable);,-- hive does not support indexes +hive,CREATE INDEX @index_name ON @table (@variable) WHERE @c;,-- hive does not support indexes +hive,"CHARINDEX(@a,@b)","INSTR(@b,@a)" +hive,COUNT_BIG(@a),COUNT(@a) +hive,"LEFT(@str,@chars)","SUBSTR(@str,1,@chars)" +hive,LEN(@a),LENGTH(@a) +hive,LOG(@expression),LN(@expression) +hive,NEWID(),"reflect('java.util.UUID','randomUUID')" +hive,"RIGHT(@str,@chars)","SUBSTR(@str,-@chars)" +hive,"ROUND(@a,@b)","ROUND(CAST(@a AS DOUBLE),@b)" +hive,SQUARE(@a),((@a)*(@a)) +hive,STDEV(@a),STDDEV_POP(@a) +hive,VAR(@a),VARIANCE(@a) +hive,"DATEADD(d,@days,CAST(@date AS DATE))","DATE_ADD(@date, @days)" +hive,"DATEADD(dd,@days,CAST(@date AS DATE))","DATE_ADD(@date, @days)" +hive,"DATEADD(day,@days,CAST(@date AS DATE))","DATE_ADD(@date, @days)" +hive,"DATEADD(month,@months,CAST(@date AS DATE))","CAST(ADD_MONTHS(@date, @months) AS TIMESTAMP)" +hive,"DATEADD(mm,@months,CAST(@date AS DATE))","CAST(ADD_MONTHS(@date, @months) AS TIMESTAMP)" +hive,"DATEADD(m,@months,CAST(@date AS DATE))","CAST(ADD_MONTHS(@date, @months) AS TIMESTAMP)" +hive,"DATEADD(year,@years,CAST(@date AS DATE))","CAST(ADD_MONTHS(@date, 12 * @years) AS TIMESTAMP)" +hive,"DATEADD(yyyy,@years,CAST(@date AS DATE))","CAST(ADD_MONTHS(@date, 12 * @years) AS TIMESTAMP)" +hive,"DATEADD(yy,@years,CAST(@date AS DATE))","CAST(ADD_MONTHS(@date, 12 * @years) AS TIMESTAMP)" +hive,"DATEADD(d,@days,@date)","DATE_ADD(CAST(@date AS TIMESTAMP), @days)" +hive,"DATEADD(dd,@days,@date)","DATE_ADD(CAST(@date AS TIMESTAMP), @days)" +hive,"DATEADD(day,@days,@date)","DATE_ADD(CAST(@date AS TIMESTAMP), @days)" +hive,"DATEADD(month,@months,@date)","CAST(ADD_MONTHS(CAST(@date AS TIMESTAMP), @months) AS TIMESTAMP)" +hive,"DATEADD(mm,@months,@date)","CAST(ADD_MONTHS(CAST(@date AS TIMESTAMP), @months) AS TIMESTAMP)" +hive,"DATEADD(m,@months,@date)","CAST(ADD_MONTHS(CAST(@date AS TIMESTAMP), @months) AS TIMESTAMP)" +hive,"DATEADD(year,@years,@date)","CAST(ADD_MONTHS(CAST(@date AS TIMESTAMP), 12 * @years) AS TIMESTAMP)" +hive,"DATEADD(yyyy,@years,@date)","CAST(ADD_MONTHS(CAST(@date AS TIMESTAMP), 12 * @years) AS TIMESTAMP)" +hive,"DATEADD(yy,@years,@date)","CAST(ADD_MONTHS(CAST(@date AS TIMESTAMP), 12 * @years) AS TIMESTAMP)" +hive,"DATEDIFF(d,@start, @end)","day(CAST(@end AS TIMESTAMP) - CAST(@start AS TIMESTAMP))" +hive,"DATEDIFF(dd,@start, @end)","day(CAST(@end AS TIMESTAMP) - CAST(@start AS TIMESTAMP))" +hive,"DATEDIFF(day,@start, @end)","day(CAST(@end AS TIMESTAMP) - CAST(@start AS TIMESTAMP))" +hive,"DATEDIFF(year,@start, @end)",(YEAR(CAST(@end AS DATE)) - YEAR(CAST(@start AS DATE))) +hive,"DATEDIFF(yyyy,@start, @end)",(YEAR(CAST(@end AS DATE)) - YEAR(CAST(@start AS DATE))) +hive,"DATEDIFF(yy,@start, @end)",(YEAR(CAST(@end AS DATE)) - YEAR(CAST(@start AS DATE))) +hive,"DATEDIFF(month,@start, @end)","CAST(MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE)) AS INT)" +hive,"DATEDIFF(mm,@start, @end)","CAST(MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE)) AS INT)" +hive,"DATEDIFF(m,@start, @end)","CAST(MONTHS_BETWEEN(CAST(@end AS DATE), CAST(@start AS DATE)) AS INT)" +hive,"DATEFROMPARTS(@year,@month,@day)","CAST(CONCAT(CAST(@year AS STRING),'-',CAST(@month AS STRING),'-',CAST(@day AS STRING)) AS TIMESTAMP)" +hive,"eomonth(@date)","CAST(last_day(@date) AS TIMESTAMP)" +hive,GETDATE(),unix_timestamp() +hive,year(unix_timestamp()),year(from_unixtime(unix_timestamp())) +hive,"DATEPART(YEAR, @date)",year(from_unixtime(unix_timestamp())) +hive,"DATEPART(MONTH, @date)",month(from_unixtime(unix_timestamp())) +hive,"DATEPART(DAY, @date)",day(from_unixtime(unix_timestamp())) +hive,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +hive,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +hive,"HINT PARTITION(@p) @before CREATE TABLE IF NOT EXISTS @table(@pid,@fields) @after;",partitioned table \n@before CREATE TABLE IF NOT EXISTS @table (@fields) @after \n PARTITIONED BY(@p); +hive,"HINT BUCKET(@bucket,@size) @before CREATE TABLE IF NOT EXISTS @table(@fields) @after;",table with bucket \n@before CREATE TABLE IF NOT EXISTS @table (@fields) @after \n CLUSTERED by (@bucket) into @size BUCKETS; +hive,"HINT PARTITION(@p) @before CREATE TABLE @table(@pid,@fields) @after;",partitioned table \n@before CREATE TABLE @table (@fields) @after \n PARTITIONED BY(@p); +hive,"HINT BUCKET(@bucket,@size) @before CREATE TABLE @table(@fields) @after;",table with bucket \n@before CREATE TABLE @table (@fields) @after \n CLUSTERED by (@bucket) into @size BUCKETS; +hive,(SELECT @a UNION SELECT @b) ORDER BY,SELECT * FROM\n(SELECT @a\nUNION\nSELECT @b)\nAS t1 ORDER BY +hive,"WITH @a AS (@b), @c AS (@d), @e AS (@f) SELECT @i INTO #@j FROM","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; DROP TABLE IF EXISTS @e;\n\nCREATE TEMPORARY TABLE @a AS @b;\nCREATE TEMPORARY TABLE @c AS @d;\nCREATE TEMPORARY TABLE @e AS @f;\nCREATE TEMPORARY TABLE @j AS SELECT @i FROM" +hive,"WITH @a AS (@b), @c AS (@d), @e AS (@f), @g AS (@h) SELECT @i INTO #@j FROM","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; DROP TABLE IF EXISTS @e; DROP TABLE IF EXISTS @g;\n\nCREATE TEMPORARY TABLE @a AS @b;\nCREATE TEMPORARY TABLE @c AS @d;\nCREATE TEMPORARY TABLE @e AS @f;\nCREATE TEMPORARY TABLE @g AS @h;\nCREATE TEMPORARY TABLE @j AS SELECT @i FROM" +hive,"WITH @a AS (@b), @c AS (@d), @e AS (@f), @g AS (@h), @i AS (@j) SELECT @k INTO #@l FROM","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; DROP TABLE IF EXISTS @e; DROP TABLE IF EXISTS @g; DROP TABLE IF EXISTS @i;\n\nCREATE TEMPORARY TABLE @a AS @b;\nCREATE TEMPORARY TABLE @c AS @d;\nCREATE TEMPORARY TABLE @e AS @f;\nCREATE TEMPORARY TABLE @g AS @h;\nCREATE TEMPORARY TABLE @i AS @j;\nCREATE TEMPORARY TABLE @l AS SELECT @k FROM" +hive,WITH @a AS @b SELECT @c INTO #@d FROM,DROP TABLE IF EXISTS @a;\n\nCREATE TEMPORARY TABLE @a AS @b;\nCREATE TEMPORARY TABLE @d AS SELECT @c FROM +hive,SELECT @a INTO #@b FROM @c;,CREATE TEMPORARY TABLE IF NOT EXISTS @b AS\nSELECT\n@a\nFROM\n@c; +hive,SELECT @a INTO #@b;,CREATE TEMPORARY TABLE IF NOT EXISTS @b AS\nSELECT\n@a; +hive,CREATE TABLE #@table (@definition),CREATE TEMPORARY TABLE IF NOT EXISTS @table (@definition) +hive,"WITH @a AS (@b), @c AS (@d), @e AS (@f) SELECT @i INTO @j FROM","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; DROP TABLE IF EXISTS @e;\n\nCREATE TEMPORARY TABLE @a AS @b;\nCREATE TEMPORARY TABLE @c AS @d;\nCREATE TEMPORARY TABLE @e AS @f;\nSELECT @i INTO @j FROM" +hive,"WITH @a AS (@b), @c AS (@d), @e AS (@f), @g AS (@h) SELECT @i INTO @j FROM","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; DROP TABLE IF EXISTS @e; DROP TABLE IF EXISTS @g;\n\nCREATE TEMPORARY TABLE @a AS @b;\nCREATE TEMPORARY TABLE @c AS @d;\nCREATE TEMPORARY TABLE @e AS @f;\nCREATE TEMPORARY TABLE @g AS @h;\nSELECT @i INTO @j FROM" +hive,"WITH @a AS (@b), @c AS (@d), @e AS (@f), @g AS (@h), @i AS (@j) SELECT @k INTO @l FROM","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; DROP TABLE IF EXISTS @e; DROP TABLE IF EXISTS @g; DROP TABLE IF EXISTS @i;\n\nCREATE TEMPORARY TABLE @a AS @b;\nCREATE TEMPORARY TABLE @c AS @d;\nCREATE TEMPORARY TABLE @e AS @f;\nCREATE TEMPORARY TABLE @g AS @h;\nCREATE TEMPORARY TABLE @i AS @j;\nSELECT @k INTO @l FROM" +hive,WITH @a AS @b SELECT @c INTO @d FROM,DROP TABLE IF EXISTS @a;\n\nCREATE TEMPORARY TABLE @a AS @b;\nSELECT @c INTO @d FROM +hive,"CREATE TEMPORARY TABLE @a AS (@b), @c (@d) as (@e)\n;","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; CREATE TEMPORARY TABLE @a AS (@b)\n;\nCREATE TEMPORARY TABLE @c AS (@e)\n;" +hive,"CREATE TEMPORARY TABLE @a AS (@b), @c as (@d)\n;","DROP TABLE IF EXISTS @a; DROP TABLE IF EXISTS @c; CREATE TEMPORARY TABLE @a AS (@b)\n;\nCREATE TEMPORARY TABLE @c AS (@d)\n;" +hive,DROP TABLE IF EXISTS @a (@b),DROP TABLE IF EXISTS @a +hive,SELECT @a INTO @b FROM @c;,CREATE TABLE IF NOT EXISTS @b AS\nSELECT\n@a\nFROM\n@c; +hive,SELECT @a INTO @b;,CREATE TABLE IF NOT EXISTS @b AS\nSELECT\n@a; +hive,SELECT DISTINCT @a FROM @b INTERSECT SELECT DISTINCT @a FROM @c;,SELECT t1.@a FROM (SELECT DISTINCT @a FROM @b UNION ALL SELECT DISTINCT @a FROM @c) AS t1 GROUP BY @a HAVING COUNT(*) >= 2; +hive,(SELECT DISTINCT @a FROM @b INTERSECT SELECT DISTINCT @a FROM @c),(SELECT t1.@a FROM (SELECT DISTINCT @a FROM @b UNION ALL SELECT DISTINCT @a FROM @c) AS t1 GROUP BY @a HAVING COUNT(*) >= 2) +hive,.dbo.,. +hive,##, +hive,#, +hive,#.@table (,@table ( +hive,#.@table;,@table; +hive,#.@table),@table) +hive,,## +hive,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +hive,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +hive,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +hive,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +hive,DATE,TIMESTAMP +hive,DATETIME,TIMESTAMP +hive,DATETIME2,TIMESTAMP +hive,BIGINT NOT NULL,BIGINT +hive,BOOLEAN NOT NULL,BOOLEAN +hive,CHAR NOT NULL,CHAR +hive,DECIMAL NOT NULL,DECIMAL +hive,DOUBLE NOT NULL,DOUBLE +hive,FLOAT NOT NULL,FLOAT +hive,INT NOT NULL,INT +hive,REAL NOT NULL,FLOAT +hive,SMALLINT NOT NULL,SMALLINT +hive,STRING NOT NULL,VARCHAR +hive,TIMESTAMP NOT NULL,TIMESTAMP +hive,TINYINT NOT NULL,TINYINT +hive,VARCHAR(@a) NOT NULL,VARCHAR(@a) +hive,BIGINT NULL,BIGINT +hive,BOOLEAN NULL,BOOLEAN +hive,CHAR NULL,CHAR +hive,DECIMAL NULL,DECIMAL +hive,DOUBLE NULL,DOUBLE +hive,FLOAT NULL,FLOAT +hive,INT NULL,INT +hive,REAL NULL,FLOAT +hive,SMALLINT NULL,SMALLINT +hive,STRING NULL,VARCHAR +hive,TIMESTAMP NULL,TIMESTAMP +hive,TINYINT NULL,TINYINT +hive,VARCHAR(@a) NULL,VARCHAR(@a) +hive,"CHAR,","CHAR(1)," +hive,"CHAR\n+","CHAR(1)\n" +hive,"CHAR)","CHAR(1))" +hive,"CONSTRAINT @a DEFAULT unix_timestamp()","" +hive,"DEFAULT unix_timestamp()","" +hive,stats,_stats +hive,UPDATE STATISTICS @a;,-- hive does not support COMPUTE STATS +hive,CAST(@a AS VARCHAR),CAST(@a AS VARCHAR(1000)) +hive,"ISNULL(@a,@b)","COALESCE(@a,@b)" +hive,(@a) AS select,(@a) AS select +hive,"TABLE @cte_name (@a) AS (","TABLE @cte_name AS (" +hive,"TABLE @cte_name (@a) AS select","TABLE @cte_name AS select" +hive,"WHEN .@digits * ","WHEN 0.@digits * " +hive,">= .@digits * ",">= 0.@digits * " +hive, _stats, stats +hive,"ISNUMERIC(@a)","case when cast(@a as double) is not null then 1 else 0 end" +hive,as \"@a\",as @a +hive,"HASHBYTES('MD5',@a)","hash(@a)" +hive,"CONVERT(VARBINARY, @a, 1)","@a" +spark,...@([0-9]+|y)a,xxx@a +spark,TRY_CAST(@a),CAST(@a) +spark,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +spark,tempdb..#@table +,%temp_prefix%%session_id% || +spark,#@([^\s]+)table.@([^\s]+)field,%session_id%@table.@field +spark,"--HINT BUCKET(@a, @b)","" +spark,"--HINT PARTITION(@a @b)","" +spark,"HINT DISTRIBUTE_ON_KEY(@key) CREATE TABLE IF NOT EXISTS @table\nUSING DELTA\nAS\n@definition;","HINT DISTRIBUTE_ON_KEY(@key)\nCREATE TABLE IF NOT EXISTS @table\nUSING DELTA\nAS\n@definition;\nOPTIMIZE @table ZORDER BY @key;" +spark,"HINT DISTRIBUTE_ON_KEY(@key) IF OBJECT_ID('@d', 'U') IS NULL WITH @a AS @b SELECT @c INTO @d FROM @e;",HINT DISTRIBUTE_ON_KEY(@key)\nCREATE TABLE IF NOT EXISTS @d\nUSING DELTA\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e;\nOPTIMIZE @d ZORDER BY @key; +spark,"HINT DISTRIBUTE_ON_KEY(@key) IF OBJECT_ID('@b', 'U') IS NULL SELECT @a INTO @b FROM @c;",HINT DISTRIBUTE_ON_KEY(@key) \nCREATE TABLE IF NOT EXISTS @b\nUSING DELTA\nAS\nSELECT\n@a\nFROM\n@c;\nOPTIMIZE @b ZORDER BY @key; +spark,"HINT DISTRIBUTE_ON_KEY(@key) IF OBJECT_ID('@b', 'U') IS NULL SELECT @a INTO @b WHERE @c;",HINT DISTRIBUTE_ON_KEY(@key) \nCREATE TABLE IF NOT EXISTS @b\nUSING DELTA\nAS\nSELECT\n@a WHERE @c;\nOPTIMIZE @b ZORDER BY @key; +spark,"HINT DISTRIBUTE_ON_KEY(@key) IF OBJECT_ID('@b', 'U') IS NULL SELECT @a INTO @b GROUP BY @c;",HINT DISTRIBUTE_ON_KEY(@key) \nCREATE TABLE IF NOT EXISTS @b\nUSING DELTA\nAS\nSELECT\n@a\nGROUP BY\n@c;\nOPTIMIZE @b ZORDER BY @key; +spark,HINT DISTRIBUTE_ON_KEY(@key) WITH @a AS @b SELECT @c INTO @d FROM @e;,HINT DISTRIBUTE_ON_KEY(@key)\nCREATE TABLE @d\nUSING DELTA\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e;\nOPTIMIZE @d ZORDER BY @key; +spark,HINT DISTRIBUTE_ON_KEY(@key) SELECT @a INTO @b FROM @c;,HINT DISTRIBUTE_ON_KEY(@key) \nCREATE TABLE @b\nUSING DELTA\nAS\nSELECT\n@a\nFROM\n@c;\nOPTIMIZE @b ZORDER BY @key; +spark,HINT DISTRIBUTE_ON_KEY(@key) SELECT @a INTO @b WHERE @c;,HINT DISTRIBUTE_ON_KEY(@key) \nCREATE TABLE @b\nUSING DELTA\nAS\nSELECT\n@a WHERE @c;\nOPTIMIZE @b ZORDER BY @key; +spark,HINT DISTRIBUTE_ON_KEY(@key) SELECT @a INTO @b GROUP BY @c;,HINT DISTRIBUTE_ON_KEY(@key) \nCREATE TABLE @b\nUSING DELTA\nAS\nSELECT\n@a\nGROUP BY\n@c;\nOPTIMIZE @b ZORDER BY @key; +spark,WITH @with SELECT @c INTO @d FROM @e;,@with\n CREATE TABLE @d\nUSING DELTA\nAS\n(SELECT\n@c\nFROM\n@e); +spark,"@a (@columns) AS (@b),",@a AS (@b) +spark,"@a AS (@b),",@a AS (@b) +spark,@a (@columns) AS (@b),@a AS (@b) +spark,"@a AS (@b)",DROP VIEW IF EXISTS @a; CREATE TEMPORARY VIEW @a AS (@b);\n +spark,SELECT @a INTO @b FROM @c;,CREATE TABLE @b\nUSING DELTA\nAS\nSELECT\n@a\nFROM\n@c; +spark,SELECT @a INTO @b WHERE @c;,CREATE TABLE @b\nUSING DELTA\n AS\nSELECT\n@a WHERE @c; +spark,SELECT @a INTO @b GROUP BY @c;,CREATE TABLE @b\nUSING DELTA\n AS\nSELECT\n@a GROUP BY @c; +spark,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table\nUSING DELTA\nAS\n@definition;",CREATE TABLE IF NOT EXISTS @table\nUSING DELTA\nAS\n@definition; +spark,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +spark,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +spark,CREATE TABLE #@([^\s]+)table,DROP TABLE IF EXISTS %temp_prefix%%session_id%@table;\nCREATE TABLE %temp_prefix%%session_id%@table +spark,#,%temp_prefix%%session_id% +spark,\"@a\",`@a` +spark,+ '@a',|| '@a' +spark,'@a' +,'@a' || +spark,CREATE INDEX @index_name ON @table (@variable);,-- spark does not support indexes +spark,"ROUND(@a,@b)","ROUND(CAST(@a AS DOUBLE),@b)" +spark,"HASHBYTES('MD5',@a)","MD5(@a)" +spark,"CONVERT(VARBINARY, CONCAT('0x', @a), 1)","CAST(CONCAT('x', @a) AS BIT(32))" +spark,"CONVERT(DATE, @a)","TO_DATE(@a, 'yyyy-MM-dd')" +spark,"DATEPART(@part, @date)","DATE_PART('@part', @date)" +spark,"DATEADD(d,@days,@date)","DATEADD(day,@days,@date)" +spark,"DATEADD(dd,@days,@date)","DATEADD(day,@days,@date)" +spark,"DATEADD(m,@months,@date)","DATEADD(month,@months,@date)" +spark,"DATEADD(mm,@months,@date)","DATEADD(month,@months,@date)" +spark,"DATEADD(yy,@years,@date)","DATEADD(year,@years,@date)" +spark,"DATEADD(yyyy,@years,@date)","DATEADD(year,@years,@date)" +spark,"DATEADD(@part,@(-?[0-9]+)a.0,@date)","DATEADD(@part,@a,@date)" +spark,INTERVAL @(-?[0-9]+)a.0,INTERVAL @a +spark,"DATEDIFF(d,@start, @end)","datediff(day,@start,@end)" +spark,"DATEDIFF(dd,@start, @end)","datediff(day,@start,@end)" +spark,"CONVERT(VARCHAR,@date,112)","@date" +spark,GETDATE(),CURRENT_DATE +spark,CAST(@a AS varchar(@b)) +,"SUBSTRING(CAST(@a AS string), 0, @b) ||" +spark,+ CAST(@a AS varchar(@b)),"|| SUBSTRING(CAST(@a AS string), 0, @b)" +spark,CAST(@a AS varchar) +,CAST(@a AS string) || +spark,+ CAST(@a AS varchar),|| CAST(@a AS string) +spark,"DATEFROMPARTS(@year,@month,@day)","to_date(cast(@year as string) || '-' || cast(@month as string) || '-' || cast(@day as string))" +spark,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)",to_timestamp(cast(@year as string) || '-' || cast(@month as string) || '-' || cast(@day as string) || ' ' || cast(@hour as string) || ':' || cast(@minute as string) || ':' || cast(@second as string) || '.' || cast(@ms as string)) +spark,EOMONTH(@date),last_day(@date) +spark,STDEV(@a),STDDEV(@a) +spark,VAR(@a),VARIANCE(@a) +spark,LEN(@a),LENGTH(@a) +spark,"CHARINDEX(@a,@b)","INSTR(@b,@a)" +spark,"LOG(@expression,@base)","(@base,@expression)" +spark,LOG(@expression),LN(@expression) +spark,,LOG +spark,LOG10(@expression),"LOG(10,@expression)" +spark,"ISNULL(@a,@b)","COALESCE(@a,@b)" +spark,"ISNUMERIC(@a)","CASE WHEN CAST(@a AS DOUBLE) IS NOT NULL THEN 1 ELSE 0 END" +spark,COUNT_BIG(@a),COUNT(@a) +spark,SQUARE(@a),((@a)*(@a)) +spark,NEWID(),UUID() +spark,.dbo.,. +spark,CREATE CLUSTERED INDEX @index_name ON @table (@variable);, +spark,CREATE UNIQUE CLUSTERED INDEX @index_name ON @table (@variable);, +spark,PRIMARY KEY NONCLUSTERED, +spark,DATETIME,TIMESTAMP +spark,DATETIME2,TIMESTAMP +spark,VARCHAR(MAX),STRING +spark,VARCHAR(@a),STRING +spark,VARCHAR,STRING +spark,DOUBLE PRECISION,DOUBLE +spark,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +spark,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +spark,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +spark,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +spark,"WITH @cte AS (SELECT @s1 '@literal' @s2)","WITH @cte AS (SELECT @s1 CAST('@literal' as STRING) @s2)" +spark,UPDATE STATISTICS @a;, +spark,"SELECT @columns FROM (@a) @x,(@b) @y;","SELECT @columns FROM (@a) @x cross join (@b) @y;" +spark,"ALTER TABLE @table ADD COLUMN @([\w_-]+)column @type DEFAULT @default;","ALTER TABLE @table ADD COLUMN @column @type; \nALTER TABLE @table SET TBLPROPERTIES('delta.feature.allowColumnDefaults' = 'supported'); \nALTER TABLE @table ALTER COLUMN @column SET DEFAULT @default;" +spark,"CAST(@a AS DATE)","IF(try_cast(@a AS DATE) IS NULL, to_date(cast(@a AS STRING), 'yyyyMMdd'), try_cast(@a AS DATE))" +spark,"DATEADD(@part,@amount,@([0-9a-zA-Z_]+_date)date)","CAST(DATEA##(@part,@amount,@date) AS DATE)" +spark,DATEA##,DATEADD +spark,FLOAT,DOUBLE +sqlite extended,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +sqlite extended,"ROUND(@a,@b)","ROUND(CAST(@a AS REAL),@b)" +sqlite extended,+ '@a',|| '@a' +sqlite extended,'@a' +,'@a' || +sqlite extended,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +sqlite extended,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +sqlite extended,CAST(@a AS varchar) +,CAST(@a AS varchar) || +sqlite extended,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +sqlite extended,VAR(@a),VARIANCE(@a) +sqlite extended,RAND(),RANDOM() +sqlite extended,LEN(@a),LENGTH(@a) +sqlite extended,"LOG(@expression,@base)","(LOG(@expression)/LOG(@base))" +sqlite extended,"ISNULL(@a,@b)","COALESCE(@a,@b)" +sqlite extended,"ISNUMERIC(@a)","CASE WHEN @a GLOB '[0-9]*' OR @a GLOB '[0-9]*.[0-9]*' OR @a GLOB '.[0-9]*' THEN 1 ELSE 0 END" +sqlite extended,COUNT_BIG(@a),COUNT(@a) +sqlite extended,NEWID(),RANDOM() +sqlite extended,"RIGHT(@a,@b)","SUBSTR(@a,-@b)" +sqlite extended,"LEFT(@str,@chars)","SUBSTR(@str,1,@chars)" +sqlite extended,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +sqlite extended,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +sqlite extended,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +sqlite extended,.dbo.,. +sqlite extended,CREATE TABLE #@table (@definition),CREATE TEMP TABLE @table (@definition) +sqlite extended,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE INDEX @index_name ON @table (@variable); +sqlite extended,CREATE UNIQUE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE UNIQUE INDEX @index_name ON @table (@variable); +sqlite extended,CREATE INDEX @index_name ON @schema.@table (@variable);,CREATE INDEX @index_name ON @table (@variable); +sqlite extended,CREATE UNIQUE INDEX @index_name ON @schema.@table (@variable);,CREATE UNIQUE INDEX @index_name ON @table (@variable); +sqlite extended,DROP INDEX @schema.@index_name;,DROP INDEX @index_name; +sqlite extended,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +sqlite extended,VARCHAR(@a),TEXT +sqlite extended,VARCHAR,TEXT +sqlite extended,FLOAT,REAL +sqlite extended,WITH @a AS @b SELECT @c INTO #@d FROM @e;,CREATE TEMP TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e;\nANALYZE @d; +sqlite extended,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +sqlite extended,SELECT @a INTO #@b FROM @c;,CREATE TEMP TABLE @b\nAS\nSELECT\n@a\nFROM\n@c;\nANALYZE @b; +sqlite extended,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS\nSELECT\n@a\nFROM\n@c; +sqlite extended,SELECT @a INTO @b;,CREATE TABLE @b AS\nSELECT\n@a; +sqlite extended,#,temp. +sqlite extended,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +sqlite extended,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +sqlite,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +sqlite,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +sqlite extended,"WITH @cte AS (SELECT @s1 '@literal' @s2)","WITH @cte AS (SELECT @s1 CAST('@literal' as TEXT) @s2)" +sqlite extended,UPDATE STATISTICS @a;,ANALYZE @a; +sqlite extended,TRUNCATE TABLE @a;,DELETE FROM @a; +sqlite extended,"CONCAT(@a, @b, @c)","CONCAT(@a, CONCAT(@b, @c))" +sqlite extended,"CONCAT(@a, @b)","@a || @b" +duckdb,...@([0-9]+|y)a,xxx@a +duckdb,TRY_CAST(@a),CAST(@a) +duckdb,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +duckdb,"ROUND(@a,@b)","ROUND(CAST(@a AS NUMERIC),@b)" +duckdb,"HASHBYTES('MD5',@a)","MD5(@a)" +duckdb,"DATEADD(second,@seconds,@datetime)",(@datetime + TO_SECONDS(CAST(@seconds AS INTEGER))) +duckdb,"DATEADD(minute,@minutes,@datetime)",(@datetime + TO_MINUTES(CAST(@minutes AS INTEGER))) +duckdb,"DATEADD(hour,@hours,@datetime)",(@datetime + TO_HOURS(CAST(@hours AS INTEGER))) +duckdb,"DATEADD(d,@days,@date)",(@date + TO_DAYS(CAST(@days AS INTEGER))) +duckdb,"DATEADD(dd,@days,@date)",(@date + TO_DAYS(CAST(@days AS INTEGER))) +duckdb,"DATEADD(day,@days,@date)",(@date + TO_DAYS(CAST(@days AS INTEGER))) +duckdb,"DATEADD(m,@months,@date)",(@date + TO_MONTHS(CAST(@months AS INTEGER))) +duckdb,"DATEADD(mm,@months,@date)",(@date + TO_MONTHS(CAST(@months AS INTEGER))) +duckdb,"DATEADD(month,@months,@date)",(@date + TO_MONTHS(CAST(@months AS INTEGER))) +duckdb,"DATEADD(yy,@years,@date)",(@date + TO_YEARS(CAST(@years AS INTEGER))) +duckdb,"DATEADD(yyyy,@years,@date)",(@date + TO_YEARS(CAST(@years AS INTEGER))) +duckdb,"DATEADD(year,@years,@date)",(@date + TO_YEARS(CAST(@years AS INTEGER))) +duckdb,INTERVAL'@(-?[0-9]+)a.0 @b',INTERVAL'@a @b' +duckdb,"DATEDIFF(d,@start, @end)","(CONVERT(DATE, @end) - CAST(@start AS DATE))" +duckdb,"DATEDIFF(dd,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +duckdb,"DATEDIFF(day,@start, @end)",(CAST(@end AS DATE) - CAST(@start AS DATE)) +duckdb,"DATEDIFF(year,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +duckdb,"DATEDIFF(yyyy,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +duckdb,"DATEDIFF(yy,@start, @end)",(EXTRACT(YEAR FROM CAST(@end AS DATE)) - EXTRACT(YEAR FROM CAST(@start AS DATE))) +duckdb,"DATEDIFF(month,@start, @end)","(extract(year from age(CAST(@end AS DATE), CAST(@start AS DATE)))*12 + extract(month from age(CAST(@end AS DATE), CAST(@start AS DATE))))" +duckdb,"DATEDIFF(mm,@start, @end)","(extract(year from age(CAST(@end AS DATE), CAST(@start AS DATE)))*12 + extract(month from age(CAST(@end AS DATE), CAST(@start AS DATE))))" +duckdb,"DATEDIFF(m,@start, @end)","(extract(year from age(CAST(@end AS DATE), CAST(@start AS DATE)))*12 + extract(month from age(CAST(@end AS DATE), CAST(@start AS DATE))))" +duckdb,"CONVERT(VARCHAR,@date,112)","STRFTIME(@date, '%Y%m%d')" +duckdb,GETDATE(),CURRENT_DATE +duckdb,"CONVERT(DATE, @a)","CAST(@a AS DATE)" +duckdb,"CAST('@a' AS DATE)","CAST(strptime('@a', '%Y%m%d') AS DATE)" +duckdb,"CAST('@a' + @b AS DATE)","CAST(strptime('@a' + @b, '%Y%m%d') AS DATE)" +duckdb,"CAST(@a + '@b' AS DATE)","CAST(strptime(@a + '@b', '%Y%m%d') AS DATE)" +duckdb,"CAST(CONCAT(@a) AS DATE)","CAST(strptime(CONCAT(@a), '%Y%m%d') AS DATE)" +duckdb,+ '@a',|| '@a' +duckdb,'@a' +,'@a' || +duckdb,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +duckdb,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +duckdb,CAST(@a AS varchar) +,CAST(@a AS varchar) || +duckdb,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +duckdb,"DATEFROMPARTS(@year,@month,@day)","(CAST(@year AS VARCHAR) || '-' || CAST(@month AS VARCHAR) || '-' || CAST(@day AS VARCHAR)) :: DATE" +duckdb,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","(CAST(@year AS VARCHAR) || '-' || CAST(@month AS VARCHAR) || '-' || CAST(@day AS VARCHAR) || '-' || CAST(@hour AS VARCHAR) || '-' || CAST(@minute AS VARCHAR) || '-' || CAST(@second AS VARCHAR)) :: DATE" +duckdb,YEAR(@date),YEAR(CAST(@date AS DATE)) +duckdb,MONTH(@date),MONTH(CAST(@date AS DATE)) +duckdb,DAY(@date),DAY(CAST(@date AS DATE)) +duckdb,"DATEPART(YEAR, @date)",YEAR(CAST(@date AS DATE)) +duckdb,"DATEPART(MONTH, @date)",MONTH(CAST(@date AS DATE)) +duckdb,"DATEPART(DAY, @date)",DAY(CAST(@date AS DATE)) +duckdb,EOMONTH(@date),"(DATE_TRUNC('MONTH', @date) + INTERVAL '1 MONTH' - INTERVAL '1 day')::DATE" +duckdb,STDEV(@a),STDDEV(@a) +duckdb,VAR(@a),VARIANCE(@a) +duckdb,RAND(),RANDOM() +duckdb,LEN(@a),LENGTH(@a) +duckdb,"CHARINDEX(@a,@b)","STRPOS(@b,@a)" +duckdb,"LOG(@expression,@base)",(LN(CAST((@expression) AS REAL))/LN(CAST((@base) AS REAL))) +duckdb,LOG(@expression),LN(CAST((@expression) AS REAL)) +duckdb,,LOG +duckdb,LOG10(@expression),"LOG(@expression)" +duckdb,"ISNULL(@a,@b)","COALESCE(@a,@b)" +duckdb,"ISNUMERIC(@a)","CASE WHEN (CAST(@a AS VARCHAR) ~ '^([0-9]+\.?[0-9]*|\.[0-9]+)$') THEN 1 ELSE 0 END" +duckdb,COUNT_BIG(@a),COUNT(@a) +duckdb,SQUARE(@a),((@a)*(@a)) +duckdb,NEWID(),uuid() +duckdb,USE @schema;,SET search_path TO @schema; +duckdb,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +duckdb,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +duckdb,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +duckdb,.dbo.,. +duckdb,CREATE TABLE #@table (@definition),CREATE TEMP TABLE @table (@definition) +duckdb,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE INDEX @index_name ON @table (@variable); +duckdb,CREATE UNIQUE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE UNIQUE INDEX @index_name ON @table (@variable); +duckdb,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +duckdb,DATETIME,TIMESTAMP +duckdb,DATETIME2,TIMESTAMP +duckdb,VARCHAR(MAX),TEXT +duckdb,FLOAT,NUMERIC +duckdb,WITH @a AS @b SELECT @c INTO #@d FROM @e;,CREATE TEMP TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e;\nANALYZE @d; +duckdb,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +duckdb,SELECT @a INTO #@b FROM @c;,CREATE TEMP TABLE @b\nAS\nSELECT\n@a\nFROM\n@c;\nANALYZE @b; +duckdb,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS\nSELECT\n@a\nFROM\n@c; +duckdb,SELECT @a INTO @b;,CREATE TABLE @b AS\nSELECT\n@a; +duckdb,#, +duckdb,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +duckdb,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +duckdb,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +duckdb,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +duckdb,"WITH @cte AS (SELECT @s1 '@literal' @s2)","WITH @cte AS (SELECT @s1 CAST('@literal' as TEXT) @s2)" +duckdb,UPDATE STATISTICS @a;,ANALYZE @a; +duckdb,TRUNCATE TABLE @a;,DELETE FROM @a; +duckdb,ALTER TABLE @a DROP CONSTRAINT @b;,CREATE TABLE @a_new AS SELECT * FROM @a;DROP TABLE @a;ALTER TABLE @a_new RENAME TO @a; +duckdb,"ALTER TABLE @table ADD @a, @b;","ALTER TABLE @table ADD @a; ALTER TABLE @table ADD @b;" +duckdb,"ALTER TABLE @table ALTER COLUMN @([0-9a-z_]+)a @b;","ALTER TABLE @table ALTER @a TYPE @b;" +snowflake,.@([0-9a-z_]+)a...@([0-9]+|y)b,x@a...@b +snowflake,.@([0-9a-z_]+)a...@([0-9]+|y)b,x@axxx@b +snowflake,...@([0-9]+|y)a,xxx@a +snowflake,"AS drvd(@a)","AS values_table(@a)" +snowflake,TRY_CAST(@a),CAST(@a) +snowflake,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +snowflake,"HASHBYTES('MD5',@a)","MD5(@a)" +snowflake,"CONVERT(VARBINARY, @a, 1)","CAST(CONCAT('x', @a) AS BIT(32))" +snowflake,"CONVERT(DATE, @a)","TO_DATE(@a, 'yyyymmdd')" +snowflake,"CONVERT(VARCHAR,@date,112)","TO_CHAR(@date, 'YYYYMMDD')" +snowflake,"CAST('@a' AS DATE)","TO_DATE('@a', 'YYYYMMDD')" +snowflake,"CAST('@a' + @b AS DATE)","TO_DATE('@a' + @b, 'YYYYMMDD')" +snowflake,"CAST(@a + '@b' AS DATE)","TO_DATE(@a + '@b', 'YYYYMMDD')" +snowflake,"CAST(CONCAT(@a) AS DATE)","TO_DATE(CONCAT(@a), 'YYYYMMDD')" +snowflake,GETDATE(),CURRENT_DATE +snowflake,+ '@a',|| '@a' +snowflake,'@a' +,'@a' || +snowflake,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +snowflake,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +snowflake,CAST(@a AS varchar) +,CAST(@a AS varchar) || +snowflake,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +snowflake,"DATEFROMPARTS(@year,@month,@day)","TO_DATE(TO_CHAR(@year,'FM0000')||'-'||TO_CHAR(@month,'FM00')||'-'||TO_CHAR(@day,'FM00'), 'YYYY-MM-DD')" +snowflake,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","TO_DATE(TO_CHAR(@year,'FM0000')||'-'||TO_CHAR(@month,'FM00')||'-'||TO_CHAR(@day,'FM00')||' '||TO_CHAR(@hour,'FM00')||':'||TO_CHAR(@minute,'FM00')||':'||TO_CHAR(@second,'FM00'), 'YYYY-MM-DD HH24:MI:SS')" +snowflake,YEAR(@date),EXTRACT(YEAR FROM @date) +snowflake,MONTH(@date),EXTRACT(MONTH FROM @date) +snowflake,DAY(@date),EXTRACT(DAY FROM @date) +snowflake,"DATEPART(YEAR, @date)",EXTRACT(YEAR FROM @date) +snowflake,"DATEPART(MONTH, @date)",EXTRACT(MONTH FROM @date) +snowflake,"DATEPART(DAY, @date)",EXTRACT(DAY FROM @date) +snowflake,EOMONTH(@date),last_day(@date) +snowflake,STDEV(@a),STDDEV(@a) +snowflake,VAR(@a),VARIANCE(@a) +snowflake,RAND(),RANDOM() +snowflake,CEILING(@a),CEIL(@a) +snowflake,"LOG(@expression,@base)","(@base,@expression)" +snowflake,LOG(@expression),LN(CAST((@expression) AS REAL)) +snowflake,,LOG +snowflake,LOG10(@expression),"LOG(10,@expression)" +snowflake,"ISNULL(@a,@b)","COALESCE(@a,@b)" +snowflake,"ISNUMERIC(@a)","IS_REAL(TRY_TO_NUMERIC(@a))" +snowflake,CAST(@a AS INTEGER),TRY_CAST(CAST(@a AS TEXT) AS INTEGER) +snowflake,CAST(@a AS int),TRY_CAST(CAST(@a AS TEXT) AS int) +snowflake,COUNT_BIG(@a),COUNT(@a) +snowflake,NEWID(),UUID_STRING() +snowflake,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +snowflake,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +snowflake,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE #@table;",DROP TABLE IF EXISTS #@table; +snowflake,.dbo.,. +snowflake,CREATE TABLE #@table (@definition),CREATE TEMP TABLE #@table (@definition) +snowflake,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,-- snowflake does not support indexes +snowflake,CREATE INDEX @index_name ON @table (@variable);,-- snowflake does not support indexes +snowflake,CREATE INDEX @index_name ON @table (@variable) WHERE @c;,-- snowflake does not support indexes +snowflake,CREATE UNIQUE CLUSTERED INDEX @index_name ON @table (@variable);,-- snowflake does not support indexes +snowflake,DROP INDEX @index_name;,-- snowflake does not support indexes +snowflake,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +snowflake,DATETIME,TIMESTAMP +snowflake,DATETIME2,TIMESTAMP +snowflake,VARCHAR(MAX),TEXT +snowflake,WITH @a AS @b SELECT @c INTO #@d FROM @e;,CREATE TEMP TABLE #@d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +snowflake,WITH @a AS @b SELECT @c INTO @d FROM @e;,CREATE TABLE @d\nAS\nWITH @a AS @b SELECT\n@c\nFROM\n@e; +snowflake,WITH @a INSERT INTO @b SELECT @c;,INSERT INTO @b WITH @a SELECT @c; +snowflake,SELECT @a INTO #@b FROM @c;,CREATE TEMP TABLE #@b\nAS\nSELECT\n@a\nFROM\n@c; +snowflake,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS\nSELECT\n@a\nFROM\n@c; +snowflake,SELECT @a INTO @b;,CREATE TABLE @b AS\nSELECT\n@a; +snowflake,SELECT @a INTO #@b;,CREATE TEMP TABLE #@b AS\nSELECT\n@a; +snowflake,SELECT TOP @([0-9]+)rows @a;,SELECT @a LIMIT @rows; +snowflake,(SELECT TOP @([0-9]+)rows @a),(SELECT @a LIMIT @rows) +snowflake,SELECT DISTINCT TOP @([0-9]+)rows @a;,SELECT DISTINCT @a LIMIT @rows; +snowflake,(SELECT DISTINCT TOP @([0-9]+)rows @a),(SELECT DISTINCT @a LIMIT @rows) +snowflake,"WITH @cte AS (SELECT @s1 '@literal' @s2)","WITH @cte AS (SELECT @s1 CAST('@literal' as TEXT) @s2)" +snowflake,UPDATE STATISTICS @a;, +snowflake,"--HINT BUCKET(@a, @b)", +snowflake,"--HINT PARTITION(@a @b)", +snowflake,"--HINT DISTRIBUTE_ON_KEY(@key)", +snowflake,#,%temp_prefix%%session_id% +snowflake,(@a & @b),"BITAND(@a, @b)" +synapse,...@([0-9]+|y)a,xxx@a +synapse,VARCHAR(MAX),VARCHAR(8000) +synapse,CREATE INDEX @index_name ON #@table (@variable);,-- synapse does not support non-clustered index on temp tables. +synapse,"CONSTRAINT @a DEFAULT GETDATE()","" +synapse,"DEFAULT GETDATE()","" +synapse,CREATE INDEX @index_name ON @table (@variable) WHERE @b;,CREATE INDEX @index_name ON @table (@variable); +synapse,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +synapse,DROP TABLE IF EXISTS #@table;,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE #@table;" +synapse,DROP TABLE IF EXISTS @table;,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;" +synapse,CREATE TABLE IF NOT EXISTS @table (@definition);,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);" +sql server,.@([^\s]+)p2...@([0-9]+)p3,x@p2...@p3 +sql server,.@([^\s]+)p2...@([0-9]+)p3,x@p2...@p3 +sql server,...@([0-9]+|y)a,xxx@a +sql server,DROP TABLE IF EXISTS #@table;,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE #@table;" +sql server,DROP TABLE IF EXISTS @table;,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;" +sql server,CREATE TABLE IF NOT EXISTS @table (@definition);,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);" +iris,...@([0-9]+)a,xxx@a --??? +iris,"IIF(@condition, @whentrue, @whenfalse)","CASE WHEN @condition THEN @whentrue ELSE @whenfalse END" +iris,TRY_CAST(@a),CAST(@a) +iris,+ '@a',|| '@a' +iris,'@a' +,'@a' || +iris,CAST(@a AS varchar(@b)) +,CAST(@a AS varchar(@b)) || +iris,+ CAST(@a AS varchar(@b)),|| CAST(@a AS varchar(@b)) +iris,CAST(@a AS varchar) +,CAST(@a AS varchar) || +iris,+ CAST(@a AS varchar),|| CAST(@a AS varchar) +iris,COUNT_BIG(@a),COUNT(@a) +iris,.dbo.,. +iris,CREATE TABLE #@table (@definition),DROP TABLE IF EXISTS #@table; CREATE GLOBAL TEMPORARY TABLE #@table (@definition) +iris,"IF OBJECT_ID('@table', 'U') IS NULL CREATE TABLE @table (@definition);",CREATE TABLE IF NOT EXISTS @table (@definition); +iris,"IF OBJECT_ID('@table', 'U') IS NOT NULL DROP TABLE @table;",DROP TABLE IF EXISTS @table; +iris,"IF OBJECT_ID('tempdb..#@table', 'U') IS NOT NULL DROP TABLE #@table;",DROP TABLE IF EXISTS #@table; +iris,PRIMARY KEY NONCLUSTERED,PRIMARY KEY +iris,"AS drvd(@a)","AS drvd(@a)" +iris,"@a, @b)","@a, @b)" +iris,"","NULL AS " +iris,"FROM (VALUES @a) AS drvd(@b)","FROM ((SELECT @b WHERE (0 = 1)) UNION ALL VALUES @a) AS values_table" +iris,"UNION ALL VALUES (@a), (@b)","UNION ALL (SELECT @a) UNION ALL VALUES (@b)" +iris,"UNION ALL VALUES (@a)","UNION ALL (SELECT @a)" +iris,SELECT @a INTO #@b FROM @c;,CREATE GLOBAL TEMPORARY TABLE #@b AS SELECT @a FROM @c; +iris,SELECT @a INTO @b FROM @c;,CREATE TABLE @b AS SELECT @a FROM @c; +iris,SELECT @a INTO @b;,CREATE TABLE @b AS SELECT @a; +iris,SELECT @a INTO #@b;,CREATE GLOBAL TEMPORARY TABLE #@b AS SELECT @a; +iris,"WITH @cte CREATE TABLE #@table AS @select;","CREATE GLOBAL TEMPORARY TABLE #@table AS WITH @cte @select;" +iris,"WITH @cte CREATE GLOBAL TEMPORARY TABLE @table AS @select;","CREATE GLOBAL TEMPORARY TABLE @table AS WITH @cte @select;" +iris,"WITH @cte CREATE TABLE @table AS @select;","CREATE TABLE @table AS WITH @cte @select;" +iris,#,%temp_prefix%%session_id% +iris,UPDATE STATISTICS @a;,TUNE TABLE @a; +iris,"--HINT BUCKET(@a, @b)", "-- haven't looked into this yet, skip it for now" +iris,"--HINT PARTITION(@a @b)", -- "haven't looked into this yet, skip it for now"" +iris,"--HINT DISTRIBUTE_ON_KEY(@key)", -- "haven't looked into this yet, skip it for now"" +iris,"DATEFROMPARTS(@year,@month,@day)","TO_DATE(TO_CHAR(@year,'FM0000')||'-'||TO_CHAR(@month,'FM00')||'-'||TO_CHAR(@day,'FM00'), 'YYYY-MM-DD')" +iris,"DATETIMEFROMPARTS(@year,@month,@day,@hour,@minute,@second,@ms)","TO_TIMESTAMP(TO_CHAR(@year,'FM0000')||'-'||TO_CHAR(@month,'FM00')||'-'||TO_CHAR(@day,'FM00')||' '||TO_CHAR(@hour,'FM00')||':'||TO_CHAR(@minute,'FM00')||':'||TO_CHAR(@second,'FM00')||'.'||TO_CHAR(@ms,'FM000'), 'YYYY-MM-DD HH24:MI:SS.FF')" +iris," DATEADD(d, @a, @b) AS"," TO_DATE(DATEADD(d, @a, @b),'YYYY-MM-DD HH:MI:SS') AS" +iris," DATEADD(dd, @a, @b) AS"," TO_DATE(DATEADD(dd, @a, @b),'YYYY-MM-DD HH:MI:SS') AS" +iris," DATEADD(day, @a, @b) AS"," TO_DATE(DATEADD(day, @a, @b),'YYYY-MM-DD HH:MI:SS') AS" +iris," DATEADD(m, @a, @b) AS"," TO_DATE(DATEADD(m, @a, @b),'YYYY-MM-DD HH:MI:SS') AS" +iris," DATEADD(mm, @a, @b) AS"," TO_DATE(DATEADD(mm, @a, @b),'YYYY-MM-DD HH:MI:SS') AS" +iris," DATEADD(yy, @a, @b) AS"," TO_DATE(DATEADD(yy, @a, @b),'YYYY-MM-DD HH:MI:SS') AS" +iris," DATEADD(yyyy, @a, @b) AS"," TO_DATE(DATEADD(yyyy, @a, @b),'YYYY-MM-DD HH:MI:SS') AS" +iris," COALESCE(p.birth_datetime", COALESCE(CAST(p.birth_datetime AS DATE) +iris,"COALESCE(@a, DATEADD(day,@b,@c))","COALESCE(@a, CAST (DATEADD(day,@b,@c) AS DATE))" +iris,"COALESCE(@a, DATEADD(day,@b,@c), DATEADD(day,@d,@e))","COALESCE(@a, CAST (DATEADD(day,@b,@c) AS DATE), CAST (DATEADD(day,@d,@e) AS DATE))" +iris,"case when DATEADD(day,@a,@b) > op_end_date then op_end_date else DATEADD(day,@a,@b) end as end_date","case when CAST (DATEADD(day,@a,@b) AS DATE) > op_end_date then op_end_date else CAST (DATEADD(day,@a,@b) AS DATE) end as end_date" +iris,"select @a as cohort_definition_id, person_id, start_date, end_date","select @a as cohort_definition_id, person_id, CAST (start_date AS DATE), CAST (end_date AS DATE)" +iris,"select @a as design_hash, person_id, start_date, end_date","select @a as design_hash, person_id, CAST (start_date AS DATE), CAST (end_date AS DATE)" +iris,"select cohort_definition_id, subject_id, cohort_start_date, cohort_end_date, @a as adjusted_start_date, @b as adjusted_end_date","select cohort_definition_id, subject_id, cohort_start_date, cohort_end_date, CAST(@a AS DATE) as adjusted_start_date, CAST(@b AS DATE) as adjusted_end_date" +iris,"CONCAT(p.year_of_birth, @b, @c)",p.year_of_birth||'-'||@b||'-'|| @c +iris,"CONCAT(@a, @b,","@a || CONCAT(@b," +iris,"CONCAT(@a,@b)",@a || @b +iris,CREATE CLUSTERED INDEX @index_name ON @table (@variable);,CREATE INDEX @index_name ON @table (@variable); +iris,CREATE INDEX @index_name ON @table (@variable) WHERE @b;,CREATE INDEX @index_name ON @table (@variable); +iris,AS MIN,AS "MIN" +iris,AS MAX,AS "MAX" +iris,AS COUNT,AS "COUNT" +iris,STDEV(@a),STDDEV(@a) +iris,STDEV_POP(@a),STDDEV_POP(@a) +iris,STDEV_SAMP(@a),STDDEV_SAMP(@a) +iris,EOMONTH(@date),LAST_DAY(@date) +iris,.DOMAIN ,."DOMAIN" +iris,NEWID(),$TSQL_NEWID() +iris,RAND(),$TSQL_NEWID() +iris,CREATE TABLE @a AS (@b) ORDER BY @c;,CREATE TABLE @a AS @b ORDER BY @c; +iris,CREATE TABLE @a AS (@b ORDER BY @c);,CREATE TABLE @a AS @b ORDER BY @c; diff --git a/circe/sqlrender/splitter.py b/circe/sqlrender/splitter.py new file mode 100644 index 0000000..cf24ddd --- /dev/null +++ b/circe/sqlrender/splitter.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from .tokenizer import tokenize_sql + + +def split_sql(sql: str) -> list[str]: + """Splits a string containing multiple SQL statements into a list of SQL statements.""" + parts: list[str] = [] + tokens = tokenize_sql(sql.lower()) + nest_stack: list[str] = [] + last_pop = "" + start = 0 + cursor = 0 + quote = False + bracket = False + quote_text = "" + + while cursor < len(tokens): + token = tokens[cursor] + + if quote: + if token.text == quote_text: + quote = False + elif bracket: + if token.text == "]": + bracket = False + elif token.text in ("'", '"'): + quote = True + quote_text = token.text + elif token.text == "[": + bracket = True + elif token.text in ("begin", "case"): + nest_stack.append(token.text) + elif token.text == "end" and (cursor == len(tokens) - 1 or tokens[cursor + 1].text != "if"): + if nest_stack: + last_pop = nest_stack.pop() + elif len(nest_stack) == 0 and token.text == ";": + if cursor == 0 or (tokens[cursor - 1].text == "end" and last_pop == "begin"): + parts.append(sql[tokens[start].start : token.end]) + else: + parts.append(sql[tokens[start].start : token.end - 1]) + start = cursor + 1 + cursor += 1 + + if start < cursor: + parts.append(sql[tokens[start].start : tokens[cursor - 1].end]) + + return parts diff --git a/circe/sqlrender/tokenizer.py b/circe/sqlrender/tokenizer.py new file mode 100644 index 0000000..8195acb --- /dev/null +++ b/circe/sqlrender/tokenizer.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +from dataclasses import dataclass + +HINT_KEY_WORD = "hint" + + +@dataclass +class Token: + start: int = 0 + end: int = 0 + text: str = "" + in_quotes: bool = False + + def is_identifier(self) -> bool: + return all(ch.isalnum() or ch == "_" for ch in self.text) + + +def tokenize_sql(sql: str) -> list[Token]: + """Splits the SQL into tokens. + + Any alphanumeric (including underscore and @) sequence is considered a token. + All other individual special characters are considered their own tokens. + White space and SQL comments are not considered for tokens. + """ + tokens: list[Token] = [] + start = 0 + cursor = 0 + comment_type1 = False # -- ... end of line + comment_type2 = False # /* .. */ + in_single_quotes = False + in_double_quotes = False + + while cursor < len(sql): + ch = sql[cursor] + + if comment_type1: + if ch == "\n": + comment_type1 = False + start = cursor + 1 + cursor += 1 + continue + + if comment_type2: + if ch == "/" and cursor > 0 and sql[cursor - 1] == "*": + comment_type2 = False + start = cursor + 1 + cursor += 1 + continue + + if not (ch.isalnum() or ch == "_" or ch == "@"): + if cursor > start: + token = Token( + start=start, + end=cursor, + text=sql[start:cursor], + in_quotes=in_single_quotes or in_double_quotes, + ) + tokens.append(token) + + if ( + ch == "-" + and cursor + 1 < len(sql) + and sql[cursor + 1] == "-" + and not in_single_quotes + and not in_double_quotes + and (len(sql) - cursor < 6 or sql[cursor + 2 : cursor + 6].lower() != HINT_KEY_WORD) + ): + comment_type1 = True + elif ( + ch == "/" + and cursor + 1 < len(sql) + and sql[cursor + 1] == "*" + and not in_single_quotes + and not in_double_quotes + ): + comment_type2 = True + elif not ch.isspace(): + token = Token( + start=cursor, + end=cursor + 1, + text=sql[cursor], + in_quotes=in_single_quotes or in_double_quotes, + ) + tokens.append(token) + if ch == "'" and not in_double_quotes: + in_single_quotes = not in_single_quotes + if ch == '"' and not in_single_quotes: + in_double_quotes = not in_double_quotes + + start = cursor + 1 + cursor += 1 + else: + cursor += 1 + + if cursor > start and not comment_type1 and not comment_type2: + token = Token( + start=start, + end=cursor, + text=sql[start:cursor], + in_quotes=in_single_quotes or in_double_quotes, + ) + tokens.append(token) + + return tokens diff --git a/circe/sqlrender/translator.py b/circe/sqlrender/translator.py new file mode 100644 index 0000000..34348c7 --- /dev/null +++ b/circe/sqlrender/translator.py @@ -0,0 +1,431 @@ +from __future__ import annotations + +import re +from dataclasses import dataclass, field + +from .patterns import MAX_TABLE_NAME_LENGTH, get_global_session_id, load_patterns +from .tokenizer import tokenize_sql + + +class SqlTranslateError(RuntimeError): + pass + + +@dataclass +class _Block: + start: int = 0 + end: int = 0 + text: str = "" + in_quotes: bool = False + is_variable: bool = False + reg_ex: str | None = None + + +@dataclass +class _MatchedPattern: + start: int = -1 + end: int = -1 + start_token: int = -1 + variable_to_value: dict[str, str] = field(default_factory=dict) + + +def parse_search_pattern(pattern: str) -> list[_Block]: + tokens = tokenize_sql(pattern.lower()) + blocks: list[_Block] = [] + + i = 0 + while i < len(tokens): + block = _Block( + start=tokens[i].start, + end=tokens[i].end, + text=tokens[i].text, + in_quotes=tokens[i].in_quotes, + ) + + if len(block.text) > 2 and block.text[0] == "@": + block.is_variable = True + + if block.text == "@@" and i < len(tokens) - 2 and tokens[i + 1].text == "(": + escape = False + nesting = 0 + for j in range(i + 2, len(tokens)): + if escape: + escape = False + elif tokens[j].text == "\\": + escape = True + elif not escape and tokens[j].text == "(": + nesting += 1 + elif not escape and tokens[j].text == ")": + if nesting == 0: + block.text = "@@" + tokens[j + 1].text + block.reg_ex = pattern[tokens[i + 1].end : tokens[j].start] + block.end = tokens[j + 1].end + block.is_variable = True + i = j + 1 + break + nesting -= 1 + blocks.append(block) + i += 1 + continue + + blocks.append(block) + i += 1 + + if blocks and blocks[0].is_variable and blocks[0].reg_ex is None: + raise SqlTranslateError( + "Error in search pattern: pattern cannot start or end with a non-regex variable: " + pattern + ) + if blocks and blocks[-1].is_variable and blocks[-1].reg_ex is None: + raise SqlTranslateError( + "Error in search pattern: pattern cannot start or end with a non-regex variable: " + pattern + ) + + return blocks + + +def _matches(regex: str, string: str) -> bool: + pattern = re.compile(regex, re.DOTALL | re.MULTILINE | re.IGNORECASE) + return bool(pattern.fullmatch(string)) + + +def _matches_end(regex: str, string: str) -> int: + string = re.sub(r"\s+$", "", string) + pattern = re.compile(regex, re.DOTALL | re.MULTILINE | re.IGNORECASE) + start = -1 + for m in pattern.finditer(string): + if m.end() == len(string): + start = m.start() + return start + + +def search(sql: str, parsed_pattern: list[_Block], start_token: int = 0) -> _MatchedPattern: + lowercase_sql = sql.lower() + tokens = tokenize_sql(lowercase_sql) + + match_count = 0 + var_start = 0 + nest_stack: list[str] = [] + in_pattern_quote = False + matched = _MatchedPattern() + + cursor = start_token + while cursor < len(tokens): + token = tokens[cursor] + + if parsed_pattern[match_count].is_variable: + block = parsed_pattern[match_count] + + # Regex variable at end of pattern or followed by another variable + if block.reg_ex is not None and ( + match_count == len(parsed_pattern) - 1 or parsed_pattern[match_count + 1].is_variable + ): + pat = re.compile(block.reg_ex, re.DOTALL | re.MULTILINE | re.IGNORECASE) + m = pat.match(sql[token.start :]) + if m and m.start() == 0: + if match_count == 0: + matched.start = token.start + matched.start_token = cursor + matched.variable_to_value[block.text] = sql[token.start : token.start + m.end()] + match_count += 1 + if match_count == len(parsed_pattern): + matched.end = token.start + m.end() + return matched + elif parsed_pattern[match_count].is_variable: + var_start = token.start + m.end() + while cursor < len(tokens) and tokens[cursor].start < token.start + m.end(): + cursor += 1 + cursor -= 1 + else: + match_count = 0 + cursor += 1 + continue + + # Check for token match after the variable + if ( + len(nest_stack) == 0 + and match_count < len(parsed_pattern) - 1 + and token.text == parsed_pattern[match_count + 1].text + ): + if block.reg_ex is not None and match_count == 0: + # First element is regex. Find last part of string prior to + # subsequent token that matches the regex: + m_end = _matches_end(block.reg_ex, sql[var_start : token.start]) + if m_end != -1: + matched.variable_to_value[block.text] = sql[var_start + m_end : token.start] + matched.start = var_start + m_end + matched.start_token = cursor + match_count += 2 + if match_count == len(parsed_pattern): + matched.end = token.end + return matched + elif parsed_pattern[match_count].is_variable: + var_start = tokens[cursor + 1].start if cursor < len(tokens) - 1 else -1 + if token.text in ("'", '"'): + in_pattern_quote = not in_pattern_quote + else: + match_count = 0 + cursor = matched.start_token + elif block.reg_ex is not None and not _matches(block.reg_ex, sql[var_start : token.start]): + # Content didn't match regex + match_count = 0 + cursor = matched.start_token + else: + # No regex or matched regex + matched.variable_to_value[block.text] = sql[var_start : token.start] + match_count += 2 + if match_count == len(parsed_pattern): + matched.end = token.end + return matched + elif parsed_pattern[match_count].is_variable: + var_start = tokens[cursor + 1].start if cursor < len(tokens) - 1 else -1 + if token.text in ("'", '"'): + in_pattern_quote = not in_pattern_quote + cursor += 1 + continue + + # Not allowed to span multiple SQL statements or outside of nesting + if ( + match_count != 0 + and len(nest_stack) == 0 + and not in_pattern_quote + and token.text in (";", ")") + ): + match_count = 0 + cursor = matched.start_token + cursor += 1 + continue + + # Track nesting + if nest_stack: + top = nest_stack[-1] + if top in ('"', "'"): + if token.text == top: + nest_stack.pop() + else: + if token.text in ('"', "'") or (not in_pattern_quote and token.text == "("): + nest_stack.append(token.text) + elif not in_pattern_quote and nest_stack and token.text == ")" and nest_stack[-1] == "(": + nest_stack.pop() + else: + if token.text in ('"', "'") or (not in_pattern_quote and token.text == "("): + nest_stack.append(token.text) + elif not in_pattern_quote and nest_stack and token.text == ")" and nest_stack[-1] == "(": + nest_stack.pop() + cursor += 1 + continue + + # Non-variable: check if token matches current part of pattern + if token.text == parsed_pattern[match_count].text and (match_count != 0 or not token.in_quotes): + if match_count == 0: + matched.start = token.start + matched.start_token = cursor + match_count += 1 + if match_count == len(parsed_pattern): + matched.end = token.end + return matched + elif parsed_pattern[match_count].is_variable: + var_start = tokens[cursor + 1].start if cursor < len(tokens) - 1 else -1 + if token.text in ("'", '"'): + in_pattern_quote = not in_pattern_quote + elif match_count != 0: + match_count = 0 + cursor = matched.start_token + + cursor += 1 + + # If at end of sql and still didn't finish pattern, we're not going to finish it + if match_count != 0 and cursor >= len(tokens): + match_count = 0 + cursor = matched.start_token + 1 + + matched.start = -1 + return matched + + +def search_and_replace(sql: str, parsed_pattern: list[_Block], replace_pattern: str) -> str: + matched = search(sql, parsed_pattern, 0) + while matched.start != -1: + replacement = replace_pattern + for var_name, var_value in matched.variable_to_value.items(): + pos = 0 + while True: + pos = replacement.find(var_name, pos) + if pos == -1: + break + replacement = replacement[:pos] + var_value + replacement[pos + len(var_name) :] + pos += len(var_value) + + sql = sql[: matched.start] + replacement + sql[matched.end :] + + delta = 1 + repl_tokens = tokenize_sql(replacement) + if len(repl_tokens) == 0: + delta = 0 + + if ( + delta > 0 + and replace_pattern.startswith("@@") + and replacement.lower().strip().startswith(parsed_pattern[0].text) + ): + delta = 0 + + matched = search(sql, parsed_pattern, matched.start_token + delta) + + return sql + + +def _strip_blank_lines(sql: str) -> str: + return re.sub(r"(?m)^[ \t]*\r?\n", "", sql) + + +def _replace_all(s: str, search_str: str, replace_str: str) -> str: + pos = 0 + while True: + pos = s.find(search_str, pos) + if pos == -1: + break + s = s[:pos] + replace_str + s[pos + len(search_str) :] + pos += len(replace_str) + return s + + +def translate( + sql: str, + target_dialect: str, + session_id: str | None = None, + temp_emulation_schema: str | None = None, +) -> str: + patterns = load_patterns() + + if session_id is None: + session_id = get_global_session_id() + else: + if len(session_id) != 8: + raise SqlTranslateError(f"Session ID has length {len(session_id)}, should be 8") + if not session_id[0].isalpha(): + raise SqlTranslateError("Session ID does not start with a letter") + for ch in session_id[1:]: + if not ch.isalnum(): + raise SqlTranslateError(f"Illegal character in session ID: {ch}") + + oracle_temp_prefix = "" + if temp_emulation_schema is not None: + oracle_temp_prefix = temp_emulation_schema + "." + + replacement_patterns = patterns.get(target_dialect) + if replacement_patterns is None: + supported = ", ".join(sorted(patterns.keys())) + raise SqlTranslateError( + f"Don't know how to translate to {target_dialect}. Valid target dialects are {supported}" + ) + + lower = target_dialect.lower() + if lower == "bigquery": + from .bigquery_spark import translate_bigquery + + sql = translate_bigquery(sql) + elif lower == "spark": + from .bigquery_spark import translate_spark + + sql = translate_spark(sql) + + for pattern, replacement in replacement_patterns: + replacement = replacement.replace("%session_id%", session_id) + replacement = replacement.replace("%temp_prefix%", oracle_temp_prefix) + parsed = parse_search_pattern(pattern) + sql = _strip_blank_lines(search_and_replace(sql, parsed, replacement)) + + sql = _strip_blank_lines(sql) + + if lower in ("impala", "bigquery", "spark"): + sql = _replace_with_concat(sql) + + return sql + + +def _replace_with_concat(val: str) -> str: + tokens = _split_and_keep(val, r"""(['\"])((?!\1).|\1{2})*\1""") + result: list[str] = [] + for token in tokens: + if re.match(r"""(['\"])((?!\1).|\1{2})*\1""", token) and "''" in token: + result.append(_escape_quotes_for_concat(token)) + else: + result.append(token) + return "".join(result) + + +def _split_and_keep(val: str, regex: str) -> list[str]: + result: list[str] = [] + pos = 0 + for m in re.finditer(regex, val): + if m.start() > pos: + result.append(val[pos : m.start()]) + result.append(m.group()) + pos = m.end() + if pos < len(val): + result.append(val[pos:]) + return result + + +def _escape_quotes_for_concat(s: str) -> str: + if s == "''": + return s + + inner = s[1:-1] + parts = _split_and_keep(inner, "''") + concat_parts: list[str] = [] + for _i, part in enumerate(parts): + if part == "''": + concat_parts.append("'\\047'") + else: + escaped = part.replace("'", "") + escaped = escaped.replace("\\", "\\\\") + escaped = escaped.replace('"', "\\042") + escaped = escaped.replace("/", "\\/") + concat_parts.append("'" + escaped + "'") + return "CONCAT(" + ",".join(concat_parts) + ")" + + +def check(sql: str, target_dialect: str) -> list[str]: + warnings: list[str] = [] + + # temp table names + pattern = re.compile(r"#[0-9a-zA-Z_]+") + long_temp_names: set[str] = set() + for m in pattern.finditer(sql): + name = m.group() + if len(name) > MAX_TABLE_NAME_LENGTH - 8 - 1: + long_temp_names.add(name) + for name in sorted(long_temp_names): + warnings.append( + f"Temp table name '{name}' is too long. Temp table names should be shorter than " + f"{MAX_TABLE_NAME_LENGTH - 8} characters to prevent some DMBSs from throwing an error." + ) + + # normal table names + pattern2 = re.compile(r"(create|drop|truncate)\s+table +[0-9a-zA-Z_]+", re.IGNORECASE) + long_names: set[str] = set() + for m in pattern2.finditer(sql.lower()): + full_match = sql[m.start() : m.end()] + name = full_match[full_match.rindex(" ") + 1 :] + if len(name) > MAX_TABLE_NAME_LENGTH and "#" + name not in long_temp_names: + long_names.add(name) + for name in sorted(long_names): + warnings.append( + f"Table name '{name}' is too long. Table names should be shorter than " + f"{MAX_TABLE_NAME_LENGTH} characters to prevent some DMBSs from throwing an error." + ) + + return warnings + + +def generate_session_id() -> str: + from .patterns import generate_session_id as _gen + + return _gen() + + +def set_replacement_patterns_path(path: str | None) -> None: + from .patterns import set_replacement_patterns_path as _set + + _set(path) diff --git a/pyproject.toml b/pyproject.toml index a69657b..f9f161d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,7 @@ exclude = ["circe.tests*"] [tool.setuptools.package-data] circe = ["py.typed"] +"circe.sqlrender" = ["replacementPatterns.csv"] "circe.extensions.waveform" = ["templates/*.j2"] [tool.mypy] diff --git a/tests/sqlrender_utils.py b/tests/sqlrender_utils.py new file mode 100644 index 0000000..c5a1f8f --- /dev/null +++ b/tests/sqlrender_utils.py @@ -0,0 +1,26 @@ +"""Shared test utilities for SqlRender translation parity tests.""" + +import re + + +def normalize_sql(sql: str) -> str: + """Normalize SQL whitespace for comparison. + + Matches the R test suite's `expect_equal_ignore_spaces` function exactly: + gsub("([;()'+-/|*\n])", " \\1 ", string) + + Note: In R, `[+-/]` is a character range from ASCII 43 to 47 that includes: + + (43), , (44), - (45), . (46), / (47). + """ + sql = re.sub(r"([;()'+,.\-/*|\n])", r" \1 ", sql) + sql = re.sub(r" +", " ", sql) + return sql.strip() + + +def assert_sql_equal(actual: str, expected: str) -> None: + """Assert two SQL strings are equal after whitespace normalization.""" + assert normalize_sql(actual) == normalize_sql(expected), ( + f"\nExpected: {expected}\nActual: {actual}\n" + f"Norm exp: {normalize_sql(expected)}\n" + f"Norm act: {normalize_sql(actual)}" + ) diff --git a/tests/test_sqlrender_csv_format.py b/tests/test_sqlrender_csv_format.py new file mode 100644 index 0000000..98208ec --- /dev/null +++ b/tests/test_sqlrender_csv_format.py @@ -0,0 +1,54 @@ +"""Test replacementPatterns.csv format - ported from OHDSI SqlRender test-replacement-patterns-file-format.R""" + +import pytest + +from circe.sqlrender import translate +from circe.sqlrender.patterns import _safe_split + + +class TestCsvFormat: + def test_csv_has_valid_format(self): + from importlib.resources import files + + f = files("circe.sqlrender").joinpath("replacementPatterns.csv").open("r", encoding="utf-8") + content = f.read() + f.close() + + lines = content.splitlines() + assert len(lines) > 1, "CSV should have header row plus at least one pattern" + + for i, line in enumerate(lines): + if i == 0: + continue + if not line.strip(): + continue + columns = _safe_split(line, ",") + if i == 0: + assert columns[0] == "To" + assert columns[1] == "Pattern" + assert columns[2] == "Replacement" + continue + assert len(columns) >= 3, ( + f"Row {i + 1} has {len(columns)} columns (expected at least 3): {columns}" + ) + + def test_all_patterns_can_be_parsed(self): + from circe.sqlrender.patterns import load_patterns + from circe.sqlrender.translator import parse_search_pattern + + patterns = load_patterns() + for dialect, pairs in patterns.items(): + for pattern, _replacement in pairs: + try: + parse_search_pattern(pattern) + except Exception as e: + pytest.fail( + f"Failed to parse pattern for dialect '{dialect}': pattern={pattern!r}, error={e}" + ) + + def test_duckdb_and_postgresql_can_translate_simple_sql(self): + sql = "SELECT * FROM table;" + for dialect in ("duckdb", "postgresql"): + result = translate(sql, dialect) + assert "SELECT" in result + assert "table" in result diff --git a/tests/test_sqlrender_render.py b/tests/test_sqlrender_render.py new file mode 100644 index 0000000..538564d --- /dev/null +++ b/tests/test_sqlrender_render.py @@ -0,0 +1,150 @@ +"""Test parameter rendering - ported from OHDSI SqlRender test-renderSql.R""" + +import pytest + +from circe.sqlrender import render +from circe.sqlrender.renderer import SqlRenderError + + +class TestRender: + S = "{DEFAULT @a = '123'} SELECT * FROM table WHERE x = @a AND {@b == 'blaat'}?{y = 1234}:{x = 1};" + + def test_parameter_substitution(self): + sql = render(self.S, a="abc") + assert sql == "SELECT * FROM table WHERE x = abc AND x = 1;" + + def test_empty_parameter(self): + sql = render(self.S, a="abc", b="") + assert sql == "SELECT * FROM table WHERE x = abc AND x = 1;" + + def test_default(self): + sql = render(self.S, b="1") + assert sql == "SELECT * FROM table WHERE x = 123 AND x = 1;" + + def test_if_then_else_then(self): + sql = render(self.S, b="blaat") + assert sql == "SELECT * FROM table WHERE x = 123 AND y = 1234;" + + def test_if_then_else_else(self): + sql = render(self.S, b="bla") + assert sql == "SELECT * FROM table WHERE x = 123 AND x = 1;" + + def test_boolean_param_true(self): + sql = render("SELECT * FROM table {@a}?{WHERE x = 1}", a=True) + assert sql == "SELECT * FROM table WHERE x = 1" + + def test_boolean_param_false(self): + sql = render("SELECT * FROM table {@a}?{WHERE x = 1}", a=False) + assert sql == "SELECT * FROM table " + + def test_in_pattern_true(self): + sql = render("{1 IN (@a)}?{SELECT * FROM table}", a=[1, 2, 3, 4]) + assert sql == "SELECT * FROM table" + + def test_in_pattern_false(self): + sql = render("{1 IN (@a)}?{SELECT * FROM table}", a=[2, 3, 4]) + assert sql == "" + + def test_in_pattern_space_start_true(self): + sql = render("{ 1 IN (@a)}?{SELECT * FROM table}", a=[1, 2, 3, 4]) + assert sql == "SELECT * FROM table" + + def test_in_pattern_space_start_false(self): + sql = render("{ 1 IN (@a)}?{SELECT * FROM table}", a=[2, 3, 4]) + assert sql == "" + + def test_and_operator_true(self): + sql = render("{true & true}?{true}:{false}") + assert sql == "true" + + def test_and_operator_false(self): + sql = render("{true & false}?{true}:{false}") + assert sql == "false" + + def test_or_operator_true_1(self): + sql = render("{true | false}?{true}:{false}") + assert sql == "true" + + def test_or_operator_true_2(self): + sql = render("{true | true}?{true}:{false}") + assert sql == "true" + + def test_or_operator_false(self): + sql = render("{false | false}?{true}:{false}") + assert sql == "false" + + def test_nested_in_boolean(self): + sql = render("{true & (true & (true & 4 IN (@a)))}?{true}:{false}", a=[1, 2, 3]) + assert sql == "false" + + def test_nested_if_then_else_true(self): + sql = render("{true}?{{true}?{double true}:{true false}}:{false}") + assert sql == "double true" + + def test_nested_if_then_else_false(self): + sql = render("{false}?{{true}?{double true}:{true false}}:{false}") + assert sql == "false" + + def test_simple_negation(self): + sql = render("{!false}?{true}:{false}") + assert sql == "true" + + def test_negation_of_param(self): + sql = render("{!@a}?{true}:{false}", a="true") + assert sql == "false" + + def test_not_equals_operator_1(self): + sql = render("{123 != 123}?{true}:{false}") + assert sql == "false" + + def test_not_equals_operator_2(self): + sql = render("{123 != 234}?{true}:{false}") + assert sql == "true" + + def test_not_equals_operator_3(self): + sql = render("{123 <> 123}?{true}:{false}") + assert sql == "false" + + def test_not_equals_operator_4(self): + sql = render("{123 <> 234}?{true}:{false}") + assert sql == "true" + + def test_nested_in_evaluates_true(self): + sql = render("{TRUE & (FALSE | 1 IN (1,2,3))}?{true}:{false}") + assert sql == "true" + + def test_nested_in_evaluates_false(self): + sql = render("{TRUE & (FALSE | 4 IN (1,2,3))}?{true}:{false}") + assert sql == "false" + + def test_backslash_in_parameter(self): + sql = render("SELECT * FROM table WHERE name = '@name';", name="NA\\joe") + assert sql == "SELECT * FROM table WHERE name = 'NA\\joe';" + + def test_dollar_in_parameter(self): + sql = render("SELECT * FROM table WHERE name = '@name';", name="NA$joe") + assert sql == "SELECT * FROM table WHERE name = 'NA\\$joe';" + + def test_error_on_bad_boolean_syntax(self): + with pytest.raises(SqlRenderError): + render("{true = true}?{true}:{false}") + + def test_inline_simple(self): + sql = render("{1 == 1}?{yes}:{no}") + assert sql == "yes" + + def test_inline_false(self): + sql = render("{1 == 2}?{yes}:{no}") + assert sql == "no" + + def test_inline_no_else(self): + sql = render("{false}?{hide}") + assert sql == "" + + def test_default_rendering_order(self): + sql = render("{DEFAULT @a = '123'}{@a == '123'}?{match}:{no_match}") + assert sql == "match" + + def test_default_overrides_provided_value(self): + sql = render("{DEFAULT @a = '123'}{@a == '123'}?{match}:{no_match}", a="456") + assert sql == "no_match" diff --git a/tests/test_sqlrender_split.py b/tests/test_sqlrender_split.py new file mode 100644 index 0000000..3cd3912 --- /dev/null +++ b/tests/test_sqlrender_split.py @@ -0,0 +1,62 @@ +"""Test SQL splitting - ported from OHDSI SqlRender test-splitSql.R""" + +from circe.sqlrender import split_sql + + +class TestSplitSql: + def test_split_simple_statements(self): + parts = split_sql("SELECT * INTO a FROM b; USE x; DROP TABLE c;") + assert parts == ["SELECT * INTO a FROM b", "USE x", "DROP TABLE c"] + + def test_split_with_begin_end(self): + parts = split_sql("BEGIN\nSELECT * INTO a FROM b;\nEND;\nUSE x;") + assert parts == ["BEGIN\nSELECT * INTO a FROM b;\nEND;", "USE x"] + + def test_split_with_case_end(self): + parts = split_sql("SELECT CASE WHEN x=1 THEN 0 ELSE 1 END AS x INTO a FROM b;\nUSE x;") + assert parts == [ + "SELECT CASE WHEN x=1 THEN 0 ELSE 1 END AS x INTO a FROM b", + "USE x", + ] + + def test_split_with_end_in_quoted_text(self): + parts = split_sql("insert into a (x) values ('end');\n insert into a (x) values ('begin');") + assert parts == [ + "insert into a (x) values ('end')", + "insert into a (x) values ('begin')", + ] + + def test_split_with_case_end_at_end(self): + sql = "SELECT CASE WHEN x=1 THEN 0 ELSE 1 END FROM a GROUP BY CASE WHEN x=1 THEN 0 ELSE 1 END;" + parts = split_sql(sql) + assert parts == [ + "SELECT CASE WHEN x=1 THEN 0 ELSE 1 END FROM a GROUP BY CASE WHEN x=1 THEN 0 ELSE 1 END" + ] + + def test_split_with_reserved_word_end_as_field(self): + sql = "INSERT INTO t (data_source, start, [end]) VALUES ('hes', '1990-01-01', '2014-12-31');" + parts = split_sql(sql) + assert parts == [ + "INSERT INTO t (data_source, start, [end]) VALUES ('hes', '1990-01-01', '2014-12-31')" + ] + + def test_split_with_comment_last_line_no_eol(self): + parts = split_sql("SELECT * FROM table;\n-- end") + assert parts == ["SELECT * FROM table"] + + def test_split_with_hint_at_start(self): + parts = split_sql("--HINT DISTRIBUTE_ON_KEY(analysis_id)\nCREATE TABLE results.achilles_results_dist") + assert parts == ["--HINT DISTRIBUTE_ON_KEY(analysis_id)\nCREATE TABLE results.achilles_results_dist"] + + def test_split_with_hint_in_second_statement(self): + parts = split_sql( + "DROP TABLE blah;\n--HINT DISTRIBUTE_ON_KEY(analysis_id)\nCREATE TABLE results.achilles_results_dist;" + ) + assert parts == [ + "DROP TABLE blah", + "--HINT DISTRIBUTE_ON_KEY(analysis_id)\nCREATE TABLE results.achilles_results_dist", + ] + + def test_split_single_statement_no_semicolon(self): + parts = split_sql("SELECT * FROM table") + assert parts == ["SELECT * FROM table"] diff --git a/tests/test_sqlrender_translate.py b/tests/test_sqlrender_translate.py new file mode 100644 index 0000000..4bc5098 --- /dev/null +++ b/tests/test_sqlrender_translate.py @@ -0,0 +1,31 @@ +"""Test general translation behavior - ported from OHDSI SqlRender test-translateSql.R""" + +import pytest + +from circe.sqlrender import translate +from circe.sqlrender.translator import SqlTranslateError, check + + +class TestGeneralTranslate: + def test_invalid_target_dialect(self): + with pytest.raises(SqlTranslateError, match="Don't know how to translate to"): + translate("SELECT * FROM a;", target_dialect="pwd") + + def test_table_name_too_long_warning(self): + warnings = check( + "DROP TABLE abcdefghijklmnopqrstuvwxyz1234567890123456789012345678901234567890", + "pdw", + ) + assert len(warnings) > 0 + assert "too long" in warnings[0].lower() + + def test_no_warning_for_short_table_name(self): + warnings = check("DROP TABLE short_name;", "pdw") + assert len(warnings) == 0 + + def test_list_supported_dialects(self): + from circe.sqlrender.patterns import load_patterns + + patterns = load_patterns() + for d in ("duckdb", "postgresql", "oracle", "bigquery"): + assert d in patterns, f"Missing dialect: {d}" diff --git a/tests/test_sqlrender_translate_duckdb.py b/tests/test_sqlrender_translate_duckdb.py new file mode 100644 index 0000000..169c374 --- /dev/null +++ b/tests/test_sqlrender_translate_duckdb.py @@ -0,0 +1,64 @@ +"""Test DuckDB translation - ported from OHDSI SqlRender test-translate-duckdb.R""" + +from circe.sqlrender import translate +from tests.sqlrender_utils import assert_sql_equal + + +class TestDuckDBTranslation: + def test_string_concat_1(self): + sql = translate("'x' + b ( 'x' + b)", "duckdb") + assert_sql_equal(sql, "'x' || b ( 'x' || b)") + + def test_string_concat_2(self): + sql = translate("a + ';b'", "duckdb") + assert_sql_equal(sql, "a || ';b'") + + def test_string_concat_3(self): + sql = translate("a + ';('", "duckdb") + assert_sql_equal(sql, "a || ';('") + + def test_add_months(self): + sql = translate("DATEADD(mm,2,date)", "duckdb") + assert_sql_equal(sql, "(date + TO_MONTHS(CAST(2 AS INTEGER)))") + + def test_add_years(self): + sql = translate("DATEADD(yy,2,date)", "duckdb") + assert_sql_equal(sql, "(date + TO_YEARS(CAST(2 AS INTEGER)))") + + def test_cte_select_into(self): + sql = translate( + "WITH cte1 AS (SELECT a FROM b) SELECT c INTO d FROM cte1;", + "duckdb", + ) + assert_sql_equal( + sql, + "CREATE TABLE d\nAS\nWITH cte1 AS (SELECT a FROM b) SELECT\nc \nFROM\ncte1;", + ) + + def test_select_into(self): + sql = translate("SELECT a INTO b FROM c;", "duckdb") + assert_sql_equal(sql, "CREATE TABLE b AS\nSELECT\na \nFROM\nc;") + + def test_left(self): + sql = translate("LEFT('Hello World', 5)", "duckdb") + assert_sql_equal(sql, "LEFT('Hello World', 5)") + + def test_right(self): + sql = translate("RIGHT('Hello World', 5)", "duckdb") + assert_sql_equal(sql, "RIGHT('Hello World', 5)") + + def test_getdate(self): + sql = translate("GETDATE()", "duckdb") + assert_sql_equal(sql, "CURRENT_DATE") + + def test_stdev(self): + sql = translate("STDEV(x)", "duckdb") + assert_sql_equal(sql, "STDDEV(x)") + + def test_var(self): + sql = translate("VAR(x)", "duckdb") + assert_sql_equal(sql, "VARIANCE(x)") + + def test_square(self): + sql = translate("SQUARE(x)", "duckdb") + assert_sql_equal(sql, "((x) * (x))") diff --git a/tests/test_sqlrender_translate_oracle.py b/tests/test_sqlrender_translate_oracle.py new file mode 100644 index 0000000..e2bb598 --- /dev/null +++ b/tests/test_sqlrender_translate_oracle.py @@ -0,0 +1,644 @@ +"""Test Oracle translation - ported from OHDSI SqlRender test-translate-oracle.R""" + +from circe.sqlrender import translate +from tests.sqlrender_utils import assert_sql_equal + + +class TestOracleTranslation: + def test_datediff_day(self): + sql = translate( + "SELECT DATEDIFF(dd,drug_era_start_date,drug_era_end_date) FROM drug_era;", + "oracle", + ) + expected = ( + "SELECT CEIL(CAST(drug_era_end_date AS DATE) - CAST(drug_era_start_date AS DATE)) FROM drug_era;" + ) + assert_sql_equal(sql, expected) + + def test_datediff_second(self): + sql = translate( + "SELECT DATEDIFF(second,drug_era_start_date,drug_era_end_date) FROM drug_era;", + "oracle", + ) + expected = "SELECT EXTRACT(SECOND FROM (drug_era_end_date - drug_era_start_date)) FROM drug_era;" + assert_sql_equal(sql, expected) + + def test_datediff_minute(self): + sql = translate( + "SELECT DATEDIFF(minute,drug_era_start_date,drug_era_end_date) FROM drug_era;", + "oracle", + ) + expected = "SELECT EXTRACT(MINUTE FROM (drug_era_end_date - drug_era_start_date)) FROM drug_era;" + assert_sql_equal(sql, expected) + + def test_datediff_hour(self): + sql = translate( + "SELECT DATEDIFF(hour,drug_era_start_date,drug_era_end_date) FROM drug_era;", + "oracle", + ) + expected = "SELECT EXTRACT(HOUR FROM (drug_era_end_date - drug_era_start_date)) FROM drug_era;" + assert_sql_equal(sql, expected) + + def test_datediff_year(self): + sql = translate( + "SELECT DATEDIFF(YEAR,drug_era_start_date,drug_era_end_date) FROM drug_era;", + "oracle", + ) + expected = "SELECT (EXTRACT(YEAR FROM CAST(drug_era_end_date AS DATE)) - EXTRACT(YEAR FROM CAST(drug_era_start_date AS DATE))) FROM drug_era;" + assert_sql_equal(sql, expected) + + def test_datediff_month(self): + sql = translate( + "SELECT DATEDIFF(month,drug_era_start_date,drug_era_end_date) FROM drug_era;", + "oracle", + ) + expected = "SELECT MONTHS_BETWEEN(CAST(drug_era_end_date AS DATE), CAST(drug_era_start_date AS DATE)) FROM drug_era;" + assert_sql_equal(sql, expected) + + def test_dateadd(self): + sql = translate("SELECT DATEADD(dd,30,drug_era_end_date) FROM drug_era;", "oracle") + expected = "SELECT (drug_era_end_date + NUMTODSINTERVAL(30, 'day')) FROM drug_era;" + assert_sql_equal(sql, expected) + + def test_functional_index(self): + sql = translate( + "CREATE INDEX name1 ON someTable (firstColumn,secondColumn) WHERE someCondition;", + "oracle", + ) + expected = "CREATE INDEX name1 ON someTable (CASE WHEN someCondition THEN firstColumn END, CASE WHEN someCondition THEN secondColumn END);" + assert_sql_equal(sql, expected) + + def test_use(self): + sql = translate("USE vocabulary;", "oracle") + expected = "ALTER SESSION SET current_schema = vocabulary;" + assert_sql_equal(sql, expected) + + def test_drop_table_if_exists(self): + sql = translate( + "IF OBJECT_ID('cohort', 'U') IS NOT NULL DROP TABLE cohort;", + "oracle", + ) + expected = ( + "BEGIN\n" + " EXECUTE IMMEDIATE 'TRUNCATE TABLE cohort';\n" + " EXECUTE IMMEDIATE 'DROP TABLE cohort';\n" + "EXCEPTION\n" + " WHEN OTHERS THEN\n" + " IF SQLCODE != -942 THEN\n" + " RAISE;\n" + " END IF;\n" + "END;" + ) + assert_sql_equal(sql, expected) + + def test_cast_as_date(self): + sql = translate("CAST('20000101' AS DATE);", "oracle") + expected = "TO_DATE('20000101', 'YYYYMMDD');" + assert_sql_equal(sql, expected) + + def test_cast_as_date_not_char_string(self): + sql = translate("CAST(some_date_time AS DATE);", "oracle") + expected = "CAST(some_date_time AS DATE);" + assert_sql_equal(sql, expected) + + def test_convert_as_date(self): + sql = translate("CONVERT(DATE, '20000101');", "oracle") + expected = "TO_DATE('20000101', 'YYYYMMDD');" + assert_sql_equal(sql, expected) + + def test_concatenate_string_operator(self): + sql = translate( + "select distinct CONVERT(DATE, cast(YEAR(observation_period_start_date) as varchar(4)) + '01' + '01') as obs_year from observation_period;", + "oracle", + ) + expected = "SELECT distinct TO_DATE(cast(EXTRACT(YEAR FROM observation_period_start_date) as varchar(4)) || '01' || '01', 'YYYYMMDD') as obs_year FROM observation_period;" + assert_sql_equal(sql, expected) + + def test_right_functions(self): + sql = translate("select RIGHT(x,4);", "oracle") + expected = "SELECT SUBSTR(x,-4) FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_complex_query(self): + sql = translate( + "select CONVERT(DATE,CAST(YEAR(DATEFROMPARTS(2000,1,1)) AS VARCHAR(12)) + RIGHT('0'+MONTH(DATEFROMPARTS(2000,1,1)),2) + '01') as X;", + "oracle", + ) + expected = ( + "SELECT TO_DATE(CAST(EXTRACT(YEAR FROM TO_DATE(TO_CHAR(2000,'0000')||'-'||" + "TO_CHAR(1,'00')||'-'||TO_CHAR(1,'00'), 'YYYY-MM-DD')) AS varchar(12)) || " + "SUBSTR('0' ||EXTRACT(MONTH FROM TO_DATE(TO_CHAR(2000,'0000')||'-'||" + "TO_CHAR(1,'00')||'-'||TO_CHAR(1,'00'), 'YYYY-MM-DD')),-2) || '01', " + "'YYYYMMDD') as X FROM DUAL;" + ) + assert_sql_equal(sql, expected) + + def test_plus_in_quote(self): + sql = translate("select '+';", "oracle") + expected = "SELECT '+' FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_union_in_dual(self): + sql = translate("select 1 union 2;", "oracle") + expected = "SELECT 1 FROM DUAL UNION 2 FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_table_alias_single(self): + sql = translate("SELECT a FROM a AS a1;", "oracle") + expected = "SELECT a FROM a a1;" + assert_sql_equal(sql, expected) + + def test_table_alias_join(self): + sql = translate( + "SELECT a, b FROM a AS a1 JOIN b AS b1 ON a = b WHERE c = 1;", + "oracle", + ) + expected = "SELECT a, b FROM a a1 JOIN b b1 ON a = b WHERE c = 1;" + assert_sql_equal(sql, expected) + + def test_table_alias_multi_join(self): + sql = translate( + "SELECT a, b FROM a as a1 INNER JOIN b AS b1 ON a = b LEFT JOIN c AS c1 ON b = c WHERE c IN (1,2,4);", + "oracle", + ) + expected = ( + "SELECT a, b FROM a a1 INNER JOIN b b1 ON a = b LEFT JOIN c c1 ON b = c WHERE c IN (1,2,4);" + ) + assert_sql_equal(sql, expected) + + def test_table_alias_comma(self): + sql = translate("SELECT a, b, d FROM a AS a1, b AS b1;", "oracle") + expected = "SELECT a, b, d FROM a a1, b b1;" + assert_sql_equal(sql, expected) + + def test_table_alias_comma_where(self): + sql = translate( + "SELECT a, b, d FROM a AS a1, b AS b1, c AS c1 WHERE c = 1;", + "oracle", + ) + expected = "SELECT a, b, d FROM a a1, b b1, c c1 WHERE c = 1;" + assert_sql_equal(sql, expected) + + def test_table_alias_subquery(self): + sql = translate( + "SELECT a, b, d FROM a AS a1,(SELECT c AS c1 FROM b AS b1) AS d1 WHERE c = 1;", + "oracle", + ) + expected = "SELECT a, b, d FROM a a1,(SELECT c AS c1 FROM b b1) d1 WHERE c = 1;" + assert_sql_equal(sql, expected) + + def test_multiple_inserts_in_one_statement(self): + sql = translate( + "INSERT INTO my_table (key,value) VALUES (1,0),(2,0),(3,1)", + "oracle", + ) + expected = ( + "INSERT ALL\n" + "INTO my_table (key,value) VALUES (INTO my_table (key,value) VALUES (1,0)\n" + " INTO my_table (key,value) VALUES (2,0)\n" + ")\n" + " INTO my_table (key,value) VALUES (3,1)\n" + "SELECT * FROM dual" + ) + assert_sql_equal(sql, expected) + + def test_with_select(self): + sql = translate( + "WITH cte1 AS (SELECT a FROM b) SELECT c FROM cte1;", + "oracle", + ) + expected = "WITH cte1 AS (SELECT a FROM b) SELECT c FROM cte1;" + assert_sql_equal(sql, expected) + + def test_with_select_into(self): + sql = translate( + "WITH cte1 AS (SELECT a FROM b) SELECT c INTO d FROM cte1;", + "oracle", + ) + expected = "CREATE TABLE d\nAS\nWITH cte1 AS (SELECT a FROM b) SELECT\nc\nFROM\ncte1;" + assert_sql_equal(sql, expected) + + def test_with_insert_into_select(self): + sql = translate( + "WITH cte1 AS (SELECT a FROM b) INSERT INTO c (d int) SELECT e FROM cte1;", + "oracle", + ) + expected = "INSERT INTO c (d int) WITH cte1 AS (SELECT a FROM b) SELECT e FROM cte1;" + assert_sql_equal(sql, expected) + + def test_create_table_if_not_exists(self): + sql = translate( + "IF OBJECT_ID('cohort', 'U') IS NULL\n CREATE TABLE cohort\n(cohort_definition_id INT);", + "oracle", + ) + expected = ( + "BEGIN\n" + " EXECUTE IMMEDIATE 'CREATE TABLE cohort\n" + " (cohort_definition_id INT)';\n" + "EXCEPTION\n" + " WHEN OTHERS THEN\n" + " IF SQLCODE != -955 THEN\n" + " RAISE;\n" + " END IF;\n" + "END;" + ) + assert_sql_equal(sql, expected) + + def test_datefromparts(self): + sql = translate("SELECT DATEFROMPARTS(year,month,day) FROM table", "oracle") + expected = "SELECT TO_DATE(TO_CHAR(year,'0000')||'-'||TO_CHAR(month,'00')||'-'||TO_CHAR(day,'00'), 'YYYY-MM-DD') FROM table" + assert_sql_equal(sql, expected) + + def test_datetime_to_timestamp(self): + sql = translate("CREATE TABLE x (a DATETIME)", "oracle") + expected = "CREATE TABLE x (a TIMESTAMP)" + assert_sql_equal(sql, expected) + + def test_select_random_row(self): + sql = translate( + "SELECT column FROM (SELECT column, ROW_NUMBER() OVER (ORDER BY RAND()) AS rn FROM table) tmp WHERE rn <= 1", + "oracle", + ) + expected = ( + "SELECT column FROM (SELECT column, ROW_NUMBER() OVER (ORDER BY " + "DBMS_RANDOM.VALUE) AS rn FROM table FROM DUAL) tmp WHERE rn <= 1" + ) + assert_sql_equal(sql, expected) + + def test_select_random_row_using_hash(self): + sql = translate( + "SELECT column FROM (SELECT column, ROW_NUMBER() OVER (ORDER BY HASHBYTES('MD5',CAST(person_id AS varchar))) tmp WHERE rn <= 1", + "oracle", + ) + expected = "SELECT column FROM (SELECT column, ROW_NUMBER() OVER (ORDER BY DBMS_CRYPTO.HASH(TO_CHAR(person_id),2)) tmp WHERE rn <= 1" + assert_sql_equal(sql, expected) + + def test_convert_varbinary(self): + sql = translate( + "SELECT ROW_NUMBER() OVER CONVERT(VARBINARY, val, 1) rn WHERE rn <= 1", + "oracle", + ) + expected = "SELECT ROW_NUMBER() OVER TO_NUMBER(val, RPAD('X', LENGTH(val), 'X')) rn WHERE rn <= 1" + assert_sql_equal(sql, expected) + + def test_hash_hash_issue_on_oracle(self): + sql = translate("SELECT a FROM c##blah.table;", "oracle") + expected = "SELECT a FROM c##blah.table;" + assert_sql_equal(sql, expected) + + def test_top(self): + sql = translate("SELECT TOP 10 * FROM my_table WHERE a = b;", "oracle") + expected = "SELECT * FROM my_table WHERE a = b FETCH FIRST 10 ROWS ONLY;" + assert_sql_equal(sql, expected) + + def test_top_subquery(self): + sql = translate( + "SELECT name FROM (SELECT TOP 1 name FROM my_table WHERE a = b);", + "oracle", + ) + expected = "SELECT name FROM (SELECT name FROM my_table WHERE a = b FETCH FIRST 1 ROWS ONLY);" + assert_sql_equal(sql, expected) + + def test_distinct_top(self): + sql = translate("SELECT DISTINCT TOP 10 a FROM my_table WHERE a = b;", "oracle") + expected = "SELECT DISTINCT a FROM my_table WHERE a = b FETCH FIRST 10 ROWS ONLY;" + assert_sql_equal(sql, expected) + + def test_concat(self): + sql = translate('SELECT CONCAT(a," , ",c,d,e) FROM x;', "oracle") + expected = 'SELECT CONCAT(a, CONCAT(" , ", CONCAT(c, CONCAT(d, e)))) FROM x;' + assert_sql_equal(sql, expected) + + def test_natural_log(self): + sql = translate("SELECT LOG(number) FROM table", "oracle") + expected = "SELECT LOG(2.718281828459,number) FROM table" + assert_sql_equal(sql, expected) + + def test_log_base_10(self): + sql = translate("SELECT LOG10(number) FROM table;", "oracle") + expected = "SELECT LOG(10,number) FROM table;" + assert_sql_equal(sql, expected) + + def test_log_any_base(self): + sql = translate("SELECT LOG(number, base) FROM table", "oracle") + expected = "SELECT LOG(base,number) FROM table" + assert_sql_equal(sql, expected) + + def test_union_1(self): + sql = translate( + "SELECT * FROM table1 WHERE a = 1 UNION SELECT * FROM table2 WHERE a = 1;", + "oracle", + ) + expected = "SELECT * FROM table1 WHERE a = 1 UNION SELECT * FROM table2 WHERE a = 1;" + assert_sql_equal(sql, expected) + + def test_union_2(self): + sql = translate( + "SELECT * FROM table1 UNION SELECT * FROM table2 WHERE a = 1;", + "oracle", + ) + expected = "SELECT * FROM table1 UNION SELECT * FROM table2 WHERE a = 1;" + assert_sql_equal(sql, expected) + + def test_from_dual_simple(self): + sql = translate("SELECT 1 AS id;", "oracle") + expected = "SELECT 1 AS id FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_from_dual_subquery(self): + sql = translate("SELECT (SELECT id FROM a WHERE b=2) AS id;", "oracle") + expected = "SELECT (SELECT id FROM a WHERE b=2) AS id FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_isnumeric_case(self): + sql = translate( + "SELECT CASE WHEN ISNUMERIC(a) = 1 THEN a ELSE b FROM c;", + "oracle", + ) + expected = "SELECT CASE WHEN CASE WHEN (LENGTH(TRIM(TRANSLATE(a, ' +-.0123456789',' '))) IS NULL) THEN 1 ELSE 0 END = 1 THEN a ELSE b FROM c;" + assert_sql_equal(sql, expected) + + def test_isnumeric_where_equal_1(self): + sql = translate("SELECT a FROM table WHERE ISNUMERIC(a) = 1", "oracle") + expected = "SELECT a FROM table WHERE CASE WHEN (LENGTH(TRIM(TRANSLATE(a, ' +-.0123456789',' '))) IS NULL) THEN 1 ELSE 0 END = 1" + assert_sql_equal(sql, expected) + + def test_isnumeric_where_equal_0(self): + sql = translate("SELECT a FROM table WHERE ISNUMERIC(a) = 0", "oracle") + expected = "SELECT a FROM table WHERE CASE WHEN (LENGTH(TRIM(TRANSLATE(a, ' +-.0123456789',' '))) IS NULL) THEN 1 ELSE 0 END = 0" + assert_sql_equal(sql, expected) + + def test_add_group_by_when_case_count(self): + sql = translate( + "SELECT CASE COUNT(*) = 1 THEN 0 ELSE SUM(x)/(COUNT(*)-1) END AS stat FROM my_table;", + "oracle", + ) + expected = ( + "SELECT CASE COUNT(*) = 1 THEN 0 ELSE SUM(x)/(COUNT(*)-1) END AS stat FROM my_table GROUP BY 1;" + ) + assert_sql_equal(sql, expected) + + def test_dont_add_group_by_when_already_group_by(self): + sql = translate( + "SELECT CASE COUNT(*) = 1 THEN 0 ELSE SUM(x)/(COUNT(*)-1) END AS stat FROM my_table GROUP BY y;", + "oracle", + ) + expected = ( + "SELECT CASE COUNT(*) = 1 THEN 0 ELSE SUM(x)/(COUNT(*)-1) END AS stat FROM my_table GROUP BY y;" + ) + assert_sql_equal(sql, expected) + + def test_union_of_two_queries_without_from(self): + sql = translate("SELECT 1,2 UNION SELECT 3,4;", "oracle") + expected = "SELECT 1,2 FROM DUAL UNION SELECT 3,4 FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_union_of_three_queries_without_from(self): + sql = translate("SELECT 1,2 UNION SELECT 3,4 UNION SELECT 5,6;", "oracle") + expected = "SELECT 1,2 FROM DUAL UNION SELECT 3,4 FROM DUAL UNION SELECT 5,6 FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_insert_plus_union_of_three_queries_without_from(self): + sql = translate( + "INSERT INTO my_table (a, b) SELECT 1,2 UNION SELECT 3,4 UNION SELECT 5,6;", + "oracle", + ) + expected = "INSERT INTO my_table (a, b) SELECT 1,2 FROM DUAL UNION SELECT 3,4 FROM DUAL UNION SELECT 5,6 FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_union_where_only_last_query_needs_from_dual(self): + sql = translate("SELECT a,b FROM my_table UNION SELECT 5,6;", "oracle") + expected = "SELECT a,b FROM my_table UNION SELECT 5,6 FROM DUAL;" + assert_sql_equal(sql, expected) + + def test_nested_queries_with_eols(self): + sql = translate( + "INSERT INTO test (a,b) SELECT a,b FROM (SELECT a,b FROM (SELECT a,b FROM my_table\n) nesti WHERE b = 2\n) nesto WHERE a = 1;", + "oracle", + ) + expected = "INSERT INTO test (a,b) SELECT a,b FROM (SELECT a,b FROM (SELECT a,b FROM my_table\n) nesti WHERE b = 2\n) nesto WHERE a = 1;" + assert_sql_equal(sql, expected) + + def test_nested_queries_with_union(self): + sql = translate( + "SELECT a,b FROM (SELECT a,b FROM x UNION ALL SELECT a,b FROM x) o;", + "oracle", + ) + expected = "SELECT a,b FROM (SELECT a,b FROM x UNION ALL SELECT a,b FROM x) o;" + assert_sql_equal(sql, expected) + + def test_bigint_in_conditional_create_table(self): + sql = translate( + "IF OBJECT_ID('test', 'U') IS NULL CREATE TABLE test (x BIGINT);", + "oracle", + ) + expected = ( + "BEGIN\n" + " EXECUTE IMMEDIATE 'CREATE TABLE test (x NUMBER(19))';\n" + "EXCEPTION\n" + " WHEN OTHERS THEN\n" + " IF SQLCODE != -955 THEN\n" + " RAISE;\n" + " END IF;\n" + "END;" + ) + assert_sql_equal(sql, expected) + + def test_not_null_and_default_in_conditional_create_table(self): + sql = translate( + "IF OBJECT_ID('test_b', 'U') IS NULL CREATE TABLE test_b (x INT NOT NULL DEFAULT 0);", + "oracle", + ) + expected = ( + "BEGIN\n" + " EXECUTE IMMEDIATE 'CREATE TABLE test_b (x INT DEFAULT 0 NOT NULL)';\n" + "EXCEPTION\n" + " WHEN OTHERS THEN\n" + " IF SQLCODE != -955 THEN\n" + " RAISE;\n" + " END IF;\n" + "END;" + ) + assert_sql_equal(sql, expected) + + def test_analyze_table(self): + sql = translate("UPDATE STATISTICS results_schema.heracles_results;", "oracle") + expected = "-- ANALYZE should not be used to collect optimizer statistics" + assert_sql_equal(sql, expected) + + def test_datetime_and_datetime2(self): + sql = translate("CREATE TABLE x (a DATETIME2, b DATETIME);", "oracle") + expected = "CREATE TABLE x (a TIMESTAMP, b TIMESTAMP);" + assert_sql_equal(sql, expected) + + def test_drop_table_if_exists_simple(self): + sql = translate("DROP TABLE IF EXISTS test;", "oracle") + expected = ( + "BEGIN\n" + " EXECUTE IMMEDIATE 'TRUNCATE TABLE test';\n" + " EXECUTE IMMEDIATE 'DROP TABLE test';\n" + "EXCEPTION\n" + " WHEN OTHERS THEN\n" + " IF SQLCODE != -942 THEN\n" + " RAISE;\n" + " END IF;\n" + "END;" + ) + assert_sql_equal(sql, expected) + + def test_select_star_comma(self): + sql = translate("SELECT *, 1 AS x FROM my_table;", "oracle") + expected = "SELECT my_table.*, 1 AS x FROM my_table;" + assert_sql_equal(sql, expected) + + def test_select_star_comma_subquery(self): + sql = translate("SELECT *, 1 AS x FROM (SELECT a FROM b) q01;", "oracle") + expected = "SELECT q01.*, 1 AS x FROM (SELECT a FROM b) q01;" + assert_sql_equal(sql, expected) + + def test_select_top_star_comma(self): + sql = translate("SELECT TOP 10 *, 1 AS x FROM my_table;", "oracle") + expected = "SELECT my_table.*, 1 AS x FROM my_table FETCH FIRST 10 ROWS ONLY;" + assert_sql_equal(sql, expected) + + def test_select_star_comma_where(self): + sql = translate("SELECT *, 1 AS x FROM my_table WHERE a = b;", "oracle") + expected = "SELECT my_table.*, 1 AS x FROM my_table WHERE a = b;" + assert_sql_equal(sql, expected) + + def test_select_star_comma_order_by(self): + sql = translate("SELECT *, 1 AS x FROM my_table WHERE a = b ORDER BY a;", "oracle") + expected = "SELECT my_table.*, 1 AS x FROM my_table WHERE a = b ORDER BY a;" + assert_sql_equal(sql, expected) + + def test_nested_select_star_comma(self): + sql = translate("(SELECT *, 1 AS x FROM my_table)", "oracle") + expected = "(SELECT my_table.*, 1 AS x FROM my_table)" + assert_sql_equal(sql, expected) + + def test_iif(self): + sql = translate("SELECT IIF(a>b, 1, b) AS max_val FROM table;", "oracle") + expected = "SELECT CASE WHEN a>b THEN 1 ELSE b END AS max_val FROM table;" + assert_sql_equal(sql, expected) + + def test_drvd(self): + sql = translate( + "SELECT\n" + " TRY_CAST(name AS VARCHAR(MAX)) AS name,\n" + " TRY_CAST(speed AS FLOAT) AS speed\n" + " FROM ( VALUES ('A', 1.0), ('B', 2.0)) AS drvd(name, speed);", + "oracle", + ) + expected = ( + "SELECT CAST(name AS VARCHAR2(1024)) AS name,\n" + " CAST(speed AS FLOAT) AS speed\n" + " FROM (SELECT NULL AS name, NULL AS speed FROM DUAL WHERE (0 = 1)" + " UNION ALL SELECT 'A', 1.0 FROM DUAL" + " UNION ALL SELECT 'B', 2.0 FROM DUAL) values_table;" + ) + assert_sql_equal(sql, expected) + + def test_temp_dplyr_dots_pattern(self): + sql = translate("SELECT * FROM table...1;", "oracle") + expected = "SELECT * FROM tablexxx1;" + assert_sql_equal(sql, expected) + + def test_bitwise_and(self): + sql = translate("SELECT ((a+b) & c/123) FROM table;", "oracle") + expected = "SELECT BITAND((a+b), c/123) FROM table;" + assert_sql_equal(sql, expected) + + # ── tests using tempEmulationSchema (ported using explicit session_id) ── + + def test_temp_table_field_ref(self): + sql = translate( + "SELECT #tmp.name FROM #tmp;", + "oracle", + temp_emulation_schema="ts", + session_id="T0000000", + ) + expected = "SELECT T0000000tmp.name FROM ts.T0000000tmp;" + assert_sql_equal(sql, expected) + + def test_create_temp_table(self): + sql = translate( + "CREATE TABLE #temp (x INT);", + "oracle", + temp_emulation_schema="ts", + session_id="T0000000", + ) + expected = ( + "BEGIN\n" + " EXECUTE IMMEDIATE 'TRUNCATE TABLE ts.T0000000temp';\n" + " EXECUTE IMMEDIATE 'DROP TABLE ts.T0000000temp';\n" + "EXCEPTION\n" + " WHEN OTHERS THEN\n" + " IF SQLCODE != -942 THEN\n" + " RAISE;\n" + " END IF;\n" + "END;\n" + "CREATE TABLE ts.T0000000temp (x INT);" + ) + assert_sql_equal(sql, expected) + + def test_select_into_temp_table(self): + sql = translate( + "SELECT * INTO #temp FROM my_table;", + "oracle", + temp_emulation_schema="ts", + session_id="T0000000", + ) + expected = ( + "BEGIN\n" + " EXECUTE IMMEDIATE 'TRUNCATE TABLE ts.T0000000temp';\n" + " EXECUTE IMMEDIATE 'DROP TABLE ts.T0000000temp';\n" + "EXCEPTION\n" + " WHEN OTHERS THEN\n" + " IF SQLCODE != -942 THEN\n" + " RAISE;\n" + " END IF;\n" + "END;\n" + "CREATE TABLE ts.T0000000temp AS\n" + "SELECT\n" + "*\n" + "FROM\n" + "my_table;" + ) + assert_sql_equal(sql, expected) + + # ── skip translateSingleStatement tests ── + # The Python implementation does not expose translateSingleStatement as a + # public API. The R equivalent relies on an internal helper. Related tests + # (trailing semicolons, BEGIN…END blocks, multi-statement error) are omitted + # for now but can be added as unit tests against split_sql + translate once + # that composition is formally supported. + + def test_single_statement_trailing_semicolon__not_applicable(self): + """translateSingleStatement not available; verify translate strips trailing semicolons.""" + # R expected: "SELECT * FROM my_table " (no trailing semicolon) + sql = translate("SELECT * FROM my_table;", "oracle") + # The Python translate() does NOT strip trailing semicolons; the R + # translateSingleStatement did. We document the current behavior. + assert sql.rstrip().endswith(";") or sql.rstrip().endswith("FROM DUAL;") + + def test_single_statement_begin_end__not_applicable(self): + """translateSingleStatement not available; verify translate handles BEGIN…END.""" + sql = translate( + "BEGIN\nSELECT * INTO a FROM b;\nEND;", + "oracle", + ) + # Verify translation produces valid Oracle CREATE TABLE AS syntax + assert "CREATE TABLE a" in sql + assert "SELECT" in sql + + def test_single_statement_multi_error__not_applicable(self): + """translateSingleStatement not available; verify translate handles multiple statements.""" + # translate does not throw for multiple statements; it translates them all + sql = translate("TRUNCATE my_table; DROP TABLE my_table;", "oracle") + # Should contain both translated statements + assert sql.strip() != "" + + # ── oracleTempSchema warning tests are not applicable ── + # The R package had a deprecated 'oracleTempSchema' parameter that emitted + # a warning. Python's translate() uses 'temp_emulation_schema' instead, and + # there is no deprecated alias. These tests are not ported. diff --git a/tests/test_sqlrender_translate_postgresql.py b/tests/test_sqlrender_translate_postgresql.py new file mode 100644 index 0000000..45d4777 --- /dev/null +++ b/tests/test_sqlrender_translate_postgresql.py @@ -0,0 +1,102 @@ +"""Test PostgreSQL translation - ported from OHDSI SqlRender test-translate-postgresql.R""" + +from circe.sqlrender import translate +from tests.sqlrender_utils import assert_sql_equal + + +class TestPostgreSQLTranslation: + def test_use(self): + sql = translate("USE vocabulary;", "postgresql") + assert_sql_equal(sql, "SET search_path TO vocabulary;") + + def test_string_concat_1(self): + sql = translate("'x' + b ( 'x' + b)", "postgresql") + assert_sql_equal(sql, "'x' || b ( 'x' || b)") + + def test_string_concat_2(self): + sql = translate("a + ';b'", "postgresql") + assert_sql_equal(sql, "a || ';b'") + + def test_string_concat_3(self): + sql = translate("a + ';('", "postgresql") + assert_sql_equal(sql, "a || ';('") + + def test_dateadd_month(self): + sql = translate("DATEADD(mm,1,date)", "postgresql") + assert_sql_equal(sql, "(date + 1*INTERVAL'1 month')") + + def test_datediff_month(self): + sql = translate( + "SELECT DATEDIFF(month,drug_era_start_date,drug_era_end_date) FROM drug_era;", + "postgresql", + ) + expected = ( + "SELECT (extract(year from age(CAST(drug_era_end_date AS DATE)," + " CAST(drug_era_start_date AS DATE)))*12 + extract(month from age" + "(CAST(drug_era_end_date AS DATE), CAST(drug_era_start_date AS DATE)))) FROM drug_era;" + ) + assert_sql_equal(sql, expected) + + def test_getdate(self): + sql = translate("GETDATE()", "postgresql") + assert_sql_equal(sql, "CURRENT_DATE") + + def test_select_into_temp(self): + sql = translate("SELECT a INTO #b FROM c;", "postgresql") + assert_sql_equal( + sql, + "CREATE TEMP TABLE b\nAS\nSELECT\na\nFROM\nc;\nANALYZE b;", + ) + + def test_select_top(self): + sql = translate("SELECT TOP 10 a FROM b;", "postgresql") + assert_sql_equal(sql, "SELECT a FROM b LIMIT 10;") + + def test_distinct_top(self): + sql = translate("SELECT DISTINCT TOP 10 a FROM b;", "postgresql") + assert_sql_equal(sql, "SELECT DISTINCT a FROM b LIMIT 10;") + + def test_create_table_if_not_exists(self): + sql = translate( + "IF OBJECT_ID('my_table', 'U') IS NULL CREATE TABLE my_table (id INT);", + "postgresql", + ) + assert_sql_equal(sql, "CREATE TABLE IF NOT EXISTS my_table (id INT);") + + def test_drop_table_if_exists(self): + sql = translate( + "IF OBJECT_ID('my_table', 'U') IS NOT NULL DROP TABLE my_table;", + "postgresql", + ) + assert_sql_equal(sql, "DROP TABLE IF EXISTS my_table;") + + def test_isnull(self): + sql = translate("ISNULL(a, b)", "postgresql") + assert_sql_equal(sql, "COALESCE(a, b)") + + def test_round(self): + sql = translate("ROUND(a, 2)", "postgresql") + assert_sql_equal(sql, "ROUND(CAST(a AS NUMERIC), 2)") + + def test_len(self): + sql = translate("LEN('hello')", "postgresql") + assert_sql_equal(sql, "CHAR_LENGTH('hello')") + + def test_iif(self): + sql = translate( + "IIF(a > 0, 'positive', 'non-positive')", + "postgresql", + ) + assert_sql_equal( + sql, + "CASE WHEN a > 0 THEN 'positive' ELSE 'non-positive' END", + ) + + def test_charindex(self): + sql = translate("CHARINDEX('needle', haystack)", "postgresql") + assert_sql_equal(sql, "STRPOS(haystack, 'needle')") + + def test_temp_table_emulation(self): + sql = translate("SELECT a INTO #temptable FROM b;", "postgresql") + assert "CREATE TEMP TABLE" in sql + assert "temptable" in sql.lower()