Skip to content
Open
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
14 changes: 14 additions & 0 deletions src/main/java/net/sf/jsqlparser/expression/JsonTableFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ public static class JsonTableValueColumnDefinition extends JsonTableColumnDefini
private String columnName;
private boolean forOrdinality;
private ColDataType dataType;
private String collation;
private boolean formatJson;
private boolean exists;
private boolean onEmptyAfterOnError;
Expand Down Expand Up @@ -526,6 +527,16 @@ public JsonTableValueColumnDefinition setDataType(ColDataType dataType) {
return this;
}

/** Returns the column collation, including any identifier quoting, or null if absent. */
public String getCollation() {
return collation;
}

public JsonTableValueColumnDefinition setCollation(String collation) {
this.collation = collation;
return this;
}

public boolean isFormatJson() {
return formatJson;
}
Expand Down Expand Up @@ -626,6 +637,9 @@ public String toString() {
if (dataType != null) {
builder.append(" ").append(dataType);
}
if (collation != null) {
builder.append(" COLLATE ").append(collation);
}
if (formatJson) {
builder.append(" FORMAT JSON");
if (encoding != null) {
Expand Down
9 changes: 9 additions & 0 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -13302,6 +13302,8 @@ JsonTableFunction.JsonTableColumnDefinition JsonTableColumnDefinition() : {
JsonTableFunction.JsonTableWrapperClause wrapperClause;
JsonTableFunction.JsonTableQuotesClause quotesClause;
String encoding;
String collation;
Token collationLiteral;
}
{
(
Expand Down Expand Up @@ -13341,6 +13343,13 @@ JsonTableFunction.JsonTableColumnDefinition JsonTableColumnDefinition() : {
getToken(1).image.equalsIgnoreCase("ALLOW")
|| getToken(1).image.equalsIgnoreCase("DISALLOW"))) } )
dataType = ColDataType() { valueColumnDefinition.setDataType(dataType); }
[ LOOKAHEAD({ Dialect.MYSQL.name().equals(getAsString(Feature.dialect))
&& getToken(1).kind == K_COLLATE })
<K_COLLATE>
( collation = RelObjectName()
| collationLiteral = <S_CHAR_LITERAL> { collation = collationLiteral.image; } )
{ valueColumnDefinition.setCollation(collation); }
]
]
[
<K_FORMAT> <K_JSON> { valueColumnDefinition.setFormatJson(true); }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.expression;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.JsonTableFunction.JsonTableValueColumnDefinition;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.create.table.ColDataType;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.TableFunction;
import net.sf.jsqlparser.test.TestUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.junit.jupiter.api.Assertions.*;

class JsonTableCollationTest {
@ParameterizedTest
@ValueSource(strings = {
"n VARCHAR(10) COLLATE utf8mb4_bin PATH '$.n'",
"n VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin PATH '$.n'",
"n VARCHAR(10) COLLATE `utf8mb4_bin` PATH '$.n'",
"n VARCHAR(10) COLLATE 'utf8mb4_bin' PATH '$.n'",
"n VARCHAR(10) COLLATE \"utf8mb4_bin\" PATH '$.n'",
"NESTED PATH '$.items[*]' COLUMNS (n VARCHAR(10) COLLATE utf8mb4_bin PATH '$.n')"
})
void collatedColumnsRoundTrip(String column) throws JSQLParserException {
PlainSelect select = parse(sql(column));
parse(select.toString());
}

@Test
void collationCanBeInspectedEditedRemovedAndConstructed() throws JSQLParserException {
PlainSelect select = parse(sql("n VARCHAR(10) COLLATE utf8mb4_bin PATH '$.n'"));
JsonTableFunction table =
(JsonTableFunction) ((TableFunction) select.getFromItem()).getFunction();
JsonTableValueColumnDefinition column =
(JsonTableValueColumnDefinition) table.getColumnsClause().getColumnDefinitions()
.get(0);
assertEquals("utf8mb4_bin", column.getCollation());
assertEquals("VARCHAR", column.getDataType().getBaseTypeName());
column.setCollation("utf8mb4_general_ci");
TestUtils.assertStatementCanBeDeparsedAs(select,
sql("n VARCHAR(10) COLLATE utf8mb4_general_ci PATH '$.n'"), true);
parse(select.toString());
column.setCollation(null);
TestUtils.assertStatementCanBeDeparsedAs(select, sql("n VARCHAR(10) PATH '$.n'"), true);
parse(select.toString());
JsonTableValueColumnDefinition constructed = new JsonTableValueColumnDefinition()
.setColumnName("v")
.setDataType(new ColDataType("VARCHAR").addArgumentsStringList("10"))
.setCollation("utf8mb4_bin").setPathExpression(new StringValue("$.v"));
table.getColumnsClause().getColumnDefinitions().set(0, constructed);
TestUtils.assertStatementCanBeDeparsedAs(select,
sql("v VARCHAR(10) COLLATE utf8mb4_bin PATH '$.v'"), true);
parse(select.toString());
}

@Test
void columnCollationRequiresMySqlDialect() {
String sql = sql("n VARCHAR(10) COLLATE utf8mb4_bin PATH '$.n'");
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql));
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse(sql,
parser -> parser.withDialect(Dialect.POSTGRESQL)));
}

private static PlainSelect parse(String sql) throws JSQLParserException {
return (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sql, true,
parser -> parser.withDialect(Dialect.MYSQL));
}

private static String sql(String column) {
return "SELECT * FROM JSON_TABLE('[{\"n\":\"ab\"}]', '$[*]' COLUMNS (" + column
+ ")) AS jt";
}
}
Loading