import platform import subprocess from enum import Enum from functools import lru_cache from typing import List, Tuple import yaml from .fsm import CorrelationFSM, MarkerFSM class MarkerType(Enum): SELECT = "select" UPDATE = "update" INSERT = "insert" DELETE = "delete" class Parser: def __init__(self): self.parser_bin_path = ( f"parser/parser-{platform.system().lower()}-{platform.machine()}" ) @lru_cache(maxsize=1024) def parse(self, query: str) -> List[dict]: p = subprocess.Popen( [self.parser_bin_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) p.stdin.write(query) p.stdin.flush() p.stdin.close() p.wait() content = p.stdout.read() if p.returncode != 0: raise ValueError( f"parser exit with non-zero code for input statement: {query}" ) return yaml.safe_load(content) def parse_correlations(self, query: str) -> List[Tuple[str, str]]: fsm = CorrelationFSM() for n in self.parse(query): fsm.process(n) return fsm.correlations() @lru_cache(maxsize=1024) def parse_placeholders( self, query: str, stmt_type: str ) -> List[Tuple[MarkerType, str]]: fsm = MarkerFSM() for n in self.parse(query): fsm.process(n) xs = [] for m in fsm.assignments(): xs.append((MarkerType.UPDATE, m)) for m in fsm.markers(): if stmt_type in ["Select", "Update"]: xs.append((MarkerType.SELECT, m)) elif stmt_type == "Insert": xs.append((MarkerType.INSERT, m)) elif stmt_type == "Delete": xs.append((MarkerType.DELETE, m)) return xs