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 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