--- a/db.sql.editor/src/org/netbeans/modules/db/sql/lexer/SQLLexer.java +++ a/db.sql.editor/src/org/netbeans/modules/db/sql/lexer/SQLLexer.java @@ -168,7 +168,20 @@ state = State.ISA_BACK_SLASH_IN_STRING; break; case '\'': // NOI18N + state = State.ISA_QUOTE_IN_STRING; + //return factory.createToken(SQLTokenId.STRING); + break; + } + break; + + case ISA_QUOTE_IN_STRING: + switch (actChar) { + case '\'': + state = State.ISI_STRING; + break; + default: state = State.INIT; + input.backup(1); return factory.createToken(SQLTokenId.STRING); } break; @@ -184,6 +197,11 @@ if (startQuoteChar != -1) { if (!isEndIdentifierQuoteChar(startQuoteChar, actChar)) { break; + } else { + if (actChar == '"') { + state = State.ISA_QUOTE_IN_IDENTIFIER; + break; + } } } else { if (Character.isLetterOrDigit(actChar) || actChar == '_' || actChar == '#') { @@ -196,6 +214,19 @@ startQuoteChar = -1; return factory.createToken(testKeyword(input.readText())); + case ISA_QUOTE_IN_IDENTIFIER: + switch (actChar) { + case '"': + state = State.ISI_IDENTIFIER; + break; + default: + state = State.INIT; + startQuoteChar = -1; + input.backup(1); + return factory.createToken(testKeyword(input.readText())); + } + break; + // If we are after a slash (/). case ISA_SLASH: switch (actChar) { @@ -309,6 +340,14 @@ SQLTokenId id = null; PartType part = PartType.COMPLETE; switch (state) { + case ISA_QUOTE_IN_STRING: + id = SQLTokenId.STRING; + break; + + case ISA_QUOTE_IN_IDENTIFIER: + id = SQLTokenId.IDENTIFIER; + break; + case ISI_WHITESPACE: id = SQLTokenId.WHITESPACE; break; @@ -411,20 +450,22 @@ private static enum State { INIT, - ISI_WHITESPACE, // inside white space - ISI_LINE_COMMENT, // inside line comment -- - ISI_BLOCK_COMMENT, // inside block comment /* ... */ - ISI_STRING, // inside string constant - ISI_IDENTIFIER, // inside identifier - ISA_SLASH, // slash char - ISA_HASH, // hash char '#' + ISI_WHITESPACE, // inside white space + ISI_LINE_COMMENT, // inside line comment -- + ISI_BLOCK_COMMENT, // inside block comment /* ... */ + ISI_STRING, // inside string constant + ISI_IDENTIFIER, // inside identifier + ISA_SLASH, // slash char + ISA_HASH, // hash char '#' ISA_MINUS, - ISA_STAR_IN_BLOCK_COMMENT, // after '*' in a block comment + ISA_STAR_IN_BLOCK_COMMENT, // after '*' in a block comment // XXX is ISA_ZERO really needed? - ISA_ZERO, // after '0' - ISI_INT, // integer number - ISI_DOUBLE, // double number - ISA_DOT, // after '.' - ISA_BACK_SLASH_IN_STRING // after \ in string + ISA_ZERO, // after '0' + ISI_INT, // integer number + ISI_DOUBLE, // double number + ISA_DOT, // after '.' + ISA_BACK_SLASH_IN_STRING, // after \ in string + ISA_QUOTE_IN_STRING, // encountered quote in string - could be sql99 escape + ISA_QUOTE_IN_IDENTIFIER // encountered quote in identifier - could be sql99 escape } } --- a/db.sql.editor/test/unit/src/org/netbeans/modules/db/sql/lexer/SQLLexerTest.java +++ a/db.sql.editor/test/unit/src/org/netbeans/modules/db/sql/lexer/SQLLexerTest.java @@ -71,6 +71,17 @@ SQLTokenId.WHITESPACE); } + public void testSimpleSQL99Quoting() throws Exception { + TokenSequence seq = getTokenSequence("select -/ from 'a''' + 1, dto"); + assertTokens(seq, SQLTokenId.KEYWORD, SQLTokenId.WHITESPACE, SQLTokenId.OPERATOR, + SQLTokenId.OPERATOR, SQLTokenId.WHITESPACE, SQLTokenId.KEYWORD, + SQLTokenId.WHITESPACE, SQLTokenId.STRING, SQLTokenId.WHITESPACE, + SQLTokenId.OPERATOR, SQLTokenId.WHITESPACE, SQLTokenId.INT_LITERAL, + SQLTokenId.COMMA, SQLTokenId.WHITESPACE, SQLTokenId.IDENTIFIER, + SQLTokenId.WHITESPACE); + + } + public void testQuotedIdentifiers() throws Exception { TokenSequence seq = getTokenSequence("select \"derby\", `mysql`, [mssql], `quo + ted`"); assertTokens(seq, SQLTokenId.KEYWORD, SQLTokenId.WHITESPACE, SQLTokenId.IDENTIFIER, @@ -80,6 +91,15 @@ SQLTokenId.WHITESPACE); } + public void testQuotedIdentifiersSQL99Quote() throws Exception { + TokenSequence seq = getTokenSequence("select \"\"\"derby\", `mysql`, [mssql], `quo + ted`"); + assertTokens(seq, SQLTokenId.KEYWORD, SQLTokenId.WHITESPACE, SQLTokenId.IDENTIFIER, + SQLTokenId.COMMA, SQLTokenId.WHITESPACE, SQLTokenId.IDENTIFIER, + SQLTokenId.COMMA, SQLTokenId.WHITESPACE, SQLTokenId.IDENTIFIER, + SQLTokenId.COMMA, SQLTokenId.WHITESPACE, SQLTokenId.IDENTIFIER, + SQLTokenId.WHITESPACE); + } + public void testComments() throws Exception { TokenSequence seq = getTokenSequence("-- line comment\n# mysql comment\n/* block \ncomment*/\n#notComment"); assertTokens(seq, SQLTokenId.LINE_COMMENT, SQLTokenId.LINE_COMMENT,