"
- ],
- "description": "Javascript library to parse CRUD (Create Retrieve Update Delete) SQL queries.",
- "keywords": [
- "sql",
- "parse",
- "ast"
- ],
- "license": "MIT",
- "ignore": [
- "**/.*",
- "node_modules",
- "bower_components",
- "tests",
- "package.json",
- "Gruntfile.js"
- ]
-}
diff --git a/demo.html b/demo.html
new file mode 100644
index 0000000..6a67225
--- /dev/null
+++ b/demo.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Demo - simpleSqlParser
+
+
+
+
+
+
+
+
+
+ AST
+
+
+
+
+
+
diff --git a/dist/simpleSqlParser.js b/dist/simpleSqlParser.js
new file mode 100644
index 0000000..53c9607
--- /dev/null
+++ b/dist/simpleSqlParser.js
@@ -0,0 +1,1236 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.simpleSqlParser = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o last.furthest) return result;
+
+ var expected = (result.furthest === last.furthest)
+ ? result.expected.concat(last.expected)
+ : last.expected;
+
+ return {
+ status: result.status,
+ index: result.index,
+ value: result.value,
+ furthest: last.furthest,
+ expected: expected
+ }
+ }
+
+ function assertParser(p) {
+ if (!(p instanceof Parser)) throw new Error('not a parser: '+p);
+ }
+
+ function formatExpected(expected) {
+ if (expected.length === 1) return expected[0];
+
+ return 'one of ' + expected.join(', ')
+ }
+
+ function formatGot(stream, error) {
+ var i = error.index;
+
+ if (i === stream.length) return ', got the end of the stream'
+
+
+ var prefix = (i > 0 ? "'..." : "'");
+ var suffix = (stream.length - i > 12 ? "...'" : "'");
+
+ return ' at character ' + i + ', got ' + prefix + stream.slice(i, i+12) + suffix
+ }
+
+ var formatError = Parsimmon.formatError = function(stream, error) {
+ return 'expected ' + formatExpected(error.expected) + formatGot(stream, error)
+ };
+
+ _.parse = function(stream) {
+ var result = this.skip(eof)._(stream, 0);
+
+ return result.status ? {
+ status: true,
+ value: result.value
+ } : {
+ status: false,
+ index: result.furthest,
+ expected: result.expected
+ };
+ };
+
+ // [Parser a] -> Parser [a]
+ var seq = Parsimmon.seq = function() {
+ var parsers = [].slice.call(arguments);
+ var numParsers = parsers.length;
+
+ return Parser(function(stream, i) {
+ var result;
+ var accum = new Array(numParsers);
+
+ for (var j = 0; j < numParsers; j += 1) {
+ result = mergeReplies(parsers[j]._(stream, i), result);
+ if (!result.status) return result;
+ accum[j] = result.value
+ i = result.index;
+ }
+
+ return mergeReplies(makeSuccess(i, accum), result);
+ });
+ };
+
+
+ var seqMap = Parsimmon.seqMap = function() {
+ var args = [].slice.call(arguments);
+ var mapper = args.pop();
+ return seq.apply(null, args).map(function(results) {
+ return mapper.apply(null, results);
+ });
+ };
+
+ /**
+ * Allows to add custom primitive parsers
+ */
+ var custom = Parsimmon.custom = function(parsingFunction) {
+ return Parser(parsingFunction(makeSuccess, makeFailure));
+ };
+
+ var alt = Parsimmon.alt = function() {
+ var parsers = [].slice.call(arguments);
+ var numParsers = parsers.length;
+ if (numParsers === 0) return fail('zero alternates')
+
+ return Parser(function(stream, i) {
+ var result;
+ for (var j = 0; j < parsers.length; j += 1) {
+ result = mergeReplies(parsers[j]._(stream, i), result);
+ if (result.status) return result;
+ }
+ return result;
+ });
+ };
+
+ // -*- primitive combinators -*- //
+ _.or = function(alternative) {
+ return alt(this, alternative);
+ };
+
+ _.then = function(next) {
+ if (typeof next === 'function') {
+ throw new Error('chaining features of .then are no longer supported, use .chain instead');
+ }
+
+ assertParser(next);
+ return seq(this, next).map(function(results) { return results[1]; });
+ };
+
+ // -*- optimized iterative combinators -*- //
+ // equivalent to:
+ // _.many = function() {
+ // return this.times(0, Infinity);
+ // };
+ // or, more explicitly:
+ // _.many = function() {
+ // var self = this;
+ // return self.then(function(x) {
+ // return self.many().then(function(xs) {
+ // return [x].concat(xs);
+ // });
+ // }).or(succeed([]));
+ // };
+ _.many = function() {
+ var self = this;
+
+ return Parser(function(stream, i) {
+ var accum = [];
+ var result;
+ var prevResult;
+
+ for (;;) {
+ result = mergeReplies(self._(stream, i), result);
+
+ if (result.status) {
+ i = result.index;
+ accum.push(result.value);
+ }
+ else {
+ return mergeReplies(makeSuccess(i, accum), result);
+ }
+ }
+ });
+ };
+
+ // equivalent to:
+ // _.times = function(min, max) {
+ // if (arguments.length < 2) max = min;
+ // var self = this;
+ // if (min > 0) {
+ // return self.then(function(x) {
+ // return self.times(min - 1, max - 1).then(function(xs) {
+ // return [x].concat(xs);
+ // });
+ // });
+ // }
+ // else if (max > 0) {
+ // return self.then(function(x) {
+ // return self.times(0, max - 1).then(function(xs) {
+ // return [x].concat(xs);
+ // });
+ // }).or(succeed([]));
+ // }
+ // else return succeed([]);
+ // };
+ _.times = function(min, max) {
+ if (arguments.length < 2) max = min;
+ var self = this;
+
+ return Parser(function(stream, i) {
+ var accum = [];
+ var start = i;
+ var result;
+ var prevResult;
+
+ for (var times = 0; times < min; times += 1) {
+ result = self._(stream, i);
+ prevResult = mergeReplies(result, prevResult);
+ if (result.status) {
+ i = result.index;
+ accum.push(result.value);
+ }
+ else return prevResult;
+ }
+
+ for (; times < max; times += 1) {
+ result = self._(stream, i);
+ prevResult = mergeReplies(result, prevResult);
+ if (result.status) {
+ i = result.index;
+ accum.push(result.value);
+ }
+ else break;
+ }
+
+ return mergeReplies(makeSuccess(i, accum), prevResult);
+ });
+ };
+
+ // -*- higher-level combinators -*- //
+ _.result = function(res) { return this.map(function(_) { return res; }); };
+ _.atMost = function(n) { return this.times(0, n); };
+ _.atLeast = function(n) {
+ var self = this;
+ return seqMap(this.times(n), this.many(), function(init, rest) {
+ return init.concat(rest);
+ });
+ };
+
+ _.map = function(fn) {
+ var self = this;
+ return Parser(function(stream, i) {
+ var result = self._(stream, i);
+ if (!result.status) return result;
+ return mergeReplies(makeSuccess(result.index, fn(result.value)), result);
+ });
+ };
+
+ _.skip = function(next) {
+ return seq(this, next).map(function(results) { return results[0]; });
+ };
+
+ _.mark = function() {
+ return seqMap(index, this, index, function(start, value, end) {
+ return { start: start, value: value, end: end };
+ });
+ };
+
+ _.desc = function(expected) {
+ var self = this;
+ return Parser(function(stream, i) {
+ var reply = self._(stream, i);
+ if (!reply.status) reply.expected = [expected];
+ return reply;
+ });
+ };
+
+ // -*- primitive parsers -*- //
+ var string = Parsimmon.string = function(str) {
+ var len = str.length;
+ var expected = "'"+str+"'";
+
+ return Parser(function(stream, i) {
+ var head = stream.slice(i, i+len);
+
+ if (head === str) {
+ return makeSuccess(i+len, head);
+ }
+ else {
+ return makeFailure(i, expected);
+ }
+ });
+ };
+
+ var regex = Parsimmon.regex = function(re, group) {
+ var anchored = RegExp('^(?:'+re.source+')', (''+re).slice((''+re).lastIndexOf('/')+1));
+ var expected = '' + re;
+ if (group == null) group = 0;
+
+ return Parser(function(stream, i) {
+ var match = anchored.exec(stream.slice(i));
+
+ if (match) {
+ var fullMatch = match[0];
+ var groupMatch = match[group];
+ if (groupMatch != null) return makeSuccess(i+fullMatch.length, groupMatch);
+ }
+
+ return makeFailure(i, expected);
+ });
+ };
+
+ var succeed = Parsimmon.succeed = function(value) {
+ return Parser(function(stream, i) {
+ return makeSuccess(i, value);
+ });
+ };
+
+ var fail = Parsimmon.fail = function(expected) {
+ return Parser(function(stream, i) { return makeFailure(i, expected); });
+ };
+
+ var letter = Parsimmon.letter = regex(/[a-z]/i).desc('a letter')
+ var letters = Parsimmon.letters = regex(/[a-z]*/i)
+ var digit = Parsimmon.digit = regex(/[0-9]/).desc('a digit');
+ var digits = Parsimmon.digits = regex(/[0-9]*/)
+ var whitespace = Parsimmon.whitespace = regex(/\s+/).desc('whitespace');
+ var optWhitespace = Parsimmon.optWhitespace = regex(/\s*/);
+
+ var any = Parsimmon.any = Parser(function(stream, i) {
+ if (i >= stream.length) return makeFailure(i, 'any character');
+
+ return makeSuccess(i+1, stream.charAt(i));
+ });
+
+ var all = Parsimmon.all = Parser(function(stream, i) {
+ return makeSuccess(stream.length, stream.slice(i));
+ });
+
+ var eof = Parsimmon.eof = Parser(function(stream, i) {
+ if (i < stream.length) return makeFailure(i, 'EOF');
+
+ return makeSuccess(i, null);
+ });
+
+ var test = Parsimmon.test = function(predicate) {
+ return Parser(function(stream, i) {
+ var char = stream.charAt(i);
+ if (i < stream.length && predicate(char)) {
+ return makeSuccess(i+1, char);
+ }
+ else {
+ return makeFailure(i, 'a character matching '+predicate);
+ }
+ });
+ };
+
+ var oneOf = Parsimmon.oneOf = function(str) {
+ return test(function(ch) { return str.indexOf(ch) >= 0; });
+ };
+
+ var noneOf = Parsimmon.noneOf = function(str) {
+ return test(function(ch) { return str.indexOf(ch) < 0; });
+ };
+
+ var takeWhile = Parsimmon.takeWhile = function(predicate) {
+ return Parser(function(stream, i) {
+ var j = i;
+ while (j < stream.length && predicate(stream.charAt(j))) j += 1;
+ return makeSuccess(j, stream.slice(i, j));
+ });
+ };
+
+ var lazy = Parsimmon.lazy = function(desc, f) {
+ if (arguments.length < 2) {
+ f = desc;
+ desc = undefined;
+ }
+
+ var parser = Parser(function(stream, i) {
+ parser._ = f()._;
+ return parser._(stream, i);
+ });
+
+ if (desc) parser = parser.desc(desc)
+
+ return parser;
+ };
+
+ var index = Parsimmon.index = Parser(function(stream, i) {
+ return makeSuccess(i, i);
+ });
+
+ //- fantasyland compat
+
+ //- Monoid (Alternative, really)
+ _.concat = _.or;
+ _.empty = fail('empty')
+
+ //- Applicative
+ _.of = Parser.of = Parsimmon.of = succeed
+
+ _.ap = function(other) {
+ return seqMap(this, other, function(f, x) { return f(x); })
+ };
+
+ //- Monad
+ _.chain = function(f) {
+ var self = this;
+ return Parser(function(stream, i) {
+ var result = self._(stream, i);
+ if (!result.status) return result;
+ var nextParser = f(result.value);
+ return mergeReplies(nextParser._(stream, result.index), result);
+ });
+ };
+
+ return Parser;
+})();
+module.exports = Parsimmon;
+
+},{}],3:[function(require,module,exports){
+module.exports = require('./build/parsimmon.commonjs');
+exports.version = require('./package.json').version;
+
+},{"./build/parsimmon.commonjs":2,"./package.json":4}],4:[function(require,module,exports){
+module.exports={
+ "_args": [
+ [
+ "parsimmon@0.7.0",
+ "/Users/ra/Workspace/ra/simpleSqlParser"
+ ]
+ ],
+ "_from": "parsimmon@0.7.0",
+ "_id": "parsimmon@0.7.0",
+ "_inCache": true,
+ "_installable": true,
+ "_location": "/parsimmon",
+ "_npmUser": {
+ "email": "jjmadkisson@gmail.com",
+ "name": "jayferd"
+ },
+ "_npmVersion": "1.4.14",
+ "_phantomChildren": {},
+ "_requested": {
+ "name": "parsimmon",
+ "raw": "parsimmon@0.7.0",
+ "rawSpec": "0.7.0",
+ "scope": null,
+ "spec": "0.7.0",
+ "type": "version"
+ },
+ "_requiredBy": [
+ "/"
+ ],
+ "_resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-0.7.0.tgz",
+ "_shasum": "652fc7cbade73c5edb42a266ec556c906d82c9fb",
+ "_shrinkwrap": null,
+ "_spec": "parsimmon@0.7.0",
+ "_where": "/Users/ra/Workspace/ra/simpleSqlParser",
+ "author": {
+ "email": "jneen at jneen dot net",
+ "name": "Jeanine Adkisson"
+ },
+ "bugs": {
+ "url": "https://github.com/jneen/parsimmon/issues"
+ },
+ "dependencies": {
+ "pjs": "5.x"
+ },
+ "description": "A monadic LL(infinity) parser combinator library",
+ "devDependencies": {
+ "chai": "1.5.x",
+ "mocha": "1.8.x",
+ "uglify-js": "2.x"
+ },
+ "directories": {},
+ "dist": {
+ "shasum": "652fc7cbade73c5edb42a266ec556c906d82c9fb",
+ "tarball": "https://registry.npmjs.org/parsimmon/-/parsimmon-0.7.0.tgz"
+ },
+ "files": [
+ "Makefile",
+ "build/parsimmon.browser.js",
+ "build/parsimmon.browser.min.js",
+ "build/parsimmon.commonjs.js",
+ "index.js",
+ "package.json",
+ "src",
+ "test"
+ ],
+ "homepage": "https://github.com/jneen/parsimmon",
+ "keywords": [
+ "parse",
+ "parser combinators",
+ "parsing"
+ ],
+ "main": "index.js",
+ "maintainers": [
+ {
+ "name": "jayferd",
+ "email": "jjmadkisson@gmail.com"
+ },
+ {
+ "name": "jneen",
+ "email": "jneen@jneen.net"
+ }
+ ],
+ "name": "parsimmon",
+ "optionalDependencies": {},
+ "readme": "ERROR: No README data found!",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/jneen/parsimmon.git"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "version": "0.7.0"
+}
+
+},{}],5:[function(require,module,exports){
+"use strict";
+
+module.exports = function(ast) {
+ if (typeof ast === 'object' && ast.status === true) ast = ast.value;
+ else return false;
+
+ function select(ast) {
+ var result = 'SELECT ';
+ result += ast.select.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ return result;
+ }
+
+ function from(ast) {
+ var result = 'FROM ';
+ result += ast.from.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ return result;
+ }
+
+ function join(ast) {
+ return ast.join.map(function(item) {
+ var result = '';
+ if (item.type === 'inner') result += 'INNER JOIN ';
+ else if (item.type === 'left') result += 'LEFT JOIN ';
+ else if (item.type === 'right') result += 'RIGHT JOIN ';
+ else return '';
+ result += item.table;
+ if (item.alias !== null) result += ' AS ' + item.alias;
+ result += ' ON ';
+ result += item.condition.expression;
+ return result;
+ }).join(' ');
+ }
+
+ function where(ast) {
+ var result = '';
+ if (ast.where !== null) result += 'WHERE ' + ast.where.expression;
+ return result;
+ }
+
+ function group(ast) {
+ var result = '';
+ if (ast.group.length > 0) {
+ result += 'GROUP BY ';
+ result += ast.group.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ }
+ return result;
+ }
+
+ function order(ast) {
+ var result = '';
+ if (ast.order.length > 0) {
+ result += 'ORDER BY ';
+ result += ast.order.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ }
+ return result;
+ }
+
+ function limit(ast) {
+ var result = '';
+ if (ast.limit !== null) {
+ result += 'LIMIT ';
+ if (ast.limit.from !== null) result += ast.limit.from + ', ';
+ result += ast.limit.nb;
+ }
+ return result;
+ }
+
+ function into(ast) {
+ return 'INSERT INTO ' + ast.into.expression;
+ }
+
+ function values(ast) {
+ var result = '';
+ var targets = ast.values.filter(function(item) {
+ return item.target !== null;
+ });
+ if (targets.length > 0) {
+ result += '(';
+ result += targets.map(function(item) {
+ return item.target.expression;
+ }).join(', ');
+ result += ') ';
+ }
+ result += 'VALUES (';
+ result += ast.values.map(function(item) {
+ return item.value;
+ }).join(', ');
+ result += ')';
+ return result;
+ }
+
+ function table(ast) {
+ return 'UPDATE ' + ast.table.expression;
+ }
+
+ function update(ast) {
+ var result = 'SET ';
+ result += ast.values.map(function(item) {
+ return item.target.expression + ' = ' + item.value;
+ }).join(', ');
+ return result;
+ }
+
+ var parts = [];
+ if (ast.type === 'select') {
+ parts.push(select(ast));
+ parts.push(from(ast));
+ parts.push(join(ast));
+ parts.push(where(ast));
+ parts.push(group(ast));
+ parts.push(order(ast));
+ parts.push(limit(ast));
+ }
+ else if (ast.type === 'insert') {
+ parts.push(into(ast));
+ parts.push(values(ast));
+ }
+ else if (ast.type === 'update') {
+ parts.push(table(ast));
+ parts.push(update(ast));
+ parts.push(where(ast));
+ }
+ else if (ast.type === 'delete') {
+ parts.push('DELETE');
+ parts.push(from(ast));
+ parts.push(where(ast));
+ }
+ else return false;
+
+ return parts.filter(function(item) {
+ return item !== '';
+ }).join(' ');
+};
+
+},{}],6:[function(require,module,exports){
+"use strict";
+var Parsimmon = require('parsimmon');
+
+/********************************************************************************************
+ ALIASES
+********************************************************************************************/
+
+var seq = Parsimmon.seq;
+var alt = Parsimmon.alt;
+var regex = Parsimmon.regex;
+var string = Parsimmon.string;
+var optWhitespace = Parsimmon.optWhitespace;
+var whitespace = Parsimmon.whitespace;
+var lazy = Parsimmon.lazy;
+
+
+
+/********************************************************************************************
+ COMMON PATTERNS
+********************************************************************************************/
+
+// Make a parser optionnal
+// "empty" parameter will be returned as result if the optionnal parser can't match
+function opt(parser, empty) {
+ if (typeof empty == 'undefined') empty = [];
+ return parser.or(Parsimmon.succeed(empty));
+}
+
+// Join results of a parser
+function mkString(node) {
+ return node.join('');
+}
+
+// Add an item to an optionnal list and return the final list
+function mergeOptionnalList(node) {
+ node[0].push(node[1]);
+ return node[0];
+}
+
+// Generate a parser that accept a comma-separated list of something
+function optionnalList(parser) {
+ return seq(
+ parser.skip(optWhitespace).skip(string(',')).skip(optWhitespace).many(),
+ parser.skip(optWhitespace)
+ ).map(mergeOptionnalList);
+}
+
+// Remove first and last character of a string
+function removeQuotes(string) {
+ return string.replace(/^([`'"])(.*)\1$/, '$2');
+}
+
+// Add the starting and ending char positions of matches of a given parser
+function getPos(parser) {
+ return seq(
+ Parsimmon.index,
+ parser,
+ Parsimmon.index
+ ).map(function(node) {
+ var pos = {
+ start: node[0],
+ end: node[2],
+ };
+ if (typeof node[1] == 'object') {
+ var n = node[1];
+ n.position = pos;
+ return n;
+ }
+ else {
+ pos.out = node[1];
+ return pos;
+ }
+ });
+}
+
+
+
+/********************************************************************************************
+ LOW LEVEL PARSERS
+********************************************************************************************/
+
+// The name of a column/table
+var colName = alt(
+ regex(/(?!(FROM|WHERE|GROUP BY|ORDER BY|LIMIT|INNER|LEFT|RIGHT|JOIN|ON|VALUES|SET)\s)[a-z*][a-z0-9_]*/i),
+ regex(/`[^`\\]*(?:\\.[^`\\]*)*`/)
+);
+
+// A string
+var str = alt(
+ regex(/"[^"\\]*(?:\\.[^"\\]*)*"/),
+ regex(/'[^'\\]*(?:\\.[^'\\]*)*'/)
+);
+
+// A function expression
+var func = seq(
+ alt(
+ regex(/[a-zA-Z0-9_]+\(/),
+ string('(')
+ ),
+ opt(lazy(function() { return argList; })).map(mkString),
+ string(')')
+).map(mkString);
+
+// A table.column expression
+var tableAndColumn = seq(
+ colName,
+ string('.'),
+ colName
+);
+
+// An operator
+var operator = alt(
+ string('+'),
+ string('-'),
+ string('*'),
+ string('/'),
+ string('&&'),
+ string('&'),
+ string('~'),
+ string('||'),
+ string('|'),
+ string('^'),
+ regex(/XOR/i),
+ string('<=>'),
+ string('='),
+ string('!='),
+ string('>='),
+ string('>>'),
+ string('>'),
+ string('<='),
+ string('<<'),
+ string('<'),
+ regex(/IS NULL/i),
+ regex(/IS NOT/i),
+ regex(/IS NOT NULL/i),
+ regex(/IS/i),
+ regex(/LIKE/i),
+ regex(/NOT LIKE/i),
+ regex(/BETWEEN/i),
+ regex(/NOT BETWEEN/i),
+ string('%'),
+ regex(/MOD/i),
+ regex(/NOT/i),
+ regex(/OR\s/i), // A space is forced after so this doesn't get mixed up with ORDER BY
+ regex(/AND/i),
+ regex(/IN/i)
+);
+
+// A number
+var number = regex(/[-]?\d+\.?\d*/);
+
+
+
+/********************************************************************************************
+ EXPRESSION PARSERS
+********************************************************************************************/
+
+// List (following IN, for example)
+var list = seq(
+ string('('),
+ optWhitespace,
+ seq(
+ alt(
+ number,
+ str
+ ),
+ optWhitespace,
+ opt(string(',')),
+ optWhitespace,
+ opt(
+ alt(
+ number,
+ str
+ )
+ )
+ ).map(mkString),
+ optWhitespace,
+ string(')')
+).map(mkString);
+
+// Expression
+var expression = seq(
+ alt(
+ tableAndColumn.map(function(node) {
+ return {
+ expression: node.join(''),
+ table: removeQuotes(node[0]),
+ column: removeQuotes(node[2])
+ };
+ }),
+ func.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ colName.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: removeQuotes(node)
+ };
+ }),
+ str.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ number.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ list.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ })
+ ),
+ opt(seq(
+ optWhitespace,
+ operator,
+ opt(seq(
+ optWhitespace,
+ lazy(function() { return expression; }).map(function(node) {
+ return node.expression;
+ })
+ ).map(mkString), null)
+ ).map(mkString), null)
+).map(function(node) {
+ if (node[1] !== null) {
+ node[0] = node[0].expression;
+ return {
+ expression: node.join(''),
+ table: null,
+ column: null
+ };
+ }
+ else return node[0];
+});
+
+// Expression following a SELECT statement
+var colListExpression = seq(
+ expression,
+ opt( // Alias
+ seq(
+ optWhitespace,
+ opt(regex(/AS\s/i)),
+ alt(colName, str)
+ ).map(function(node) {
+ var n = {};
+ n.alias = removeQuotes(node[2]);
+ n.expression = node.join('');
+ return n;
+ }),
+ null
+ )
+).map(function(node) {
+ var n = node[0];
+ n.alias = (node[1] !== null) ? node[1].alias : null;
+ n.expression = n.expression + ((node[1] !== null) ? node[1].expression : '');
+ return n;
+});
+
+// Expression inside a function
+var argListExpression = expression.map(function(node) {
+ return node.expression;
+});
+
+// Expression following a FROM statement
+var tableListExpression = seq(
+ alt(
+ tableAndColumn.map(mkString),
+ colName
+ ),
+ opt( // Alias
+ seq(
+ optWhitespace,
+ opt(regex(/AS\s/i)),
+ alt(colName, str)
+ ).map(function(node) {
+ return {
+ alias: removeQuotes(node[2]),
+ expression : node.join(''),
+ };
+ }),
+ null
+ )
+).map(function(node) {
+ var n = {};
+ n.table = node[0];
+ n.alias = (node[1] !== null) ? node[1].alias : null;
+ n.expression = node[0] + ((node[1] !== null) ? node[1].expression : '');
+ return n;
+});
+
+// JOIN expression (including JOIN statements)
+var joinExpression = seq(
+ opt(seq(
+ regex(/INNER|LEFT|RIGHT/i),
+ whitespace
+ ).map(function(node) {
+ return node[0].toLowerCase();
+ }), null),
+ regex(/JOIN/i),
+ optWhitespace,
+ getPos(tableListExpression),
+ optWhitespace,
+ regex(/ON/i),
+ optWhitespace,
+ getPos(expression)
+).map(function(node) {
+ var n = {};
+ n.type = node[0] || 'inner';
+ n.table = node[3].table;
+ n.alias = node[3].alias;
+ n.position = node[3].position;
+ n.condition = {
+ expression: node[7].expression,
+ position: node[7].position,
+ };
+ return n;
+});
+
+// Expression following a WHERE statement
+var whereExpression = getPos(expression).map(function(node) {
+ return {
+ expression: node.expression,
+ position: node.position,
+ };
+});
+
+// Expression following an ORDER BY statement
+var orderListExpression = seq(
+ expression,
+ opt(seq(
+ optWhitespace,
+ regex(/ASC|DESC/i)
+ ), null)
+).map(function(node) {
+ return {
+ expression: node[0].expression + ((node[1] !== null) ? node[1].join('') : ''),
+ order: (node[1] !== null) ? node[1][1] : 'ASC',
+ table: node[0].table,
+ column: node[0].column,
+ };
+});
+
+// Expression following a LIMIT statement
+var limitExpression = seq(
+ number,
+ opt(seq(
+ optWhitespace,
+ string(','),
+ optWhitespace,
+ number
+ ), null)
+).map(function(node) {
+ if (node[1] === null) return {
+ from: null,
+ nb: parseInt(node[0], 10),
+ };
+ else return {
+ from: parseInt(node[0], 10),
+ nb: parseInt(node[1][3], 10),
+ };
+});
+
+// Expression designating a column before VALUES in INSERT query
+var insertColListExpression = alt(
+ tableAndColumn.map(function(node) {
+ return {
+ expression: node.join(''),
+ column: removeQuotes(node[2])
+ };
+ }),
+ colName.map(function(node) {
+ return {
+ expression: node,
+ column: removeQuotes(node)
+ };
+ })
+);
+
+// Expression following a VALUES statement
+var valueExpression = expression.map(function(node) {
+ return node.expression;
+});
+
+// Expression that assign a value to a column
+var assignExpression = seq(
+ insertColListExpression,
+ optWhitespace,
+ string('='),
+ optWhitespace,
+ expression
+).map(function(node) {
+ return {
+ target: node[0],
+ value: node[4].expression,
+ };
+});
+
+
+
+/********************************************************************************************
+ HIGH LEVEL PARSERS
+********************************************************************************************/
+
+// List of arguments inside a function
+var argList = seq(
+ seq(argListExpression, optWhitespace, string(','), optWhitespace).map(mkString).many(),
+ argListExpression.skip(optWhitespace)
+).map(mergeOptionnalList);
+
+// List of expressions following a SELECT statement
+var colList = optionnalList(getPos(colListExpression));
+
+// List of table following a FROM statement
+var tableList = optionnalList(getPos(tableListExpression));
+
+// List of table following an GROUP BY statement
+var groupList = optionnalList(getPos(expression));
+
+// List of table following an ORDER BY statement
+var orderList = optionnalList(getPos(orderListExpression));
+
+// List of joins (including JOIN statements)
+var joinList = optWhitespace.then(joinExpression).skip(optWhitespace).many();
+
+// List of columns before VALUES in INSERT query
+var insertColList = optionnalList(insertColListExpression);
+
+// List of values following a VALUES statement
+var valuesList = optionnalList(valueExpression);
+
+// List of assign expression following a SET statement
+var assignList = optionnalList(assignExpression);
+
+
+
+/********************************************************************************************
+ MAIN PARSERS
+********************************************************************************************/
+
+// SELECT parser
+var selectParser = seq(
+ regex(/SELECT/i).skip(optWhitespace).then(opt(colList)),
+ regex(/FROM/i).skip(optWhitespace).then(opt(tableList)),
+ opt(joinList),
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null),
+ opt(regex(/\s?GROUP BY/i).skip(optWhitespace).then(opt(groupList))),
+ opt(regex(/\s?ORDER BY/i).skip(optWhitespace).then(opt(orderList))),
+ opt(regex(/\s?LIMIT/i).skip(optWhitespace).then(opt(limitExpression)), null)
+).map(function(node) {
+ return {
+ type: 'select',
+ select: node[0],
+ from: node[1],
+ join: node[2],
+ where: node[3],
+ group: node[4],
+ order: node[5],
+ limit: node[6],
+ };
+});
+
+// INSERT parser
+var insertParser = seq(
+ regex(/INSERT INTO/i).skip(optWhitespace).then(tableListExpression),
+ optWhitespace,
+ opt(
+ seq(
+ string('('),
+ insertColList,
+ string(')')
+ ).map(function(node) {
+ return node[1];
+ })
+ ),
+ optWhitespace,
+ regex(/VALUES\s?\(/i).skip(optWhitespace).then(valuesList),
+ string(')')
+).map(function(node) {
+ var values = [];
+ var bigger = Math.max(node[2].length, node[4].length);
+
+ for (var i = 0; i < bigger; ++i) {
+ values[i] = {
+ target: node[2][i] || null,
+ value: node[4][i] || null,
+ };
+ }
+
+ return {
+ type: 'insert',
+ into: node[0],
+ values: values,
+ };
+});
+
+// UPDATE parser
+var updateParser = seq(
+ regex(/UPDATE/i).skip(optWhitespace).then(tableListExpression),
+ optWhitespace,
+ regex(/SET/i).skip(optWhitespace).then(assignList),
+ optWhitespace,
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null)
+).map(function(node) {
+ return {
+ type: 'update',
+ table: node[0],
+ values: node[2],
+ where: node[4],
+ };
+});
+
+// DELETE parser
+var deleteParser = seq(
+ regex(/DELETE FROM/i).skip(optWhitespace).then(opt(tableList)),
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null)
+).map(function(node) {
+ return {
+ type: 'delete',
+ from: node[0],
+ where: node[1],
+ };
+});
+
+// Main parser
+var p = alt(selectParser, insertParser, updateParser, deleteParser);
+
+
+
+/********************************************************************************************
+ PUBLIC FUNCTIONS
+********************************************************************************************/
+
+module.exports = function(sql) {
+ var result = p.parse(sql);
+ if (result.status === false) result.error = Parsimmon.formatError(sql, result);
+ return result;
+};
+
+},{"parsimmon":3}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/dist/simpleSqlParser.min.js b/dist/simpleSqlParser.min.js
new file mode 100644
index 0000000..820d5ff
--- /dev/null
+++ b/dist/simpleSqlParser.min.js
@@ -0,0 +1 @@
+!function(n){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.simpleSqlParser=n()}}(function(){return function n(e,t,r){function i(s,o){if(!t[s]){if(!e[s]){var a="function"==typeof require&&require;if(!o&&a)return a(s,!0);if(u)return u(s,!0);var l=new Error("Cannot find module '"+s+"'");throw l.code="MODULE_NOT_FOUND",l}var p=t[s]={exports:{}};e[s][0].call(p.exports,function(n){var t=e[s][1][n];return i(t?t:n)},p,p.exports,n,e,t,r)}return t[s].exports}for(var u="function"==typeof require&&require,s=0;se.furthest)return n;var t=n.furthest===e.furthest?n.expected.concat(e.expected):e.expected;return{status:n.status,index:n.index,value:n.value,furthest:e.furthest,expected:t}}function u(e){if(!(e instanceof n))throw new Error("not a parser: "+e)}function s(n){return 1===n.length?n[0]:"one of "+n.join(", ")}function o(n,e){var t=e.index;if(t===n.length)return", got the end of the stream";var r=t>0?"'...":"'",i=n.length-t>12?"...'":"'";return" at character "+t+", got "+r+n.slice(t,t+12)+i}{var a=n.prototype;r.formatError=function(n,e){return"expected "+s(e.expected)+o(n,e)}}a.parse=function(n){var e=this.skip(d)._(n,0);return e.status?{status:!0,value:e.value}:{status:!1,index:e.furthest,expected:e.expected}};var l=r.seq=function(){var t=[].slice.call(arguments),r=t.length;return n(function(n,u){for(var s,o=new Array(r),a=0;r>a;a+=1){if(s=i(t[a]._(n,u),s),!s.status)return s;o[a]=s.value,u=s.index}return i(e(u,o),s)})},p=r.seqMap=function(){var n=[].slice.call(arguments),e=n.pop();return l.apply(null,n).map(function(n){return e.apply(null,n)})},c=(r.custom=function(r){return n(r(e,t))},r.alt=function(){var e=[].slice.call(arguments),t=e.length;return 0===t?h("zero alternates"):n(function(n,t){for(var r,u=0;up;p+=1){if(o=u._(n,s),a=i(o,a),!o.status)return a;s=o.index,l.push(o.value)}for(;r>p&&(o=u._(n,s),a=i(o,a),o.status);p+=1)s=o.index,l.push(o.value);return i(e(s,l),a)})},a.result=function(n){return this.map(function(e){return n})},a.atMost=function(n){return this.times(0,n)},a.atLeast=function(n){return p(this.times(n),this.many(),function(n,e){return n.concat(e)})},a.map=function(t){var r=this;return n(function(n,u){var s=r._(n,u);return s.status?i(e(s.index,t(s.value)),s):s})},a.skip=function(n){return l(this,n).map(function(n){return n[0]})},a.mark=function(){return p(v,this,v,function(n,e,t){return{start:n,value:e,end:t}})},a.desc=function(e){var t=this;return n(function(n,r){var i=t._(n,r);return i.status||(i.expected=[e]),i})};var f=(r.string=function(r){var i=r.length,u="'"+r+"'";return n(function(n,s){var o=n.slice(s,s+i);return o===r?e(s+i,o):t(s,u)})},r.regex=function(r,i){var u=RegExp("^(?:"+r.source+")",(""+r).slice((""+r).lastIndexOf("/")+1)),s=""+r;return null==i&&(i=0),n(function(n,r){var o=u.exec(n.slice(r));if(o){var a=o[0],l=o[i];if(null!=l)return e(r+a.length,l)}return t(r,s)})}),m=r.succeed=function(t){return n(function(n,r){return e(r,t)})},h=r.fail=function(e){return n(function(n,r){return t(r,e)})},d=(r.letter=f(/[a-z]/i).desc("a letter"),r.letters=f(/[a-z]*/i),r.digit=f(/[0-9]/).desc("a digit"),r.digits=f(/[0-9]*/),r.whitespace=f(/\s+/).desc("whitespace"),r.optWhitespace=f(/\s*/),r.any=n(function(n,r){return r>=n.length?t(r,"any character"):e(r+1,n.charAt(r))}),r.all=n(function(n,t){return e(n.length,n.slice(t))}),r.eof=n(function(n,r){return r=0})},r.noneOf=function(n){return x(function(e){return n.indexOf(e)<0})},r.takeWhile=function(t){return n(function(n,r){for(var i=r;i0&&(e+="GROUP BY ",e+=n.group.map(function(n){return n.expression}).join(", ")),e}function s(n){var e="";return n.order.length>0&&(e+="ORDER BY ",e+=n.order.map(function(n){return n.expression}).join(", ")),e}function o(n){var e="";return null!==n.limit&&(e+="LIMIT ",null!==n.limit.from&&(e+=n.limit.from+", "),e+=n.limit.nb),e}function a(n){return"INSERT INTO "+n.into.expression}function l(n){var e="",t=n.values.filter(function(n){return null!==n.target});return t.length>0&&(e+="(",e+=t.map(function(n){return n.target.expression}).join(", "),e+=") "),e+="VALUES (",e+=n.values.map(function(n){return n.value}).join(", "),e+=")"}function p(n){return"UPDATE "+n.table.expression}function c(n){var e="SET ";return e+=n.values.map(function(n){return n.target.expression+" = "+n.value}).join(", ")}if("object"!=typeof n||n.status!==!0)return!1;n=n.value;var f=[];if("select"===n.type)f.push(e(n)),f.push(t(n)),f.push(r(n)),f.push(i(n)),f.push(u(n)),f.push(s(n)),f.push(o(n));else if("insert"===n.type)f.push(a(n)),f.push(l(n));else if("update"===n.type)f.push(p(n)),f.push(c(n)),f.push(i(n));else{if("delete"!==n.type)return!1;f.push("DELETE"),f.push(t(n)),f.push(i(n))}return f.filter(function(n){return""!==n}).join(" ")}},{}],6:[function(n,e,t){"use strict";function r(n,e){return"undefined"==typeof e&&(e=[]),n.or(l.succeed(e))}function i(n){return n.join("")}function u(n){return n[0].push(n[1]),n[0]}function s(n){return p(n.skip(h).skip(m(",")).skip(h).many(),n.skip(h)).map(u)}function o(n){return n.replace(/^([`'"])(.*)\1$/,"$2")}function a(n){return p(l.index,n,l.index).map(function(n){var e={start:n[0],end:n[2]};if("object"==typeof n[1]){var t=n[1];return t.position=e,t}return e.out=n[1],e})}var l=n("parsimmon"),p=l.seq,c=l.alt,f=l.regex,m=l.string,h=l.optWhitespace,d=l.whitespace,x=l.lazy,v=c(f(/(?!(FROM|WHERE|GROUP BY|ORDER BY|LIMIT|INNER|LEFT|RIGHT|JOIN|ON|VALUES|SET)\s)[a-z*][a-z0-9_]*/i),f(/`[^`\\]*(?:\\.[^`\\]*)*`/)),g=c(f(/"[^"\\]*(?:\\.[^"\\]*)*"/),f(/'[^'\\]*(?:\\.[^'\\]*)*'/)),E=p(c(f(/[a-zA-Z0-9_]+\(/),m("(")),r(x(function(){return D})).map(i),m(")")).map(i),j=p(v,m("."),v),y=c(m("+"),m("-"),m("*"),m("/"),m("&&"),m("&"),m("~"),m("||"),m("|"),m("^"),f(/XOR/i),m("<=>"),m("="),m("!="),m(">="),m(">>"),m(">"),m("<="),m("<<"),m("<"),f(/IS NULL/i),f(/IS NOT/i),f(/IS NOT NULL/i),f(/IS/i),f(/LIKE/i),f(/NOT LIKE/i),f(/BETWEEN/i),f(/NOT BETWEEN/i),m("%"),f(/MOD/i),f(/NOT/i),f(/OR\s/i),f(/AND/i),f(/IN/i)),b=f(/[-]?\d+\.?\d*/),O=p(m("("),h,p(c(b,g),h,r(m(",")),h,r(c(b,g))).map(i),h,m(")")).map(i),k=p(c(j.map(function(n){return{expression:n.join(""),table:o(n[0]),column:o(n[2])}}),E.map(function(n){return{expression:n,table:null,column:null}}),v.map(function(n){return{expression:n,table:null,column:o(n)}}),g.map(function(n){return{expression:n,table:null,column:null}}),b.map(function(n){return{expression:n,table:null,column:null}}),O.map(function(n){return{expression:n,table:null,column:null}})),r(p(h,y,r(p(h,x(function(){return k}).map(function(n){return n.expression})).map(i),null)).map(i),null)).map(function(n){return null!==n[1]?(n[0]=n[0].expression,{expression:n.join(""),table:null,column:null}):n[0]}),_=p(k,r(p(h,r(f(/AS\s/i)),c(v,g)).map(function(n){var e={};return e.alias=o(n[2]),e.expression=n.join(""),e}),null)).map(function(n){var e=n[0];return e.alias=null!==n[1]?n[1].alias:null,e.expression=e.expression+(null!==n[1]?n[1].expression:""),e}),R=k.map(function(n){return n.expression}),I=p(c(j.map(i),v),r(p(h,r(f(/AS\s/i)),c(v,g)).map(function(n){return{alias:o(n[2]),expression:n.join("")}}),null)).map(function(n){var e={};return e.table=n[0],e.alias=null!==n[1]?n[1].alias:null,e.expression=n[0]+(null!==n[1]?n[1].expression:""),e}),N=p(r(p(f(/INNER|LEFT|RIGHT/i),d).map(function(n){return n[0].toLowerCase()}),null),f(/JOIN/i),h,a(I),h,f(/ON/i),h,a(k)).map(function(n){var e={};return e.type=n[0]||"inner",e.table=n[3].table,e.alias=n[3].alias,e.position=n[3].position,e.condition={expression:n[7].expression,position:n[7].position},e}),T=a(k).map(function(n){return{expression:n.expression,position:n.position}}),w=p(k,r(p(h,f(/ASC|DESC/i)),null)).map(function(n){return{expression:n[0].expression+(null!==n[1]?n[1].join(""):""),order:null!==n[1]?n[1][1]:"ASC",table:n[0].table,column:n[0].column}}),L=p(b,r(p(h,m(","),h,b),null)).map(function(n){return null===n[1]?{from:null,nb:parseInt(n[0],10)}:{from:parseInt(n[0],10),nb:parseInt(n[1][3],10)}}),S=c(j.map(function(n){return{expression:n.join(""),column:o(n[2])}}),v.map(function(n){return{expression:n,column:o(n)}})),A=k.map(function(n){return n.expression}),q=p(S,h,m("="),h,k).map(function(n){return{target:n[0],value:n[4].expression}}),D=p(p(R,h,m(","),h).map(i).many(),R.skip(h)).map(u),U=s(a(_)),M=s(a(I)),W=s(a(k)),z=s(a(w)),B=h.then(N).skip(h).many(),C=s(S),F=s(A),P=s(q),H=p(f(/SELECT/i).skip(h).then(r(U)),f(/FROM/i).skip(h).then(r(M)),r(B),r(f(/WHERE/i).skip(h).then(r(T)),null),r(f(/\s?GROUP BY/i).skip(h).then(r(W))),r(f(/\s?ORDER BY/i).skip(h).then(r(z))),r(f(/\s?LIMIT/i).skip(h).then(r(L)),null)).map(function(n){return{type:"select",select:n[0],from:n[1],join:n[2],where:n[3],group:n[4],order:n[5],limit:n[6]}}),G=p(f(/INSERT INTO/i).skip(h).then(I),h,r(p(m("("),C,m(")")).map(function(n){return n[1]})),h,f(/VALUES\s?\(/i).skip(h).then(F),m(")")).map(function(n){for(var e=[],t=Math.max(n[2].length,n[4].length),r=0;t>r;++r)e[r]={target:n[2][r]||null,value:n[4][r]||null};return{type:"insert",into:n[0],values:e}}),J=p(f(/UPDATE/i).skip(h).then(I),h,f(/SET/i).skip(h).then(P),h,r(f(/WHERE/i).skip(h).then(r(T)),null)).map(function(n){return{type:"update",table:n[0],values:n[2],where:n[4]}}),Y=p(f(/DELETE FROM/i).skip(h).then(r(M)),r(f(/WHERE/i).skip(h).then(r(T)),null)).map(function(n){return{type:"delete",from:n[0],where:n[1]}}),V=c(H,G,J,Y);e.exports=function(n){var e=V.parse(n);return e.status===!1&&(e.error=l.formatError(n,e)),e}},{parsimmon:3}]},{},[1])(1)});
\ No newline at end of file
diff --git a/dist/simpleSqlParser.withoutDeps.js b/dist/simpleSqlParser.withoutDeps.js
new file mode 100644
index 0000000..125fb58
--- /dev/null
+++ b/dist/simpleSqlParser.withoutDeps.js
@@ -0,0 +1,705 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.simpleSqlParser = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
+ result += 'GROUP BY ';
+ result += ast.group.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ }
+ return result;
+ }
+
+ function order(ast) {
+ var result = '';
+ if (ast.order.length > 0) {
+ result += 'ORDER BY ';
+ result += ast.order.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ }
+ return result;
+ }
+
+ function limit(ast) {
+ var result = '';
+ if (ast.limit !== null) {
+ result += 'LIMIT ';
+ if (ast.limit.from !== null) result += ast.limit.from + ', ';
+ result += ast.limit.nb;
+ }
+ return result;
+ }
+
+ function into(ast) {
+ return 'INSERT INTO ' + ast.into.expression;
+ }
+
+ function values(ast) {
+ var result = '';
+ var targets = ast.values.filter(function(item) {
+ return item.target !== null;
+ });
+ if (targets.length > 0) {
+ result += '(';
+ result += targets.map(function(item) {
+ return item.target.expression;
+ }).join(', ');
+ result += ') ';
+ }
+ result += 'VALUES (';
+ result += ast.values.map(function(item) {
+ return item.value;
+ }).join(', ');
+ result += ')';
+ return result;
+ }
+
+ function table(ast) {
+ return 'UPDATE ' + ast.table.expression;
+ }
+
+ function update(ast) {
+ var result = 'SET ';
+ result += ast.values.map(function(item) {
+ return item.target.expression + ' = ' + item.value;
+ }).join(', ');
+ return result;
+ }
+
+ var parts = [];
+ if (ast.type === 'select') {
+ parts.push(select(ast));
+ parts.push(from(ast));
+ parts.push(join(ast));
+ parts.push(where(ast));
+ parts.push(group(ast));
+ parts.push(order(ast));
+ parts.push(limit(ast));
+ }
+ else if (ast.type === 'insert') {
+ parts.push(into(ast));
+ parts.push(values(ast));
+ }
+ else if (ast.type === 'update') {
+ parts.push(table(ast));
+ parts.push(update(ast));
+ parts.push(where(ast));
+ }
+ else if (ast.type === 'delete') {
+ parts.push('DELETE');
+ parts.push(from(ast));
+ parts.push(where(ast));
+ }
+ else return false;
+
+ return parts.filter(function(item) {
+ return item !== '';
+ }).join(' ');
+};
+
+},{}],3:[function(require,module,exports){
+(function (global){
+"use strict";
+var Parsimmon = (typeof window !== "undefined" ? window.parsimmon : typeof global !== "undefined" ? global.parsimmon : null);
+
+/********************************************************************************************
+ ALIASES
+********************************************************************************************/
+
+var seq = Parsimmon.seq;
+var alt = Parsimmon.alt;
+var regex = Parsimmon.regex;
+var string = Parsimmon.string;
+var optWhitespace = Parsimmon.optWhitespace;
+var whitespace = Parsimmon.whitespace;
+var lazy = Parsimmon.lazy;
+
+
+
+/********************************************************************************************
+ COMMON PATTERNS
+********************************************************************************************/
+
+// Make a parser optionnal
+// "empty" parameter will be returned as result if the optionnal parser can't match
+function opt(parser, empty) {
+ if (typeof empty == 'undefined') empty = [];
+ return parser.or(Parsimmon.succeed(empty));
+}
+
+// Join results of a parser
+function mkString(node) {
+ return node.join('');
+}
+
+// Add an item to an optionnal list and return the final list
+function mergeOptionnalList(node) {
+ node[0].push(node[1]);
+ return node[0];
+}
+
+// Generate a parser that accept a comma-separated list of something
+function optionnalList(parser) {
+ return seq(
+ parser.skip(optWhitespace).skip(string(',')).skip(optWhitespace).many(),
+ parser.skip(optWhitespace)
+ ).map(mergeOptionnalList);
+}
+
+// Remove first and last character of a string
+function removeQuotes(string) {
+ return string.replace(/^([`'"])(.*)\1$/, '$2');
+}
+
+// Add the starting and ending char positions of matches of a given parser
+function getPos(parser) {
+ return seq(
+ Parsimmon.index,
+ parser,
+ Parsimmon.index
+ ).map(function(node) {
+ var pos = {
+ start: node[0],
+ end: node[2],
+ };
+ if (typeof node[1] == 'object') {
+ var n = node[1];
+ n.position = pos;
+ return n;
+ }
+ else {
+ pos.out = node[1];
+ return pos;
+ }
+ });
+}
+
+
+
+/********************************************************************************************
+ LOW LEVEL PARSERS
+********************************************************************************************/
+
+// The name of a column/table
+var colName = alt(
+ regex(/(?!(FROM|WHERE|GROUP BY|ORDER BY|LIMIT|INNER|LEFT|RIGHT|JOIN|ON|VALUES|SET)\s)[a-z*][a-z0-9_]*/i),
+ regex(/`[^`\\]*(?:\\.[^`\\]*)*`/)
+);
+
+// A string
+var str = alt(
+ regex(/"[^"\\]*(?:\\.[^"\\]*)*"/),
+ regex(/'[^'\\]*(?:\\.[^'\\]*)*'/)
+);
+
+// A function expression
+var func = seq(
+ alt(
+ regex(/[a-zA-Z0-9_]+\(/),
+ string('(')
+ ),
+ opt(lazy(function() { return argList; })).map(mkString),
+ string(')')
+).map(mkString);
+
+// A table.column expression
+var tableAndColumn = seq(
+ colName,
+ string('.'),
+ colName
+);
+
+// An operator
+var operator = alt(
+ string('+'),
+ string('-'),
+ string('*'),
+ string('/'),
+ string('&&'),
+ string('&'),
+ string('~'),
+ string('||'),
+ string('|'),
+ string('^'),
+ regex(/XOR/i),
+ string('<=>'),
+ string('='),
+ string('!='),
+ string('>='),
+ string('>>'),
+ string('>'),
+ string('<='),
+ string('<<'),
+ string('<'),
+ regex(/IS NULL/i),
+ regex(/IS NOT/i),
+ regex(/IS NOT NULL/i),
+ regex(/IS/i),
+ regex(/LIKE/i),
+ regex(/NOT LIKE/i),
+ regex(/BETWEEN/i),
+ regex(/NOT BETWEEN/i),
+ string('%'),
+ regex(/MOD/i),
+ regex(/NOT/i),
+ regex(/OR\s/i), // A space is forced after so this doesn't get mixed up with ORDER BY
+ regex(/AND/i),
+ regex(/IN/i)
+);
+
+// A number
+var number = regex(/[-]?\d+\.?\d*/);
+
+
+
+/********************************************************************************************
+ EXPRESSION PARSERS
+********************************************************************************************/
+
+// List (following IN, for example)
+var list = seq(
+ string('('),
+ optWhitespace,
+ seq(
+ alt(
+ number,
+ str
+ ),
+ optWhitespace,
+ opt(string(',')),
+ optWhitespace,
+ opt(
+ alt(
+ number,
+ str
+ )
+ )
+ ).map(mkString),
+ optWhitespace,
+ string(')')
+).map(mkString);
+
+// Expression
+var expression = seq(
+ alt(
+ tableAndColumn.map(function(node) {
+ return {
+ expression: node.join(''),
+ table: removeQuotes(node[0]),
+ column: removeQuotes(node[2])
+ };
+ }),
+ func.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ colName.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: removeQuotes(node)
+ };
+ }),
+ str.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ number.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ list.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ })
+ ),
+ opt(seq(
+ optWhitespace,
+ operator,
+ opt(seq(
+ optWhitespace,
+ lazy(function() { return expression; }).map(function(node) {
+ return node.expression;
+ })
+ ).map(mkString), null)
+ ).map(mkString), null)
+).map(function(node) {
+ if (node[1] !== null) {
+ node[0] = node[0].expression;
+ return {
+ expression: node.join(''),
+ table: null,
+ column: null
+ };
+ }
+ else return node[0];
+});
+
+// Expression following a SELECT statement
+var colListExpression = seq(
+ expression,
+ opt( // Alias
+ seq(
+ optWhitespace,
+ opt(regex(/AS\s/i)),
+ alt(colName, str)
+ ).map(function(node) {
+ var n = {};
+ n.alias = removeQuotes(node[2]);
+ n.expression = node.join('');
+ return n;
+ }),
+ null
+ )
+).map(function(node) {
+ var n = node[0];
+ n.alias = (node[1] !== null) ? node[1].alias : null;
+ n.expression = n.expression + ((node[1] !== null) ? node[1].expression : '');
+ return n;
+});
+
+// Expression inside a function
+var argListExpression = expression.map(function(node) {
+ return node.expression;
+});
+
+// Expression following a FROM statement
+var tableListExpression = seq(
+ alt(
+ tableAndColumn.map(mkString),
+ colName
+ ),
+ opt( // Alias
+ seq(
+ optWhitespace,
+ opt(regex(/AS\s/i)),
+ alt(colName, str)
+ ).map(function(node) {
+ return {
+ alias: removeQuotes(node[2]),
+ expression : node.join(''),
+ };
+ }),
+ null
+ )
+).map(function(node) {
+ var n = {};
+ n.table = node[0];
+ n.alias = (node[1] !== null) ? node[1].alias : null;
+ n.expression = node[0] + ((node[1] !== null) ? node[1].expression : '');
+ return n;
+});
+
+// JOIN expression (including JOIN statements)
+var joinExpression = seq(
+ opt(seq(
+ regex(/INNER|LEFT|RIGHT/i),
+ whitespace
+ ).map(function(node) {
+ return node[0].toLowerCase();
+ }), null),
+ regex(/JOIN/i),
+ optWhitespace,
+ getPos(tableListExpression),
+ optWhitespace,
+ regex(/ON/i),
+ optWhitespace,
+ getPos(expression)
+).map(function(node) {
+ var n = {};
+ n.type = node[0] || 'inner';
+ n.table = node[3].table;
+ n.alias = node[3].alias;
+ n.position = node[3].position;
+ n.condition = {
+ expression: node[7].expression,
+ position: node[7].position,
+ };
+ return n;
+});
+
+// Expression following a WHERE statement
+var whereExpression = getPos(expression).map(function(node) {
+ return {
+ expression: node.expression,
+ position: node.position,
+ };
+});
+
+// Expression following an ORDER BY statement
+var orderListExpression = seq(
+ expression,
+ opt(seq(
+ optWhitespace,
+ regex(/ASC|DESC/i)
+ ), null)
+).map(function(node) {
+ return {
+ expression: node[0].expression + ((node[1] !== null) ? node[1].join('') : ''),
+ order: (node[1] !== null) ? node[1][1] : 'ASC',
+ table: node[0].table,
+ column: node[0].column,
+ };
+});
+
+// Expression following a LIMIT statement
+var limitExpression = seq(
+ number,
+ opt(seq(
+ optWhitespace,
+ string(','),
+ optWhitespace,
+ number
+ ), null)
+).map(function(node) {
+ if (node[1] === null) return {
+ from: null,
+ nb: parseInt(node[0], 10),
+ };
+ else return {
+ from: parseInt(node[0], 10),
+ nb: parseInt(node[1][3], 10),
+ };
+});
+
+// Expression designating a column before VALUES in INSERT query
+var insertColListExpression = alt(
+ tableAndColumn.map(function(node) {
+ return {
+ expression: node.join(''),
+ column: removeQuotes(node[2])
+ };
+ }),
+ colName.map(function(node) {
+ return {
+ expression: node,
+ column: removeQuotes(node)
+ };
+ })
+);
+
+// Expression following a VALUES statement
+var valueExpression = expression.map(function(node) {
+ return node.expression;
+});
+
+// Expression that assign a value to a column
+var assignExpression = seq(
+ insertColListExpression,
+ optWhitespace,
+ string('='),
+ optWhitespace,
+ expression
+).map(function(node) {
+ return {
+ target: node[0],
+ value: node[4].expression,
+ };
+});
+
+
+
+/********************************************************************************************
+ HIGH LEVEL PARSERS
+********************************************************************************************/
+
+// List of arguments inside a function
+var argList = seq(
+ seq(argListExpression, optWhitespace, string(','), optWhitespace).map(mkString).many(),
+ argListExpression.skip(optWhitespace)
+).map(mergeOptionnalList);
+
+// List of expressions following a SELECT statement
+var colList = optionnalList(getPos(colListExpression));
+
+// List of table following a FROM statement
+var tableList = optionnalList(getPos(tableListExpression));
+
+// List of table following an GROUP BY statement
+var groupList = optionnalList(getPos(expression));
+
+// List of table following an ORDER BY statement
+var orderList = optionnalList(getPos(orderListExpression));
+
+// List of joins (including JOIN statements)
+var joinList = optWhitespace.then(joinExpression).skip(optWhitespace).many();
+
+// List of columns before VALUES in INSERT query
+var insertColList = optionnalList(insertColListExpression);
+
+// List of values following a VALUES statement
+var valuesList = optionnalList(valueExpression);
+
+// List of assign expression following a SET statement
+var assignList = optionnalList(assignExpression);
+
+
+
+/********************************************************************************************
+ MAIN PARSERS
+********************************************************************************************/
+
+// SELECT parser
+var selectParser = seq(
+ regex(/SELECT/i).skip(optWhitespace).then(opt(colList)),
+ regex(/FROM/i).skip(optWhitespace).then(opt(tableList)),
+ opt(joinList),
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null),
+ opt(regex(/\s?GROUP BY/i).skip(optWhitespace).then(opt(groupList))),
+ opt(regex(/\s?ORDER BY/i).skip(optWhitespace).then(opt(orderList))),
+ opt(regex(/\s?LIMIT/i).skip(optWhitespace).then(opt(limitExpression)), null)
+).map(function(node) {
+ return {
+ type: 'select',
+ select: node[0],
+ from: node[1],
+ join: node[2],
+ where: node[3],
+ group: node[4],
+ order: node[5],
+ limit: node[6],
+ };
+});
+
+// INSERT parser
+var insertParser = seq(
+ regex(/INSERT INTO/i).skip(optWhitespace).then(tableListExpression),
+ optWhitespace,
+ opt(
+ seq(
+ string('('),
+ insertColList,
+ string(')')
+ ).map(function(node) {
+ return node[1];
+ })
+ ),
+ optWhitespace,
+ regex(/VALUES\s?\(/i).skip(optWhitespace).then(valuesList),
+ string(')')
+).map(function(node) {
+ var values = [];
+ var bigger = Math.max(node[2].length, node[4].length);
+
+ for (var i = 0; i < bigger; ++i) {
+ values[i] = {
+ target: node[2][i] || null,
+ value: node[4][i] || null,
+ };
+ }
+
+ return {
+ type: 'insert',
+ into: node[0],
+ values: values,
+ };
+});
+
+// UPDATE parser
+var updateParser = seq(
+ regex(/UPDATE/i).skip(optWhitespace).then(tableListExpression),
+ optWhitespace,
+ regex(/SET/i).skip(optWhitespace).then(assignList),
+ optWhitespace,
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null)
+).map(function(node) {
+ return {
+ type: 'update',
+ table: node[0],
+ values: node[2],
+ where: node[4],
+ };
+});
+
+// DELETE parser
+var deleteParser = seq(
+ regex(/DELETE FROM/i).skip(optWhitespace).then(opt(tableList)),
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null)
+).map(function(node) {
+ return {
+ type: 'delete',
+ from: node[0],
+ where: node[1],
+ };
+});
+
+// Main parser
+var p = alt(selectParser, insertParser, updateParser, deleteParser);
+
+
+
+/********************************************************************************************
+ PUBLIC FUNCTIONS
+********************************************************************************************/
+
+module.exports = function(sql) {
+ var result = p.parse(sql);
+ if (result.status === false) result.error = Parsimmon.formatError(sql, result);
+ return result;
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/dist/simpleSqlParser.withoutDeps.min.js b/dist/simpleSqlParser.withoutDeps.min.js
new file mode 100644
index 0000000..9f4a949
--- /dev/null
+++ b/dist/simpleSqlParser.withoutDeps.min.js
@@ -0,0 +1 @@
+!function(n){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.simpleSqlParser=n()}}(function(){return function n(e,t,r){function i(o,s){if(!t[o]){if(!e[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(u)return u(o,!0);var p=new Error("Cannot find module '"+o+"'");throw p.code="MODULE_NOT_FOUND",p}var a=t[o]={exports:{}};e[o][0].call(a.exports,function(n){var t=e[o][1][n];return i(t?t:n)},a,a.exports,n,e,t,r)}return t[o].exports}for(var u="function"==typeof require&&require,o=0;o0&&(e+="GROUP BY ",e+=n.group.map(function(n){return n.expression}).join(", ")),e}function o(n){var e="";return n.order.length>0&&(e+="ORDER BY ",e+=n.order.map(function(n){return n.expression}).join(", ")),e}function s(n){var e="";return null!==n.limit&&(e+="LIMIT ",null!==n.limit.from&&(e+=n.limit.from+", "),e+=n.limit.nb),e}function l(n){return"INSERT INTO "+n.into.expression}function p(n){var e="",t=n.values.filter(function(n){return null!==n.target});return t.length>0&&(e+="(",e+=t.map(function(n){return n.target.expression}).join(", "),e+=") "),e+="VALUES (",e+=n.values.map(function(n){return n.value}).join(", "),e+=")"}function a(n){return"UPDATE "+n.table.expression}function f(n){var e="SET ";return e+=n.values.map(function(n){return n.target.expression+" = "+n.value}).join(", ")}if("object"!=typeof n||n.status!==!0)return!1;n=n.value;var c=[];if("select"===n.type)c.push(e(n)),c.push(t(n)),c.push(r(n)),c.push(i(n)),c.push(u(n)),c.push(o(n)),c.push(s(n));else if("insert"===n.type)c.push(l(n)),c.push(p(n));else if("update"===n.type)c.push(a(n)),c.push(f(n)),c.push(i(n));else{if("delete"!==n.type)return!1;c.push("DELETE"),c.push(t(n)),c.push(i(n))}return c.filter(function(n){return""!==n}).join(" ")}},{}],3:[function(n,e,t){(function(n){"use strict";function t(n,e){return"undefined"==typeof e&&(e=[]),n.or(l.succeed(e))}function r(n){return n.join("")}function i(n){return n[0].push(n[1]),n[0]}function u(n){return p(n.skip(m).skip(c(",")).skip(m).many(),n.skip(m)).map(i)}function o(n){return n.replace(/^([`'"])(.*)\1$/,"$2")}function s(n){return p(l.index,n,l.index).map(function(n){var e={start:n[0],end:n[2]};if("object"==typeof n[1]){var t=n[1];return t.position=e,t}return e.out=n[1],e})}var l="undefined"!=typeof window?window.parsimmon:"undefined"!=typeof n?n.parsimmon:null,p=l.seq,a=l.alt,f=l.regex,c=l.string,m=l.optWhitespace,d=l.whitespace,x=l.lazy,E=a(f(/(?!(FROM|WHERE|GROUP BY|ORDER BY|LIMIT|INNER|LEFT|RIGHT|JOIN|ON|VALUES|SET)\s)[a-z*][a-z0-9_]*/i),f(/`[^`\\]*(?:\\.[^`\\]*)*`/)),h=a(f(/"[^"\\]*(?:\\.[^"\\]*)*"/),f(/'[^'\\]*(?:\\.[^'\\]*)*'/)),v=p(a(f(/[a-zA-Z0-9_]+\(/),c("(")),t(x(function(){return D})).map(r),c(")")).map(r),y=p(E,c("."),E),I=a(c("+"),c("-"),c("*"),c("/"),c("&&"),c("&"),c("~"),c("||"),c("|"),c("^"),f(/XOR/i),c("<=>"),c("="),c("!="),c(">="),c(">>"),c(">"),c("<="),c("<<"),c("<"),f(/IS NULL/i),f(/IS NOT/i),f(/IS NOT NULL/i),f(/IS/i),f(/LIKE/i),f(/NOT LIKE/i),f(/BETWEEN/i),f(/NOT BETWEEN/i),c("%"),f(/MOD/i),f(/NOT/i),f(/OR\s/i),f(/AND/i),f(/IN/i)),N=f(/[-]?\d+\.?\d*/),O=p(c("("),m,p(a(N,h),m,t(c(",")),m,t(a(N,h))).map(r),m,c(")")).map(r),T=p(a(y.map(function(n){return{expression:n.join(""),table:o(n[0]),column:o(n[2])}}),v.map(function(n){return{expression:n,table:null,column:null}}),E.map(function(n){return{expression:n,table:null,column:o(n)}}),h.map(function(n){return{expression:n,table:null,column:null}}),N.map(function(n){return{expression:n,table:null,column:null}}),O.map(function(n){return{expression:n,table:null,column:null}})),t(p(m,I,t(p(m,x(function(){return T}).map(function(n){return n.expression})).map(r),null)).map(r),null)).map(function(n){return null!==n[1]?(n[0]=n[0].expression,{expression:n.join(""),table:null,column:null}):n[0]}),R=p(T,t(p(m,t(f(/AS\s/i)),a(E,h)).map(function(n){var e={};return e.alias=o(n[2]),e.expression=n.join(""),e}),null)).map(function(n){var e=n[0];return e.alias=null!==n[1]?n[1].alias:null,e.expression=e.expression+(null!==n[1]?n[1].expression:""),e}),b=T.map(function(n){return n.expression}),j=p(a(y.map(r),E),t(p(m,t(f(/AS\s/i)),a(E,h)).map(function(n){return{alias:o(n[2]),expression:n.join("")}}),null)).map(function(n){var e={};return e.table=n[0],e.alias=null!==n[1]?n[1].alias:null,e.expression=n[0]+(null!==n[1]?n[1].expression:""),e}),g=p(t(p(f(/INNER|LEFT|RIGHT/i),d).map(function(n){return n[0].toLowerCase()}),null),f(/JOIN/i),m,s(j),m,f(/ON/i),m,s(T)).map(function(n){var e={};return e.type=n[0]||"inner",e.table=n[3].table,e.alias=n[3].alias,e.position=n[3].position,e.condition={expression:n[7].expression,position:n[7].position},e}),w=s(T).map(function(n){return{expression:n.expression,position:n.position}}),L=p(T,t(p(m,f(/ASC|DESC/i)),null)).map(function(n){return{expression:n[0].expression+(null!==n[1]?n[1].join(""):""),order:null!==n[1]?n[1][1]:"ASC",table:n[0].table,column:n[0].column}}),S=p(N,t(p(m,c(","),m,N),null)).map(function(n){return null===n[1]?{from:null,nb:parseInt(n[0],10)}:{from:parseInt(n[0],10),nb:parseInt(n[1][3],10)}}),k=a(y.map(function(n){return{expression:n.join(""),column:o(n[2])}}),E.map(function(n){return{expression:n,column:o(n)}})),q=T.map(function(n){return n.expression}),A=p(k,m,c("="),m,T).map(function(n){return{target:n[0],value:n[4].expression}}),D=p(p(b,m,c(","),m).map(r).many(),b.skip(m)).map(i),U=u(s(R)),M=u(s(j)),B=u(s(T)),F=u(s(L)),H=m.then(g).skip(m).many(),W=u(k),C=u(q),G=u(A),P=p(f(/SELECT/i).skip(m).then(t(U)),f(/FROM/i).skip(m).then(t(M)),t(H),t(f(/WHERE/i).skip(m).then(t(w)),null),t(f(/\s?GROUP BY/i).skip(m).then(t(B))),t(f(/\s?ORDER BY/i).skip(m).then(t(F))),t(f(/\s?LIMIT/i).skip(m).then(t(S)),null)).map(function(n){return{type:"select",select:n[0],from:n[1],join:n[2],where:n[3],group:n[4],order:n[5],limit:n[6]}}),Y=p(f(/INSERT INTO/i).skip(m).then(j),m,t(p(c("("),W,c(")")).map(function(n){return n[1]})),m,f(/VALUES\s?\(/i).skip(m).then(C),c(")")).map(function(n){for(var e=[],t=Math.max(n[2].length,n[4].length),r=0;t>r;++r)e[r]={target:n[2][r]||null,value:n[4][r]||null};return{type:"insert",into:n[0],values:e}}),J=p(f(/UPDATE/i).skip(m).then(j),m,f(/SET/i).skip(m).then(G),m,t(f(/WHERE/i).skip(m).then(t(w)),null)).map(function(n){return{type:"update",table:n[0],values:n[2],where:n[4]}}),z=p(f(/DELETE FROM/i).skip(m).then(t(M)),t(f(/WHERE/i).skip(m).then(t(w)),null)).map(function(n){return{type:"delete",from:n[0],where:n[1]}}),_=a(P,Y,J,z);e.exports=function(n){var e=_.parse(n);return e.status===!1&&(e.error=l.formatError(n,e)),e}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1])(1)});
\ No newline at end of file
diff --git a/example.html b/example.html
deleted file mode 100644
index 885ddc3..0000000
--- a/example.html
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- simpleSqlParser
-
-
-
- Check console to see the output
-
-
-
-
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..3808d2b
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,58 @@
+var gulp = require('gulp');
+var jshint = require('gulp-jshint');
+var mocha = require('gulp-mocha');
+var uglify = require('gulp-uglify');
+var rename = require('gulp-rename');
+var source = require('vinyl-source-stream');
+var browserify = require('browserify');
+
+gulp.task('lint', function() {
+ return gulp.src(['*.js', 'src/*.js', 'tests/*.js'])
+ .pipe(jshint())
+ .pipe(jshint.reporter('default'))
+ .pipe(jshint.reporter('fail'));
+});
+
+gulp.task('mocha', function() {
+ return gulp.src('./tests/test*.js')
+ .pipe(mocha({
+ ui: 'qunit',
+ reporter: 'spec',
+ }));
+});
+
+gulp.task('test', ['lint', 'mocha']);
+
+gulp.task('browserifyWithDeps', function() {
+ var bundler = browserify({ standalone: 'simpleSqlParser', entries: './index.js' });
+ return bundler
+ .bundle()
+ .pipe(source('simpleSqlParser.js'))
+ .pipe(gulp.dest('./dist'));
+});
+
+gulp.task('browserifyWithoutDeps', function() {
+ var bundler = browserify({ standalone: 'simpleSqlParser', entries: './index.js' });
+ bundler.exclude('Parsimmon');
+ bundler.transform('browserify-shim');
+ return bundler
+ .bundle()
+ .pipe(source('simpleSqlParser.withoutDeps.js'))
+ .pipe(gulp.dest('./dist'));
+});
+
+gulp.task('uglifyWithDeps', ['browserifyWithDeps'], function() {
+ return gulp.src('dist/simpleSqlParser.js')
+ .pipe(uglify())
+ .pipe(rename('simpleSqlParser.min.js'))
+ .pipe(gulp.dest('./dist'));
+});
+
+gulp.task('uglifyWithoutDeps', ['browserifyWithoutDeps'], function() {
+ return gulp.src('dist/simpleSqlParser.withoutDeps.js')
+ .pipe(uglify())
+ .pipe(rename('simpleSqlParser.withoutDeps.min.js'))
+ .pipe(gulp.dest('./dist'));
+});
+
+gulp.task('default', ['test', 'uglifyWithDeps', 'uglifyWithoutDeps']);
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..53b68c9
--- /dev/null
+++ b/index.js
@@ -0,0 +1,4 @@
+"use strict";
+
+module.exports.sql2ast = require('./src/sql2ast.js');
+module.exports.ast2sql = require('./src/ast2sql.js');
diff --git a/package.json b/package.json
index 635fab9..342f5d8 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "simple-sql-parser",
"description": "Javascript library to parse CRUD (Create Retrieve Update Delete) SQL queries.",
- "version": "1.3.0",
- "main": "simpleSqlParser.js",
+ "version": "2.0.0-alpha.1",
+ "main": "index.js",
"keywords": [
"sql",
"parse",
@@ -13,12 +13,6 @@
"email": "david.sferruzza@gmail.com",
"url": "https://github.com/dsferruzza"
},
- "contributors": [
- {
- "name": "RienNeVaPlus",
- "url": "https://github.com/RienNeVaPlus"
- }
- ],
"maintainers": [
{
"name": "David Sferruzza",
@@ -35,14 +29,21 @@
},
"license": "MIT",
"homepage": "https://github.com/dsferruzza/simpleSqlParser",
+ "dependencies": {
+ "parsimmon": "0.7.0"
+ },
"devDependencies": {
- "qunitjs": "*",
- "grunt": "*",
- "grunt-contrib-qunit": "*",
- "grunt-contrib-jshint": "*",
- "grunt-contrib-watch": "*"
+ "browserify": "10.2.3",
+ "browserify-shim": "3.8.7",
+ "chai": "2.3.0",
+ "gulp": "3.8.11",
+ "gulp-jshint": "1.11.0",
+ "gulp-mocha": "2.1.0",
+ "gulp-rename": "1.2.2",
+ "gulp-uglify": "1.2.0",
+ "vinyl-source-stream": "1.1.0"
},
- "scripts": {
- "test": "grunt"
+ "browserify-shim": {
+ "parsimmon": "global:parsimmon"
}
}
diff --git a/simpleSqlParser.js b/simpleSqlParser.js
deleted file mode 100644
index fe6db2a..0000000
--- a/simpleSqlParser.js
+++ /dev/null
@@ -1,768 +0,0 @@
-(function(exports) {
- "use strict";
-
- function trim(str) {
- if (typeof str == 'string') return str.replace(/^\s+/g,'').replace(/\s+$/g,'');
- else return str;
- }
-
- // Split a string using a separator, only if this separator isn't beetween brackets
- function protect_split(separator, str) {
- var sep = '######';
-
- var string = false;
- var nb_brackets = 0;
- var new_str = "";
- for (var i = 0 ; i < str.length ; i++) {
- if (!string && /['"`]/.test(str[i])) string = str[i];
- else if (string && str[i] == string) string = false;
- else if (!string && str[i] == '(') nb_brackets ++;
- else if (!string && str[i] == ')') nb_brackets --;
-
- if (str[i] == separator && (nb_brackets > 0 || string)) new_str += sep;
- else new_str += str[i];
- }
- str = new_str;
-
- str = str.split(separator);
- str = str.map(function (item) {
- return trim(item.replace(new RegExp(sep, 'g'), separator));
- });
-
- return str;
- }
-
- // Add some # inside a string to avoid it to match a regex/split
- function protect(str) {
- var result = '#';
- var length = str.length;
- for (var i = 0 ; i < length ; i++) result += str[i] + "#";
- return result;
- }
-
- // Restore a string output by protect() to its original state
- function unprotect(str) {
- var result = '';
- var length = str.length;
- for (var i = 1 ; i < length ; i = i + 2) result += str[i];
- return result;
- }
-
-
- // Parse a query
- // parseCond: (bool) parse conditions in WHERE and JOIN (default true)
- exports.sql2ast = function (query, parseCond) {
- if (typeof parseCond == 'undefined' || parseCond === null) parseCond = true;
-
- // Remove semi-colons and keep only the first query
- var semi_colon = '###semi-colon###';
- query = query.replace(/[("'`].*;.*[)"'`]/g, function (match) {
- return match.replace(/;/g, semi_colon);
- });
- var eor = '###EOR###';
- query = query.replace(/;/g, eor);
- query = query.split(eor)[0];
- query = query.replace(new RegExp(semi_colon, 'g'), ';');
-
- // Define which words can act as separator
- var keywords = ['SELECT', 'FROM', 'DELETE FROM', 'INSERT INTO', 'UPDATE', 'JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN', 'ORDER BY', 'GROUP BY', 'HAVING', 'WHERE', 'LIMIT', 'VALUES', 'SET'];
- var parts_name = keywords.map(function (item) {
- return item + ' ';
- });
- parts_name = parts_name.concat(keywords.map(function (item) {
- return item + '(';
- }));
- parts_name = parts_name.concat(parts_name.map(function (item) {
- return item.toLowerCase();
- }));
- var parts_name_escaped = parts_name.map(function (item) {
- return item.replace('(', '[\\(]');
- });
-
- // Hide words defined as separator but written inside brackets in the query
- query = query.replace(/\((.+?)\)|"(.+?)"|'(.+?)'|`(.+?)`/gi, function (match) {
- return match.replace(new RegExp(parts_name_escaped.join('|'), 'gi'), protect);
- });
-
- // Write the position(s) in query of these separators
- var parts_order = [];
- function realNameCallback(match, name) {
- return name;
- }
- parts_name.forEach(function (item) {
- var pos = 0;
- var part;
-
- do {
- part = query.indexOf(item, pos);
- if (part != -1) {
- var realName = item.replace(/^((\w|\s)+?)\s?\(?$/i, realNameCallback);
- parts_order[part] = realName; // Position won't be exact because the use of protect() (above) and unprotect() alter the query string ; but we just need the order :)
- pos = part + realName.length;
- }
- }
- while (part != -1);
- });
-
- // Delete duplicates (caused, for example, by JOIN and INNER JOIN)
- var busy_until = 0;
- parts_order.forEach(function (item, key) {
- if (busy_until > key) delete parts_order[key];
- else {
- busy_until = parseInt(key, 10) + item.length;
-
- // Replace JOIN by INNER JOIN
- if (item == 'JOIN') parts_order[key] = 'INNER JOIN';
- }
- });
-
- // Generate protected word list to reverse the use of protect()
- var words = parts_name_escaped.slice(0);
- words = words.map(function (item) {
- return protect(item);
- });
- words = words.join('|');
-
- // Split parts
- var parts = query.split(new RegExp(parts_name_escaped.join('|'), 'i'));
-
- // Unhide words precedently hidden with protect()
- query = query.replace(/\((.+?)\)|"(.+?)"|'(.+?)'|`(.+?)`/gi, function (match) {
- return match.replace(new RegExp(words, 'gi'), unprotect);
- });
- parts = parts.map(function (item) {
- return item.replace(/\((.+?)\)|"(.+?)"|'(.+?)'|`(.+?)`/gi, function (match) {
- return match.replace(new RegExp(words, 'gi'), unprotect);
- });
- });
-
- // Define analysis functions
- var analysis = [];
-
- analysis['SELECT'] = function (str) {
- var result = protect_split(',', str);
- result = result.filter(function(item) {
- return item !== '';
- }).map(function(item) {
- return {name: item};
- });
- return result;
- };
-
- analysis['SET'] = function (str) {
- var result = protect_split(',', str);
- result = result.filter(function(item) {
- return item !== '';
- }).map(function(item) {
- return {expression: item};
- });
- return result;
- };
-
- analysis['FROM'] = analysis['DELETE FROM'] = analysis['UPDATE'] = function (str) {
- var result = str.split(',');
- result = result.map(function(item) {
- return trim(item);
- });
- result.forEach(function(item, key) {
- if (item === '') result.splice(key);
- });
- result = result.map(function(item) {
- var table = item.split(' AS ');
- var alias = table[1] || '';
- if (alias.indexOf('"') === 0 && alias.lastIndexOf('"') == alias.length - 1) alias = alias.substring(1, alias.length - 1);
- return {table: table[0], as: alias};
- });
- return result;
- };
-
- analysis['LEFT JOIN'] = analysis['JOIN'] = analysis['INNER JOIN'] = analysis['RIGHT JOIN'] = function (str) {
- str = str.split(' ON ');
- var table = str[0].split(' AS ');
- var result = {};
- result['table'] = trim(table[0]);
- result['as'] = trim(table[1]) || '';
- result['cond'] = trim(str[1]);
-
- return result;
- };
-
- analysis['WHERE'] = function (str) {
- return trim(str);
- };
-
- analysis['ORDER BY'] = function (str) {
- str = str.split(',');
- var result = [];
- str.forEach(function (item, key) {
- var order_by = /([A-Za-z0-9_\.]+)\s*(ASC|DESC){0,1}/gi;
- order_by = order_by.exec(item);
- if (order_by !== null) {
- var tmp = {};
- tmp['column'] = trim(order_by[1]);
- tmp['order'] = trim(order_by[2]);
- if(order_by[2] === undefined ){
- tmp['order']="ASC";
- }
- result.push(tmp);
- }
- });
- return result;
- };
-
- analysis['GROUP BY'] = function (str) {
- str = str.split(',');
- var result = [];
- str.forEach(function (item, key) {
- var group_by = /([A-Za-z0-9_\.]+)/gi;
- group_by = group_by.exec(item);
- if (group_by !== null) {
- var tmp = {};
- tmp['column'] = trim(group_by[1]);
- result.push(tmp);
- }
- });
- return result;
- };
- analysis['LIMIT'] = function (str) {
- var limit = /((\d+)\s*,\s*)?(\d+)/gi;
- limit = limit.exec(str);
- if (typeof limit[2] == 'undefined') limit[2] = 0;
- var result = {};
- result['nb'] = parseInt(trim(limit[3]), 10);
- result['from'] = parseInt(trim(limit[2]), 10);
- return result;
- };
-
- analysis['INSERT INTO'] = function (str) {
- var insert = /([A-Za-z0-9_\.]+)\s*(\(([A-Za-z0-9_\., ]+)\))?/gi;
- insert = insert.exec(str);
- var result = {};
- result['table'] = trim(insert[1]);
- if (typeof insert[3] != 'undefined') {
- result['columns'] = insert[3].split(',');
- result['columns'] = result['columns'].map(function (item) {
- return trim(item);
- });
- }
- return result;
- };
-
- analysis['VALUES'] = function (str) {
- str = trim(str);
- if (str[0] != '(') str = '(' + str; // If query has "VALUES(...)" instead of "VALUES (...)"
- var groups = protect_split(',', str);
- var result = [];
- groups.forEach(function(group) {
- group = group.replace(/^\(/g,'').replace(/\)$/g,'');
- group = protect_split(',', group);
- result.push(group);
- });
- return result;
- };
-
- // TODO: handle HAVING
-
- // Analyze parts
- var result = {};
- var j = 0;
- parts_order.forEach(function (item, key) {
- item = item.toUpperCase();
- j++;
- if (typeof analysis[item] != 'undefined') {
- var part_result = analysis[item](parts[j]);
-
- if (typeof result[item] != 'undefined') {
- if (typeof result[item] == 'string' || typeof result[item][0] == 'undefined') {
- var tmp = result[item];
- result[item] = [];
- result[item].push(tmp);
- }
-
- result[item].push(part_result);
- }
- else result[item] = part_result;
- }
- else console.log('Can\'t analyze statement "' + item + '"');
- });
-
- // Reorganize joins
- if (typeof result['LEFT JOIN'] != 'undefined') {
- if (typeof result['JOIN'] == 'undefined') result['JOIN'] = [];
- if (typeof result['LEFT JOIN'][0] != 'undefined') {
- result['LEFT JOIN'].forEach(function (item) {
- item.type = 'left';
- result['JOIN'].push(item);
- });
- }
- else {
- result['LEFT JOIN'].type = 'left';
- result['JOIN'].push(result['LEFT JOIN']);
- }
- delete result['LEFT JOIN'];
- }
- if (typeof result['INNER JOIN'] != 'undefined') {
- if (typeof result['JOIN'] == 'undefined') result['JOIN'] = [];
- if (typeof result['INNER JOIN'][0] != 'undefined') {
- result['INNER JOIN'].forEach(function (item) {
- item.type = 'inner';
- result['JOIN'].push(item);
- });
- }
- else {
- result['INNER JOIN'].type = 'inner';
- result['JOIN'].push(result['INNER JOIN']);
- }
- delete result['INNER JOIN'];
- }
- if (typeof result['RIGHT JOIN'] != 'undefined') {
- if (typeof result['JOIN'] == 'undefined') result['JOIN'] = [];
- if (typeof result['RIGHT JOIN'][0] != 'undefined') {
- result['RIGHT JOIN'].forEach(function (item) {
- item.type = 'right';
- result['JOIN'].push(item);
- });
- }
- else {
- result['RIGHT JOIN'].type = 'right';
- result['JOIN'].push(result['RIGHT JOIN']);
- }
- delete result['RIGHT JOIN'];
- }
-
- // Parse conditions
- if (parseCond) {
- if (typeof result['WHERE'] == 'string') {
- result['WHERE'] = CondParser.parse(result['WHERE']);
- }
- if (typeof result['JOIN'] != 'undefined') {
- result['JOIN'].forEach(function (item, key) {
- result['JOIN'][key]['cond'] = CondParser.parse(item['cond']);
- });
- }
- }
-
- return result;
- };
-
-
- /*
- * LEXER & PARSER FOR SQL CONDITIONS
- * Inspired by https://github.com/DmitrySoshnikov/Essentials-of-interpretation
- */
-
- // Constructor
- function CondLexer(source) {
- this.source = source;
- this.cursor = 0;
- this.currentChar = "";
-
- this.readNextChar();
- }
-
- CondLexer.prototype = {
- constructor: CondLexer,
-
- // Read the next character (or return an empty string if cursor is at the end of the source)
- readNextChar: function () {
- if (typeof this.source != 'string') this.currentChar = "";
- else this.currentChar = this.source[this.cursor++] || "";
- },
-
- // Determine the next token
- readNextToken: function () {
- if (/\w/.test(this.currentChar)) return this.readWord();
- if (/["'`]/.test(this.currentChar)) return this.readString();
- if (/[()]/.test(this.currentChar)) return this.readGroupSymbol();
- if (/[!=<>]/.test(this.currentChar)) return this.readOperator();
-
- if (this.currentChar === "") return {type: 'eot', value: ''};
- else {
- this.readNextChar();
- return {type: 'empty', value: ''};
- }
- },
-
- readWord: function () {
- var tokenValue = "";
- var nb_brackets = 0;
- var string = false;
- while (/./.test(this.currentChar)) {
- // Check if we are in a string
- if (!string && /['"`]/.test(this.currentChar)) string = this.currentChar;
- else if (string && this.currentChar == string) string = false;
- else {
- // Allow spaces inside functions (only if we are not in a string)
- if (!string) {
- // Token is finished if there is a closing bracket outside a string and with no opening
- if (this.currentChar == ')' && nb_brackets <= 0) break;
-
- if (this.currentChar == '(') nb_brackets++;
- else if (this.currentChar == ')') nb_brackets--;
-
- // Token is finished if there is a operator symbol outside a string
- if (/[!=<>]/.test(this.currentChar)) break;
- }
-
- // Token is finished on the first space which is outside a string or a function
- if (this.currentChar == ' ' && nb_brackets <= 0) break;
- }
-
- tokenValue += this.currentChar;
- this.readNextChar();
- }
-
- if (/^(AND|OR)$/i.test(tokenValue)) return {type: 'logic', value: tokenValue};
- if (/^(IS|NOT)$/i.test(tokenValue)) return {type: 'operator', value: tokenValue};
- else return {type: 'word', value: tokenValue};
- },
-
- readString: function () {
- var tokenValue = "";
- var quote = this.currentChar;
-
- tokenValue += this.currentChar;
- this.readNextChar();
-
- while (this.currentChar != quote && this.currentChar !== "") {
- tokenValue += this.currentChar;
- this.readNextChar();
- }
-
- tokenValue += this.currentChar;
- this.readNextChar();
-
- // Handle this case : `table`.`column`
- if (this.currentChar == '.') {
- tokenValue += this.currentChar;
- this.readNextChar();
- tokenValue += this.readString().value;
-
- return {type: 'word', value: tokenValue};
- }
-
- return {type: 'string', value: tokenValue};
- },
-
- readGroupSymbol: function () {
- var tokenValue = this.currentChar;
- this.readNextChar();
-
- return {type: 'group', value: tokenValue};
- },
-
- readOperator: function () {
- var tokenValue = this.currentChar;
- this.readNextChar();
-
- if (/[=<>]/.test(this.currentChar)) {
- tokenValue += this.currentChar;
- this.readNextChar();
- }
-
- return {type: 'operator', value: tokenValue};
- },
- };
-
- // Tokenise a string (only useful for debug)
- CondLexer.tokenize = function (source) {
- var lexer = new CondLexer(source);
- var tokens = [];
- do {
- var token = lexer.readNextToken();
- if (token.type != 'empty') tokens.push(token);
- }
- while (lexer.currentChar);
- return tokens;
- };
-
-
- // Constructor
- function CondParser(source) {
- this.lexer = new CondLexer(source);
- this.currentToken = "";
-
- this.readNextToken();
- }
-
- CondParser.prototype = {
- constructor: CondParser,
-
- // Read the next token (skip empty tokens)
- readNextToken: function () {
- this.currentToken = this.lexer.readNextToken();
- while (this.currentToken.type == 'empty') this.currentToken = this.lexer.readNextToken();
- return this.currentToken;
- },
-
- // Wrapper function ; parse the source
- parseExpressionsRecursively: function () {
- return this.parseLogicalExpression();
- },
-
- // Parse logical expressions (AND/OR)
- parseLogicalExpression: function () {
- var leftNode = this.parseConditionExpression();
-
- while (this.currentToken.type == 'logic') {
- var logic = this.currentToken.value;
- this.readNextToken();
-
- var rightNode = this.parseConditionExpression();
-
- // If we are chaining the same logical operator, add nodes to existing object instead of creating another one
- if (typeof leftNode.logic != 'undefined' && leftNode.logic == logic && typeof leftNode.terms != 'undefined') leftNode.terms.push(rightNode);
- else {
- var terms = [leftNode, rightNode];
- leftNode = {'logic': logic, 'terms': terms.slice(0)};
- }
- }
-
- return leftNode;
- },
-
- // Parse conditions ([word/string] [operator] [word/string])
- parseConditionExpression: function () {
- var leftNode = this.parseBaseExpression();
-
- if (this.currentToken.type == 'operator') {
- var operator = this.currentToken.value;
- this.readNextToken();
-
- // If there are 2 adjacent operators, join them with a space (exemple: IS NOT)
- if (this.currentToken.type == 'operator') {
- operator += ' ' + this.currentToken.value;
- this.readNextToken();
- }
-
- var rightNode = this.parseBaseExpression();
-
- leftNode = {'operator': operator, 'left': leftNode, 'right': rightNode};
- }
-
- return leftNode;
- },
-
- // Parse base items
- parseBaseExpression: function () {
- var astNode = "";
-
- // If this is a word/string, return its value
- if (this.currentToken.type == 'word' || this.currentToken.type == 'string') {
- astNode = this.currentToken.value;
- this.readNextToken();
- }
- // If this is a group, skip brackets and parse the inside
- else if (this.currentToken.type == 'group') {
- this.readNextToken();
- astNode = this.parseExpressionsRecursively();
- this.readNextToken();
- }
-
- return astNode;
- },
- };
-
- // Parse a string
- CondParser.parse = function (source) {
- return new CondParser(source).parseExpressionsRecursively();
- };
-
- // Generate the SQL query corresponding to an AST output by sql2ast()
- exports.ast2sql = function (ast) {
- var result = '';
-
- // Define subfunctions
- function select(ast) {
- if (typeof ast['SELECT'] != 'undefined') {
- return 'SELECT ' + ast['SELECT'].map(function(item) {
- return item.name;
- }).join(', ');
- }
- else return '';
- }
-
- function from(ast) {
- if (typeof ast['FROM'] != 'undefined') {
- var result = ' FROM ';
- var tmp = ast['FROM'].map(function (item) {
- var str = item.table;
- if (item.as !== '') str += ' AS ' + item.as;
- return str;
- });
- result += tmp.join(', ');
- return result;
- }
- else return '';
- }
-
- function join(ast) {
- if (typeof ast['JOIN'] != 'undefined') {
- var result = '';
- ast['JOIN'].forEach(function(item) {
- result += ' ' + item.type.toUpperCase() + ' JOIN ' + item.table;
- if (item.as !== '') result += ' AS ' + item.as;
- result += ' ON ' + cond2sql(item.cond);
- });
- return result;
- }
- else return '';
- }
-
- function where(ast) {
- if (typeof ast['WHERE'] != 'undefined') {
- return ' WHERE ' + cond2sql(ast['WHERE']);
- }
- else return '';
- }
-
- function order_by(ast) {
- if (typeof ast['ORDER BY'] != 'undefined') {
- var result = ' ORDER BY ';
- var orders = ast['ORDER BY'].map(function (item) {
- return item.column + ' ' + item.order;
- });
- result += orders.join(', ');
- return result;
- }
- else return '';
- }
-
- function group_by(ast) {
- if (typeof ast['GROUP BY'] != 'undefined') {
- var result = ' GROUP BY ';
- var groups = ast['GROUP BY'].map(function (item) {
- return item.column;
- });
- result += groups.join(', ');
- return result;
- }
- else return '';
- }
-
- function limit(ast) {
- if (typeof ast['LIMIT'] != 'undefined' && typeof ast['LIMIT'].nb != 'undefined' && parseInt(ast['LIMIT'].nb, 10) > 0) {
- var result = ' LIMIT ';
- if (typeof ast['LIMIT'].from != 'undefined' && parseInt(ast['LIMIT'].from, 10) > 1) result += ast['LIMIT'].from + ',';
- result += ast['LIMIT'].nb;
- return result;
- }
- else return '';
- }
-
- function insert_into(ast) {
- if (typeof ast['INSERT INTO'] != 'undefined') {
- var result = 'INSERT INTO ' + ast['INSERT INTO'].table;
- if (typeof ast['INSERT INTO'].columns != 'undefined') {
- result += ' (';
- result += ast['INSERT INTO'].columns.join(', ');
- result += ')';
- }
- return result;
- }
- else return '';
- }
-
- function values(ast) {
- if (typeof ast['VALUES'] != 'undefined') {
- var result = ' VALUES ';
- var vals = ast['VALUES'].map(function (item) {
- return '(' + item.join(', ') + ')';
- });
- result += vals.join(', ');
- return result;
- }
- else return '';
- }
-
- function delete_from(ast) {
- if (typeof ast['DELETE FROM'] != 'undefined') {
- var result = 'DELETE FROM ';
- result += ast['DELETE FROM'].map(function (item) {
- var str = item.table;
- if (item.as !== '') str += ' AS ' + item.as;
- return str;
- }).join(', ');
- return result;
- }
- else return '';
- }
-
- function update(ast) {
- if (typeof ast['UPDATE'] != 'undefined') {
- var result = 'UPDATE ';
- result += ast['UPDATE'].map(function (item) {
- var str = item.table;
- if (item.as !== '') str += ' AS ' + item.as;
- return str;
- }).join(', ');
- return result;
- }
- else return '';
- }
-
- function set(ast) {
- if (typeof ast['SET'] != 'undefined') {
- return ' SET ' + ast['SET'].map(function(item) {
- return item.expression;
- }).join(', ');
- }
- else return '';
- }
-
-
- // Check request's type
- if (typeof ast['SELECT'] != 'undefined' && typeof ast['FROM'] != 'undefined') {
- result = select(ast) + from(ast) + join(ast) + where(ast) + group_by(ast) + order_by(ast) + limit(ast);
- }
- else if (typeof ast['INSERT INTO'] != 'undefined') {
- result = insert_into(ast) + values(ast);
- }
- else if (typeof ast['UPDATE'] != 'undefined') {
- result = update(ast) + set(ast) + where(ast);
- }
- else if (typeof ast['DELETE FROM'] != 'undefined') {
- result = delete_from(ast) + where(ast);
- }
- else result = null;
-
- return result;
- };
-
- // Generate SQL from a condition AST output by sql2ast() or CondParser
- function cond2sql(cond, not_first) {
- var result = '';
-
- // If there is a logical operation
- if (typeof cond.logic != 'undefined') {
- result = cond.terms.map(function (item) {
- return cond2sql(item, true);
- });
- result = result.join(' ' + cond.logic + ' ');
- if (typeof not_first !== 'undefined') result = '(' + result + ')';
- }
- // If there is a condition
- else if (typeof cond.left != 'undefined') {
- result = cond.left;
- if (typeof cond.operator != 'undefined') {
- result += ' ' + cond.operator;
- if (typeof cond.right != 'undefined') {
- result += ' ' + cond.right;
- }
- }
- }
- // If there is a boolean
- else result = cond;
-
- return result;
- }
-
- // Export some methods for tests
- exports.trim = trim;
- exports.protect = protect;
- exports.unprotect = unprotect;
- exports.protect_split = protect_split;
- exports.CondLexer = CondLexer;
- exports.CondParser = CondParser;
-
-}(typeof exports === "undefined" ? (this.simpleSqlParser = {}) : exports));
diff --git a/src/ast2sql.js b/src/ast2sql.js
new file mode 100644
index 0000000..d25d2e1
--- /dev/null
+++ b/src/ast2sql.js
@@ -0,0 +1,141 @@
+"use strict";
+
+module.exports = function(ast) {
+ if (typeof ast === 'object' && ast.status === true) ast = ast.value;
+ else return false;
+
+ function select(ast) {
+ var result = 'SELECT ';
+ result += ast.select.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ return result;
+ }
+
+ function from(ast) {
+ var result = 'FROM ';
+ result += ast.from.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ return result;
+ }
+
+ function join(ast) {
+ return ast.join.map(function(item) {
+ var result = '';
+ if (item.type === 'inner') result += 'INNER JOIN ';
+ else if (item.type === 'left') result += 'LEFT JOIN ';
+ else if (item.type === 'right') result += 'RIGHT JOIN ';
+ else return '';
+ result += item.table;
+ if (item.alias !== null) result += ' AS ' + item.alias;
+ result += ' ON ';
+ result += item.condition.expression;
+ return result;
+ }).join(' ');
+ }
+
+ function where(ast) {
+ var result = '';
+ if (ast.where !== null) result += 'WHERE ' + ast.where.expression;
+ return result;
+ }
+
+ function group(ast) {
+ var result = '';
+ if (ast.group.length > 0) {
+ result += 'GROUP BY ';
+ result += ast.group.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ }
+ return result;
+ }
+
+ function order(ast) {
+ var result = '';
+ if (ast.order.length > 0) {
+ result += 'ORDER BY ';
+ result += ast.order.map(function(item) {
+ return item.expression;
+ }).join(', ');
+ }
+ return result;
+ }
+
+ function limit(ast) {
+ var result = '';
+ if (ast.limit !== null) {
+ result += 'LIMIT ';
+ if (ast.limit.from !== null) result += ast.limit.from + ', ';
+ result += ast.limit.nb;
+ }
+ return result;
+ }
+
+ function into(ast) {
+ return 'INSERT INTO ' + ast.into.expression;
+ }
+
+ function values(ast) {
+ var result = '';
+ var targets = ast.values.filter(function(item) {
+ return item.target !== null;
+ });
+ if (targets.length > 0) {
+ result += '(';
+ result += targets.map(function(item) {
+ return item.target.expression;
+ }).join(', ');
+ result += ') ';
+ }
+ result += 'VALUES (';
+ result += ast.values.map(function(item) {
+ return item.value;
+ }).join(', ');
+ result += ')';
+ return result;
+ }
+
+ function table(ast) {
+ return 'UPDATE ' + ast.table.expression;
+ }
+
+ function update(ast) {
+ var result = 'SET ';
+ result += ast.values.map(function(item) {
+ return item.target.expression + ' = ' + item.value;
+ }).join(', ');
+ return result;
+ }
+
+ var parts = [];
+ if (ast.type === 'select') {
+ parts.push(select(ast));
+ parts.push(from(ast));
+ parts.push(join(ast));
+ parts.push(where(ast));
+ parts.push(group(ast));
+ parts.push(order(ast));
+ parts.push(limit(ast));
+ }
+ else if (ast.type === 'insert') {
+ parts.push(into(ast));
+ parts.push(values(ast));
+ }
+ else if (ast.type === 'update') {
+ parts.push(table(ast));
+ parts.push(update(ast));
+ parts.push(where(ast));
+ }
+ else if (ast.type === 'delete') {
+ parts.push('DELETE');
+ parts.push(from(ast));
+ parts.push(where(ast));
+ }
+ else return false;
+
+ return parts.filter(function(item) {
+ return item !== '';
+ }).join(' ');
+};
diff --git a/src/sql2ast.js b/src/sql2ast.js
new file mode 100644
index 0000000..4f4f246
--- /dev/null
+++ b/src/sql2ast.js
@@ -0,0 +1,550 @@
+"use strict";
+var Parsimmon = require('parsimmon');
+
+/********************************************************************************************
+ ALIASES
+********************************************************************************************/
+
+var seq = Parsimmon.seq;
+var alt = Parsimmon.alt;
+var regex = Parsimmon.regex;
+var string = Parsimmon.string;
+var optWhitespace = Parsimmon.optWhitespace;
+var whitespace = Parsimmon.whitespace;
+var lazy = Parsimmon.lazy;
+
+
+
+/********************************************************************************************
+ COMMON PATTERNS
+********************************************************************************************/
+
+// Make a parser optionnal
+// "empty" parameter will be returned as result if the optionnal parser can't match
+function opt(parser, empty) {
+ if (typeof empty == 'undefined') empty = [];
+ return parser.or(Parsimmon.succeed(empty));
+}
+
+// Join results of a parser
+function mkString(node) {
+ return node.join('');
+}
+
+// Add an item to an optionnal list and return the final list
+function mergeOptionnalList(node) {
+ node[0].push(node[1]);
+ return node[0];
+}
+
+// Generate a parser that accept a comma-separated list of something
+function optionnalList(parser) {
+ return seq(
+ parser.skip(optWhitespace).skip(string(',')).skip(optWhitespace).many(),
+ parser.skip(optWhitespace)
+ ).map(mergeOptionnalList);
+}
+
+// Remove first and last character of a string
+function removeQuotes(string) {
+ return string.replace(/^([`'"])(.*)\1$/, '$2');
+}
+
+// Add the starting and ending char positions of matches of a given parser
+function getPos(parser) {
+ return seq(
+ Parsimmon.index,
+ parser,
+ Parsimmon.index
+ ).map(function(node) {
+ var pos = {
+ start: node[0],
+ end: node[2],
+ };
+ if (typeof node[1] == 'object') {
+ var n = node[1];
+ n.position = pos;
+ return n;
+ }
+ else {
+ pos.out = node[1];
+ return pos;
+ }
+ });
+}
+
+
+
+/********************************************************************************************
+ LOW LEVEL PARSERS
+********************************************************************************************/
+
+// The name of a column/table
+var colName = alt(
+ regex(/(?!(FROM|WHERE|GROUP BY|ORDER BY|LIMIT|INNER|LEFT|RIGHT|JOIN|ON|VALUES|SET)\s)[a-z*][a-z0-9_]*/i),
+ regex(/`[^`\\]*(?:\\.[^`\\]*)*`/)
+);
+
+// A string
+var str = alt(
+ regex(/"[^"\\]*(?:\\.[^"\\]*)*"/),
+ regex(/'[^'\\]*(?:\\.[^'\\]*)*'/)
+);
+
+// A function expression
+var func = seq(
+ alt(
+ regex(/[a-zA-Z0-9_]+\(/),
+ string('(')
+ ),
+ opt(lazy(function() { return argList; })).map(mkString),
+ string(')')
+).map(mkString);
+
+// A table.column expression
+var tableAndColumn = seq(
+ colName,
+ string('.'),
+ colName
+);
+
+// An operator
+var operator = alt(
+ string('+'),
+ string('-'),
+ string('*'),
+ string('/'),
+ string('&&'),
+ string('&'),
+ string('~'),
+ string('||'),
+ string('|'),
+ string('^'),
+ regex(/XOR/i),
+ string('<=>'),
+ string('='),
+ string('!='),
+ string('>='),
+ string('>>'),
+ string('>'),
+ string('<='),
+ string('<<'),
+ string('<'),
+ regex(/IS NULL/i),
+ regex(/IS NOT/i),
+ regex(/IS NOT NULL/i),
+ regex(/IS/i),
+ regex(/LIKE/i),
+ regex(/NOT LIKE/i),
+ regex(/BETWEEN/i),
+ regex(/NOT BETWEEN/i),
+ string('%'),
+ regex(/MOD/i),
+ regex(/NOT/i),
+ regex(/OR\s/i), // A space is forced after so this doesn't get mixed up with ORDER BY
+ regex(/AND/i),
+ regex(/IN/i)
+);
+
+// A number
+var number = regex(/[-]?\d+\.?\d*/);
+
+
+
+/********************************************************************************************
+ EXPRESSION PARSERS
+********************************************************************************************/
+
+// List (following IN, for example)
+var list = seq(
+ string('('),
+ optWhitespace,
+ seq(
+ alt(
+ number,
+ str
+ ),
+ optWhitespace,
+ opt(string(',')),
+ optWhitespace,
+ opt(
+ alt(
+ number,
+ str
+ )
+ )
+ ).map(mkString),
+ optWhitespace,
+ string(')')
+).map(mkString);
+
+// Expression
+var expression = seq(
+ alt(
+ tableAndColumn.map(function(node) {
+ return {
+ expression: node.join(''),
+ table: removeQuotes(node[0]),
+ column: removeQuotes(node[2])
+ };
+ }),
+ func.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ colName.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: removeQuotes(node)
+ };
+ }),
+ str.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ number.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ }),
+ list.map(function(node) {
+ return {
+ expression: node,
+ table: null,
+ column: null
+ };
+ })
+ ),
+ opt(seq(
+ optWhitespace,
+ operator,
+ opt(seq(
+ optWhitespace,
+ lazy(function() { return expression; }).map(function(node) {
+ return node.expression;
+ })
+ ).map(mkString), null)
+ ).map(mkString), null)
+).map(function(node) {
+ if (node[1] !== null) {
+ node[0] = node[0].expression;
+ return {
+ expression: node.join(''),
+ table: null,
+ column: null
+ };
+ }
+ else return node[0];
+});
+
+// Expression following a SELECT statement
+var colListExpression = seq(
+ expression,
+ opt( // Alias
+ seq(
+ optWhitespace,
+ opt(regex(/AS\s/i)),
+ alt(colName, str)
+ ).map(function(node) {
+ var n = {};
+ n.alias = removeQuotes(node[2]);
+ n.expression = node.join('');
+ return n;
+ }),
+ null
+ )
+).map(function(node) {
+ var n = node[0];
+ n.alias = (node[1] !== null) ? node[1].alias : null;
+ n.expression = n.expression + ((node[1] !== null) ? node[1].expression : '');
+ return n;
+});
+
+// Expression inside a function
+var argListExpression = expression.map(function(node) {
+ return node.expression;
+});
+
+// Expression following a FROM statement
+var tableListExpression = seq(
+ alt(
+ tableAndColumn.map(mkString),
+ colName
+ ),
+ opt( // Alias
+ seq(
+ optWhitespace,
+ opt(regex(/AS\s/i)),
+ alt(colName, str)
+ ).map(function(node) {
+ return {
+ alias: removeQuotes(node[2]),
+ expression : node.join(''),
+ };
+ }),
+ null
+ )
+).map(function(node) {
+ var n = {};
+ n.table = node[0];
+ n.alias = (node[1] !== null) ? node[1].alias : null;
+ n.expression = node[0] + ((node[1] !== null) ? node[1].expression : '');
+ return n;
+});
+
+// JOIN expression (including JOIN statements)
+var joinExpression = seq(
+ opt(seq(
+ regex(/INNER|LEFT|RIGHT/i),
+ whitespace
+ ).map(function(node) {
+ return node[0].toLowerCase();
+ }), null),
+ regex(/JOIN/i),
+ optWhitespace,
+ getPos(tableListExpression),
+ optWhitespace,
+ regex(/ON/i),
+ optWhitespace,
+ getPos(expression)
+).map(function(node) {
+ var n = {};
+ n.type = node[0] || 'inner';
+ n.table = node[3].table;
+ n.alias = node[3].alias;
+ n.position = node[3].position;
+ n.condition = {
+ expression: node[7].expression,
+ position: node[7].position,
+ };
+ return n;
+});
+
+// Expression following a WHERE statement
+var whereExpression = getPos(expression).map(function(node) {
+ return {
+ expression: node.expression,
+ position: node.position,
+ };
+});
+
+// Expression following an ORDER BY statement
+var orderListExpression = seq(
+ expression,
+ opt(seq(
+ optWhitespace,
+ regex(/ASC|DESC/i)
+ ), null)
+).map(function(node) {
+ return {
+ expression: node[0].expression + ((node[1] !== null) ? node[1].join('') : ''),
+ order: (node[1] !== null) ? node[1][1] : 'ASC',
+ table: node[0].table,
+ column: node[0].column,
+ };
+});
+
+// Expression following a LIMIT statement
+var limitExpression = seq(
+ number,
+ opt(seq(
+ optWhitespace,
+ string(','),
+ optWhitespace,
+ number
+ ), null)
+).map(function(node) {
+ if (node[1] === null) return {
+ from: null,
+ nb: parseInt(node[0], 10),
+ };
+ else return {
+ from: parseInt(node[0], 10),
+ nb: parseInt(node[1][3], 10),
+ };
+});
+
+// Expression designating a column before VALUES in INSERT query
+var insertColListExpression = alt(
+ tableAndColumn.map(function(node) {
+ return {
+ expression: node.join(''),
+ column: removeQuotes(node[2])
+ };
+ }),
+ colName.map(function(node) {
+ return {
+ expression: node,
+ column: removeQuotes(node)
+ };
+ })
+);
+
+// Expression following a VALUES statement
+var valueExpression = expression.map(function(node) {
+ return node.expression;
+});
+
+// Expression that assign a value to a column
+var assignExpression = seq(
+ insertColListExpression,
+ optWhitespace,
+ string('='),
+ optWhitespace,
+ expression
+).map(function(node) {
+ return {
+ target: node[0],
+ value: node[4].expression,
+ };
+});
+
+
+
+/********************************************************************************************
+ HIGH LEVEL PARSERS
+********************************************************************************************/
+
+// List of arguments inside a function
+var argList = seq(
+ seq(argListExpression, optWhitespace, string(','), optWhitespace).map(mkString).many(),
+ argListExpression.skip(optWhitespace)
+).map(mergeOptionnalList);
+
+// List of expressions following a SELECT statement
+var colList = optionnalList(getPos(colListExpression));
+
+// List of table following a FROM statement
+var tableList = optionnalList(getPos(tableListExpression));
+
+// List of table following an GROUP BY statement
+var groupList = optionnalList(getPos(expression));
+
+// List of table following an ORDER BY statement
+var orderList = optionnalList(getPos(orderListExpression));
+
+// List of joins (including JOIN statements)
+var joinList = optWhitespace.then(joinExpression).skip(optWhitespace).many();
+
+// List of columns before VALUES in INSERT query
+var insertColList = optionnalList(insertColListExpression);
+
+// List of values following a VALUES statement
+var valuesList = optionnalList(valueExpression);
+
+// List of assign expression following a SET statement
+var assignList = optionnalList(assignExpression);
+
+
+
+/********************************************************************************************
+ MAIN PARSERS
+********************************************************************************************/
+
+// SELECT parser
+var selectParser = seq(
+ regex(/SELECT/i).skip(optWhitespace).then(opt(colList)),
+ regex(/FROM/i).skip(optWhitespace).then(opt(tableList)),
+ opt(joinList),
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null),
+ opt(regex(/\s?GROUP BY/i).skip(optWhitespace).then(opt(groupList))),
+ opt(regex(/\s?ORDER BY/i).skip(optWhitespace).then(opt(orderList))),
+ opt(regex(/\s?LIMIT/i).skip(optWhitespace).then(opt(limitExpression)), null)
+).map(function(node) {
+ return {
+ type: 'select',
+ select: node[0],
+ from: node[1],
+ join: node[2],
+ where: node[3],
+ group: node[4],
+ order: node[5],
+ limit: node[6],
+ };
+});
+
+// INSERT parser
+var insertParser = seq(
+ regex(/INSERT INTO/i).skip(optWhitespace).then(tableListExpression),
+ optWhitespace,
+ opt(
+ seq(
+ string('('),
+ insertColList,
+ string(')')
+ ).map(function(node) {
+ return node[1];
+ })
+ ),
+ optWhitespace,
+ regex(/VALUES\s?\(/i).skip(optWhitespace).then(valuesList),
+ string(')')
+).map(function(node) {
+ var values = [];
+ var bigger = Math.max(node[2].length, node[4].length);
+
+ for (var i = 0; i < bigger; ++i) {
+ values[i] = {
+ target: node[2][i] || null,
+ value: node[4][i] || null,
+ };
+ }
+
+ return {
+ type: 'insert',
+ into: node[0],
+ values: values,
+ };
+});
+
+// UPDATE parser
+var updateParser = seq(
+ regex(/UPDATE/i).skip(optWhitespace).then(tableListExpression),
+ optWhitespace,
+ regex(/SET/i).skip(optWhitespace).then(assignList),
+ optWhitespace,
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null)
+).map(function(node) {
+ return {
+ type: 'update',
+ table: node[0],
+ values: node[2],
+ where: node[4],
+ };
+});
+
+// DELETE parser
+var deleteParser = seq(
+ regex(/DELETE FROM/i).skip(optWhitespace).then(opt(tableList)),
+ opt(regex(/WHERE/i).skip(optWhitespace).then(opt(whereExpression)), null)
+).map(function(node) {
+ return {
+ type: 'delete',
+ from: node[0],
+ where: node[1],
+ };
+});
+
+// Main parser
+var p = alt(selectParser, insertParser, updateParser, deleteParser);
+
+
+
+/********************************************************************************************
+ PUBLIC FUNCTIONS
+********************************************************************************************/
+
+module.exports = function(sql) {
+ var result = p.parse(sql);
+ if (result.status === false) result.error = Parsimmon.formatError(sql, result);
+ return result;
+};
diff --git a/tests/helpers.js b/tests/helpers.js
new file mode 100644
index 0000000..1ac276c
--- /dev/null
+++ b/tests/helpers.js
@@ -0,0 +1,35 @@
+"use strict";
+
+var expect = require('chai').expect;
+var m = require('../index');
+
+function ok(ast) {
+ return {
+ status: true,
+ value: ast
+ };
+}
+
+function testAst(comment, query, ast) {
+ expect(m.sql2ast(query)).to.eql(ok(ast), comment + ': ' + query);
+}
+
+function testBackAndForth(comment, query) {
+ expect(m.ast2sql(m.sql2ast(query))).to.eql(query, comment + ': ' + query);
+}
+
+function isArray(variable, message) {
+ expect(variable).to.be.instanceOf(Array, message);
+}
+
+function isObject(variable, message) {
+ expect(variable).not.to.be.instanceOf(Array, message);
+}
+
+module.exports = {
+ ok: ok,
+ testAst: testAst,
+ testBackAndForth: testBackAndForth,
+ isArray: isArray,
+ isObject: isObject,
+};
diff --git a/tests/tests-api.js b/tests/tests-api.js
new file mode 100644
index 0000000..9df42c3
--- /dev/null
+++ b/tests/tests-api.js
@@ -0,0 +1,56 @@
+/*global test:true,suite:true*/
+"use strict";
+
+var expect = require('chai').expect;
+var m = require('../index');
+var h = require('./helpers.js');
+
+suite('sql2ast - API');
+
+var q = [
+ 'SELECT * FROM table',
+ 'INSERT INTO table VALUES (1, 2, 3)',
+ 'UPDATE table SET col = "value"',
+ 'DELETE FROM table',
+];
+var ast = q.map(m.sql2ast);
+
+var types = ['select', 'delete', 'insert', 'update'];
+
+ast.forEach(function(a) {
+ test('A basic query should parse', function() {
+ expect(a.status).to.equal(true, 'Parser must parse valid SQL');
+ expect(types.indexOf(a.value.type)).not.to.equal(-1, 'AST must contain a valid type');
+ });
+
+ if (a.value.type === types[0]) {
+ test('SELECT AST is cool', function() {
+ h.isArray(a.value.select, "(SELECT) AST must contain a 'select' array");
+ h.isArray(a.value.from, "(SELECT) AST must contain a 'from' array");
+ h.isArray(a.value.join, "(SELECT) AST must contain a 'join' array");
+ h.isObject(a.value.where, "(SELECT) AST must contain a 'where' object");
+ h.isArray(a.value.group, "(SELECT) AST must contain a 'group' array");
+ h.isArray(a.value.order, "(SELECT) AST must contain a 'order' array");
+ h.isObject(a.value.limit, "(SELECT) AST must contain a 'limit' object");
+ });
+ }
+ else if (a.value.type === types[1]) {
+ test('DELETE AST is cool', function() {
+ h.isArray(a.value.from, "(DELETE) AST must contain a 'from' array");
+ h.isObject(a.value.where, "(DELETE) AST must contain a 'where' object");
+ });
+ }
+ else if (a.value.type === types[2]) {
+ test('INSERT AST is cool', function() {
+ h.isObject(a.value.into, "(INSERT) AST must contain a 'into' object");
+ h.isArray(a.value.values, "(INSERT) AST must contain a 'values' array");
+ });
+ }
+ else if (a.value.type === types[3]) {
+ test('UPDATE AST is cool', function() {
+ h.isObject(a.value.table, "(UPDATE) AST must contain a 'table' object");
+ h.isArray(a.value.values, "(UPDATE) AST must contain a 'values' array");
+ h.isObject(a.value.where, "(UPDATE) AST must contain a 'where' object");
+ });
+ }
+});
diff --git a/tests/tests-delete.js b/tests/tests-delete.js
new file mode 100644
index 0000000..73f8f64
--- /dev/null
+++ b/tests/tests-delete.js
@@ -0,0 +1,75 @@
+/*global test:true,suite:true*/
+"use strict";
+
+var expect = require('chai').expect;
+var h = require('./helpers.js');
+
+var Delete = [
+ {
+ c: 'Simple delete',
+ q: 'DELETE FROM table',
+ a: {
+ type: 'delete',
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 12, end: 17 } },
+ ],
+ where: null,
+ },
+ },
+ {
+ c: 'Several tables with aliases',
+ q: 'DELETE FROM table1 AS t1, table2 "t2"',
+ a: {
+ type: 'delete',
+ from: [
+ { expression: 'table1 AS t1', table: 'table1', alias: 't1', position: { start: 12, end: 24 } },
+ { expression: 'table2 "t2"', table: 'table2', alias: 't2', position: { start: 26, end: 37 } },
+ ],
+ where: null,
+ },
+ },
+ {
+ c: 'Where #1',
+ q: 'DELETE FROM table WHERE this >= that AND col IS NOT NULL',
+ a: {
+ type: 'delete',
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 12, end: 17 } },
+ ],
+ where: {
+ expression: "this >= that AND col IS NOT NULL",
+ position: { start: 24, end: 56 },
+ },
+ },
+ },
+ {
+ c: 'Where #2',
+ q: 'DELETE FROM table WHERE (FUNC(this) = "string") AND (1+5 OR col1)',
+ a: {
+ type: 'delete',
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 12, end: 17 } },
+ ],
+ where: {
+ expression: "(FUNC(this) = \"string\") AND (1+5 OR col1)",
+ position: { start: 24, end: 65 },
+ },
+ },
+ },
+];
+
+suite('sql2ast - delete');
+
+Delete.forEach(function(item) {
+ test(item.c, function() {
+ h.testAst(item.c, item.q, item.a);
+ });
+});
+
+suite('ast2sql - delete');
+
+Delete.forEach(function(item) {
+ test(item.c, function() {
+ h.testBackAndForth(item.c, item.q);
+ });
+});
diff --git a/tests/tests-insert.js b/tests/tests-insert.js
new file mode 100644
index 0000000..74a7514
--- /dev/null
+++ b/tests/tests-insert.js
@@ -0,0 +1,75 @@
+/*global test:true,suite:true*/
+"use strict";
+
+var expect = require('chai').expect;
+var h = require('./helpers.js');
+
+var Insert = [
+ {
+ c: 'Simple insert',
+ q: 'INSERT INTO table VALUES (1, 2, 3)',
+ a: {
+ type: 'insert',
+ into: {
+ expression: 'table',
+ table: 'table',
+ alias: null,
+ },
+ values: [
+ { target: null, value: '1'},
+ { target: null, value: '2'},
+ { target: null, value: '3'},
+ ],
+ },
+ },
+ {
+ c: 'Complex values',
+ q: 'INSERT INTO table VALUES (1 + 9, FUNC(2, col), "string")',
+ a: {
+ type: 'insert',
+ into: {
+ expression: 'table',
+ table: 'table',
+ alias: null,
+ },
+ values: [
+ { target: null, value: '1 + 9'},
+ { target: null, value: 'FUNC(2, col)'},
+ { target: null, value: '"string"'},
+ ],
+ },
+ },
+ {
+ c: 'Insert with columns',
+ q: 'INSERT INTO table (col1, `col2`, col3) VALUES (1, 2, 3)',
+ a: {
+ type: 'insert',
+ into: {
+ expression: 'table',
+ table: 'table',
+ alias: null,
+ },
+ values: [
+ { target: { expression: 'col1', column: 'col1' }, value: '1'},
+ { target: { expression: '`col2`', column: 'col2' }, value: '2'},
+ { target: { expression: 'col3', column: 'col3' }, value: '3'},
+ ],
+ },
+ },
+];
+
+suite('sql2ast - insert');
+
+Insert.forEach(function(item) {
+ test(item.c, function() {
+ h.testAst(item.c, item.q, item.a);
+ });
+});
+
+suite('ast2sql - insert');
+
+Insert.forEach(function(item) {
+ test(item.c, function() {
+ h.testBackAndForth(item.c, item.q);
+ });
+});
diff --git a/tests/tests-select.js b/tests/tests-select.js
new file mode 100644
index 0000000..a00242d
--- /dev/null
+++ b/tests/tests-select.js
@@ -0,0 +1,480 @@
+/*global test:true,suite:true*/
+"use strict";
+
+var expect = require('chai').expect;
+var h = require('./helpers.js');
+
+var Select = [
+ {
+ c: 'Simple select',
+ q: 'SELECT * FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Column quotes',
+ q: 'SELECT col1, `col2` FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: 'col1', column: 'col1', table: null, alias: null, position: { start: 7, end: 11 } },
+ { expression: '`col2`', column: 'col2', table: null, alias: null, position: { start: 13, end: 19 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 25, end: 30 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Special words',
+ q: 'SELECT fromage "from", asymetric AS as FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: 'fromage "from"', column: 'fromage', table: null, alias: 'from', position: { start: 7, end: 21 } },
+ { expression: 'asymetric AS as', column: 'asymetric', table: null, alias: 'as', position: { start: 23, end: 38 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 44, end: 49 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: '"table.column" notation',
+ q: 'SELECT table.col1, table.`col2`, `table`.col3, `table`.`col4` FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: 'table.col1', column: 'col1', table: 'table', alias: null, position: { start: 7, end: 17 } },
+ { expression: 'table.`col2`', column: 'col2', table: 'table', alias: null, position: { start: 19, end: 31 } },
+ { expression: '`table`.col3', column: 'col3', table: 'table', alias: null, position: { start: 33, end: 45 } },
+ { expression: '`table`.`col4`', column: 'col4', table: 'table', alias: null, position: { start: 47, end: 61 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 67, end: 72 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Strings',
+ q: 'SELECT "string", "\\"special\\" string" FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '"string"', column: null, table: null, alias: null, position: { start: 7, end: 15 } },
+ { expression: '"\\"special\\" string"', column: null, table: null, alias: null, position: { start: 17, end: 37 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 43, end: 48 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Column alias #1',
+ q: 'SELECT col1 AS alias, col2 AS "alias" FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: 'col1 AS alias', column: 'col1', table: null, alias: 'alias', position: { start: 7, end: 20 } },
+ { expression: 'col2 AS "alias"', column: 'col2', table: null, alias: 'alias', position: { start: 22, end: 37 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 43, end: 48 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Column alias #2',
+ q: 'SELECT col1 alias, col2 "alias" FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: 'col1 alias', column: 'col1', table: null, alias: 'alias', position: { start: 7, end: 17 } },
+ { expression: 'col2 "alias"', column: 'col2', table: null, alias: 'alias', position: { start: 19, end: 31 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 37, end: 42 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Mathematical expressions',
+ q: 'SELECT 1 + 1, col1*0.7 AS test FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '1 + 1', column: null, table: null, alias: null, position: { start: 7, end: 12 } },
+ { expression: 'col1*0.7 AS test', column: null, table: null, alias: 'test', position: { start: 14, end: 30 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 36, end: 41 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Functions',
+ q: 'SELECT FUNC(), OTHERFUN(col, FUNC(1/4, -3.05), "string") FROM table',
+ a: {
+ type: 'select',
+ select: [
+ { expression: 'FUNC()', column: null, table: null, alias: null, position: { start: 7, end: 13 } },
+ { expression: 'OTHERFUN(col, FUNC(1/4, -3.05), "string")', column: null, table: null, alias: null, position: { start: 15, end: 56 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 62, end: 67 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Table alias',
+ q: 'SELECT * FROM table AS t, table2 AS "t2"',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table AS t', table: 'table', alias: 't', position: { start: 14, end: 24 } },
+ { expression: 'table2 AS "t2"', table: 'table2', alias: 't2', position: { start: 26, end: 40 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Simple inner join',
+ q: 'SELECT * FROM table INNER JOIN table2 ON table2.id = id_table2',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [
+ { type: 'inner', table: 'table2', alias: null, position: { start: 31, end: 37 }, condition: { expression: 'table2.id = id_table2', position: { start: 41, end: 62 } } },
+ ],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Several joins',
+ q: 'SELECT * FROM table INNER JOIN t1 ON t1.id = id_table2 AND t1.bool LEFT JOIN t2 ON t2.id = t1.id_t2 RIGHT JOIN t3 AS table3 ON t3.id = FUNC(t1.stuff)',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [
+ { type: 'inner', table: 't1', alias: null, position: { start: 31, end: 33 }, condition: { expression: 't1.id = id_table2 AND t1.bool', position: { start: 37, end: 66 } } },
+ { type: 'left', table: 't2', alias: null, position: { start: 77, end: 79 }, condition: { expression: 't2.id = t1.id_t2', position: { start: 83, end: 99 } } },
+ { type: 'right', table: 't3', alias: 'table3', position: { start: 111, end: 123 }, condition: { expression: 't3.id = FUNC(t1.stuff)', position: { start: 127, end: 149 } } },
+ ],
+ where: null,
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Where #1',
+ q: 'SELECT * FROM table WHERE this >= that AND col IS NOT NULL',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: {
+ expression: "this >= that AND col IS NOT NULL",
+ position: { start: 26, end: 58 },
+ },
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Where #2',
+ q: 'SELECT * FROM table WHERE (FUNC(this) = "string") AND (1+5 OR col1)',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: {
+ expression: "(FUNC(this) = \"string\") AND (1+5 OR col1)",
+ position: { start: 26, end: 67 },
+ },
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Where #3',
+ q: 'SELECT * FROM table WHERE column IN ("val1", "val2")',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: {
+ expression: "column IN (\"val1\", \"val2\")",
+ position: { start: 26, end: 52 },
+ },
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Where #3 with spaces',
+ q: 'SELECT * FROM table WHERE column IN ( "val1", "val2" )',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: {
+ expression: "column IN ( \"val1\", \"val2\" )",
+ position: { start: 26, end: 54 },
+ },
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Where followed by something else',
+ q: 'SELECT * FROM table WHERE cond ORDER BY col LIMIT 1',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: {
+ expression: "cond",
+ position: { start: 26, end: 30 },
+ },
+ group: [],
+ order: [
+ { expression: "col", table: null, column: "col", order: "ASC", position: { start: 40, end: 43 } },
+ ],
+ limit: { from: null, nb: 1 },
+ },
+ },
+ {
+ c: 'Group by',
+ q: 'SELECT * FROM table GROUP BY col1, MONTH(col2), table.col3',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: null,
+ group: [
+ { expression: 'col1', table: null, column: 'col1', position: { start: 29, end: 33 } },
+ { expression: 'MONTH(col2)', table: null, column: null, position: { start: 35, end: 46 } },
+ { expression: 'table.col3', table: 'table', column: 'col3', position: { start: 48, end: 58 } },
+ ],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'Order by',
+ q: 'SELECT * FROM table ORDER BY table.col1, col2 DESC, FUNC(col3 + 7) ASC',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [
+ { expression: "table.col1", table: "table", column: "col1", order: "ASC", position: { start: 29, end: 39 } },
+ { expression: "col2 DESC", table: null, column: "col2", order: "DESC", position: { start: 41, end: 50 } },
+ { expression: "FUNC(col3 + 7) ASC", table: null, column: null, order: "ASC", position: { start: 52, end: 70 } },
+ ],
+ limit: null,
+ },
+ },
+ {
+ c: 'Limit #1',
+ q: 'SELECT * FROM table LIMIT 5',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: { from: null, nb: 5 },
+ },
+ },
+ {
+ c: 'Limit #2',
+ q: 'SELECT * FROM table LIMIT 1, 2',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: null,
+ group: [],
+ order: [],
+ limit: { from: 1, nb: 2 },
+ },
+ },
+ {
+ c: 'Between',
+ q: 'SELECT * FROM table WHERE col BETWEEN 1 and 5',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: { expression: 'col BETWEEN 1 and 5', position: { start: 26, end: 45 } },
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+ {
+ c: 'NOT Between',
+ q: 'SELECT * FROM table WHERE col NOT BETWEEN 1 and 5',
+ a: {
+ type: 'select',
+ select: [
+ { expression: '*', column: '*', table: null, alias: null, position: { start: 7, end: 8 } },
+ ],
+ from: [
+ { expression: 'table', table: 'table', alias: null, position: { start: 14, end: 19 } },
+ ],
+ join: [],
+ where: { expression: 'col NOT BETWEEN 1 and 5', position: { start: 26, end: 49 } },
+ group: [],
+ order: [],
+ limit: null,
+ },
+ },
+];
+
+suite('sql2ast - select');
+
+Select.forEach(function(item) {
+ test(item.c, function() {
+ h.testAst(item.c, item.q, item.a);
+ });
+});
+
+suite('ast2sql - select');
+
+Select.forEach(function(item) {
+ test(item.c, function() {
+ h.testBackAndForth(item.c, item.q);
+ });
+});
diff --git a/tests/tests-update.js b/tests/tests-update.js
new file mode 100644
index 0000000..56024aa
--- /dev/null
+++ b/tests/tests-update.js
@@ -0,0 +1,77 @@
+/*global test:true,suite:true*/
+"use strict";
+
+var expect = require('chai').expect;
+var h = require('./helpers.js');
+
+var Update = [
+ {
+ c: 'Simple update',
+ q: 'UPDATE table SET col = "value"',
+ a: {
+ type: 'update',
+ table: {
+ expression: 'table',
+ table: 'table',
+ alias: null,
+ },
+ where: null,
+ values: [
+ { target: { expression: 'col', column: 'col' }, value: '"value"'},
+ ],
+ },
+ },
+ {
+ c: 'Several columns',
+ q: 'UPDATE table SET col = "value", col2 = NULL, table.col3 = col',
+ a: {
+ type: 'update',
+ table: {
+ expression: 'table',
+ table: 'table',
+ alias: null,
+ },
+ where: null,
+ values: [
+ { target: { expression: 'col', column: 'col' }, value: '"value"'},
+ { target: { expression: 'col2', column: 'col2' }, value: 'NULL'},
+ { target: { expression: 'table.col3', column: 'col3' }, value: 'col'},
+ ],
+ },
+ },
+ {
+ c: 'Where #1',
+ q: 'UPDATE table SET col = "value" WHERE this >= that AND col IS NOT NULL',
+ a: {
+ type: 'update',
+ table: {
+ expression: 'table',
+ table: 'table',
+ alias: null,
+ },
+ where: {
+ expression: "this >= that AND col IS NOT NULL",
+ position: { start: 37, end: 69 },
+ },
+ values: [
+ { target: { expression: 'col', column: 'col' }, value: '"value"'},
+ ],
+ },
+ },
+];
+
+suite('sql2ast - update');
+
+Update.forEach(function(item) {
+ test(item.c, function() {
+ h.testAst(item.c, item.q, item.a);
+ });
+});
+
+suite('ast2sql - update');
+
+Update.forEach(function(item) {
+ test(item.c, function() {
+ h.testBackAndForth(item.c, item.q);
+ });
+});
diff --git a/tests/tests.html b/tests/tests.html
deleted file mode 100644
index 212e226..0000000
--- a/tests/tests.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- Tests - simpleSqlParser
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/tests.js b/tests/tests.js
deleted file mode 100644
index 4339c97..0000000
--- a/tests/tests.js
+++ /dev/null
@@ -1,835 +0,0 @@
-(function () {
- "use strict";
-
- var m = simpleSqlParser;
-
- module('sql2ast');
-
- test('trim', function () {
- expect(9);
-
- deepEqual(m.trim('test'), 'test');
- deepEqual(m.trim('test '), 'test');
- deepEqual(m.trim(' test'), 'test');
- deepEqual(m.trim(' test '), 'test');
- deepEqual(m.trim('test test'), 'test test');
- deepEqual(m.trim(' test \
- '), 'test');
-
- var integer = 5;
- var array = [0, 1, 2];
- var object = {test: "test", great_aswer: 42};
- deepEqual(m.trim(integer), integer);
- deepEqual(m.trim(array), array);
- deepEqual(m.trim(object), object);
- });
-
- test('protect/unprotect', function () {
- expect(9);
-
- deepEqual(m.protect('test'), '#t#e#s#t#');
- deepEqual(m.protect('#t#e#s#t#'), '###t###e###s###t###');
-
- deepEqual(m.unprotect('#t#e#s#t#'), 'test');
- deepEqual(m.unprotect('###t###e###s###t###'), '#t#e#s#t#');
-
- var string = "this is a (complex) string, with some special chars like !:@.#ù%$^\
- and also a line break and des caractères français !";
- deepEqual(m.unprotect(m.protect(string)), string);
-
- deepEqual(m.protect(''), '#');
- deepEqual(m.unprotect('#'), '');
- deepEqual(m.protect('#'), '###');
- deepEqual(m.unprotect('###'), '#');
- });
-
- test('protect_split', function () {
- expect(10);
-
- deepEqual(m.protect_split(',', 'test'), ['test']);
- deepEqual(m.protect_split(',', 'test(1,2)'), ['test(1,2)']);
- deepEqual(m.protect_split(',', 'test1,test2'), ['test1', 'test2']);
- deepEqual(m.protect_split(',', 'test1,(test2,test3)'), ['test1', '(test2,test3)']);
- deepEqual(m.protect_split(';', 'test1;(test2;test3)'), ['test1', '(test2;test3)']);
- deepEqual(m.protect_split(',', 'test1,"test2,test3"'), ['test1', '"test2,test3"']);
- deepEqual(m.protect_split(',', 'test1,\'test2,test3\''), ['test1', '\'test2,test3\'']);
- deepEqual(m.protect_split(',', 'test1,`test2,test3`'), ['test1', '`test2,test3`']);
- deepEqual(m.protect_split(',', 'test1,function(test2,"test3(test4)\'test5\'"),test6()'), ['test1', 'function(test2,"test3(test4)\'test5\'")', 'test6()']);
- deepEqual(m.protect_split(',', 'column1, column2, FUNCTION("string )\'\'", column3), column4 AS Test1,column5 AS "Test 2", column6 "Test,3" '), [
- 'column1',
- 'column2',
- 'FUNCTION("string )\'\'", column3)',
- 'column4 AS Test1',
- 'column5 AS "Test 2"',
- 'column6 "Test,3"',
- ]);
- });
-
- test('condition lexer', function () {
- expect(19);
-
- deepEqual(m.CondLexer.tokenize('column = othercolumn'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column=othercolumn'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column = \
- othercolumn'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('table.column = othertable.othercolumn'), [
- {type: 'word', value: 'table.column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othertable.othercolumn'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('table.column <= othertable.othercolumn'), [
- {type: 'word', value: 'table.column'},
- {type: 'operator', value: '<='},
- {type: 'word', value: 'othertable.othercolumn'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('table.column = "string"'), [
- {type: 'word', value: 'table.column'},
- {type: 'operator', value: '='},
- {type: 'string', value: '"string"'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('table.column = FUNCTION("string")'), [
- {type: 'word', value: 'table.column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'FUNCTION("string")'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('table.column = FUNCTION("string", columns,"otherstring" ,\
- "string with SQL stuff like = AND OR ()")'), [
- {type: 'word', value: 'table.column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'FUNCTION("string", columns,"otherstring" ,\
- "string with SQL stuff like = AND OR ()")'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('table.column != "string with SQL stuff like = AND OR ()"'), [
- {type: 'word', value: 'table.column'},
- {type: 'operator', value: '!='},
- {type: 'string', value: '"string with SQL stuff like = AND OR ()"'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column = othercolumn AND column < 2'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'logic', value: 'AND'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '<'},
- {type: 'word', value: '2'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column = othercolumn AND column < 2 OR column = "string"'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'logic', value: 'AND'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '<'},
- {type: 'word', value: '2'},
- {type: 'logic', value: 'OR'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'string', value: '"string"'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column = othercolumn OR column < 2 AND column = "string"'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'logic', value: 'OR'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '<'},
- {type: 'word', value: '2'},
- {type: 'logic', value: 'AND'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'string', value: '"string"'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('(column = othercolumn AND column < 2) OR column = "string"'), [
- {type: 'group', value: '('},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'logic', value: 'AND'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '<'},
- {type: 'word', value: '2'},
- {type: 'group', value: ')'},
- {type: 'logic', value: 'OR'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'string', value: '"string"'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column = othercolumn AND (column < 2 OR column = "string")'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'logic', value: 'AND'},
- {type: 'group', value: '('},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '<'},
- {type: 'word', value: '2'},
- {type: 'logic', value: 'OR'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'string', value: '"string"'},
- {type: 'group', value: ')'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column = othercolumn AND column < 2 AND column = "string"'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'logic', value: 'AND'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '<'},
- {type: 'word', value: '2'},
- {type: 'logic', value: 'AND'},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'string', value: '"string"'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('(column = othercolumn)'), [
- {type: 'group', value: '('},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'group', value: ')'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column = othercolumn AND (column < 2 OR (column = "string" AND table.othercolumn))'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'word', value: 'othercolumn'},
- {type: 'logic', value: 'AND'},
- {type: 'group', value: '('},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '<'},
- {type: 'word', value: '2'},
- {type: 'logic', value: 'OR'},
- {type: 'group', value: '('},
- {type: 'word', value: 'column'},
- {type: 'operator', value: '='},
- {type: 'string', value: '"string"'},
- {type: 'logic', value: 'AND'},
- {type: 'word', value: 'table.othercolumn'},
- {type: 'group', value: ')'},
- {type: 'group', value: ')'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column IS NULL'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: 'IS'},
- {type: 'word', value: 'NULL'},
- ]);
-
- deepEqual(m.CondLexer.tokenize('column IS NOT NULL'), [
- {type: 'word', value: 'column'},
- {type: 'operator', value: 'IS'},
- {type: 'operator', value: 'NOT'},
- {type: 'word', value: 'NULL'},
- ]);
- });
-
- test('condition parser', function () {
- expect(20);
-
- deepEqual(m.CondParser.parse('column = othercolumn'), {left: 'column', operator: '=', right: 'othercolumn'});
-
- deepEqual(m.CondParser.parse('column=othercolumn'), {left: 'column', operator: '=', right: 'othercolumn'});
-
- deepEqual(m.CondParser.parse('column = \
- othercolumn'), {left: 'column', operator: '=', right: 'othercolumn'});
-
- deepEqual(m.CondParser.parse('table.column = othertable.othercolumn'), {left: 'table.column', operator: '=', right: 'othertable.othercolumn'});
-
- deepEqual(m.CondParser.parse('table.column <= othertable.othercolumn'), {left: 'table.column', operator: '<=', right: 'othertable.othercolumn'});
-
- deepEqual(m.CondParser.parse('table.column = "string"'), {left: 'table.column', operator: '=', right: '"string"'});
-
- deepEqual(m.CondParser.parse('table.column = FUNCTION("string")'), {left: 'table.column', operator: '=', right: 'FUNCTION("string")'});
-
- deepEqual(m.CondParser.parse('table.column = FUNCTION("string", columns,"otherstring" ,\
- "string with SQL stuff like = AND OR ()")'), {left: 'table.column', operator: '=', right: 'FUNCTION("string", columns,"otherstring" ,\
- "string with SQL stuff like = AND OR ()")'});
-
- deepEqual(m.CondParser.parse('table.column != "string with SQL stuff like = AND OR ()"'), {left: 'table.column', operator: '!=', right: '"string with SQL stuff like = AND OR ()"'});
-
- deepEqual(m.CondParser.parse('column = othercolumn AND column < 2'), {
- logic: 'AND', terms: [
- {left: 'column', operator: '=', right: 'othercolumn'},
- {left: 'column', operator: '<', right: '2'},
- ]});
-
- deepEqual(m.CondParser.parse('column = othercolumn AND column < 2 OR column = "string"'), {
- logic: 'OR', terms: [
- {logic: 'AND', terms: [
- {left: 'column', operator: '=', right: 'othercolumn'},
- {left: 'column', operator: '<', right: '2'},
- ]},
- {left: 'column', operator: '=', right: '"string"'},
- ]});
-
- deepEqual(m.CondParser.parse('column = othercolumn OR column < 2 AND column = "string"'), {
- logic: 'AND', terms: [
- {logic: 'OR', terms: [
- {left: 'column', operator: '=', right: 'othercolumn'},
- {left: 'column', operator: '<', right: '2'},
- ]},
- {left: 'column', operator: '=', right: '"string"'},
- ]});
-
- deepEqual(m.CondParser.parse('(column = othercolumn AND column < 2) OR column = "string"'), {
- logic: 'OR', terms: [
- {logic: 'AND', terms: [
- {left: 'column', operator: '=', right: 'othercolumn'},
- {left: 'column', operator: '<', right: '2'},
- ]},
- {left: 'column', operator: '=', right: '"string"'},
- ]});
-
- deepEqual(m.CondParser.parse('column = othercolumn AND (column < 2 OR column = "string")'), {
- logic: 'AND', terms: [
- {left: 'column', operator: '=', right: 'othercolumn'},
- {logic: 'OR', terms: [
- {left: 'column', operator: '<', right: '2'},
- {left: 'column', operator: '=', right: '"string"'},
- ]},
- ]});
-
- deepEqual(m.CondParser.parse('column = othercolumn AND column < 2 AND column = "string"'), {
- logic: 'AND', terms: [
- {left: 'column', operator: '=', right: 'othercolumn'},
- {left: 'column', operator: '<', right: '2'},
- {left: 'column', operator: '=', right: '"string"'},
- ]});
-
- deepEqual(m.CondParser.parse('(column = othercolumn)'), {left: 'column', operator: '=', right: 'othercolumn'});
-
- deepEqual(m.CondParser.parse('column = othercolumn AND (column < 2 OR (column = "string" AND table.othercolumn))'), {
- logic: 'AND', terms: [
- {left: 'column', operator: '=', right: 'othercolumn'},
- {logic: 'OR', terms: [
- {left: 'column', operator: '<', right: '2'},
- {logic: 'AND', terms: [
- {left: 'column', operator: '=', right: '"string"'},
- 'table.othercolumn',
- ]},
- ]},
- ]});
-
- deepEqual(m.CondParser.parse('(a AND b) OR (c AND d)'), {
- logic: 'OR', terms: [
- {logic: 'AND', terms: ['a', 'b']},
- {logic: 'AND', terms: ['c', 'd']},
- ]});
-
- deepEqual(m.CondParser.parse('column IS NULL'), {left: 'column', operator: 'IS', right: 'NULL'});
-
- deepEqual(m.CondParser.parse('column IS NOT NULL'), {left: 'column', operator: 'IS NOT', right: 'NULL'});
- });
-
- test('parse SQL', function() {
- expect(30);
- var q;
-
- q = 'SELECT * FROM table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
- q = 'select * from table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
- q = 'SELECT * FROM table;';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
-
- q = 'SELECT * FROM table AS t';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: 't',
- }],
- }, q);
-
- q = 'SELECT * FROM table; SELECT * FROM table2';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
- q = 'SELECT column1, column2, FUNCTION("string ()\'\'", column3), column4 AS Test1,column5 AS "Test 2", column6 "Test 3" FROM table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [
- {name: 'column1'},
- {name: 'column2'},
- {name: 'FUNCTION("string ()\'\'", column3)'},
- {name: 'column4 AS Test1'},
- {name: 'column5 AS "Test 2"'},
- {name: 'column6 "Test 3"'},
- ],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
- q = 'SELECT FUCTION("SQL syntax like: FROM or LIMIT", \'SQL syntax like: FROM or LIMIT\', `SQL syntax like: FROM or LIMIT`) FROM table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: 'FUCTION("SQL syntax like: FROM or LIMIT", \'SQL syntax like: FROM or LIMIT\', `SQL syntax like: FROM or LIMIT`)'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
- q = 'SELECT * FROM table1,table2 AS t2 , table3 AS "t 3"';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [
- {table: 'table1', as: ''},
- {table: 'table2', as: 't2'},
- {table: 'table3', as: 't 3'},
- ],
- }, q);
-
- q = 'SELECT * FROM table LEFT JOIN table2 ON table.id = table2.id_table INNER JOIN table4 AS t4 ON table.id = FUNCTION(table4.id_table, "string()") JOIN table3 ON table.id=table3.id_table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'JOIN': [
- {
- type: 'left',
- table: 'table2',
- as: '',
- cond: {left: 'table.id', operator: '=', right: 'table2.id_table'},
- },
- {
- type: 'inner',
- table: 'table4',
- as: 't4',
- cond: {left: 'table.id', operator: '=', right: 'FUNCTION(table4.id_table, "string()")'},
- },
- {
- type: 'inner',
- table: 'table3',
- as: '',
- cond: {left: 'table.id', operator: '=', right: 'table3.id_table'},
- },
- ],
- }, q);
-
- q = 'SELECT * FROM table LEFT JOIN table10 ON table.id = table10.id RIGHT JOIN table2 ON table.id = table2.id INNER JOIN table3 AS t3 ON table.id = FUNCTION(table4.id_table, "string()") JOIN table4 ON table.id=table4.id';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'JOIN': [
- {
- type: 'left',
- table: 'table10',
- as: '',
- cond: {left: 'table.id', operator: '=', right: 'table10.id'},
- },
- {
- type: 'inner',
- table: 'table3',
- as: 't3',
- cond: {left: 'table.id', operator: '=', right: 'FUNCTION(table4.id_table, "string()")'},
- },
- {
- type: 'inner',
- table: 'table4',
- as: '',
- cond: {left: 'table.id', operator: '=', right: 'table4.id'},
- },
- {
- type: 'right',
- table: 'table2',
- as: '',
- cond: {left: 'table.id', operator: '=', right: 'table2.id'},
- },
- ],
- }, q);
-
- q = 'SELECT * FROM table LEFT JOIN table2 AS t2 ON table.id = t2.id_table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'JOIN': [
- {
- type: 'left',
- table: 'table2',
- as: 't2',
- cond: {left: 'table.id', operator: '=', right: 't2.id_table'},
- },
- ],
- }, q);
-
- // This is an invalid query, but the parser should not crash
- q = 'SELECT * FROM table LEFT JOIN table2 table.id = t2.id_table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'JOIN': [
- {
- type: 'left',
- table: 'table2 table.id = t2.id_table',
- as: '',
- cond: '',
- },
- ],
- }, q);
-
- q = 'SELECT * FROM table WHERE (column1 = "something ()" AND table.column2 != column3) AND (column4 OR column5 IS NOT NULL)';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'WHERE': {
- logic: 'AND', terms: [
- {left: 'column1', operator: '=', right: '"something ()"'},
- {left: 'table.column2', operator: '!=', right: 'column3'},
- {logic: 'OR', terms: [
- 'column4',
- {left: 'column5', operator: 'IS NOT', right: 'NULL'},
- ]},
- ],
- },
- }, q);
-
- q = 'SELECT * FROM table ORDER BY column1 ASC, column2 DESC';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'ORDER BY': [
- {column: 'column1', order: 'ASC'},
- {column: 'column2', order: 'DESC'},
- ],
- }, q);
-
- q = 'SELECT * FROM table ORDER BY column1, column2';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'ORDER BY': [
- {column: 'column1', order: 'ASC'},
- {column: 'column2', order: 'ASC'},
- ],
- }, q);
-
- q = 'SELECT * FROM table GROUP BY column1, column2';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'GROUP BY': [
- {column: 'column1'},
- {column: 'column2'},
- ],
- }, q);
-
- deepEqual(m.sql2ast('SELECT * FROM table LIMIT 5'), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'LIMIT': {nb: 5, from: 0},
- }, q);
-
- q = 'SELECT * FROM table LIMIT 10,20';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: '*'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'LIMIT': {nb: 20, from: 10},
- }, q);
-
- q = 'SELECT EXTRACT(MICROSECOND FROM "2003-01-02 10:30:00.00123") FROM table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: 'EXTRACT(MICROSECOND FROM "2003-01-02 10:30:00.00123")'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
- q = 'SELECT column1, FROM table, ORDER BY id ASC,';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: 'column1'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'ORDER BY': [{column: 'id', order: 'ASC'}],
- }, q);
-
- q = 'SELECT column1, FROM table, GROUP BY id,';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: 'column1'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- 'GROUP BY': [{column: 'id'}],
- }, q);
-
- q = 'SELECT column1, setup, column2 FROM table';
- deepEqual(m.sql2ast(q), {
- 'SELECT': [{name: 'column1'}, {name: 'setup'}, {name: 'column2'}],
- 'FROM': [{
- table: 'table',
- as: '',
- }],
- }, q);
-
- q = 'DELETE FROM table WHERE id = 5';
- deepEqual(m.sql2ast(q), {
- 'DELETE FROM': [{
- table: 'table',
- as: '',
- }],
- 'WHERE': {left: 'id', operator: '=', right: '5'},
- }, q);
-
- q = 'DELETE FROM table WHERE id = 5';
- deepEqual(m.sql2ast(q, false), {
- 'DELETE FROM': [{
- table: 'table',
- as: '',
- }],
- 'WHERE': 'id = 5',
- }, q);
-
- q = 'INSERT INTO table (column1, column2) VALUES("test ()", CURDATE())';
- deepEqual(m.sql2ast(q), {
- 'INSERT INTO': {
- table: 'table',
- columns: ['column1', 'column2'],
- },
- 'VALUES': [['"test ()"', 'CURDATE()']],
- }, q);
-
- q = 'INSERT INTO table (col_A,col_B,col_C) VALUES (1,2,3)';
- deepEqual(m.sql2ast(q), {
- 'INSERT INTO': {
- table: 'table',
- columns: ['col_A', 'col_B', 'col_C'],
- },
- 'VALUES': [['1', '2', '3']],
- }, q);
-
- q = 'INSERT INTO table VALUES (1,2,3), (4,5,6), (7,8,9)';
- deepEqual(m.sql2ast(q), {
- 'INSERT INTO': {table: 'table'},
- 'VALUES': [
- ['1', '2', '3'],
- ['4', '5', '6'],
- ['7', '8', '9'],
- ],
- }, q);
-
- q = 'INSERT INTO table (col_A,col_B,col_C) VALUES (1,2,3), (4,5,6), (7,8,9)';
- deepEqual(m.sql2ast(q), {
- 'INSERT INTO': {
- table: 'table',
- columns: ['col_A', 'col_B', 'col_C'],
- },
- 'VALUES': [
- ['1', '2', '3'],
- ['4', '5', '6'],
- ['7', '8', '9'],
- ],
- }, q);
-
- q = 'INSERT INTO table VALUES (1,2,3)';
- deepEqual(m.sql2ast(q), {
- 'INSERT INTO': {table: 'table'},
- 'VALUES': [['1', '2', '3']],
- }, q);
-
- q = 'UPDATE table SET column1 = "string ()", column2=5,column3=column4, column5 = CURDATE(), column6 = FUNCTION("string ()", column7) WHERE id = 5';
- deepEqual(m.sql2ast(q), {
- 'UPDATE': [{
- table: 'table',
- as: '',
- }],
- 'SET': [
- { expression: 'column1 = "string ()"'},
- { expression: 'column2=5'},
- { expression: 'column3=column4'},
- { expression: 'column5 = CURDATE()'},
- { expression: 'column6 = FUNCTION("string ()", column7)'},
- ],
- 'WHERE': {left: 'id', operator: '=', right: '5'},
- }, q);
- });
-
- test('Infinite loop', function() {
- m.sql2ast('SELECT * FROM foo where "a');
- ok(true);
- });
-
-
-
-
- module('ast2sql');
-
- test('SELECT query', function() {
- expect(15);
- var q;
-
- q = 'SELECT * FROM table';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT * FROM table';
- deepEqual(m.ast2sql(m.sql2ast('select * from table')), q, q);
-
- q = 'SELECT * FROM table1, table2';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT t.column1, ot.column2 FROM table AS t LEFT JOIN othertable AS ot ON t.id = ot.id_table WHERE t.column3 = 5';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT t.column1, ot.column2 FROM table AS t RIGHT JOIN othertable AS ot ON t.id = ot.id_table WHERE t.column3 = 5';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT * FROM table AS t LEFT JOIN othertable AS ot ON t.id = ot.id_table LEFT JOIN othertable2 AS ot2 ON t.id = ot2.id_table AND ot2.column INNER JOIN othertable3 AS ot3 ON t.id = ot3.id_table';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT * FROM table WHERE (column1 = "something ()" AND table.column2 != column3) AND (column4 OR column5 IS NOT NULL)';
- deepEqual(m.ast2sql(m.sql2ast(q)), 'SELECT * FROM table WHERE column1 = "something ()" AND table.column2 != column3 AND (column4 OR column5 IS NOT NULL)', q);
-
- q = 'SELECT * FROM table ORDER BY a ASC, b DESC, c ASC';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT * FROM table GROUP BY a, b';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT * FROM table LIMIT 1';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT * FROM table LIMIT 10,1';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'SELECT * FROM table LIMIT 1';
- deepEqual(m.ast2sql({'SELECT': [{name: '*'}], 'FROM': [{table: 'table', as: ''}], 'LIMIT': {'nb': '1'}}), q, q);
-
- q = 'SELECT * FROM table';
- deepEqual(m.ast2sql({'SELECT': [{name: '*'}], 'FROM': [{table: 'table', as: ''}], 'LIMIT': {'nb': ''}}), q, q);
-
- q = 'SELECT * FROM table LIMIT 1';
- deepEqual(m.ast2sql({'SELECT': [{name: '*'}], 'FROM': [{table: 'table', as: ''}], 'LIMIT': {'from': null, 'nb': '1'}}), q, q);
-
- q = 'SELECT * FROM table LIMIT 1';
- deepEqual(m.ast2sql({'SELECT': [{name: '*'}], 'FROM': [{table: 'table', as: ''}], 'LIMIT': {'from': '-1', 'nb': '1'}}), q, q);
- });
-
- test('INSERT query', function() {
- expect(4);
- var q;
-
- q = 'INSERT INTO table (col_A, col_B, col_C) VALUES (1, 2, 3)';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'INSERT INTO table VALUES (1, 2, 3), (4, 5, 6), (7, 8, 9)';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'INSERT INTO table (col_A, col_B, col_C) VALUES (1, 2, 3), (4, 5, 6), (7, 8, 9)';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'INSERT INTO table VALUES (1, 2, 3)';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
- });
-
- test('DELETE query', function() {
- expect(2);
- var q;
-
- q = 'DELETE FROM table WHERE id = 5';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'DELETE FROM table1, table2 WHERE id = 5';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
- });
-
- test('UPDATE query', function() {
- expect(3);
- var q;
-
- q = 'UPDATE table SET column = 1';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'UPDATE table1, table2 SET column = 1';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
-
- q = 'UPDATE table SET column1 = "string ()", column2=5, column3=column4, column5 = CURDATE(), column6 = FUNCTION("string ()", column7) WHERE id = 5';
- deepEqual(m.ast2sql(m.sql2ast(q)), q, q);
- });
-
-}());