Skip to content

Commit

Permalink
Javascript: Implement ExceptionCollectorListener and make it defaul…
Browse files Browse the repository at this point in the history
…t behavior
  • Loading branch information
surister committed Jun 14, 2024
1 parent 373d682 commit b27d754
Showing 1 changed file with 123 additions and 11 deletions.
134 changes: 123 additions & 11 deletions cratedb_sqlparse_js/cratedb_sqlparse/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ function BEGIN_DOLLAR_QUOTED_STRING_action(localctx, actionIndex) {
}
}


function END_DOLLAR_QUOTED_STRING_action(localctx, actionIndex) {
if (actionIndex === 1) {
this.tags.pop();
Expand All @@ -28,6 +27,44 @@ SqlBaseLexer.prototype.END_DOLLAR_QUOTED_STRING_sempred = END_DOLLAR_QUOTED_STRI

export class ParseError extends Error {
name = 'ParseError'

constructor(message, offending_token) {
super(message);
this.msg = message;
this.offending_token = offending_token;
this.line = this.getLine();
this.column = this.getColumn();
this.message = this.error_message();
}

error_message() {
return `[line ${this.line}:${this.column} ${this.message}]`
}

getColumn() {
return this.offending_token.column
}

getLine() {
return this.offending_token.line
}

getOriginalQueryWithErrorMarked() {
const query = this.offending_token.source[1].strdata
const offendingTokenText = query.substring(this.offending_token.start, this.offending_token.stop + 1)
const queryLines = query.split("\n")
const offendingLine = queryLines[this.getLine() - 1]
const newLineOffset = offendingLine.indexOf(offendingTokenText)

const newline = (
offendingLine
+ "\n"
+ ("‎".repeat(newLineOffset) + "^".repeat(this.offending_token.stop - this.offending_token.start + 1))
)
queryLines[this.line - 1] = newline

return queryLines.join("\n")
}
}

class CaseInsensitiveStream extends InputStream {
Expand All @@ -42,36 +79,111 @@ class CaseInsensitiveStream extends InputStream {

class ExceptionErrorListener extends ErrorListener {
syntaxError(recognizer, offendingSymbol, line, column, msg, e) {
throw new ParseError(`line${line}:${column} ${msg}`);
throw new ParseError(
msg,
offendingSymbol,
e,
e.ctx.parser.getTokenStream().getText(e.ctx.start, e.offendingToken.tokenIndex)
)
}
}

class ExceptionCollectorListener extends ErrorListener {
constructor() {
super();
this.errors = [];
}

syntaxError(recognizer, offendingSymbol, line, column, msg, e) {
super.syntaxError(recognizer, offendingSymbol, line, column, msg, e);
const error = new ParseError(
msg,
offendingSymbol,
e,
e.ctx.parser.getTokenStream().getText(e.ctx.start, e.offendingToken.tokenIndex)
)
this.errors.push(error)
}
}

class Metadata {
/*
* Represents the metadata of the query, the actual interesting parts of the query such as:
* table, schema, columns, options...
*/
constructor(schema, table, parameterized_properties, with_properties) {
this.schema = schema
this.table = table
this.parameterized_properties = parameterized_properties
this.with_properties = with_properties
}

toString() {

}
}

class Statement {
constructor(ctx) {
/*
* Represents a CrateDB SQL statement.
* */
constructor(ctx, exception) {
this.query = ctx.parser.getTokenStream().getText(
new Interval(
ctx.start.tokenIndex,
ctx.stop.tokenIndex,
)
)
this.original_query = ctx.parser.getTokenStream().getText()
this.tree = ctx.toStringTree(null, ctx.parser)
this.type = ctx.start.text
this.ctx = ctx
this.original_query = ctx.parser.getTokenStream().getText();
this.tree = ctx.toStringTree(null, ctx.parser);
this.type = ctx.start.text;
this.ctx = ctx;
self.exception = exception;

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on OS ubuntu-latest

tests/parser.test.js > sqlparse parses one statement

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:7:21

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on OS ubuntu-latest

tests/parser.test.js > sqlparse parses several statements

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:22:21

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on OS ubuntu-latest

tests/parser.test.js > sqlparse supports dollar string

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:32:19

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on OS ubuntu-latest

tests/parser.test.js > sqlparse is case insensitive

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:43:19

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on OS ubuntu-latest

tests/parser.test.js > sqlparse parses one statement

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:7:21

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on OS ubuntu-latest

tests/parser.test.js > sqlparse parses several statements

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:22:21

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on OS ubuntu-latest

tests/parser.test.js > sqlparse supports dollar string

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:32:19

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on OS ubuntu-latest

tests/parser.test.js > sqlparse is case insensitive

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:43:19

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 22 on OS ubuntu-latest

tests/parser.test.js > sqlparse parses one statement

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:7:21

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 22 on OS ubuntu-latest

tests/parser.test.js > sqlparse parses several statements

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:22:21

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 22 on OS ubuntu-latest

tests/parser.test.js > sqlparse supports dollar string

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:32:19

Check failure on line 141 in cratedb_sqlparse_js/cratedb_sqlparse/parser.js

View workflow job for this annotation

GitHub Actions / Node.js 22 on OS ubuntu-latest

tests/parser.test.js > sqlparse is case insensitive

ReferenceError: self is not defined ❯ new Statement cratedb_sqlparse/parser.js:141:9 ❯ Module.sqlparse cratedb_sqlparse/parser.js:186:20 ❯ tests/parser.test.js:43:19
self.metadata = new Metadata();
}
}

export function sqlparse(query) {
function trim(string) {
return string.replace(/^\s+|\s+$/gm, '');
}


function findSuitableError(statement, errors) {
for (const error of errors) {
let error_query = error.query;
if (error_query.endsWith(";")) {
error_query = error_query.substring(0, error_query.length - 1);
}

error_query = trim(error_query);

// If a good match error_query contains statement.query
if (error_query.contains(statement.query)) {
statement.exception = error;
errors.unshift(error);
}
}
}

export function sqlparse(query, raise_exception = false) {
const input = new CaseInsensitiveStream(query);
const lexer = new SqlBaseLexer(input);
lexer.removeErrorListeners();
const stream = new CommonTokenStream(lexer);

const parser = new SqlBaseParser(stream);
parser.removeErrorListeners();
parser.addErrorListener(new ExceptionErrorListener());

const errorListener = raise_exception ? new ExceptionErrorListener() : new ExceptionCollectorListener()
parser.addErrorListener(errorListener);

const tree = parser.statements();

return tree.children.filter((children) => children instanceof SqlBaseParser.StatementContext).map((children) => new Statement(children))
}
const statementsContext = tree.children.filter((children) => children instanceof SqlBaseParser.StatementContext)

let statements = []
for (const statementContext of statementsContext) {
let stmt = new Statement(statementContext)
findSuitableError(stmt, errorListener.errors)
}
}

0 comments on commit b27d754

Please sign in to comment.