Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -25128,7 +25128,8 @@ List<String> captureRest() {
int l = tokens.size();
if( tok.kind == EOF || tok.kind == ST_SEMICOLON ) {
break;
} else if ( l>0 && ( tok.image.equals(".") || tokens.get(l-1).endsWith(".")) ) {
} else if ( l>0 && !joinsNumberPart(tok) && joinsDot(tok, l) ) {
// collapse a qualified name such as a . b into the single a.b
tokens.set(l-1, tokens.get(l-1) + tok.image);
} else {
tokens.add(tok.image);
Expand All @@ -25138,6 +25139,62 @@ List<String> captureRest() {
return tokens;
}

/**
* Whether a dot which is about to be consumed joins the previous token to the
* one behind it.
*
* @param tok the token which is about to be consumed
* @param size the number of tokens captured so far
* @return whether the dot belongs to the previous token
*/
JAVACODE
boolean joinsDot(Token tok, int size) {
if (".".equals(tok.image)) {
// the dot closes the previous token, unless it opens a number as in a . 5
return !numberFollows();
}
return size > 0 && tok.kind != S_DOUBLE && tok.kind != S_LONG
&& numberPrecedes();
}

/**
* Whether the token behind the dot which is about to be consumed is a number,
* which makes the dot part of the number instead of a qualifier.
*
* @return whether a number follows the dot
*/
JAVACODE
boolean numberFollows() {
Token next = getToken(2);
return next != null && (next.kind == S_LONG || next.kind == S_DOUBLE);
}

/**
* Whether the previous token ends with a dot, which makes the token about to be
* consumed the part of a qualified name after that dot.
*
* @return whether a dot precedes the token
*/
JAVACODE
boolean numberPrecedes() {
Token previous = getToken(0);
return previous != null && previous.image.endsWith(".");
}

/**
* Whether a token has to stay separate because a {@code .} in it starts a
* number. The lexer reads {@code .5} and {@code 5.} as single doubles, so
* gluing such a token to its neighbour changes the token stream and the
* captured statement would no longer parse back.
*
* @param tok the token which is about to be consumed
* @return whether the token is a number
*/
JAVACODE
boolean joinsNumberPart(Token tok) {
return tok.kind == S_DOUBLE || tok.kind == S_LONG;
}

/**
* Captures opaque routine declarations through their terminating semicolon.
* PostgreSQL AS string bodies can use ordinary, escape or dollar quotes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,47 @@ public void testCaptureRestIssue1993() throws JSQLParserException {
Assertions.assertEquals(4, statements.size());
}

@Test
void testCaptureRestKeepsADotInFrontOfANumber() throws JSQLParserException {
// a dot which starts a number is not a qualifier, so it must not be
// glued to the token in front of it: the captured tokens have to read
// back the same way once they are rendered and parsed again
for (String sqlStr : Arrays.asList("REPLACE( . 5",
"REPLACE( INTO t VALUES (1, . 5)",
"REPLACE( . . 5)",
"REPLACE( 5 . 5)",
"REPLACE( SELECT . 5",
"REPLACE( x . 1 . 2")) {
Statement statement = CCJSqlParserUtil.parse(sqlStr,
parser -> parser.withUnsupportedStatements(true));
assertInstanceOf(UnsupportedStatement.class, statement);

assertEquals(sqlStr.replaceAll("\\s+", ""), statement.toString().replaceAll("\\s+", ""));

String rendered = statement.toString();
assertEquals(rendered, CCJSqlParserUtil.parse(rendered,
parser -> parser.withUnsupportedStatements(true)).toString());
}
}

@Test
void testCaptureRestStillCollapsesAQualifiedName() throws JSQLParserException {
// the dot of a qualified name is a qualifier and is still collapsed
Statement statement = CCJSqlParserUtil.parse("REPLACE( t . a . b",
parser -> parser.withUnsupportedStatements(true));
assertInstanceOf(UnsupportedStatement.class, statement);

assertEquals("REPLACE ( t.a.b", statement.toString());
assertEquals(statement.toString(), CCJSqlParserUtil.parse(statement.toString(),
parser -> parser.withUnsupportedStatements(true)).toString());

// and a quoted name still collapses to the single identifier
Statements statements = CCJSqlParserUtil.parseStatements("Select 1; ALTER TABLE \"inter\".\"inter_user_rec\" \n"
+ " OWNER TO \"postgres\"; select 2; select 3;",
parser -> parser.withErrorRecovery(false));
Assertions.assertEquals(4, statements.size());
}

@Test
void testFormerlyUnsupportedAlterIndex() throws JSQLParserException {
String sqlStr =
Expand Down