A work-in-progress SQL parser written in TypeScript
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parser.test.ts 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. /* tslint:disable:no-unused-expression */
  2. import { expect } from "chai";
  3. import * as AST from "../src/ast";
  4. import { isError } from "../src/error";
  5. import Lexer from "../src/lexer";
  6. import Parser from "../src/parser";
  7. const parse = (source: string): AST.Statement[] => {
  8. const tokens = new Lexer(source).scan();
  9. if (isError(tokens)) {
  10. throw tokens.message;
  11. } else {
  12. const tree = new Parser(tokens).parse();
  13. if (isError(tree)) {
  14. throw tree.message;
  15. }
  16. return tree;
  17. }
  18. };
  19. describe("Parser", () => {
  20. it("should parse a number selection", () => {
  21. const tree = parse("select 5");
  22. expect(tree).to.deep.equal([
  23. new AST.SelectStatement({
  24. arguments: [new AST.Number(5)],
  25. }),
  26. ]);
  27. });
  28. it("should parse a multiple number selection", () => {
  29. const tree = parse("select 5, 6");
  30. expect(tree).to.deep.equal([
  31. new AST.SelectStatement({
  32. arguments: [new AST.Number(5), new AST.Number(6)],
  33. }),
  34. ]);
  35. });
  36. it("should parse a selection of identifiers", () => {
  37. const tree = parse("select a, b");
  38. expect(tree).to.deep.equal([
  39. new AST.SelectStatement({
  40. arguments: [new AST.Identifier("a"), new AST.Identifier("b")],
  41. }),
  42. ]);
  43. });
  44. it("should parse a selection of identifiers and numbers", () => {
  45. const tree = parse("select a, 5");
  46. expect(tree).to.deep.equal([
  47. new AST.SelectStatement({
  48. arguments: [new AST.Identifier("a"), new AST.Number(5)],
  49. }),
  50. ]);
  51. });
  52. it("should parse the from of a selection", () => {
  53. const tree = parse("select a from t");
  54. expect(tree).to.deep.equal([
  55. new AST.SelectStatement({
  56. arguments: [new AST.Identifier("a")],
  57. from: new AST.Identifier("t"),
  58. }),
  59. ]);
  60. });
  61. it("should parse aliases", () => {
  62. const tree = parse("select a as b");
  63. expect(tree).to.deep.equal([
  64. new AST.SelectStatement({
  65. arguments: [
  66. new AST.Alias(new AST.Identifier("a"), new AST.Identifier("b")),
  67. ],
  68. }),
  69. ]);
  70. });
  71. it("should parse aliases as from targets", () => {
  72. const tree = parse("select a from t as u");
  73. expect(tree).to.deep.equal([
  74. new AST.SelectStatement({
  75. arguments: [new AST.Identifier("a")],
  76. from: new AST.Alias(new AST.Identifier("t"), new AST.Identifier("u")),
  77. }),
  78. ]);
  79. });
  80. it("should not allow a number as a from target", () => {
  81. const fn = () => parse("select 1 from 2");
  82. expect(fn).to.throw("Unexpected token: { kind: NUMBER, value: 2 }");
  83. });
  84. it("should parse a where with a number", () => {
  85. const tree = parse("select a from b where 1");
  86. expect(tree).to.deep.equal([
  87. new AST.SelectStatement({
  88. arguments: [new AST.Identifier("a")],
  89. from: new AST.Identifier("b"),
  90. where: new AST.Number(1),
  91. }),
  92. ]);
  93. });
  94. it("should parse a where with an identifier", () => {
  95. const tree = parse("select a from b where c");
  96. expect(tree).to.deep.equal([
  97. new AST.SelectStatement({
  98. arguments: [new AST.Identifier("a")],
  99. from: new AST.Identifier("b"),
  100. where: new AST.Identifier("c"),
  101. }),
  102. ]);
  103. });
  104. it("should parse a where with an equality", () => {
  105. const tree = parse("select a from b where c = d");
  106. expect(tree).to.deep.equal([
  107. new AST.SelectStatement({
  108. arguments: [new AST.Identifier("a")],
  109. from: new AST.Identifier("b"),
  110. where: new AST.Binary({
  111. left: new AST.Identifier("c"),
  112. right: new AST.Identifier("d"),
  113. type: AST.BinaryExpressionTypes.EQUALITY,
  114. }),
  115. }),
  116. ]);
  117. });
  118. it("should parse a where with an addition", () => {
  119. const tree = parse("select a from b where c + d");
  120. expect(tree).to.deep.equal([
  121. new AST.SelectStatement({
  122. arguments: [new AST.Identifier("a")],
  123. from: new AST.Identifier("b"),
  124. where: new AST.Binary({
  125. left: new AST.Identifier("c"),
  126. right: new AST.Identifier("d"),
  127. type: AST.BinaryExpressionTypes.ADDITION,
  128. }),
  129. }),
  130. ]);
  131. });
  132. it("should parse a where with a subtraction", () => {
  133. const tree = parse("select a from b where c - d");
  134. expect(tree).to.deep.equal([
  135. new AST.SelectStatement({
  136. arguments: [new AST.Identifier("a")],
  137. from: new AST.Identifier("b"),
  138. where: new AST.Binary({
  139. left: new AST.Identifier("c"),
  140. right: new AST.Identifier("d"),
  141. type: AST.BinaryExpressionTypes.SUBTRACTION,
  142. }),
  143. }),
  144. ]);
  145. });
  146. it("should parse a where with a multiplication", () => {
  147. const tree = parse("select a from b where c * d");
  148. expect(tree).to.deep.equal([
  149. new AST.SelectStatement({
  150. arguments: [new AST.Identifier("a")],
  151. from: new AST.Identifier("b"),
  152. where: new AST.Binary({
  153. left: new AST.Identifier("c"),
  154. right: new AST.Identifier("d"),
  155. type: AST.BinaryExpressionTypes.MULTIPLICATION,
  156. }),
  157. }),
  158. ]);
  159. });
  160. it("should parse a where with a division", () => {
  161. const tree = parse("select a from b where c / d");
  162. expect(tree).to.deep.equal([
  163. new AST.SelectStatement({
  164. arguments: [new AST.Identifier("a")],
  165. from: new AST.Identifier("b"),
  166. where: new AST.Binary({
  167. left: new AST.Identifier("c"),
  168. right: new AST.Identifier("d"),
  169. type: AST.BinaryExpressionTypes.DIVISION,
  170. }),
  171. }),
  172. ]);
  173. });
  174. it("should parse a where with a complex binary expression", () => {
  175. const tree = parse("select a from b where c + d = e");
  176. expect(tree).to.deep.equal([
  177. new AST.SelectStatement({
  178. arguments: [new AST.Identifier("a")],
  179. from: new AST.Identifier("b"),
  180. where: new AST.Binary({
  181. left: new AST.Binary({
  182. left: new AST.Identifier("c"),
  183. right: new AST.Identifier("d"),
  184. type: AST.BinaryExpressionTypes.ADDITION,
  185. }),
  186. right: new AST.Identifier("e"),
  187. type: AST.BinaryExpressionTypes.EQUALITY,
  188. }),
  189. }),
  190. ]);
  191. });
  192. it("should parse a where with a boolean expression", () => {
  193. const tree = parse("select a from b where c and d");
  194. expect(tree).to.deep.equal([
  195. new AST.SelectStatement({
  196. arguments: [new AST.Identifier("a")],
  197. from: new AST.Identifier("b"),
  198. where: new AST.Binary({
  199. left: new AST.Identifier("c"),
  200. right: new AST.Identifier("d"),
  201. type: AST.BinaryExpressionTypes.AND,
  202. }),
  203. }),
  204. ]);
  205. });
  206. it("should parse chained ANDs", () => {
  207. const tree = parse("select a from b where c and d and e");
  208. expect(tree).to.deep.equal([
  209. new AST.SelectStatement({
  210. arguments: [new AST.Identifier("a")],
  211. from: new AST.Identifier("b"),
  212. where: new AST.Binary({
  213. left: new AST.Binary({
  214. left: new AST.Identifier("c"),
  215. right: new AST.Identifier("d"),
  216. type: AST.BinaryExpressionTypes.AND,
  217. }),
  218. right: new AST.Identifier("e"),
  219. type: AST.BinaryExpressionTypes.AND,
  220. }),
  221. }),
  222. ]);
  223. });
  224. it("should parse chained ORs", () => {
  225. const tree = parse("select a from b where c or d or e");
  226. expect(tree).to.deep.equal([
  227. new AST.SelectStatement({
  228. arguments: [new AST.Identifier("a")],
  229. from: new AST.Identifier("b"),
  230. where: new AST.Binary({
  231. left: new AST.Binary({
  232. left: new AST.Identifier("c"),
  233. right: new AST.Identifier("d"),
  234. type: AST.BinaryExpressionTypes.OR,
  235. }),
  236. right: new AST.Identifier("e"),
  237. type: AST.BinaryExpressionTypes.OR,
  238. }),
  239. }),
  240. ]);
  241. });
  242. it("should parse backticked column names", () => {
  243. const tree = parse("select `a` from b");
  244. expect(tree).to.deep.equal([
  245. new AST.SelectStatement({
  246. arguments: [new AST.Backtick(new AST.Identifier("a"))],
  247. from: new AST.Identifier("b"),
  248. }),
  249. ]);
  250. });
  251. it("should not allow unbalanced backticks", () => {
  252. const fn = () => parse("select `a from b");
  253. expect(fn).to.throw("Unexpected token: { kind: FROM }");
  254. });
  255. it("should parse backticked FROM targets", () => {
  256. const tree = parse("select a from `b`");
  257. expect(tree).to.deep.equal([
  258. new AST.SelectStatement({
  259. arguments: [new AST.Identifier("a")],
  260. from: new AST.Backtick(new AST.Identifier("b")),
  261. }),
  262. ]);
  263. });
  264. it("should parse backticked aliases", () => {
  265. const tree = parse("select a as `b`");
  266. expect(tree).to.deep.equal([
  267. new AST.SelectStatement({
  268. arguments: [new AST.Alias(new AST.Identifier("a"), new AST.Backtick(new AST.Identifier("b")))],
  269. }),
  270. ]);
  271. });
  272. it("should parse backticked aliases", () => {
  273. const tree = parse("select a as `b`");
  274. expect(tree).to.deep.equal([
  275. new AST.SelectStatement({
  276. arguments: [new AST.Alias(new AST.Identifier("a"), new AST.Backtick(new AST.Identifier("b")))],
  277. }),
  278. ]);
  279. });
  280. it("should parse backticked where operands", () => {
  281. const tree = parse("select a where `b` = `c`");
  282. expect(tree).to.deep.equal([
  283. new AST.SelectStatement({
  284. arguments: [new AST.Identifier("a")],
  285. where: new AST.Binary({
  286. left: new AST.Backtick(new AST.Identifier("b")),
  287. right: new AST.Backtick(new AST.Identifier("c")),
  288. type: AST.BinaryExpressionTypes.EQUALITY,
  289. }),
  290. }),
  291. ]);
  292. });
  293. it("should parse expressions as select arguments", () => {
  294. const tree = parse("select a = b");
  295. expect(tree).to.deep.equal([
  296. new AST.SelectStatement({
  297. arguments: [
  298. new AST.Binary({
  299. left: new AST.Identifier("a"),
  300. right: new AST.Identifier("b"),
  301. type: AST.BinaryExpressionTypes.EQUALITY,
  302. }),
  303. ],
  304. }),
  305. ]);
  306. });
  307. it("should parse dot operators", () => {
  308. const tree = parse("select a.b");
  309. expect(tree).to.deep.equal([
  310. new AST.SelectStatement({
  311. arguments: [
  312. new AST.Binary({
  313. left: new AST.Identifier("a"),
  314. right: new AST.Identifier("b"),
  315. type: AST.BinaryExpressionTypes.DOT,
  316. }),
  317. ],
  318. }),
  319. ]);
  320. });
  321. it("should parse parens", () => {
  322. const tree = parse("select a * (b + c)");
  323. expect(tree).to.deep.equal([
  324. new AST.SelectStatement({
  325. arguments: [
  326. new AST.Binary({
  327. left: new AST.Identifier("a"),
  328. right: new AST.Binary({
  329. left: new AST.Identifier("b"),
  330. right: new AST.Identifier("c"),
  331. type: AST.BinaryExpressionTypes.ADDITION,
  332. }),
  333. type: AST.BinaryExpressionTypes.MULTIPLICATION,
  334. }),
  335. ],
  336. }),
  337. ]);
  338. });
  339. it("should parse a parenthesized SELECT as a FROM target", () => {
  340. const tree = parse("select a from (select b)");
  341. expect(tree).to.deep.equal([
  342. new AST.SelectStatement({
  343. arguments: [
  344. new AST.Identifier("a"),
  345. ],
  346. from: new AST.SelectStatement({
  347. arguments: [new AST.Identifier("b")],
  348. }),
  349. }),
  350. ]);
  351. });
  352. it("should allow aliases on subselects", () => {
  353. const tree = parse("select a from (select b) as c");
  354. expect(tree).to.deep.equal([
  355. new AST.SelectStatement({
  356. arguments: [
  357. new AST.Identifier("a"),
  358. ],
  359. from: new AST.Alias(
  360. new AST.SelectStatement({
  361. arguments: [new AST.Identifier("b")],
  362. }),
  363. new AST.Identifier("c"),
  364. ),
  365. }),
  366. ]);
  367. });
  368. it("should parse SELECT *", () => {
  369. const tree = parse("select *");
  370. expect(tree).to.deep.equal([
  371. new AST.SelectStatement({
  372. arguments: [
  373. new AST.Star(),
  374. ],
  375. }),
  376. ]);
  377. });
  378. it("should parse * on the right hand side of a dot in a SELECT", () => {
  379. const tree = parse("select a.*");
  380. expect(tree).to.deep.equal([
  381. new AST.SelectStatement({
  382. arguments: [
  383. new AST.Binary({
  384. left: new AST.Identifier("a"),
  385. right: new AST.Star(),
  386. type: AST.BinaryExpressionTypes.DOT,
  387. }),
  388. ],
  389. }),
  390. ]);
  391. });
  392. it("should parse * on the right of a dot where the left is a backtick", () => {
  393. const tree = parse("select `a`.*");
  394. expect(tree).to.deep.equal([
  395. new AST.SelectStatement({
  396. arguments: [
  397. new AST.Binary({
  398. left: new AST.Backtick(new AST.Identifier("a")),
  399. right: new AST.Star(),
  400. type: AST.BinaryExpressionTypes.DOT,
  401. }),
  402. ],
  403. }),
  404. ]);
  405. });
  406. it("should not allow stars in arithmetic operations", () => {
  407. expect(() => parse("select 1 + *")).to.throw("Unexpected token: { kind: STAR }");
  408. expect(() => parse("select 1 + a.*")).to.throw("Unexpected token: { kind: STAR }");
  409. expect(() => parse("select * + 1")).to.throw("Unexpected token: { kind: PLUS }");
  410. expect(() => parse("select a.* + b.*")).to.throw("Unexpected token: { kind: PLUS }");
  411. expect(() => parse("select a from b where c.*")).to.throw("Unexpected token: { kind: STAR }");
  412. });
  413. });