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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
| **MySQL table maintenance** | `ANALYZE` · `CHECK` · `CHECKSUM` · `OPTIMIZE` · `REPAIR TABLE` |
| **PostgreSQL maintenance** | `VACUUM` · `ANALYZE` · `CLUSTER` · `CHECKPOINT` |
| **MySQL locks** | `LOCK TABLES` · `UNLOCK TABLES` · `LOCK INSTANCE FOR BACKUP` · `UNLOCK INSTANCE` |
| **PostgreSQL cursors** | `DECLARE … CURSOR` · `FETCH` · `MOVE` · `CLOSE` |
| **MySQL account management** | `CREATE`/`ALTER`/`RENAME USER` · `CREATE ROLE` · `SET ROLE` · `SET DEFAULT ROLE` · `SET PASSWORD` |
| **Tablespaces** | MySQL and PostgreSQL `CREATE TABLESPACE` · `ALTER TABLESPACE` |
| **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` |
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
import net.sf.jsqlparser.statement.create.trigger.CreateEventTrigger;
import net.sf.jsqlparser.statement.create.view.AlterView;
import net.sf.jsqlparser.statement.create.view.CreateView;
import net.sf.jsqlparser.statement.cursor.CloseCursorStatement;
import net.sf.jsqlparser.statement.cursor.DeclareCursorStatement;
import net.sf.jsqlparser.statement.cursor.FetchCursorStatement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.drop.Drop;
import net.sf.jsqlparser.statement.execute.Execute;
Expand Down Expand Up @@ -915,6 +918,24 @@ public enum Feature {
* @see DeclareStatement
*/
declare,
/**
* PostgreSQL "DECLARE name CURSOR FOR query" statement is allowed
*
* @see DeclareCursorStatement
*/
declareCursor,
/**
* PostgreSQL "FETCH" and "MOVE" statements of a cursor are allowed
*
* @see FetchCursorStatement
*/
fetchCursor,
/**
* PostgreSQL "CLOSE" statement of a cursor is allowed
*
* @see CloseCursorStatement
*/
closeCursor,

/** SQL Server local table variables in queries and DML targets. */
tableVariable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@
import net.sf.jsqlparser.statement.create.usermapping.CreateUserMapping;
import net.sf.jsqlparser.statement.create.view.AlterView;
import net.sf.jsqlparser.statement.create.view.CreateView;
import net.sf.jsqlparser.statement.cursor.CloseCursorStatement;
import net.sf.jsqlparser.statement.cursor.DeclareCursorStatement;
import net.sf.jsqlparser.statement.cursor.FetchCursorStatement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.drop.Drop;
import net.sf.jsqlparser.statement.drop.DropPolicy;
Expand Down Expand Up @@ -981,6 +984,33 @@ public <S> Void visit(DeclareStatement declareStatement, S context) {
return sessionOnly();
}

@Override
public <S> Void visit(DeclareCursorStatement declareCursor, S context) {
// opens a cursor of the session over its query, which reads as the cursor is fetched
analysis.claimTopLevel();
analysis.certain(StmtFeature.MODIFIES_SESSION);
if (declareCursor.getQuery() != null) {
declareCursor.getQuery().accept(analysis.selects, context);
}
return null;
}

@Override
public <S> Void visit(FetchCursorStatement fetchCursor, S context) {
// moves the cursor and runs its query that far; only FETCH returns the rows
if (analysis.claimTopLevel() && fetchCursor.getKind() == FetchCursorStatement.Kind.FETCH) {
analysis.certain(StmtFeature.RETURNS_RESULT_SET);
}
analysis.certain(StmtFeature.MODIFIES_SESSION);
analysis.possible(StmtFeature.READS_DATA);
return null;
}

@Override
public <S> Void visit(CloseCursorStatement closeCursor, S context) {
return sessionOnly();
}

@Override
public <S> Void visit(AlterSystemStatement alterSystemStatement, S context) {
analysis.claimTopLevel();
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@
import net.sf.jsqlparser.statement.upsert.Upsert;
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
import net.sf.jsqlparser.statement.create.extension.CreateExtensionRepository;
import net.sf.jsqlparser.statement.cursor.CloseCursorStatement;
import net.sf.jsqlparser.statement.cursor.DeclareCursorStatement;
import net.sf.jsqlparser.statement.cursor.FetchCursorStatement;
import net.sf.jsqlparser.statement.export.ExportDataStatement;
import net.sf.jsqlparser.statement.load.LoadDataStatement;

Expand Down Expand Up @@ -720,6 +723,30 @@ default void visit(BackupLockStatement backupLock) {
visit(backupLock, null);
}

default <S> T visit(DeclareCursorStatement declareCursor, S context) {
return null;
}

default void visit(DeclareCursorStatement declareCursor) {
visit(declareCursor, null);
}

default <S> T visit(FetchCursorStatement fetchCursor, S context) {
return null;
}

default void visit(FetchCursorStatement fetchCursor) {
visit(fetchCursor, null);
}

default <S> T visit(CloseCursorStatement closeCursor, S context) {
return null;
}

default void visit(CloseCursorStatement closeCursor) {
visit(closeCursor, null);
}

<S> T visit(CreatePolicy createPolicy, S context);

default void visit(CreatePolicy createPolicy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@
import net.sf.jsqlparser.util.TableDefinitionTraversal;
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
import net.sf.jsqlparser.statement.create.extension.CreateExtensionRepository;
import net.sf.jsqlparser.statement.cursor.CloseCursorStatement;
import net.sf.jsqlparser.statement.cursor.DeclareCursorStatement;
import net.sf.jsqlparser.statement.cursor.FetchCursorStatement;
import net.sf.jsqlparser.statement.export.ExportDataStatement;
import net.sf.jsqlparser.statement.load.LoadDataStatement;

Expand Down Expand Up @@ -559,6 +562,24 @@ public <S> T visit(BackupLockStatement backupLock, S context) {
return null;
}

@Override
public <S> T visit(DeclareCursorStatement declareCursor, S context) {
if (declareCursor.getQuery() != null) {
declareCursor.getQuery().accept(selectVisitor, context);
}
return null;
}

@Override
public <S> T visit(FetchCursorStatement fetchCursor, S context) {
return null;
}

@Override
public <S> T visit(CloseCursorStatement closeCursor, S context) {
return null;
}

@Override
public <S> T visit(CreatePolicy createPolicy, S context) {
fromItemVisitor.visitFromItem(createPolicy.getTable(), context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*-
* #%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.cursor;

import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitor;

/**
* PostgreSQL {@code CLOSE {name | ALL}}, which closes one cursor or every open cursor of the
* session.<br>
* Examples:<br>
* CLOSE c<br>
* CLOSE ALL<br>
*
* @see <a href="https://www.postgresql.org/docs/18/sql-close.html">PostgreSQL CLOSE</a>
*/
public class CloseCursorStatement extends ASTNodeAccessImpl implements Statement {
private String cursorName;

/** CLOSE ALL. */
public CloseCursorStatement() {}

public CloseCursorStatement(String cursorName) {
this.cursorName = cursorName;
}

/** Returns null for CLOSE ALL. */
public String getCursorName() {
return cursorName;
}

/** A null name makes the statement CLOSE ALL. */
public void setCursorName(String cursorName) {
this.cursorName = cursorName;
}

/** Whether the statement is CLOSE ALL, which is when it has no cursor name. */
public boolean isAll() {
return cursorName == null;
}

@Override
public StringBuilder appendTo(StringBuilder builder) {
return builder.append("CLOSE ").append(isAll() ? "ALL" : cursorName);
}

@Override
public String toString() {
return appendTo(new StringBuilder()).toString();
}

@Override
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
return statementVisitor.visit(this, context);
}
}
44 changes: 44 additions & 0 deletions src/main/java/net/sf/jsqlparser/statement/cursor/CursorOption.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*-
* #%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.cursor;

/**
* An option of PostgreSQL's {@code DECLARE name [options] CURSOR}. PostgreSQL's grammar takes them
* in any order and repeated; the server rejects contradicting ones such as SCROLL and NO SCROLL
* when it runs the statement.
*
* @see <a href="https://www.postgresql.org/docs/18/sql-declare.html">PostgreSQL DECLARE</a>
*/
public enum CursorOption {
/** Returns the rows in binary rather than text format. */
BINARY("BINARY"),

/** Leaves the visibility of later changes to the implementation. */
ASENSITIVE("ASENSITIVE"),

/** Hides later changes; PostgreSQL's cursors are always insensitive. */
INSENSITIVE("INSENSITIVE"),

/** Allows fetching backward. */
SCROLL("SCROLL"),

/** Forbids fetching backward. */
NO_SCROLL("NO SCROLL");

private final String keyword;

CursorOption(String keyword) {
this.keyword = keyword;
}

public String getKeyword() {
return keyword;
}
}
Loading
Loading