Skip to content

Commit 34033a7

Browse files
authored
fix: share ALTER index option boundaries and MySQL engine attributes (#2702)
Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent c12c3aa commit 34033a7

3 files changed

Lines changed: 94 additions & 6 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/create/table/TableOption.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
public class TableOption implements Serializable {
2222

2323
public enum Kind {
24-
ENGINE, CHARACTER_SET, COLLATE, COMMENT, AUTO_INCREMENT, STATS_AUTO_RECALC, STATS_PERSISTENT, STATS_SAMPLE_PAGES, UNION, ENCRYPTION, PASSWORD, DATA_DIRECTORY, INDEX_DIRECTORY, OTHER
24+
ENGINE, CHARACTER_SET, COLLATE, COMMENT, AUTO_INCREMENT, STATS_AUTO_RECALC, STATS_PERSISTENT, STATS_SAMPLE_PAGES, UNION, ENCRYPTION, PASSWORD, DATA_DIRECTORY, INDEX_DIRECTORY, ENGINE_ATTRIBUTE, SECONDARY_ENGINE_ATTRIBUTE, OTHER
2525
}
2626

2727
private Kind kind = Kind.OTHER;

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,9 +1556,13 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
15561556
|| "STATS_SAMPLE_PAGES".equalsIgnoreCase(name);
15571557
}
15581558

1559+
private boolean isMySqlEngineAttributeAhead() {
1560+
return isKeywordAhead("ENGINE_ATTRIBUTE") || isKeywordAhead("SECONDARY_ENGINE_ATTRIBUTE");
1561+
}
1562+
15591563
private boolean isMySqlScalarTableOptionAhead() {
15601564
int kind = getToken(1).kind;
1561-
return kind == K_ENCRYPTION || kind == K_PASSWORD || kind == K_AUTO_INCREMENT
1565+
return isMySqlEngineAttributeAhead() || kind == K_ENCRYPTION || kind == K_PASSWORD || kind == K_AUTO_INCREMENT
15621566
|| (kind == K_DATA || kind == K_INDEX) && getToken(2).kind == K_DIRECTORY;
15631567
}
15641568

@@ -15301,6 +15305,9 @@ TableOption MySqlTableOption(): {
1530115305
"Expected DEFAULT, 0, or 1 for " + name);
1530215306
}
1530315307
|
15308+
LOOKAHEAD({ isMySqlScalarTableOptionAhead() }) option=MySqlScalarTableOption()
15309+
{ return option; }
15310+
|
1530415311
tk=<K_ENGINE> {
1530515312
name = tk.image;
1530615313
kind = TableOption.Kind.ENGINE;
@@ -15335,9 +15342,6 @@ TableOption MySqlTableOption(): {
1533515342
}
1533615343
[ "=" { useEquals = true; } ]
1533715344
value=MySqlTableOptionValue()
15338-
|
15339-
LOOKAHEAD({ isMySqlScalarTableOptionAhead() }) option=MySqlScalarTableOption()
15340-
{ return option; }
1534115345
)
1534215346
{
1534315347
option = new TableOption(kind, name, value, useEquals);
@@ -15365,6 +15369,10 @@ TableOption MySqlScalarTableOption():
1536515369
| <K_PASSWORD> { name = "PASSWORD"; kind = TableOption.Kind.PASSWORD; }
1536615370
| <K_DATA> <K_DIRECTORY> { name = "DATA DIRECTORY"; kind = TableOption.Kind.DATA_DIRECTORY; }
1536715371
| <K_INDEX> <K_DIRECTORY> { name = "INDEX DIRECTORY"; kind = TableOption.Kind.INDEX_DIRECTORY; }
15372+
| LOOKAHEAD({ isMySqlEngineAttributeAhead() }) token=<S_IDENTIFIER> {
15373+
name = token.image.toUpperCase(Locale.ROOT);
15374+
kind = TableOption.Kind.valueOf(name);
15375+
}
1536815376
)
1536915377
[ "=" { useEquals = true; } ] token=<S_CHAR_LITERAL> { value = token.image; }
1537015378
)
@@ -16743,7 +16751,8 @@ void IndexOptionList(List<String> list) :
1674316751
{}
1674416752
{
1674516753
(
16746-
LOOKAHEAD(2) IndexOption(list)
16754+
LOOKAHEAD(2, { !(getToken(1).kind == K_USING && getToken(2).kind == K_INDEX) })
16755+
IndexOption(list)
1674716756
)*
1674816757
}
1674916758

@@ -16764,9 +16773,13 @@ void IndexOption(List<String> list) :
1676416773
Token tk2 = null;
1676516774
String sk1 = null;
1676616775
boolean useEqual = false;
16776+
TableOption attribute;
1676716777
}
1676816778
{
1676916779
(
16780+
LOOKAHEAD({ isMySqlEngineAttributeAhead() }) attribute=MySqlScalarTableOption()
16781+
{ list.addAll(attribute.getTokens()); }
16782+
|
1677016783
tk1=<K_KEY_BLOCK_SIZE> ["=" { useEqual = true; } ] tk2=<S_LONG>
1677116784
{
1677216785
list.add("KEY_BLOCK_SIZE" + (useEqual ? " = " : "") + tk2.image);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.alter;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
import net.sf.jsqlparser.JSQLParserException;
14+
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
15+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
16+
import net.sf.jsqlparser.statement.Statement;
17+
import net.sf.jsqlparser.statement.create.table.Index;
18+
import net.sf.jsqlparser.statement.create.table.TableOption;
19+
import net.sf.jsqlparser.util.deparser.StatementDeParser;
20+
import org.junit.jupiter.api.Test;
21+
import org.junit.jupiter.params.ParameterizedTest;
22+
import org.junit.jupiter.params.provider.ValueSource;
23+
24+
class IndexOptionBoundaryTest {
25+
@ParameterizedTest
26+
@ValueSource(strings = {"UNIQUE", "PRIMARY KEY"})
27+
void tablespaceBelongsToConstraintOptions(String key) throws JSQLParserException {
28+
Alter alter = (Alter) parse("ALTER TABLE t ADD CONSTRAINT k " + key
29+
+ " (id) USING INDEX TABLESPACE pg_default DEFERRABLE", Dialect.POSTGRESQL);
30+
Index index = alter.getAlterExpressions().get(0).getIndex();
31+
assertEquals("pg_default", index.getTableSpace());
32+
assertNull(index.getUsing());
33+
assertTrue(index.getConstraintAttributes().getDeferrable());
34+
index.setTableSpace("other_space");
35+
assertEquals("ALTER TABLE t ADD CONSTRAINT k " + key
36+
+ " (id) USING INDEX TABLESPACE other_space DEFERRABLE", alter.toString());
37+
assertRoundTrip(alter, Dialect.POSTGRESQL);
38+
}
39+
40+
@ParameterizedTest
41+
@ValueSource(strings = {"SECONDARY_ENGINE_ATTRIBUTE = '{}'",
42+
"SECONDARY_ENGINE_ATTRIBUTE '{\"key\": \"value\"}'",
43+
"SECONDARY_ENGINE_ATTRIBUTE = '{}' VISIBLE COMMENT 'index'"})
44+
void mysqlAddIndexAttributes(String options) throws JSQLParserException {
45+
Alter alter = (Alter) parse("ALTER TABLE t ADD INDEX ix (id) " + options, Dialect.MYSQL);
46+
assertEquals("ix", alter.getAlterExpressions().get(0).getIndex().getName());
47+
assertEquals("ALTER TABLE t ADD INDEX ix (id) " + options, alter.toString());
48+
assertRoundTrip(alter, Dialect.MYSQL);
49+
}
50+
51+
@Test
52+
void sharedTableAttributeRemainsMutable() throws JSQLParserException {
53+
Alter alter =
54+
(Alter) parse("ALTER TABLE t SECONDARY_ENGINE_ATTRIBUTE = '{}'", Dialect.MYSQL);
55+
AlterExpressionTableOption action =
56+
(AlterExpressionTableOption) alter.getAlterExpressions().get(0);
57+
TableOption option = action.getStructuredTableOption();
58+
assertEquals(TableOption.Kind.SECONDARY_ENGINE_ATTRIBUTE, option.getKind());
59+
option.setValue("'{\"key\":1}'");
60+
assertEquals("ALTER TABLE t SECONDARY_ENGINE_ATTRIBUTE = '{\"key\":1}'", alter.toString());
61+
assertRoundTrip(alter, Dialect.MYSQL);
62+
}
63+
64+
private static Statement parse(String sql, Dialect dialect) throws JSQLParserException {
65+
return CCJSqlParserUtil.parse(sql, p -> p.withDialect(dialect));
66+
}
67+
68+
private static void assertRoundTrip(Statement statement, Dialect dialect)
69+
throws JSQLParserException {
70+
StringBuilder sql = new StringBuilder();
71+
statement.accept(new StatementDeParser(sql), null);
72+
assertEquals(statement.toString(), sql.toString());
73+
assertEquals(statement.toString(), parse(sql.toString(), dialect).toString());
74+
}
75+
}

0 commit comments

Comments
 (0)