Skip to content

Commit d0b0642

Browse files
hayssamsclaude
andcommitted
Support PRAGMA statement
Adds PRAGMA name, PRAGMA name(arguments) and PRAGMA name = value, as used by DuckDB and SQLite to read and set database settings. PRAGMA stays a non-reserved keyword. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
1 parent 26a1fba commit d0b0642

9 files changed

Lines changed: 199 additions & 0 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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;
11+
12+
import net.sf.jsqlparser.expression.Expression;
13+
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
14+
15+
/**
16+
* {@code PRAGMA name}, {@code PRAGMA name(arguments)} and {@code PRAGMA name = value}, used by
17+
* DuckDB and SQLite to read or set database settings.
18+
*
19+
* @see <a href="https://duckdb.org/docs/stable/configuration/pragmas">PRAGMA statements</a>
20+
*/
21+
public class PragmaStatement implements Statement {
22+
private String name;
23+
private ExpressionList<?> parameters;
24+
private Expression value;
25+
26+
public PragmaStatement() {}
27+
28+
public PragmaStatement(String name) {
29+
this.name = name;
30+
}
31+
32+
public String getName() {
33+
return name;
34+
}
35+
36+
public void setName(String name) {
37+
this.name = name;
38+
}
39+
40+
public PragmaStatement withName(String name) {
41+
setName(name);
42+
return this;
43+
}
44+
45+
public ExpressionList<?> getParameters() {
46+
return parameters;
47+
}
48+
49+
public void setParameters(ExpressionList<?> parameters) {
50+
this.parameters = parameters;
51+
}
52+
53+
public PragmaStatement withParameters(ExpressionList<?> parameters) {
54+
setParameters(parameters);
55+
return this;
56+
}
57+
58+
public Expression getValue() {
59+
return value;
60+
}
61+
62+
public void setValue(Expression value) {
63+
this.value = value;
64+
}
65+
66+
public PragmaStatement withValue(Expression value) {
67+
setValue(value);
68+
return this;
69+
}
70+
71+
public StringBuilder appendTo(StringBuilder builder) {
72+
builder.append("PRAGMA ").append(name);
73+
if (parameters != null) {
74+
builder.append("(").append(parameters).append(")");
75+
}
76+
if (value != null) {
77+
builder.append(" = ").append(value);
78+
}
79+
return builder;
80+
}
81+
82+
@Override
83+
public String toString() {
84+
return appendTo(new StringBuilder()).toString();
85+
}
86+
87+
@Override
88+
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
89+
return statementVisitor.visit(this, context);
90+
}
91+
}

‎src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,13 @@ public <S> Void visit(Drop drop, S context) {
521521
return null;
522522
}
523523

