diff --git a/CHANGELOG.md b/CHANGELOG.md index 986dcbf..0a7953c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,46 @@ All notable changes to this project will be documented in this file. ## [Unreleased][unreleased] +## [v0.8.0] - 2015-07-04 +### Added +- added several array methods (e.g.: `findLast()`, `takeRight()`, `pluck()`) so that I could remove `lodash` as a dependency of the "smart error" `Tracer` class + +### Changed +- removed `lodash` dependency in core `Tracer`. `lodash` is now only a `devDependency` again! + +### Notes +- considering removing the `promise` dependency from the core `sqlite-parser` library before `v1.0.0`, as well, so that the parser can be dependency free as a standalone library. people could choose to "promisify" the parser or just use it synchronously instead of being forced to bundle the `promise` dependency when bundling this package for use in the browser. It actually looks like all the evergreen browsers except IE currently support a native `Promise` implementation, so having a non-native `Promise` implementation as a dependency will probably be obsolete pretty soon. + +## [v0.7.0] - 2015-07-02 +### Added +- additional rule descriptions in `grammar.pegjs` + +### Changed +- cleaned up css in the interactive demo + +### Fixed +- fixed error reporting when there is more than one statement in the input SQL. + - still need to make sure previous tree is not used if a subsequent statement has an error at the highest level + + ``` sql + SELECT * + FROM cats; + SELECT * d + ``` + +### Notes +- to support the "smart errors" changes were made to the `pegjs` library code in `lib/compiler/passes/generate-javascript.js`. this was done to allow `Tracer` to get the `description` names for the rules that are referenced in the error messages. will need to fork `pegjs` to get the changes to `pegjs` core into version control so they are not accidentally overwritten. + +do not show parenthesis in error message for syntax error when thereis nothing to put inside them. fixes for css in demo + +demo layout off by 1px when at smallest resolution + +did a lot of cleanup on demo styles, responsive layout, error notification. changed error message format for smart errors + +fixed rules for double-quoted, backticked, and bracketed identifiers to allow for escapes, leading or trailing spaces, and the full character set that is legal for quoted identifiers, where allowed. fixed datatype names that did not display correctly in generated AST. fixed string literal definition to allow all possible input + +fixed value format for direction key in PRIMARY KEY table contrainsts cleaned up CSS for demo. + ## [v0.6.0] - 2015-07-01 ### Changed - updated grammar to remove all dependence on `modifier` clause as it was being used as a catch-all clause for stray parts of statements @@ -211,20 +251,8 @@ All notable changes to this project will be documented in this file. ### Added - First working version of sqlite-parser -[unreleased]: https://github.com/codeschool/sqlite-parser/compare/v0.6.0...HEAD -[v0.6.0]: https://github.com/codeschool/sqlite-parser/compare/v0.5.1...v0.6.0 -[v0.5.1]: https://github.com/codeschool/sqlite-parser/compare/v0.5.0...v0.5.1 -[v0.5.0]: https://github.com/codeschool/sqlite-parser/compare/v0.4.1...v0.5.0 -[v0.4.1]: https://github.com/codeschool/sqlite-parser/compare/v0.4.0...v0.4.1 -[v0.4.0]: https://github.com/codeschool/sqlite-parser/compare/v0.3.1...v0.4.0 -[v0.3.1]: https://github.com/codeschool/sqlite-parser/compare/v0.3.0...v0.3.1 -[v0.3.0]: https://github.com/codeschool/sqlite-parser/compare/v0.2.3...v0.3.0 -[v0.2.3]: https://github.com/codeschool/sqlite-parser/compare/v0.2.2...v0.2.3 -[v0.2.2]: https://github.com/codeschool/sqlite-parser/compare/v0.2.1...v0.2.2 -[v0.2.1]: https://github.com/codeschool/sqlite-parser/compare/v0.2.0...v0.2.1 -[v0.2.0]: https://github.com/codeschool/sqlite-parser/compare/v0.1.1...v0.2.0 -[v0.1.1]: https://github.com/codeschool/sqlite-parser/compare/v0.1.0...v0.1.1 -[v0.1.0]: https://github.com/codeschool/sqlite-parser/compare/v0.0.9...v0.1.0 -[v0.0.9]: https://github.com/codeschool/sqlite-parser/compare/v0.0.8...v0.0.9 -[v0.0.8]: https://github.com/codeschool/sqlite-parser/compare/v0.0.7...v0.0.8 -[v0.0.7]: https://github.com/codeschool/sqlite-parser/commit/ba1f7af0af1c7c4c4462e8bd80835eaf62f2a9f6 +[unreleased]: https://github.com/codeschool/sqlite-parser/compare/v0.8.0...HEAD +[v0.8.0]: https://github.com/codeschool/sqlite-parser/compare/v0.6.0...v0.8.0 +[v0.6.0]: https://github.com/codeschool/sqlite-parser/compare/v0.3.1...v0.6.0 +[v0.3.1]: https://github.com/codeschool/sqlite-parser/compare/6388118d601a89d011ecd6f5c215bbc9763444db...v0.3.1 +[v0.1.1]: https://github.com/codeschool/sqlite-parser/commit/6388118d601a89d011ecd6f5c215bbc9763444db diff --git a/Gruntfile.js b/Gruntfile.js index 2fe13c9..2ae6a21 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,7 +9,8 @@ module.exports = function(grunt) { demo: { options: { alias: { - 'sqlite-parser': './index.js', + 'sqlite-parser': './index', + 'sqlite-parser-util': './lib/parser-util', 'codemirror': './node_modules/codemirror/lib/codemirror', 'foldcode': './node_modules/codemirror/addon/fold/foldcode', 'foldgutter': './node_modules/codemirror/addon/fold/foldgutter', diff --git a/README.md b/README.md index 6a7e4b4..a240b96 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,14 @@ The parser implements the basic components of the SQLite 3 spec, such as: - `JOIN` types `INNER`, `OUTER`, `LEFT` - Query modifiers `WHERE`, `GROUP BY`, `HAVING` +## Demo + +There interactive demo of the parser hosted +[at this location](http://codeschool.github.io/sqlite-parser/demo/). The source +for the interactive demo exists in the `demo/` folder of this repository. You +can serve a LiveReload version of the demo on your local machine by running +`grunt interactive`. + ## Install ``` @@ -24,7 +32,7 @@ npm install sqlite-parser ## Usage The library exposes a function that accepts a single argument: a string -containing SQL to parse. The method returns a promise that resolves to the +containing SQL to parse. The method returns a `Promise` that resolves to the AST object generated from the source string. ``` javascript @@ -41,6 +49,13 @@ sqliteParser(sampleSQL) }); ``` +## Syntax Errors + +This parser uses the `--trace` flag exposed in `pegjs` to create "smart" error +messages. The parser includes a `Trace` class that keeps track of which grammar +rules were being traversed just prior to the error and uses that information +to improve the error message and location information. + ## AST **NOTE: The SQLite AST is a work-in-progress and subject to change.** @@ -55,53 +70,67 @@ the parsed statements. ``` sql SELECT - MIN(salary) AS "MinSalary", - MAX(salary) AS "MaxSalary" + MIN(honey) AS "Min Honey", + MAX(honey) AS "Max Honey" FROM - Actors + BeeHive ``` #### Result AST -``` -statement: - - - type: statement - variant: select - from: - - - type: identifier - variant: table - name: Actors - alias: null - index: null - where: null - group: null - result: - - - type: function - name: MIN - distinct: false - args: - - - type: identifier - variant: column - name: salary - alias: MinSalary - - - type: function - name: MAX - distinct: false - args: - - - type: identifier - variant: column - name: salary - alias: MaxSalary - distinct: false - all: false - order: null - limit: null +``` json +{ + "statement": [ + { + "explain": false, + "type": "statement", + "variant": "select", + "from": [ + { + "type": "identifier", + "variant": "table", + "name": "BeeHive", + "alias": null, + "index": null + } + ], + "where": null, + "group": null, + "result": [ + { + "type": "function", + "name": "MIN", + "distinct": false, + "args": [ + { + "type": "identifier", + "variant": "column", + "name": "honey" + } + ], + "alias": "Min Honey" + }, + { + "type": "function", + "name": "MAX", + "distinct": false, + "args": [ + { + "type": "identifier", + "variant": "column", + "name": "honey" + } + ], + "alias": "Max Honey" + } + ], + "distinct": false, + "all": false, + "order": null, + "limit": null + } + ] +} ``` ## Contributing @@ -114,6 +143,9 @@ which will automatically compile the parser and run the tests in `test/index-spe Optionally, run `grunt debug` to get extended output and start a file watcher. +Finally, you should run `grunt release`, before creating any PR, to run all tests +and rebuild the `dist/` and `demo/` folders. + ### Writing tests Tests refer to a SQL test file in `test/sql/` and the test name is a @@ -133,15 +165,14 @@ var tree = require('./helpers'); describe('sqlite-parser', function() { // uses: test/sql/basicSelect.sql it('basic select', function(done) { - var resultTree = '{"statement":[{"type":"statement","variant":"select","from":[{"type":"identifier","variant":"table","name":"bananas","alias":null,"index":null}],"where":[{"type":"expression","format":"binary","variant":"operation","operation":"=","left":{"type":"identifier","variant":"column","name":"color"},"right":{"type":"literal","variant":"string","value":"red"},"modifier":null}],"group":null,"result":[{"type":"identifier","variant":"star","value":"*"}],"distinct":false,"all":false,"order":null,"limit":null}]}'; + var resultTree = '{"statement":[{"explain":false,"type":"statement","variant":"select","from":[{"type":"identifier","variant":"table","name":"bananas","alias":null,"index":null}],"where":[{"type":"expression","format":"binary","variant":"operation","operation":"=","left":{"type":"identifier","variant":"column","name":"color"},"right":{"type":"literal","variant":"string","value":"red"}}],"group":null,"result":[{"type":"identifier","variant":"star","name":"*"}],"distinct":false,"all":false,"order":null,"limit":null}]}'; tree.equals(resultTree, this, done); }); - // uses: test/sql/invalidUpdate2.sql - it('invalid update 2', function(done) { + // uses: test/sql/parseError1.sql + it('parse error 1', function(done) { tree.error({ - 'message': 'Unexpected FROM keyword found', - 'line': 5 + 'message': 'There is a syntax error near FROM Clause [Table Identifier]' }, this, done); }); }); diff --git a/demo/css/demo.css b/demo/css/demo.css index 755f5a4..9eb181a 100644 --- a/demo/css/demo.css +++ b/demo/css/demo.css @@ -8,127 +8,141 @@ @import url("monokai.css"); @import url("foldgutter.css"); -/* CodeMirror Panel */ - -.border { - border: 1px solid #E03210; -} -.remove-panel { - float: right; - cursor: pointer; -} -.panel { - background: #7A2716; - padding: 3px 7px; - font-size: 12px; -} -.panel.top, .panel.after-top { - border-bottom: 1px solid #585858; -} -.panel.bottom, .panel.before-bottom { - border-top: 1px solid #585858; -} - /* Demo Layout */ *, *:before, *:after { - -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; } body { margin: 0; font-family: 'Inconsolata', monospace; - font-size: 12px; + font-size: 10px; color: #efefef; background-color: #272822; } .container, footer, header { - position: relative; margin: .5em; + position: relative; + margin: 8px; font-size: 14px; + line-height: 16px; } -.container h3 { - border-bottom: 1px solid #585858; - padding-bottom: 8px; +.left { + margin-bottom: 8px; + border: 1px solid #d7782c; } -footer h3, header h2 { - margin: 0 auto; - width: 500px; - text-align: center; +.right { + border: 1px solid #77A025; + transition: all 0.75s; } -footer a, footer a:visited { - color: #2f6a9e; - text-decoration: none; +.right.alert { + border: 1px solid #E42267; } -footer a:hover, footer a:active { - color: #329e2f; - text-decoration: underline; - +.container h3 { + padding: 8px; + margin: 0; + height: 32px; } -.left, .right { - font-size: 16px; - min-height: 400px; - height: 400px; +.container .left h3 { + background-color: #ec9652; + color: #aa5714; + border-bottom: 1px solid #d7782c; } -footer, header { padding: 0.5em; color: #dbdbdb; } +.container .right h3 { + background-color: #daf6a1; + color: #557B0A; + border-bottom: 1px solid #77A025; + transition: all 0.75s; +} -.left, .right { - border: 1px solid #585858; - padding: 0.25em 0.75em 1.75em; +.container .right.alert h3 { + background-color: #fca4c3; + color: #CB0048; + border-bottom: 1px solid #E42267; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.left { margin-bottom: .5em; } +.CodeMirror { + height: 361px !important; +} -.nav { - list-style: none; - margin-left: 0; - margin-bottom: 0; - padding-left: 0; +footer, header { + color: #dbdbdb; + padding: 0; } -.nav > li, -.nav > li > a { - display: inline-block; - *display: inline; - zoom: 1; +footer h3, header h2 { + width: 350px; } -.inline-items { - margin-top: 0; +footer h3 { + margin: 0 auto; + text-align: center; + line-height: 24px; + font-size: 16px; } -.inline-items li { - margin-left: 0; - border-left: 1px solid black; - padding-left: 10px; - padding-right: 10px; +a, a:visited { + color: #e6db74; + text-decoration: none; } -.inline-items li:first-child { - margin-left: 0; - border: none; - padding-left: 0; - padding-right: 10px; +a:hover, a:active { + color: #FFF7A8; + text-decoration: none; + } -.inline-items li:last-child { - padding-right: 0; +@media screen and (min-width: 996px) { + .right { + position: absolute; + top: 0; + right: 0; + } + + .CodeMirror { + height: 764px !important; + } } -.CodeMirror { height: 300px !important; } +@media screen and (max-width: 1233px) and (min-width: 996px) { -@media screen and (min-width: 84.75em ) { - .left { margin-right: 49.5em; } + .left { + margin-right: 488px; + } - .right { position: absolute; top: 0; right: 0; width: 48.75em; } + .right { + width: 480px; + } +} + +@media screen and (max-width: 1501px) and (min-width: 1234px) { + .left { + margin-right: 622px; + } + + .right { + width: 614px; + } +} - .right, .left { min-height: 800px; height: 800px; } +@media screen and (min-width: 1502px) { + .left { + margin-right: 755px; + } - .CodeMirror { height: 700px !important; } + .right { + width: 747px; + } } diff --git a/demo/index.html b/demo/index.html index 2073ff3..c266029 100644 --- a/demo/index.html +++ b/demo/index.html @@ -9,57 +9,56 @@ - - - -