1 Star 0 Fork 1

RockieYang/snowflakeArcSecond

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
snowflakequery.js 9.04 KB
一键复制 编辑 原始数据 按行查看 历史
RockieYang 提交于 2021-11-21 21:49 . add some test and package.json
import {
parse,
char,
str,
sequenceOf,
choice,
between,
letter,
digit,
digits,
letters,
many,
many1,
regex,
sepBy,
possibly,
whitespace,
optionalWhitespace,
anyOfString,
anythingExcept,
recursiveParser
} from 'arcsecond';
const makeBasicType = typeName => value => ({
type: typeName,
value,
toString: () => `${typeName}(${value})`
});
// Function to create a multi-valued "type constructor"
const makeMultiType = typeName => values => ({
type: typeName,
value: values,
toString: () => `${typeName}(${values.map(v => v.toString()).join(', ')})`
});
const arrayType = makeMultiType('Array');
const stringType = makeBasicType('String');
const numberType = makeBasicType('Number');
const booleanType = makeBasicType('Boolean');
const timeTime = makeBasicType('Time');
const variableType = makeBasicType('Variable');
const functionCall = makeBasicType('FunctionCall');
const selectClauseType = makeMultiType("Select");
const makeSelectObject = values => ({
columns: arrayType(values[3])
})
export const whitespaceSurrounded = between(whitespace)(whitespace);
export const optionalWhitespaceSurrounded = between(optionalWhitespace)(optionalWhitespace);
const inParanthesis = between(char('('))(char(')'));
const betweenSingleQuotes = between(char('\''))(char('\''));
const commaSeparated = sepBy(optionalWhitespaceSurrounded(char(',')));
const selectTree = values => ({
with: values[0],
select: makeSelectObject(values[1]),
from: values[3],
where: values[4],
groupBy: values[5],
qualify: values[6],
orderBy: values[7],
limit: values[8]
})
const nullType = () => ({
type: 'Null',
value: null,
toString: () => 'Null'
});
const WITH = str('WITH');
const AS = str('AS');
const UNION = str('UNION');
const ALL = str('ALL');
const SELECT = str('SELECT');
const FROM = str('FROM');
const JOIN = str('JOIN');
const TOP = str('TOP');
const DOT = char('.');
const WHERE = whitespaceSurrounded(str('WHERE')).map(x => x.join('').trim());
const GROUPBY = sequenceOf([str('GROUP'), str('BY')]);
const ORDERBY = sequenceOf([str('ORDER'), str('BY')]);
const LIMIT = str('LIMIT');
const QUALIFY = str('QUALIFY');
const HAVING = str('HAVING');
const AT = str('AT');
const BEFORE = str('BEFORE');
const CHANGES = str('CHANGES');
const CONNECT_BY = sequenceOf([str('CONNECT'), str('BY')]);
const STARTWITH = sequenceOf([str('START'), str('WITH')]);
const VALUES = str('VALUES');
const SAMPLE = str('SAMPLE');
const PIVOT = str('PIVOT');
const UNPIVOT = str('UNPIVOT');
const INFORMATION = str('INFORMATION');
const STATEMENT = str('STATEMENT');
const TIMESTAMP = str('TIMESTAMP');
const OFFSET = str('OFFSET');
const LEAD_TO = optionalWhitespaceSurrounded(str('=>'));
const DEFAULT = str('DEFAULT');
const APPEND_ONLY = str('APPEND_ONLY');
const END = str('END');
const PRIOR = str('PRIOR');
const AND = str('AND');
const OR = str('OR');
const NOT = str('NOT');
const EQ = str('=');
const NE = str('!=');
const NE1 = str('<>');
const GT = str('>=');
const LT = str('<');
const LE = str('<=');
const join = seperator => array => array.join(seperator);
const joinedMany = parser => many(parser).map(x => x.join(''));
const joinedSequence = parsers => sequenceOf(parsers).map(x => x.join(''));
export const escapedQuote = joinedSequence([str('\\'), anyOfString(`"'`)]);
export const literalString = betweenSingleQuotes(
joinedMany(choice([escapedQuote, anythingExcept(char('"'))]))
).map(stringType);
export const identifier = joinedMany(choice([
letter, digit, char('_')
]));
const variable = sequenceOf([char('$'), identifier]);
const cteName = joinedSequence([
identifier,
joinedMany(joinedSequence([
char('.'),
identifier
]))
]);
const cteNameList = commaSeparated(cteName);
export const columnList = cteNameList;
const objectReference = cteName;
const anchorClause = sequenceOf([SELECT,
columnList,
FROM,
whitespace,
objectReference
])
const recursiveClause = sequenceOf([anchorClause,
JOIN,
])
const withClause = sequenceOf([WITH,
cteName,
inParanthesis(cteNameList),
AS,
inParanthesis(sequenceOf[anchorClause, UNION, ALL, recursiveClause])
]);
const top = sequenceOf([TOP, whitespace, digits]);
const selectCluse = sequenceOf([
SELECT,
whitespace,
possibly(top),
columnList
]);
const date = regex(/^([0-9]{2}|[0-9]{4})-[0-9]{1,2}-[0-9]{1,2}/);
const time = regex(/^[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}/);
const timestampPostfix = sequenceOf([str('::'), TIMESTAMP]);
const timeString = betweenSingleQuotes(sequenceOf([date, whitespace, time])).map(
values => ({
date: values[0],
time: values[2]
})
);
const timeLiteral = sequenceOf([
timeString,
possibly(timestampPostfix)
]).map(values => values[0]);
const compareTimestamp = sequenceOf([
TIMESTAMP,
LEAD_TO,
choice([timeLiteral, variable])
]).map(
values => ({
timestamp: values[2]
})
);
const compareOffset = sequenceOf([OFFSET, LEAD_TO, choice(digits, variable)]);
const compareStatement = sequenceOf([STATEMENT, LEAD_TO, identifier]);
const timeAnchorExpr = choice([compareTimestamp, compareOffset, compareStatement]);
const atClause = sequenceOf([
AT,
inParanthesis(timeAnchorExpr)
]).map(
values => ({
type: "AT",
value: values[1]
})
);
const endClause = sequenceOf([END, timeAnchorExpr]).map(values => ({
type: "END",
value: values[1]
}));
const beforeClause = sequenceOf([BEFORE, compareStatement]).map(values => ({
type: "BEFORE",
value: values[1]
}));
const atOrBeforeClause = choice([atClause, beforeClause]);
const fromObjectReference = sequenceOf([
objectReference,
possibly(sequenceOf([
whitespace,
atOrBeforeClause
]).map(
values => values[1]
))
]).map(values => ({
source: values[0],
timeLimit: values[1]
}))
const changesClause = sequenceOf([CHANGES,
whitespace,
inParanthesis(sequenceOf([INFORMATION, LEAD_TO, choice(DEFAULT, APPEND_ONLY)])),
whitespace,
atOrBeforeClause,
possibly(endClause)
]);
const value = choice([digits, literalString]);
const comparable = choice([cteName, value]);
const operator = sequenceOf([char('+'), char('-'), char('*'), char('/'), char('%')]);
const numbericExpression = sequenceOf([comparable, operator, comparable]);
const logicOperator = choice([EQ, NE, NE1, GT, GT, LT, LE]);
const compareExpression = sequenceOf([comparable, logicOperator, comparable]);
const notExpression = sequenceOf([NOT, choice([compareExpression,
numbericExpression,
comparable
])]);
const andExpression = sepBy(AND)(notExpression);
const orExpression = sepBy(OR)(andExpression);
const predicate = orExpression;
const connenctByCondition = sequenceOf(possibly(PRIOR), identifier, EQ, possibly(PRIOR), identifier);
const connenctByConditions = many1(connenctByCondition);
const connectByClause = sequenceOf([STARTWITH, predicate, CONNECT_BY, connenctByConditions]);
const functionCallExpression = sequenceOf([identifier, optionalWhitespace, inParanthesis(
commaSeparated(predicate)
)])
const fromClause = sequenceOf([FROM,
whitespace,
choice([
fromObjectReference,
changesClause,
connectByClause
])
]).map(values => ({
typeName: 'from',
from: values[2],
toString: () => `${typeName}(${from.each(v => v.toString()).join(', ')})`
}));
const whereClause = sequenceOf([WHERE, predicate]);
const groupByClause = sequenceOf([GROUPBY]);
const qualifyClause = sequenceOf([QUALIFY]);
const orderByClause = sequenceOf([ORDERBY]);
const limitCaluse = sequenceOf([LIMIT]);
export const snowflake = sequenceOf([
possibly(withClause),
selectCluse,
whitespace,
fromClause,
possibly(whereClause),
possibly(groupByClause),
possibly(qualifyClause),
possibly(orderByClause),
possibly(limitCaluse)
]).map(selectTree);
export const snowflakeParser = parse(snowflake);
export function deepLog(objName, obj, padding = '', currentLevel = 1) {
if (Array.isArray(obj)) {
// console.log("is array : " + typeof(obj) + " "+ obj);
if (objName.length) {
console.log(padding + objName + ": [")
} else {
console.log(padding + " [");
}
obj.forEach((e, i) => deepLog("[" + i + "]", e, padding + ' ', currentLevel + 1));
console.log(padding + "]");
} else if (typeof obj == "object") {
if (objName.length) {
console.log(padding + objName + ": {")
} else {
console.log(padding + " {")
}
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
deepLog(property, obj[property], padding + ' ', currentLevel + 1);
}
}
console.log(padding + "}")
} else {
console.log(padding + objName + " : " + obj);
}
}
export const columnListParser = parse(columnList);
// console.log(columnListParser("a,b.d,c"));
// deepLog('test', ["a","b"]);
// deepLog('tree', snowflakeParser('SELECT a.b, c , d FROM Table'));
// console.log(snowflakeParser('SELECT a.b FROM Table WHERE a.b=\'xx\''));
// deepLog('tree', timeAnchorExpr.run("TIMESTAMP => '2018-07-27 12:00:00'"));
// deepLog('tree', fromClause.run("FROM Table AT(TIMESTAMP => '2018-07-27 12:00:00')"));
// deepLog('tree', snowflakeParser("SELECT a.b FROM Table AT(TIMESTAMP => '2018-07-27 12:00:00')"));
// console.log(snowflakeParser('SELECT a.b FROM Table AT(TIMESTAMP => \'2018-07-27 12:00:00\'::TIMESTAMP'));
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
JavaScript
1
https://gitee.com/RockieYang/snowflake-arc-second.git
git@gitee.com:RockieYang/snowflake-arc-second.git
RockieYang
snowflake-arc-second
snowflakeArcSecond
master

搜索帮助