@@ -62,7 +62,7 @@ EqualsTo equalsTo = (EqualsTo) select.getWhere();
6262Column a = (Column ) equalsTo. getLeftExpression();
6363Column b = (Column ) equalsTo. getRightExpression();
6464Assertions . assertEquals(" a" , a. getColumnName());
65- Assertions . assertEquals(" b" , b. getColumnName());
65+ Assertions . assertEquals(" b" , b. getColumnName());
6666```
6767
6868The tree is traversable with the Visitor pattern, and the same object model works in reverse:
@@ -148,6 +148,40 @@ Beyond statement shapes, the grammar handles nested sub-selects, bind parameters
148148array-literal ambiguity. The complete reference is on the
149149[ syntax page] ( https://jsqlparser.github.io/JSqlParser/syntax.html ) .
150150
151+ ## Statement classification
152+
153+ Any parsed statement can say what it actually does — no second parse, no visitor to write:
154+
155+ ``` java
156+ StatementFeatures features = CCJSqlParserUtil . parse(sqlStr). getFeatures();
157+
158+ // safeguard a read-only client before anything reaches the database
159+ if (connection. isReadOnly() && features. mayModifyData()) {
160+ throw new SQLException (" rejected: " + features. getUnresolvedReferences());
161+ }
162+
163+ // dispatch correctly
164+ if (features. returnsResultSet()) { statement. executeQuery(sqlStr); }
165+ else { statement. executeUpdate(sqlStr); }
166+ ```
167+
168+ This is not ` sqlStr.startsWith("SELECT") ` with extra steps. ` RETURNING ` turns DML into a row
169+ source, a data-modifying CTE hides a ` DELETE ` inside a ` SELECT ` , and ` INSERT INTO x SELECT .. `
170+ contains a query but returns nothing:
171+
172+ | SQL | returns rows | modifies data |
173+ | ---| ---| ---|
174+ | ` SELECT * FROM t ` | yes | no |
175+ | ` INSERT INTO x SELECT * FROM t ` | no | yes |
176+ | ` DELETE FROM t RETURNING * ` | yes | yes |
177+ | ` WITH c AS (DELETE FROM t RETURNING *) SELECT * FROM c ` | yes | yes |
178+ | ` SELECT nextval('s') ` | yes | * unproven* |
179+
180+ Features are not mutually exclusive, and each is three-valued: proven, not excludable, or ruled
181+ out. ` is() ` answers "did the grammar prove it", ` may() ` answers "could it be ruled out" — so a
182+ guard uses ` may() ` and a dispatcher uses ` is() ` . Function volatility is not a syntactic property,
183+ so anything the caller has not declared pure stays unproven and is listed by name.
184+
151185## Piped SQL
152186
153187Support is progressing for Piped SQL, which writes queries in the order they actually
0 commit comments