123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- /* tslint:disable:no-unused-expression */
- import { expect } from "chai";
-
- import * as AST from "../src/ast";
- import { isError } from "../src/error";
- import Lexer from "../src/lexer";
- import Parser from "../src/parser";
-
- const parse = (source: string): AST.Statement[] => {
- const tokens = new Lexer(source).scan();
- if (isError(tokens)) {
- throw tokens.message;
- } else {
- const tree = new Parser(tokens).parse();
- if (isError(tree)) {
- throw tree.message;
- }
- return tree;
- }
- };
-
- describe("Parser", () => {
- it("should parse a number selection", () => {
- const tree = parse("select 5");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Number(5)],
- }),
- ]);
- });
-
- it("should parse a multiple number selection", () => {
- const tree = parse("select 5, 6");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Number(5), new AST.Number(6)],
- }),
- ]);
- });
-
- it("should parse a selection of identifiers", () => {
- const tree = parse("select a, b");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a"), new AST.Identifier("b")],
- }),
- ]);
- });
-
- it("should parse a selection of identifiers and numbers", () => {
- const tree = parse("select a, 5");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a"), new AST.Number(5)],
- }),
- ]);
- });
-
- it("should parse the from of a selection", () => {
- const tree = parse("select a from t");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("t"),
- }),
- ]);
- });
-
- it("should parse aliases", () => {
- const tree = parse("select a as b");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Alias(new AST.Identifier("a"), new AST.Identifier("b")),
- ],
- }),
- ]);
- });
-
- it("should parse aliases as from targets", () => {
- const tree = parse("select a from t as u");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Alias(new AST.Identifier("t"), new AST.Identifier("u")),
- }),
- ]);
- });
-
- it("should not allow a number as a from target", () => {
- const fn = () => parse("select 1 from 2");
- expect(fn).to.throw("Unexpected token: { kind: NUMBER, value: 2 }");
- });
-
- it("should parse a where with a number", () => {
- const tree = parse("select a from b where 1");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Number(1),
- }),
- ]);
- });
-
- it("should parse a where with an identifier", () => {
- const tree = parse("select a from b where c");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Identifier("c"),
- }),
- ]);
- });
-
- it("should parse a where with an equality", () => {
- const tree = parse("select a from b where c = d");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.EQUALITY,
- }),
- }),
- ]);
- });
-
- it("should parse a where with an addition", () => {
- const tree = parse("select a from b where c + d");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.ADDITION,
- }),
- }),
- ]);
- });
-
- it("should parse a where with a subtraction", () => {
- const tree = parse("select a from b where c - d");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.SUBTRACTION,
- }),
- }),
- ]);
- });
-
- it("should parse a where with a multiplication", () => {
- const tree = parse("select a from b where c * d");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.MULTIPLICATION,
- }),
- }),
- ]);
- });
-
- it("should parse a where with a division", () => {
- const tree = parse("select a from b where c / d");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.DIVISION,
- }),
- }),
- ]);
- });
-
- it("should parse a where with a complex binary expression", () => {
- const tree = parse("select a from b where c + d = e");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.ADDITION,
- }),
- right: new AST.Identifier("e"),
- type: AST.BinaryExpressionTypes.EQUALITY,
- }),
- }),
- ]);
- });
-
- it("should parse a where with a boolean expression", () => {
- const tree = parse("select a from b where c and d");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.AND,
- }),
- }),
- ]);
- });
-
- it("should parse chained ANDs", () => {
- const tree = parse("select a from b where c and d and e");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.AND,
- }),
- right: new AST.Identifier("e"),
- type: AST.BinaryExpressionTypes.AND,
- }),
- }),
- ]);
- });
-
- it("should parse chained ORs", () => {
- const tree = parse("select a from b where c or d or e");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Identifier("b"),
- where: new AST.Binary({
- left: new AST.Binary({
- left: new AST.Identifier("c"),
- right: new AST.Identifier("d"),
- type: AST.BinaryExpressionTypes.OR,
- }),
- right: new AST.Identifier("e"),
- type: AST.BinaryExpressionTypes.OR,
- }),
- }),
- ]);
- });
-
- it("should parse backticked column names", () => {
- const tree = parse("select `a` from b");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Backtick(new AST.Identifier("a"))],
- from: new AST.Identifier("b"),
- }),
- ]);
- });
-
- it("should not allow unbalanced backticks", () => {
- const fn = () => parse("select `a from b");
- expect(fn).to.throw("Unexpected token: { kind: FROM }");
- });
-
- it("should parse backticked FROM targets", () => {
- const tree = parse("select a from `b`");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- from: new AST.Backtick(new AST.Identifier("b")),
- }),
- ]);
- });
-
- it("should parse backticked aliases", () => {
- const tree = parse("select a as `b`");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Alias(new AST.Identifier("a"), new AST.Backtick(new AST.Identifier("b")))],
- }),
- ]);
- });
-
- it("should parse backticked aliases", () => {
- const tree = parse("select a as `b`");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Alias(new AST.Identifier("a"), new AST.Backtick(new AST.Identifier("b")))],
- }),
- ]);
- });
-
- it("should parse backticked where operands", () => {
- const tree = parse("select a where `b` = `c`");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [new AST.Identifier("a")],
- where: new AST.Binary({
- left: new AST.Backtick(new AST.Identifier("b")),
- right: new AST.Backtick(new AST.Identifier("c")),
- type: AST.BinaryExpressionTypes.EQUALITY,
- }),
- }),
- ]);
- });
-
- it("should parse expressions as select arguments", () => {
- const tree = parse("select a = b");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Binary({
- left: new AST.Identifier("a"),
- right: new AST.Identifier("b"),
- type: AST.BinaryExpressionTypes.EQUALITY,
- }),
- ],
- }),
- ]);
- });
-
- it("should parse dot operators", () => {
- const tree = parse("select a.b");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Binary({
- left: new AST.Identifier("a"),
- right: new AST.Identifier("b"),
- type: AST.BinaryExpressionTypes.DOT,
- }),
- ],
- }),
- ]);
- });
-
- it("should parse parens", () => {
- const tree = parse("select a * (b + c)");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Binary({
- left: new AST.Identifier("a"),
- right: new AST.Binary({
- left: new AST.Identifier("b"),
- right: new AST.Identifier("c"),
- type: AST.BinaryExpressionTypes.ADDITION,
- }),
- type: AST.BinaryExpressionTypes.MULTIPLICATION,
- }),
- ],
- }),
- ]);
- });
-
- it("should parse a parenthesized SELECT as a FROM target", () => {
- const tree = parse("select a from (select b)");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Identifier("a"),
- ],
- from: new AST.SelectStatement({
- arguments: [new AST.Identifier("b")],
- }),
- }),
- ]);
- });
-
- it("should allow aliases on subselects", () => {
- const tree = parse("select a from (select b) as c");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Identifier("a"),
- ],
- from: new AST.Alias(
- new AST.SelectStatement({
- arguments: [new AST.Identifier("b")],
- }),
- new AST.Identifier("c"),
- ),
- }),
- ]);
- });
-
- it("should parse SELECT *", () => {
- const tree = parse("select *");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Star(),
- ],
- }),
- ]);
- });
-
- it("should parse * on the right hand side of a dot in a SELECT", () => {
- const tree = parse("select a.*");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Binary({
- left: new AST.Identifier("a"),
- right: new AST.Star(),
- type: AST.BinaryExpressionTypes.DOT,
- }),
- ],
- }),
- ]);
- });
-
- it("should parse * on the right of a dot where the left is a backtick", () => {
- const tree = parse("select `a`.*");
- expect(tree).to.deep.equal([
- new AST.SelectStatement({
- arguments: [
- new AST.Binary({
- left: new AST.Backtick(new AST.Identifier("a")),
- right: new AST.Star(),
- type: AST.BinaryExpressionTypes.DOT,
- }),
- ],
- }),
- ]);
- });
-
- it("should not allow stars in arithmetic operations", () => {
- expect(() => parse("select 1 + *")).to.throw("Unexpected token: { kind: STAR }");
- expect(() => parse("select 1 + a.*")).to.throw("Unexpected token: { kind: STAR }");
- expect(() => parse("select * + 1")).to.throw("Unexpected token: { kind: PLUS }");
- expect(() => parse("select a.* + b.*")).to.throw("Unexpected token: { kind: PLUS }");
- expect(() => parse("select a from b where c.*")).to.throw("Unexpected token: { kind: STAR }");
- });
- });
|