parent
90712aff7d
commit
49a3fc0a78
@ -0,0 +1,13 @@
|
||||
from engine.ast import Context, ast_node
|
||||
import engine.ddl
|
||||
|
||||
def initialize():
|
||||
return Context()
|
||||
|
||||
def generate(ast, cxt):
|
||||
for k in ast.keys():
|
||||
if k in ast_node.types.keys():
|
||||
root = ast_node.types[k](None, ast, cxt)
|
||||
|
||||
|
||||
__all__ = ["generate"]
|
@ -0,0 +1,65 @@
|
||||
from typing import List
|
||||
|
||||
|
||||
class TableInfo:
|
||||
def __init__(self, table_name, cols, cxt:'Context'):
|
||||
# statics
|
||||
self.table_name = table_name
|
||||
self.columns = dict() # column_name, type
|
||||
for c in cols:
|
||||
self.columns[c['name']] = ((list(c['type'].keys()))[0], c)
|
||||
k9name = self.table_name + c['name']
|
||||
if k9name in cxt.k9cols_byname: # duplicate names?
|
||||
root = cxt.k9cols_byname[k9name]
|
||||
k9name = k9name + root[1]
|
||||
root[1] += 1
|
||||
cxt.k9cols[c] = k9name
|
||||
cxt.k9cols_byname[k9name] = (c, 1)
|
||||
# runtime
|
||||
self.n_cols = 0 # number of cols
|
||||
self.order = [] # assumptions
|
||||
|
||||
cxt.tables_byname[self.table_name] = self # construct reverse map
|
||||
|
||||
def get_k9colname(self, cxt:'Context', col_name):
|
||||
return cxt.k9cols[self.columns[col_name][1]] # well, this is gnarly.. will change later
|
||||
|
||||
class Context:
|
||||
def __init__(self):
|
||||
self.tables:List[TableInfo] = []
|
||||
self.tables_byname = dict()
|
||||
self.k9cols = dict()
|
||||
self.k9cols_byname = dict()
|
||||
|
||||
self.k9code = ''
|
||||
|
||||
def add_table(self, table_name, cols):
|
||||
tbl = TableInfo(table_name, cols, self)
|
||||
self.tables.append(tbl)
|
||||
return tbl
|
||||
|
||||
def emit(self, codelet):
|
||||
self.k9code += codelet + '\n'
|
||||
|
||||
def __str__(self):
|
||||
return self.k9code
|
||||
|
||||
class ast_node:
|
||||
types = dict()
|
||||
def __init__(self, parent:"ast_node", node, context:Context = None):
|
||||
self.context = parent.context if context is None else context
|
||||
self.produce(node)
|
||||
self.enumerate(node)
|
||||
self.consume(node)
|
||||
|
||||
def emit(self, code):
|
||||
self.context.emit(code)
|
||||
|
||||
name = 'null'
|
||||
|
||||
def produce(self, _):
|
||||
pass
|
||||
def enumerate(self, _):
|
||||
pass
|
||||
def consume(self, _):
|
||||
pass
|
@ -0,0 +1,22 @@
|
||||
from engine.ast import TableInfo, ast_node
|
||||
class create_table(ast_node):
|
||||
name = 'create_table'
|
||||
def produce(self, node):
|
||||
ct = node[self.name]
|
||||
tbl = self.context.add_table(ct['name'], ct['columns'])
|
||||
# create tables in k9
|
||||
for c in ct['columns']:
|
||||
self.emit(f"{tbl.get_k9colname((list(c['name'].keys())))[0]}:()")
|
||||
|
||||
class insert_into(ast_node):
|
||||
name = 'insert'
|
||||
def produce(self, node):
|
||||
ct = node[self.name]
|
||||
table:TableInfo = self.context.tables_byname[ct]
|
||||
|
||||
|
||||
import sys, inspect
|
||||
|
||||
for name, cls in inspect.getmembers(sys.modules[__name__]):
|
||||
if inspect.isclass(cls) and issubclass(cls, ast_node):
|
||||
ast_node.types[name] = cls
|
@ -1,602 +0,0 @@
|
||||
# encoding: utf-8
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Beto Dealmeida (beto@dealmeida.net)
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from mo_dots import split_field
|
||||
from mo_future import first, is_text, string_types, text
|
||||
from mo_parsing import listwrap
|
||||
|
||||
from mo_sql_parsing.keywords import RESERVED, join_keywords, precedence
|
||||
from mo_sql_parsing.utils import binary_ops, is_set_op
|
||||
|
||||
MAX_PRECEDENCE = 100
|
||||
VALID = re.compile(r"^[a-zA-Z_]\w*$")
|
||||
|
||||
|
||||
def is_keyword(identifier):
|
||||
try:
|
||||
RESERVED.parse_string(identifier)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def should_quote(identifier):
|
||||
"""
|
||||
Return true if a given identifier should be quoted.
|
||||
|
||||
This is usually true when the identifier:
|
||||
|
||||
- is a reserved word
|
||||
- contain spaces
|
||||
- does not match the regex `[a-zA-Z_]\\w*`
|
||||
|
||||
"""
|
||||
return identifier != "*" and (not VALID.match(identifier) or is_keyword(identifier))
|
||||
|
||||
|
||||
def escape(ident, ansi_quotes, should_quote):
|
||||
"""
|
||||
Escape identifiers.
|
||||
|
||||
ANSI uses double quotes, but many databases use back quotes.
|
||||
|
||||
"""
|
||||
|
||||
def esc(identifier):
|
||||
if not should_quote(identifier):
|
||||
return identifier
|
||||
|
||||
quote = '"' if ansi_quotes else "`"
|
||||
identifier = identifier.replace(quote, 2 * quote)
|
||||
return "{0}{1}{2}".format(quote, identifier, quote)
|
||||
|
||||
return ".".join(esc(f) for f in split_field(ident))
|
||||
|
||||
|
||||
def Operator(_op):
|
||||
op_prec = precedence[binary_ops[_op]]
|
||||
op = " {0} ".format(_op).replace("_", " ").upper()
|
||||
|
||||
def func(self, json, prec):
|
||||
acc = []
|
||||
|
||||
if isinstance(json, dict):
|
||||
# {VARIABLE: VALUE} FORM
|
||||
k, v = first(json.items())
|
||||
json = [k, {"literal": v}]
|
||||
|
||||
for i, v in enumerate(listwrap(json)):
|
||||
if i == 0:
|
||||
acc.append(self.dispatch(v, op_prec + 0.25))
|
||||
else:
|
||||
acc.append(self.dispatch(v, op_prec))
|
||||
if prec >= op_prec:
|
||||
return op.join(acc)
|
||||
else:
|
||||
return f"({op.join(acc)})"
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def isolate(expr, sql, prec):
|
||||
"""
|
||||
RETURN sql IN PARENTHESIS IF PREEDENCE > prec
|
||||
:param expr: expression to isolate
|
||||
:param sql: sql to return
|
||||
:param prec: current precedence
|
||||
"""
|
||||
if is_text(expr):
|
||||
return sql
|
||||
ps = [p for k in expr.keys() for p in [precedence.get(k)] if p is not None]
|
||||
if not ps:
|
||||
return sql
|
||||
elif min(ps) >= prec:
|
||||
return f"({sql})"
|
||||
else:
|
||||
return sql
|
||||
|
||||
|
||||
unordered_clauses = [
|
||||
"with",
|
||||
"distinct_on",
|
||||
"select_distinct",
|
||||
"select",
|
||||
"from",
|
||||
"where",
|
||||
"groupby",
|
||||
"having",
|
||||
]
|
||||
|
||||
ordered_clauses = [
|
||||
"orderby",
|
||||
"limit",
|
||||
"offset",
|
||||
"fetch",
|
||||
]
|
||||
|
||||
|
||||
class Formatter:
|
||||
# infix operators
|
||||
_concat = Operator("||")
|
||||
_mul = Operator("*")
|
||||
_div = Operator("/")
|
||||
_mod = Operator("%")
|
||||
_add = Operator("+")
|
||||
_sub = Operator("-")
|
||||
_neq = Operator("<>")
|
||||
_gt = Operator(">")
|
||||
_lt = Operator("<")
|
||||
_gte = Operator(">=")
|
||||
_lte = Operator("<=")
|
||||
_eq = Operator("=")
|
||||
_or = Operator("or")
|
||||
_and = Operator("and")
|
||||
_binary_and = Operator("&")
|
||||
_binary_or = Operator("|")
|
||||
_like = Operator("like")
|
||||
_not_like = Operator("not like")
|
||||
_rlike = Operator("rlike")
|
||||
_not_rlike = Operator("not rlike")
|
||||
_union = Operator("union")
|
||||
_union_all = Operator("union all")
|
||||
_intersect = Operator("intersect")
|
||||
_minus = Operator("minus")
|
||||
_except = Operator("except")
|
||||
|
||||
def __init__(self, ansi_quotes=True, should_quote=should_quote):
|
||||
self.ansi_quotes = ansi_quotes
|
||||
self.should_quote = should_quote
|
||||
|
||||
def format(self, json):
|
||||
return self.dispatch(json, 50)
|
||||
|
||||
def dispatch(self, json, prec=100):
|
||||
if isinstance(json, list):
|
||||
return self.sql_list(json, prec=precedence["list"])
|
||||
if isinstance(json, dict):
|
||||
if len(json) == 0:
|
||||
return ""
|
||||
elif "value" in json:
|
||||
return self.value(json, prec)
|
||||
elif "join" in json:
|
||||
return self._join_on(json)
|
||||
elif "insert" in json:
|
||||
return self.insert(json)
|
||||
elif json.keys() & set(ordered_clauses):
|
||||
return self.ordered_query(json, prec)
|
||||
elif json.keys() & set(unordered_clauses):
|
||||
return self.unordered_query(json, prec)
|
||||
elif "null" in json:
|
||||
return "NULL"
|
||||
elif "trim" in json:
|
||||
return self._trim(json, prec)
|
||||
elif "extract" in json:
|
||||
return self._extract(json, prec)
|
||||
else:
|
||||
return self.op(json, prec)
|
||||
if isinstance(json, string_types):
|
||||
return escape(json, self.ansi_quotes, self.should_quote)
|
||||
if json == None:
|
||||
return "NULL"
|
||||
|
||||
return text(json)
|
||||
|
||||
def sql_list(self, json, prec=precedence["from"] - 1):
|
||||
sql = ", ".join(self.dispatch(element, prec=MAX_PRECEDENCE) for element in json)
|
||||
if prec >= precedence["from"]:
|
||||
return sql
|
||||
else:
|
||||
return f"({sql})"
|
||||
|
||||
def value(self, json, prec=precedence["from"]):
|
||||
parts = [self.dispatch(json["value"], prec)]
|
||||
if "over" in json:
|
||||
over = json["over"]
|
||||
parts.append("OVER")
|
||||
window = []
|
||||
if "partitionby" in over:
|
||||
window.append("PARTITION BY")
|
||||
window.append(self.dispatch(over["partitionby"]))
|
||||
if "orderby" in over:
|
||||
window.append(self.orderby(over, precedence["window"]))
|
||||
if "range" in over:
|
||||
|
||||
def wordy(v):
|
||||
if v < 0:
|
||||
return [text(abs(v)), "PRECEDING"]
|
||||
elif v > 0:
|
||||
return [text(v), "FOLLOWING"]
|
||||
|
||||
window.append("ROWS")
|
||||
range = over["range"]
|
||||
min = range.get("min")
|
||||
max = range.get("max")
|
||||
|
||||
if min is None:
|
||||
if max is None:
|
||||
window.pop() # not expected, but deal
|
||||
elif max == 0:
|
||||
window.append("UNBOUNDED PRECEDING")
|
||||
else:
|
||||
window.append("BETWEEN")
|
||||
window.append("UNBOUNDED PRECEDING")
|
||||
window.append("AND")
|
||||
window.extend(wordy(max))
|
||||
elif min == 0:
|
||||
if max is None:
|
||||
window.append("UNBOUNDED FOLLOWING")
|
||||
elif max == 0:
|
||||
window.append("CURRENT ROW")
|
||||
else:
|
||||
window.extend(wordy(max))
|
||||
else:
|
||||
if max is None:
|
||||
window.append("BETWEEN")
|
||||
window.extend(wordy(min))
|
||||
window.append("AND")
|
||||
window.append("UNBOUNDED FOLLOWING")
|
||||
elif max == 0:
|
||||
window.extend(wordy(min))
|
||||
else:
|
||||
window.append("BETWEEN")
|
||||
window.extend(wordy(min))
|
||||
window.append("AND")
|
||||
window.extend(wordy(max))
|
||||
|
||||
window = " ".join(window)
|
||||
parts.append(f"({window})")
|
||||
if "name" in json:
|
||||
parts.extend(["AS", self.dispatch(json["name"])])
|
||||
|
||||
return " ".join(parts)
|
||||
|
||||
def op(self, json, prec):
|
||||
if len(json) > 1:
|
||||
raise Exception("Operators should have only one key!")
|
||||
key, value = list(json.items())[0]
|
||||
|
||||
# check if the attribute exists, and call the corresponding method;
|
||||
# note that we disallow keys that start with `_` to avoid giving access
|
||||
# to magic methods
|
||||
attr = f"_{key}"
|
||||
if hasattr(self, attr) and not key.startswith("_"):
|
||||
method = getattr(self, attr)
|
||||
op_prec = precedence.get(key, MAX_PRECEDENCE)
|
||||
if prec >= op_prec:
|
||||
return method(value, op_prec)
|
||||
else:
|
||||
return f"({method(value, op_prec)})"
|
||||
|
||||
# treat as regular function call
|
||||
if isinstance(value, dict) and len(value) == 0:
|
||||
return (
|
||||
key.upper() + "()"
|
||||
) # NOT SURE IF AN EMPTY dict SHOULD BE DELT WITH HERE, OR IN self.format()
|
||||
else:
|
||||
params = ", ".join(self.dispatch(p) for p in listwrap(value))
|
||||
return f"{key.upper()}({params})"
|
||||
|
||||
def _binary_not(self, value, prec):
|
||||
return "~{0}".format(self.dispatch(value))
|
||||
|
||||
def _exists(self, value, prec):
|
||||
return "{0} IS NOT NULL".format(self.dispatch(value, precedence["is"]))
|
||||
|
||||
def _missing(self, value, prec):
|
||||
return "{0} IS NULL".format(self.dispatch(value, precedence["is"]))
|
||||
|
||||
def _collate(self, pair, prec):
|
||||
return "{0} COLLATE {1}".format(
|
||||
self.dispatch(pair[0], precedence["collate"]), pair[1]
|
||||
)
|
||||
|
||||
def _in(self, json, prec):
|
||||
member, set = json
|
||||
if "literal" in set:
|
||||
set = {"literal": listwrap(set["literal"])}
|
||||
sql = (
|
||||
self.dispatch(member, precedence["in"])
|
||||
+ " IN "
|
||||
+ self.dispatch(set, precedence["in"])
|
||||
)
|
||||
if prec < precedence["in"]:
|
||||
sql = f"({sql})"
|
||||
return sql
|
||||
|
||||
def _nin(self, json, prec):
|
||||
member, set = json
|
||||
if "literal" in set:
|
||||
set = {"literal": listwrap(set["literal"])}
|
||||
sql = (
|
||||
self.dispatch(member, precedence["in"])
|
||||
+ " NOT IN "
|
||||
+ self.dispatch(set, precedence["in"])
|
||||
)
|
||||
if prec < precedence["in"]:
|
||||
sql = f"({sql})"
|
||||
return sql
|
||||
|
||||
def _case(self, checks, prec):
|
||||
parts = ["CASE"]
|
||||
for check in checks if isinstance(checks, list) else [checks]:
|
||||
if isinstance(check, dict):
|
||||
if "when" in check and "then" in check:
|
||||
parts.extend(["WHEN", self.dispatch(check["when"])])
|
||||
parts.extend(["THEN", self.dispatch(check["then"])])
|
||||
else:
|
||||
parts.extend(["ELSE", self.dispatch(check)])
|
||||
else:
|
||||
parts.extend(["ELSE", self.dispatch(check)])
|
||||
parts.append("END")
|
||||
return " ".join(parts)
|
||||
|
||||
def _cast(self, json, prec):
|
||||
expr, type = json
|
||||
|
||||
type_name, params = first(type.items())
|
||||
if not params:
|
||||
type = type_name.upper()
|
||||
else:
|
||||
type = {type_name.upper(): params}
|
||||
|
||||
return f"CAST({self.dispatch(expr)} AS {self.dispatch(type)})"
|
||||
|
||||
def _extract(self, json, prec):
|
||||
interval, value = json["extract"]
|
||||
i = self.dispatch(interval).upper()
|
||||
v = self.dispatch(value)
|
||||
return f"EXTRACT({i} FROM {v})"
|
||||
|
||||
def _interval(self, json, prec):
|
||||
amount = self.dispatch(json[0], precedence["and"])
|
||||
type = self.dispatch(json[1], precedence["and"])
|
||||
return f"INTERVAL {amount} {type.upper()}"
|
||||
|
||||
def _literal(self, json, prec=0):
|
||||
if isinstance(json, list):
|
||||
return "({0})".format(", ".join(
|
||||
self._literal(v, precedence["literal"]) for v in json
|
||||
))
|
||||
elif isinstance(json, string_types):
|
||||
return "'{0}'".format(json.replace("'", "''"))
|
||||
else:
|
||||
return str(json)
|
||||
|
||||
def _get(self, json, prec):
|
||||
v, i = json
|
||||
v_sql = self.dispatch(v, prec=precedence["literal"])
|
||||
i_sql = self.dispatch(i)
|
||||
return f"{v_sql}[{i_sql}]"
|
||||
|
||||
def _between(self, json, prec):
|
||||
return "{0} BETWEEN {1} AND {2}".format(
|
||||
self.dispatch(json[0], precedence["between"]),
|
||||
self.dispatch(json[1], precedence["between"]),
|
||||
self.dispatch(json[2], precedence["between"]),
|
||||
)
|
||||
|
||||
def _trim(self, json, prec):
|
||||
c = json.get("characters")
|
||||
d = json.get("direction")
|
||||
v = json["trim"]
|
||||
acc = ["TRIM("]
|
||||
if d:
|
||||
acc.append(d.upper())
|
||||
acc.append(" ")
|
||||
if c:
|
||||
acc.append(self.dispatch(c))
|
||||
acc.append(" ")
|
||||
if c or d:
|
||||
acc.append("FROM ")
|
||||
acc.append(self.dispatch(v))
|
||||
acc.append(")")
|
||||
return "".join(acc)
|
||||
|
||||
def _not_between(self, json, prec):
|
||||
return "{0} NOT BETWEEN {1} AND {2}".format(
|
||||
self.dispatch(json[0], precedence["between"]),
|
||||
self.dispatch(json[1], precedence["between"]),
|
||||
self.dispatch(json[2], precedence["between"]),
|
||||
)
|
||||
|
||||
def _distinct(self, json, prec):
|
||||
return "DISTINCT " + ", ".join(
|
||||
self.dispatch(v, precedence["select"]) for v in listwrap(json)
|
||||
)
|
||||
|
||||
def _select_distinct(self, json, prec):
|
||||
return "SELECT DISTINCT " + ", ".join(self.dispatch(v) for v in listwrap(json))
|
||||
|
||||
def _distinct_on(self, json, prec):
|
||||
return (
|
||||
"DISTINCT ON (" + ", ".join(self.dispatch(v) for v in listwrap(json)) + ")"
|
||||
)
|
||||
|
||||
def _join_on(self, json, prec):
|
||||
detected_join = join_keywords & set(json.keys())
|
||||
if len(detected_join) == 0:
|
||||
raise Exception(
|
||||
'Fail to detect join type! Detected: "{}" Except one of: "{}"'.format(
|
||||
[on_keyword for on_keyword in json if on_keyword != "on"][0],
|
||||
'", "'.join(join_keywords),
|
||||
)
|
||||
)
|
||||
|
||||
join_keyword = detected_join.pop()
|
||||
|
||||
acc = []
|
||||
acc.append(join_keyword.upper())
|
||||
acc.append(self.dispatch(json[join_keyword], precedence["join"]))
|
||||
|
||||
if json.get("on"):
|
||||
acc.append("ON")
|
||||
acc.append(self.dispatch(json["on"]))
|
||||
if json.get("using"):
|
||||
acc.append("USING")
|
||||
acc.append(self.dispatch(json["using"]))
|
||||
return " ".join(acc)
|
||||
|
||||
def ordered_query(self, json, prec):
|
||||
if json.keys() & set(unordered_clauses) - {"from"}:
|
||||
# regular query
|
||||
acc = [self.unordered_query(json, precedence["order"])]
|
||||
else:
|
||||
# set-op expression
|
||||
acc = [self.dispatch(json["from"], precedence["order"])]
|
||||
|
||||
acc.extend(
|
||||
part
|
||||
for clause in ordered_clauses
|
||||
if clause in json
|
||||
for part in [getattr(self, clause)(json, precedence["order"])]
|
||||
if part
|
||||
)
|
||||
sql = " ".join(acc)
|
||||
if prec >= precedence["order"]:
|
||||
return sql
|
||||
else:
|
||||
return f"({sql})"
|
||||
|
||||
def unordered_query(self, json, prec):
|
||||
sql = " ".join(
|
||||
part
|
||||
for clause in unordered_clauses
|
||||
if clause in json
|
||||
for part in [getattr(self, clause)(json, precedence["from"])]
|
||||
if part
|
||||
)
|
||||
if prec >= precedence["from"]:
|
||||
return sql
|
||||
else:
|
||||
return f"({sql})"
|
||||
|
||||
def with_(self, json, prec):
|
||||
if "with" in json:
|
||||
with_ = json["with"]
|
||||
if not isinstance(with_, list):
|
||||
with_ = [with_]
|
||||
parts = ", ".join(
|
||||
"{0} AS ({1})".format(part["name"], self.dispatch(part["value"]))
|
||||
for part in with_
|
||||
)
|
||||
return "WITH {0}".format(parts)
|
||||
|
||||
def select(self, json, prec):
|
||||
param = ", ".join(self.dispatch(s) for s in listwrap(json["select"]))
|
||||
if "top" in json:
|
||||
top = self.dispatch(json["top"])
|
||||
return f"SELECT TOP ({top}) {param}"
|
||||
if "distinct_on" in json:
|
||||
return param
|
||||
else:
|
||||
return f"SELECT {param}"
|
||||
|
||||
def distinct_on(self, json, prec):
|
||||
param = ", ".join(self.dispatch(s) for s in listwrap(json["distinct_on"]))
|
||||
return f"SELECT DISTINCT ON ({param})"
|
||||
|
||||
def select_distinct(self, json, prec):
|
||||
param = ", ".join(self.dispatch(s) for s in listwrap(json["select_distinct"]))
|
||||
return f"SELECT DISTINCT {param}"
|
||||
|
||||
def from_(self, json, prec):
|
||||
is_join = False
|
||||
from_ = json["from"]
|
||||
if isinstance(from_, dict) and is_set_op & from_.keys():
|
||||
source = self.op(from_, precedence["from"])
|
||||
return f"FROM {source}"
|
||||
|
||||
from_ = listwrap(from_)
|
||||
parts = []
|
||||
for v in from_:
|
||||
if join_keywords & set(v):
|
||||
is_join = True
|
||||
parts.append(self._join_on(v, precedence["from"] - 1))
|
||||
else:
|
||||
parts.append(self.dispatch(v, precedence["from"] - 1))
|
||||
joiner = " " if is_join else ", "
|
||||
rest = joiner.join(parts)
|
||||
return f"FROM {rest}"
|
||||
|
||||
def where(self, json, prec):
|
||||
expr = self.dispatch(json["where"])
|
||||
return f"WHERE {expr}"
|
||||
|
||||
def groupby(self, json, prec):
|
||||
param = ", ".join(self.dispatch(s) for s in listwrap(json["groupby"]))
|
||||
return f"GROUP BY {param}"
|
||||
|
||||
def having(self, json, prec):
|
||||
return "HAVING {0}".format(self.dispatch(json["having"]))
|
||||
|
||||
def orderby(self, json, prec):
|
||||
param = ", ".join(
|
||||
(
|
||||
self.dispatch(s["value"], precedence["order"])
|
||||
+ " "
|
||||
+ s.get("sort", "").upper()
|
||||
).strip()
|
||||
for s in listwrap(json["orderby"])
|
||||
)
|
||||
return f"ORDER BY {param}"
|
||||
|
||||
def limit(self, json, prec):
|
||||
num = self.dispatch(json["limit"], precedence["order"])
|
||||
return f"LIMIT {num}"
|
||||
|
||||
def offset(self, json, prec):
|
||||
num = self.dispatch(json["offset"], precedence["order"])
|
||||
return f"OFFSET {num}"
|
||||
|
||||
def fetch(self, json, prec):
|
||||
num = self.dispatch(json["offset"], precedence["order"])
|
||||
return f"FETCH {num} ROWS ONLY"
|
||||
|
||||
def insert(self, json, prec=precedence["from"]):
|
||||
acc = ["INSERT"]
|
||||
if "overwrite" in json:
|
||||
acc.append("OVERWRITE")
|
||||
else:
|
||||
acc.append("INTO")
|
||||
acc.append(json["insert"])
|
||||
|
||||
if "columns" in json:
|
||||
acc.append(self.sql_list(json))
|
||||
if "values" in json:
|
||||
values = json["values"]
|
||||
if all(isinstance(row, dict) for row in values):
|
||||
columns = list(sorted(set(k for row in values for k in row.keys())))
|
||||
acc.append(self.sql_list(columns))
|
||||
if "if exists" in json:
|
||||
acc.append("IF EXISTS")
|
||||
acc.append("VALUES")
|
||||
acc.append(",\n".join(
|
||||
"(" + ", ".join(self._literal(row[c]) for c in columns) + ")"
|
||||
for row in values
|
||||
))
|
||||
else:
|
||||
if "if exists" in json:
|
||||
acc.append("IF EXISTS")
|
||||
acc.append("VALUES")
|
||||
for row in values:
|
||||
acc.append("(" + ", ".join(self._literal(row)) + ")")
|
||||
|
||||
else:
|
||||
if json["if exists"]:
|
||||
acc.append("IF EXISTS")
|
||||
acc.append(self.dispatch(json["query"]))
|
||||
return " ".join(acc)
|
||||
|
||||
|
||||
setattr(Formatter, "with", Formatter.with_)
|
||||
setattr(Formatter, "from", Formatter.from_)
|
@ -0,0 +1,11 @@
|
||||
CREATE TABLE sale(Month INT, sales INT)
|
||||
|
||||
LOAD DATA INFILE "moving_avg.csv"
|
||||
INTO TABLE sale
|
||||
FIELDS TERMINATED BY "\t"
|
||||
|
||||
SELECT Month,avgs(3,sales)
|
||||
FROM sale
|
||||
ASSUMING ASC Month
|
||||
INTO OUTFILE "moving_avg_output.csv"
|
||||
FIELDS TERMINATED BY ","
|
|
@ -0,0 +1,22 @@
|
||||
CREATE TABLE stocks(timestamp INT, price INT)
|
||||
|
||||
INSERT INTO stocks VALUES(1,15)
|
||||
INSERT INTO stocks VALUES(2,19)
|
||||
INSERT INTO stocks VALUES(3,16)
|
||||
INSERT INTO stocks VALUES(4,17)
|
||||
INSERT INTO stocks VALUES(5,15)
|
||||
INSERT INTO stocks VALUES(6,13)
|
||||
INSERT INTO stocks VALUES(7,5)
|
||||
INSERT INTO stocks VALUES(8,8)
|
||||
INSERT INTO stocks VALUES(9,7)
|
||||
INSERT INTO stocks VALUES(10,13)
|
||||
INSERT INTO stocks VALUES(11,11)
|
||||
INSERT INTO stocks VALUES(12,14)
|
||||
INSERT INTO stocks VALUES(13,10)
|
||||
INSERT INTO stocks VALUES(14,5)
|
||||
INSERT INTO stocks VALUES(15,2)
|
||||
INSERT INTO stocks VALUES(16,5)
|
||||
|
||||
SELECT max(price-mins(price))
|
||||
FROM stocks
|
||||
ASSUMING ASC timestamp
|
Loading…
Reference in new issue