524+
@Override
525+
public <S> Void visit(PragmaStatement pragmaStatement, S context) {
526+
analysis.claimTopLevel();
527+
analysis.certain(StmtFeature.MODIFIES_SESSION);
528+
return null;
529+
}
530+
524531
@Override
525532
public <S> Void visit(PurgeStatement purgeStatement, S context) {
526533
analysis.claimTopLevel();

‎src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,12 @@ default void visit(RenameTableStatement renameTableStatement) {
371371
this.visit(renameTableStatement, null);
372372
}
373373

374+
<S> T visit(PragmaStatement pragmaStatement, S context);
375+
376+
default void visit(PragmaStatement pragmaStatement) {
377+
this.visit(pragmaStatement, null);
378+
}
379+
374380
<S> T visit(PurgeStatement purgeStatement, S context);
375381

376382
default void visit(PurgeStatement purgeStatement) {

‎src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,11 @@ public <S> T visit(RenameTableStatement renameTableStatement, S context) {
618618
return null;
619619
}
620620

621+
@Override
622+
public <S> T visit(PragmaStatement pragmaStatement, S context) {
623+
return null;
624+
}
625+
621626
@Override
622627
public <S> T visit(PurgeStatement purgeStatement, S context) {
623628
return null;

‎src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@
209209
import net.sf.jsqlparser.statement.update.Update;
210210
import net.sf.jsqlparser.statement.update.UpdateSet;
211211
import net.sf.jsqlparser.statement.upsert.Upsert;
212+
import net.sf.jsqlparser.statement.PragmaStatement;
212213

213214

214215
/**
@@ -2468,6 +2469,17 @@ public void visit(RenameTableStatement renameTableStatement) {
24682469
StatementVisitor.super.visit(renameTableStatement);
24692470
}
24702471

2472+
@Override
2473+
public <S> Void visit(PragmaStatement pragmaStatement, S context) {
2474+
// no tables involved in this statement
2475+
return null;
2476+
}
2477+
2478+
@Override
2479+
public void visit(PragmaStatement pragmaStatement) {
2480+
StatementVisitor.super.visit(pragmaStatement);
2481+
}
2482+
24712483
@Override
24722484
public <S> Void visit(PurgeStatement purgeStatement, S context) {
24732485
if (purgeStatement.getPurgeObjectType() == PurgeObjectType.TABLE) {

‎src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
import net.sf.jsqlparser.statement.update.ParenthesedUpdate;
9898
import net.sf.jsqlparser.statement.update.Update;
9999
import net.sf.jsqlparser.statement.upsert.Upsert;
100+
import net.sf.jsqlparser.statement.PragmaStatement;
100101

101102
public class StatementDeParser extends AbstractDeParser<Statement>
102103
implements StatementVisitor<StringBuilder> {
@@ -556,6 +557,12 @@ public <S> StringBuilder visit(RenameTableStatement renameTableStatement, S cont
556557
return builder;
557558
}
558559

560+
@Override
561+
public <S> StringBuilder visit(PragmaStatement pragmaStatement, S context) {
562+
pragmaStatement.appendTo(builder);
563+
return builder;
564+
}
565+
559566
@Override
560567
public <S> StringBuilder visit(PurgeStatement purgeStatement, S context) {
561568
purgeStatement.appendTo(builder);

‎src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
import net.sf.jsqlparser.statement.upsert.Upsert;
100100
import net.sf.jsqlparser.util.validation.ValidationCapability;
101101
import net.sf.jsqlparser.util.validation.metadata.NamedObject;
102+
import net.sf.jsqlparser.statement.PragmaStatement;
102103

103104
/**
104105
* @author gitmotte
@@ -477,6 +478,12 @@ public <S> Void visit(RenameTableStatement renameTableStatement, S context) {
477478
return null;
478479
}
479480

481+
@Override
482+
public <S> Void visit(PragmaStatement pragmaStatement, S context) {
483+
// TODO: not yet implemented
484+
return null;
485+
}
486+
480487
@Override
481488
public <S> Void visit(PurgeStatement purgeStatement, S context) {
482489
// TODO: not yet implemented
@@ -693,6 +700,10 @@ public void visit(RenameTableStatement renameTableStatement) {
693700
visit(renameTableStatement, null);
694701
}
695702

703+
public void visit(PragmaStatement pragmaStatement) {
704+
visit(pragmaStatement, null);
705+
}
706+
696707
public void visit(PurgeStatement purgeStatement) {
697708
visit(purgeStatement, null);
698709
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,6 +2201,7 @@ String NonReservedWord() :
22012201
| tk=<K_PLACING:"PLACING">
22022202
| tk=<K_PLAN:"PLAN">
22032203
| tk=<K_PLUS:"PLUS">
2204+
| tk=<K_PRAGMA: "PRAGMA">
22042205
| tk=<K_PRECEDING: "PRECEDING">
22052206
| tk=<K_PRIMARY:"PRIMARY">
22062207
| tk=<K_POLICY:"POLICY">
@@ -2939,6 +2940,8 @@ Statement SingleStatement() :
29392940
|
29402941
LOOKAHEAD({ isKeywordAhead("REVOKE") }) stm = Revoke(false)
29412942
|
2943+
LOOKAHEAD({ getToken(1).kind == K_PRAGMA }) stm = PragmaStatement()
2944+
|
29422945
stm = PurgeStatement()
29432946
|
29442947
stm = SessionStatement()
@@ -4340,6 +4343,31 @@ RenameTableStatement RenameTableStatement(): {
43404343
}
43414344
}
43424345

4346+
/**
4347+
* DuckDB/SQLite PRAGMA name [(arguments) | = value].
4348+
*/
4349+
PragmaStatement PragmaStatement() #PragmaStatement:
4350+
{
4351+
PragmaStatement pragmaStatement = new PragmaStatement();
4352+
ObjectNames name;
4353+
ExpressionList parameters;
4354+
Expression value;
4355+
}
4356+
{
4357+
<K_PRAGMA> name=RelObjectNames()
4358+
{ pragmaStatement.setName(String.join(".", name.getNames())); }
4359+
// greedy: parentheses right after the pragma name are its arguments
4360+
[
4361+
LOOKAHEAD(2)
4362+
(
4363+
"(" parameters=ExpressionList() ")" { pragmaStatement.setParameters(parameters); }
4364+
|
4365+
"=" value=SimpleExpression() { pragmaStatement.setValue(value); }
4366+
)
4367+
]
4368+
{ return pragmaStatement; }
4369+
}
4370+
43434371
PurgeStatement PurgeStatement(): {
43444372
PurgeStatement purgeStatement = null;
43454373
Table table;

‎src/test/java/net/sf/jsqlparser/statement/select/DuckDBTest.java‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.sf.jsqlparser.statement.create.table.CreateTable;
1515
import net.sf.jsqlparser.statement.insert.Insert;
1616
import net.sf.jsqlparser.statement.DescribeStatement;
17+
import net.sf.jsqlparser.statement.PragmaStatement;
1718
import net.sf.jsqlparser.test.TestUtils;
1819
import org.junit.jupiter.api.Assertions;
1920
import org.junit.jupiter.api.Test;
@@ -205,4 +206,35 @@ void testDescribeTableStillParses() throws JSQLParserException {
205206

206207
Assertions.assertEquals("t", describe.getTable().getName());
207208
}
209+
210+
@Test
211+
void testPragmaWithArguments() throws JSQLParserException {
212+
String sqlStr = "PRAGMA table_info('t')";
213+
PragmaStatement pragma =
214+
(PragmaStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
215+
216+
Assertions.assertEquals("table_info", pragma.getName());
217+
Assertions.assertEquals(1, pragma.getParameters().size());
218+
}
219+
220+
@Test
221+
void testPragmaAssignment() throws JSQLParserException {
222+
String sqlStr = "PRAGMA memory_limit = '1GB'";
223+
PragmaStatement pragma =
224+
(PragmaStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
225+
226+
Assertions.assertEquals("'1GB'", pragma.getValue().toString());
227+
}
228+
229+
@Test
230+
void testPragmaWithoutArguments() throws JSQLParserException {
231+
String sqlStr = "PRAGMA database_list";
232+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
233+
}
234+
235+
@Test
236+
void testPragmaRemainsUsableAsIdentifier() throws JSQLParserException {
237+
String sqlStr = "SELECT pragma FROM t";
238+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
239+
}
208240
}

0 commit comments

Comments
 (0)