Last active
August 29, 2015 14:05
-
-
Save yoichiro/a192cf77abac52cf519a to your computer and use it in GitHub Desktop.
QueryDivider - A parser to divide SQL queries with a delimiter string which is decided by the "delimiter" command.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * var parser = new QueryDivider(); | |
| * var result = parser.parse(sql); | |
| * if (result.success) { | |
| * var result = result.result; | |
| * for (var i = 0; i < result.length; i++) { | |
| * var query = result[i]; | |
| * // do something... | |
| * } | |
| * } | |
| * | |
| */ | |
| var ParseError = function(message) { | |
| this.message = message; | |
| }; | |
| ParseError.prototype = new Error(); | |
| ParseError.prototype.name = "ParseError"; | |
| var QueryDivider = function() { | |
| this.DELIMITER = "delimiter "; | |
| this.initialize(); | |
| }; | |
| QueryDivider.prototype = { | |
| initialize: function() { | |
| this.result = []; | |
| this.stateMap = { | |
| query: this.query.bind(this), | |
| lineStart: this.lineStart.bind(this), | |
| escapedQuery: this.escapedQuery.bind(this), | |
| sharpComment: this.sharpComment.bind(this), | |
| maybeDashComment: this.maybeDashComment.bind(this), | |
| dashComment: this.dashComment.bind(this), | |
| maybeInlineCommentStart: this.maybeInlineCommentStart.bind(this), | |
| inlineComment: this.inlineComment.bind(this), | |
| maybeInlineCommentEnd: this.maybeInlineCommentEnd.bind(this), | |
| maybeDelimiterDef: this.maybeDelimiterDef.bind(this), | |
| delimiterDef: this.delimiterDef.bind(this), | |
| delimiterDefEnd: this.delimiterDefEnd.bind(this), | |
| maybeDelimiter: this.maybeDelimiter.bind(this) | |
| }; | |
| this.currentState = this.stateMap.lineStart; | |
| this.buffer = []; | |
| this.maybeDashCommentCount = 0; | |
| this.maybeDelimiterDefBuffer = []; | |
| this.maybeDelimiterDefCount = 0; | |
| this.delimiterDefCandidate = []; | |
| this.delimiter = ";"; | |
| this.maybeDelimiterCount = 0; | |
| this.skipDelimiterCheck = false; | |
| }, | |
| parse: function(query) { | |
| try { | |
| this.apply(query, 0); | |
| this.appendBufferToResult(); | |
| return { | |
| success: true, | |
| result: this.result | |
| }; | |
| } catch(e) { | |
| if (e instanceof ParseError) { | |
| return { | |
| success: false, | |
| error: e | |
| }; | |
| } else { | |
| throw e; | |
| } | |
| } | |
| }, | |
| apply: function(query, pos) { | |
| if (query.length === pos) { | |
| return; | |
| } | |
| var ch = query.charAt(pos); | |
| var incr = this.currentState(query, ch, pos); | |
| this.apply(query, pos + incr); | |
| }, | |
| lineStart: function(query, ch, pos) { | |
| //console.log("lineStart: " + ch); | |
| if (ch === ' ') { | |
| this.buffer.push(ch); | |
| return 1; | |
| } else if (ch === 'd') { | |
| this.currentState = this.stateMap.maybeDelimiterDef; | |
| this.maybeDelimiterDefBuffer = [ch]; | |
| this.maybeDelimiterDefCount = 0; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.query; | |
| return 1; | |
| } | |
| }, | |
| query: function(query, ch, pos) { | |
| //console.log("query: " + ch + " [delimiter=" + this.delimiter + "]"); | |
| var skipDelimiterCheck = this.skipDelimiterCheck; | |
| this.skipDelimiterCheck = false; | |
| if (ch === '\\') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.escapedQuery; | |
| return 1; | |
| } else if (!skipDelimiterCheck && ch === this.delimiter.charAt(0) && this.delimiter.length === 1) { | |
| this.appendBufferToResult(); | |
| return 1; | |
| } else if (!skipDelimiterCheck && ch === this.delimiter.charAt(0)) { | |
| this.currentState = this.stateMap.maybeDelimiter; | |
| this.maybeDelimiterCount = 0; | |
| return 1; | |
| } else if (ch === '#') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.sharpComment; | |
| return 1; | |
| } else if (ch === '-') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.maybeDashComment; | |
| this.maybeDashCommentCount = 0; | |
| return 1; | |
| } else if (ch === '/') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.maybeInlineCommentStart; | |
| return 1; | |
| } else if (ch === '\n') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.lineStart; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| return 1; | |
| } | |
| }, | |
| escapedQuery: function(query, ch, pos) { | |
| //console.log("escapedQuery: " + ch); | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.query; | |
| return 1; | |
| }, | |
| sharpComment: function(query, ch, pos) { | |
| //console.log("sharpComment: " + ch); | |
| if (ch === '\n') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.lineStart; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| return 1; | |
| } | |
| }, | |
| maybeDashComment: function(query, ch, pos) { | |
| //console.log("maybeDashComment: " + ch); | |
| if (this.maybeDashCommentCount === 0 && ch === '-') { | |
| this.buffer.push(ch); | |
| this.maybeDashCommentCount++; | |
| return 1; | |
| } else if (this.maybeDashCommentCount === 1 && ch === ' ') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.dashComment; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.query; | |
| return 1; | |
| } | |
| }, | |
| dashComment: function(query, ch, pos) { | |
| //console.log("dashComment: " + ch); | |
| if (ch === '\n') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.lineStart; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| return 1; | |
| } | |
| }, | |
| maybeInlineCommentStart: function(query, ch, pos) { | |
| //console.log("maybeInlineCommentStart: " + ch); | |
| if (ch === '*') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.inlineComment; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.query; | |
| return 1; | |
| } | |
| }, | |
| inlineComment: function(query, ch, pos) { | |
| //console.log("inlineComment: " + ch); | |
| if (ch === '*') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.maybeInlineCommentEnd; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| return 1; | |
| } | |
| }, | |
| maybeInlineCommentEnd: function(query, ch, pos) { | |
| //console.log("maybeInlineCommentEnd: " + ch); | |
| if (ch === '*') { | |
| this.buffer.push(ch); | |
| return 1; | |
| } else if (ch === '/') { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.query; | |
| return 1; | |
| } else { | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.inlineComment; | |
| return 1; | |
| } | |
| }, | |
| maybeDelimiterDef: function(query, ch, pos) { | |
| //console.log("maybeDelimiterDef: " + ch); | |
| this.maybeDelimiterDefCount++; | |
| if (ch === this.DELIMITER.charAt(this.maybeDelimiterDefCount)) { | |
| if ((this.maybeDelimiterDefCount + 1) === this.DELIMITER.length) { | |
| this.currentState = this.stateMap.delimiterDef; | |
| this.delimiterDefCandidate = []; | |
| return 1; | |
| } else { | |
| this.maybeDelimiterDefBuffer.push(ch); | |
| return 1; | |
| } | |
| } else { | |
| this.buffer = this.buffer.concat(this.maybeDelimiterDefBuffer); | |
| this.buffer.push(ch); | |
| this.currentState = this.stateMap.query; | |
| return 1; | |
| } | |
| }, | |
| delimiterDef: function(query, ch, pos) { | |
| //console.log("delimiterDef: " + ch); | |
| if (ch === ' ') { | |
| if (this.delimiterDefCandidate.length === 0) { | |
| return 1; | |
| } else { | |
| this.delimiter = this.delimiterDefCandidate.join(""); | |
| this.currentState = this.stateMap.delimiterDefEnd; | |
| this.appendBufferToResult(); | |
| return 1; | |
| } | |
| } else if (ch === '\n') { | |
| if (this.delimiterDefCandidate.length === 0) { | |
| throw new ParseError("Delimiter not defined at " + pos); | |
| } else { | |
| this.delimiter = this.delimiterDefCandidate.join(""); | |
| this.currentState = this.stateMap.lineStart; | |
| this.appendBufferToResult(); | |
| return 1; | |
| } | |
| } else { | |
| this.delimiterDefCandidate.push(ch); | |
| return 1; | |
| } | |
| }, | |
| delimiterDefEnd: function(query, ch, pos) { | |
| //console.log("delimiterEnd: " + ch); | |
| if (ch === '\n') { | |
| this.currentState = this.stateMap.lineStart; | |
| return 1; | |
| } else { | |
| return 1; | |
| } | |
| }, | |
| maybeDelimiter: function(query, ch, pos) { | |
| //console.log("maybeDelimiter: " + ch); | |
| this.maybeDelimiterCount++; | |
| if (ch === this.delimiter.charAt(this.maybeDelimiterCount)) { | |
| if ((this.maybeDelimiterCount + 1) === this.delimiter.length) { | |
| this.appendBufferToResult(); | |
| this.currentState = this.stateMap.query; | |
| return 1; | |
| } else { | |
| return 1; | |
| } | |
| } else { | |
| this.currentState = this.stateMap.query; | |
| this.skipDelimiterCheck = true; | |
| return this.maybeDelimiterCount * -1; | |
| } | |
| }, | |
| appendBufferToResult: function() { | |
| var joined = this.buffer.join(""); | |
| var candidate = this.trim(joined); | |
| if (candidate) { | |
| this.result.push(joined); | |
| } | |
| this.buffer = []; | |
| }, | |
| trim: function(str) { | |
| return str.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, ""); | |
| } | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment