Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*-
* #%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.statement.select;

import java.io.Serializable;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;

/**
* Models the legacy MySQL {@code PROCEDURE ANALYSE()} clause.
*
* @see <a href="https://dev.mysql.com/doc/refman/5.7/en/procedure-analyse.html">MySQL 5.7 Reference
* Manual</a>
*/
public class MySqlProcedureAnalyse extends ASTNodeAccessImpl implements Serializable {

private LongValue maxElements;
private LongValue maxMemory;

public MySqlProcedureAnalyse() {}

public MySqlProcedureAnalyse(LongValue maxElements, LongValue maxMemory) {
this.maxElements = maxElements;
this.maxMemory = maxMemory;
}

public LongValue getMaxElements() {
return maxElements;
}

public void setMaxElements(LongValue maxElements) {
this.maxElements = maxElements;
}

public MySqlProcedureAnalyse withMaxElements(LongValue maxElements) {
setMaxElements(maxElements);
return this;
}

public LongValue getMaxMemory() {
return maxMemory;
}

public void setMaxMemory(LongValue maxMemory) {
this.maxMemory = maxMemory;
}

public MySqlProcedureAnalyse withMaxMemory(LongValue maxMemory) {
setMaxMemory(maxMemory);
return this;
}

public StringBuilder appendTo(StringBuilder builder) {
builder.append(" PROCEDURE ANALYSE(");
if (maxElements != null) {
builder.append(maxElements);
if (maxMemory != null) {
builder.append(", ").append(maxMemory);
}
}
return builder.append(")");
}

@Override
public String toString() {
return appendTo(new StringBuilder()).toString();
}
}
17 changes: 17 additions & 0 deletions src/main/java/net/sf/jsqlparser/statement/select/Select.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public abstract class Select extends ASTNodeAccessImpl implements Statement, Exp
Offset offset;
Fetch fetch;
WithIsolation isolation;
MySqlProcedureAnalyse mySqlProcedureAnalyse;
boolean oracleSiblings = false;

ForClause forClause = null;
Expand Down Expand Up @@ -322,6 +323,19 @@ public Select withIsolation(WithIsolation isolation) {
return this;
}

public MySqlProcedureAnalyse getMySqlProcedureAnalyse() {
return mySqlProcedureAnalyse;
}

public void setMySqlProcedureAnalyse(MySqlProcedureAnalyse mySqlProcedureAnalyse) {
this.mySqlProcedureAnalyse = mySqlProcedureAnalyse;
}

public Select withMySqlProcedureAnalyse(MySqlProcedureAnalyse mySqlProcedureAnalyse) {
setMySqlProcedureAnalyse(mySqlProcedureAnalyse);
return this;
}

public ForMode getForMode() {
return this.forMode;
}
Expand Down Expand Up @@ -521,6 +535,9 @@ public StringBuilder appendTo(StringBuilder builder) {
if (offset != null) {
builder.append(offset);
}
if (mySqlProcedureAnalyse != null) {
mySqlProcedureAnalyse.appendTo(builder);
}
if (fetch != null) {
builder.append(fetch);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ public <S> T visit(PlainSelect plainSelect, S context) {
if (plainSelect.getOffset() != null) {
expressionVisitor.visitExpression(plainSelect.getOffset().getOffset(), context);
}
if (plainSelect.getMySqlProcedureAnalyse() != null) {
expressionVisitor.visitExpression(
plainSelect.getMySqlProcedureAnalyse().getMaxElements(), context);
expressionVisitor.visitExpression(
plainSelect.getMySqlProcedureAnalyse().getMaxMemory(), context);
}
if (plainSelect.getFetch() != null) {
expressionVisitor.visitExpression(plainSelect.getFetch().getExpression(), context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,9 @@ public <S> StringBuilder visit(PlainSelect plainSelect, S context) {
if (plainSelect.getOffset() != null) {
visit(plainSelect.getOffset());
}
if (plainSelect.getMySqlProcedureAnalyse() != null) {
plainSelect.getMySqlProcedureAnalyse().appendTo(builder);
}
if (plainSelect.getFetch() != null) {
visit(plainSelect.getFetch());
}
Expand Down
33 changes: 32 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -819,11 +819,12 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {

switch (kind) {
// Safe as implicit identifiers (no clause collision)
case K_TABLES: case K_OPTIMIZE: case K_PROCEDURE: case K_PUBLIC:
case K_TABLES: case K_OPTIMIZE: case K_PUBLIC:
case K_CASEWHEN: case K_IIF:
return true;

// Safe when the follower doesn't form a clause
case K_PROCEDURE: return nextKind != K_ANALYSE;
case K_GROUP: return nextKind != K_BY;
case K_ORDER: return nextKind != K_BY && nextKind != K_SIBLINGS;
case K_CONNECT: return nextKind != K_BY;
Expand Down Expand Up @@ -1250,6 +1251,7 @@ String NonReservedWord() :
| tk=<K_ALIGN:"ALIGN">
| tk=<K_ALTER:"ALTER">
| tk=<K_ALWAYS:"ALWAYS">
| tk=<K_ANALYSE:"ANALYSE">
| tk=<K_ANALYZE:"ANALYZE">
| tk=<K_APPEND_ONLY:"APPEND_ONLY">
| tk=<K_APPLY:"APPLY">
Expand Down Expand Up @@ -5451,6 +5453,7 @@ PlainSelect PlainSelect() #PlainSelect:
boolean partitionByBrackets = false;
List<Table> intoTables = null;
MySqlSelectIntoClause mySqlSelectIntoClause = null;
MySqlProcedureAnalyse mySqlProcedureAnalyse = null;
Table updateTable = null;
List<Table> updateTables = new ArrayList<Table>();
Wait wait = null;
Expand Down Expand Up @@ -5580,6 +5583,10 @@ PlainSelect PlainSelect() #PlainSelect:
} ]
[ LOOKAHEAD(<K_OFFSET>) offset = Offset() { plainSelect.setOffset(offset); } ]
[ LOOKAHEAD(<K_LIMIT>, { limit==null }) limit = LimitWithOffset() { plainSelect.setLimit(limit); } ]
[ LOOKAHEAD(<K_PROCEDURE> <K_ANALYSE>)
mySqlProcedureAnalyse = MySqlProcedureAnalyse()
{ plainSelect.setMySqlProcedureAnalyse(mySqlProcedureAnalyse); }
]
[ LOOKAHEAD(<K_FETCH>) fetch = Fetch() { plainSelect.setFetch(fetch); } ]
[ LOOKAHEAD(<K_WITH> <K_ISOLATION>) withIsolation = WithIsolation() { plainSelect.setIsolation(withIsolation); } ]
[ LOOKAHEAD(2)
Expand Down Expand Up @@ -6226,6 +6233,30 @@ MySqlSelectIntoClause MySqlSelectIntoClause(MySqlSelectIntoClause.Position posit
}
}

MySqlProcedureAnalyse MySqlProcedureAnalyse():
{
MySqlProcedureAnalyse procedureAnalyse = new MySqlProcedureAnalyse();
Token maxElements = null;
Token maxMemory = null;
}
{
<K_PROCEDURE> <K_ANALYSE> "("
[
maxElements=<S_LONG>
[ "," maxMemory=<S_LONG> ]
]
")"
{
if (maxElements != null) {
procedureAnalyse.setMaxElements(new LongValue(maxElements.image));
}
if (maxMemory != null) {
procedureAnalyse.setMaxMemory(new LongValue(maxMemory.image));
}
return procedureAnalyse;
}
}

void MySqlSelectIntoOutfileTail(MySqlSelectIntoClause intoClause):
{
Token token;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*-
* #%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.statement.select;

import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import org.junit.jupiter.api.Test;

class MySqlProcedureAnalyseTest {

@Test
void parseWithoutParameters() throws JSQLParserException {
Select select = (Select) assertSqlCanBeParsedAndDeparsed(
"SELECT col1, col2 FROM heavy_table PROCEDURE ANALYSE()");

MySqlProcedureAnalyse procedureAnalyse = select.getMySqlProcedureAnalyse();
assertNotNull(procedureAnalyse);
assertNull(procedureAnalyse.getMaxElements());
assertNull(procedureAnalyse.getMaxMemory());
}

@Test
void parseOneParameter() throws JSQLParserException {
Select select = (Select) assertSqlCanBeParsedAndDeparsed(
"SELECT col1 FROM heavy_table PROCEDURE ANALYSE(10)");

MySqlProcedureAnalyse procedureAnalyse = select.getMySqlProcedureAnalyse();
assertEquals("10", procedureAnalyse.getMaxElements().getStringValue());
assertNull(procedureAnalyse.getMaxMemory());
}

@Test
void parseTwoParametersAfterLimit() throws JSQLParserException {
Select select = (Select) assertSqlCanBeParsedAndDeparsed(
"SELECT col1 FROM heavy_table LIMIT 20 PROCEDURE ANALYSE(10, 256)");

MySqlProcedureAnalyse procedureAnalyse = select.getMySqlProcedureAnalyse();
assertEquals("10", procedureAnalyse.getMaxElements().getStringValue());
assertEquals("256", procedureAnalyse.getMaxMemory().getStringValue());
}

@Test
void keepAnalyseAndProcedureAvailableAsIdentifiers() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT analyse FROM heavy_table");
assertSqlCanBeParsedAndDeparsed("SELECT * FROM heavy_table procedure");
}

@Test
void rejectInvalidForms() {
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("SELECT * FROM t PROCEDURE ANALYSE"));
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("SELECT * FROM t PROCEDURE ANALYSE(-1)"));
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("SELECT * FROM t PROCEDURE ANALYSE(1, 2, 3)"));
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("SELECT * FROM t PROCEDURE ANALYZE()"));
}
}
Loading