Skip to content

Commit e602d5d

Browse files
authored
Merge pull request #42 from EzFramework/feature/postgresql-support
PostgreSQL Support
2 parents 87e2ef6 + 604ad78 commit e602d5d

13 files changed

Lines changed: 437 additions & 6 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>com.github.EzFramework</groupId>
77
<artifactId>java-query-builder</artifactId>
8-
<version>1.0.7</version>
8+
<version>1.1.0</version>
99
<packaging>jar</packaging>
1010

1111
<name>JavaQueryBuilder</name>

src/main/java/com/github/ezframework/javaquerybuilder/query/Query.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public class Query {
6767
/** Suffix appended to values for LIKE and NOT LIKE conditions. */
6868
private String likeSuffix = "%";
6969

70+
/** The columns to include in a {@code RETURNING} clause; empty means no RETURNING. */
71+
private List<String> returningColumns = new ArrayList<>();
72+
7073
/**
7174
* Gets the source table for the query.
7275
*
@@ -378,4 +381,24 @@ public String getLikeSuffix() {
378381
public void setLikeSuffix(final String likeSuffix) {
379382
this.likeSuffix = likeSuffix;
380383
}
384+
385+
/**
386+
* Returns the columns to include in a {@code RETURNING} clause.
387+
*
388+
* <p>An empty list means no {@code RETURNING} clause will be rendered.
389+
*
390+
* @return the list of returning column names; never {@code null}
391+
*/
392+
public List<String> getReturningColumns() {
393+
return returningColumns;
394+
}
395+
396+
/**
397+
* Sets the columns to include in a {@code RETURNING} clause.
398+
*
399+
* @param returningColumns the list of column names; must not be {@code null}
400+
*/
401+
public void setReturningColumns(final List<String> returningColumns) {
402+
this.returningColumns = returningColumns;
403+
}
381404
}

src/main/java/com/github/ezframework/javaquerybuilder/query/builder/DeleteBuilder.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class DeleteBuilder {
2626
/** The WHERE conditions. */
2727
private final List<ConditionEntry> conditions = new ArrayList<>();
2828

29+
/** The RETURNING columns (PostgreSQL only). */
30+
private final List<String> returningColumns = new ArrayList<>();
31+
2932
/** The defaults configuration for this builder instance. */
3033
private QueryBuilderDefaults queryBuilderDefaults = QueryBuilderDefaults.global();
3134

@@ -190,6 +193,20 @@ public DeleteBuilder whereExistsSubquery(Query subquery) {
190193
return this;
191194
}
192195

196+
/**
197+
* Specifies the columns to include in a {@code RETURNING} clause (PostgreSQL only).
198+
*
199+
* <p>This is only rendered when the active dialect supports {@code RETURNING}
200+
* (i.e., {@link com.github.ezframework.javaquerybuilder.query.sql.SqlDialect#POSTGRESQL}).
201+
*
202+
* @param columns one or more column names; must not be {@code null} or empty
203+
* @return this builder instance for chaining
204+
*/
205+
public DeleteBuilder returning(final String... columns) {
206+
returningColumns.addAll(java.util.Arrays.asList(columns));
207+
return this;
208+
}
209+
193210
/**
194211
* Builds the SQL DELETE statement using standard SQL.
195212
*
@@ -217,6 +234,7 @@ private Query toQuery() {
217234
q.setTable(table);
218235
q.setConditions(new ArrayList<>(conditions));
219236
q.setLimit(-1);
237+
q.setReturningColumns(new ArrayList<>(returningColumns));
220238
return q;
221239
}
222240
}

src/main/java/com/github/ezframework/javaquerybuilder/query/builder/InsertBuilder.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,52 @@ public class InsertBuilder {
2424
/** The values to insert. */
2525
private final List<Object> values = new ArrayList<>();
2626

27+
/** The RETURNING columns (PostgreSQL only). */
28+
private final List<String> returningColumns = new ArrayList<>();
29+
30+
/**
31+
* Sets the table to insert into.
32+
*
33+
* @param table the table name
34+
* @return this builder instance for chaining
35+
*/
2736
public InsertBuilder into(String table) {
2837
this.table = table;
2938
return this;
3039
}
3140

41+
/**
42+
* Adds a column-value pair to the INSERT statement.
43+
*
44+
* @param column the column name
45+
* @param value the value to insert
46+
* @return this builder instance for chaining
47+
*/
3248
public InsertBuilder value(String column, Object value) {
3349
columns.add(column);
3450
values.add(value);
3551
return this;
3652
}
3753

54+
/**
55+
* Specifies the columns to include in a {@code RETURNING} clause (PostgreSQL only).
56+
*
57+
* <p>The {@code RETURNING} clause is appended unconditionally to the SQL string;
58+
* it is the caller's responsibility to use a PostgreSQL connection.
59+
*
60+
* @param columns one or more column names; must not be {@code null} or empty
61+
* @return this builder instance for chaining
62+
*/
63+
public InsertBuilder returning(final String... columns) {
64+
returningColumns.addAll(java.util.Arrays.asList(columns));
65+
return this;
66+
}
67+
68+
/**
69+
* Builds the SQL INSERT statement using the default dialect.
70+
*
71+
* @return the SQL result
72+
*/
3873
public SqlResult build() {
3974
return build(null);
4075
}
@@ -52,6 +87,9 @@ public SqlResult build(SqlDialect dialect) {
5287
sql.append(") VALUES (");
5388
sql.append(String.join(", ", Collections.nCopies(values.size(), "?")));
5489
sql.append(")");
90+
if (!returningColumns.isEmpty()) {
91+
sql.append(" RETURNING ").append(String.join(", ", returningColumns));
92+
}
5593
return new SqlResult() {
5694
@Override
5795
public String getSql() {

src/main/java/com/github/ezframework/javaquerybuilder/query/builder/QueryBuilder.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,51 @@ public QueryBuilder whereNotLike(String column, String value) {
284284
return this;
285285
}
286286

287+
/**
288+
* Adds an {@code ILIKE} WHERE condition joined with AND (PostgreSQL only).
289+
*
290+
* <p>Produces case-insensitive pattern matching. Rendered correctly only when
291+
* the active dialect is {@link com.github.ezframework.javaquerybuilder.query.sql.SqlDialect#POSTGRESQL}.
292+
* The configured like prefix and suffix are applied to the value.
293+
*
294+
* @param column the column name
295+
* @param value the pattern to match (prefix/suffix applied automatically)
296+
* @return this builder instance for chaining
297+
*/
298+
public QueryBuilder whereILike(final String column, final String value) {
299+
conditions.add(
300+
new ConditionEntry(
301+
column,
302+
new Condition(Operator.ILIKE, value),
303+
conditions.isEmpty() ? Connector.AND : Connector.AND
304+
)
305+
);
306+
return this;
307+
}
308+
309+
/**
310+
* Adds a {@code NOT ILIKE} WHERE condition joined with OR (PostgreSQL only).
311+
*
312+
* <p>Produces negated case-insensitive pattern matching. Rendered correctly only
313+
* when the active dialect is
314+
* {@link com.github.ezframework.javaquerybuilder.query.sql.SqlDialect#POSTGRESQL}.
315+
* The configured like prefix and suffix are applied to the value.
316+
*
317+
* @param column the column name
318+
* @param value the pattern to match (prefix/suffix applied automatically)
319+
* @return this builder instance for chaining
320+
*/
321+
public QueryBuilder orWhereILike(final String column, final String value) {
322+
conditions.add(
323+
new ConditionEntry(
324+
column,
325+
new Condition(Operator.ILIKE, value),
326+
Connector.OR
327+
)
328+
);
329+
return this;
330+
}
331+
287332
/**
288333
* Adds an {@code EXISTS} WHERE condition joined with AND.
289334
*

src/main/java/com/github/ezframework/javaquerybuilder/query/builder/SelectBuilder.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,24 @@ public SelectBuilder whereLike(String column, String value) {
153153
return this;
154154
}
155155

156+
/**
157+
* Adds a {@code WHERE ILIKE} condition (PostgreSQL case-insensitive match) joined with AND.
158+
*
159+
* <p>Rendered correctly only when the active dialect is
160+
* {@link com.github.ezframework.javaquerybuilder.query.sql.SqlDialect#POSTGRESQL}.
161+
*
162+
* @param column the column name
163+
* @param value the pattern to match (prefix/suffix applied automatically)
164+
* @return this builder
165+
*/
166+
public SelectBuilder whereILike(final String column, final String value) {
167+
conditions.add(new ConditionEntry(
168+
column,
169+
new Condition(Operator.ILIKE, value),
170+
conditions.isEmpty() ? Connector.AND : Connector.AND));
171+
return this;
172+
}
173+
156174
/**
157175
* Adds a GROUP BY clause.
158176
* @param columns the columns to group by

src/main/java/com/github/ezframework/javaquerybuilder/query/builder/UpdateBuilder.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public class UpdateBuilder {
2929
/** The WHERE conditions. */
3030
private final List<ConditionEntry> conditions = new ArrayList<>();
3131

32+
/** The RETURNING columns (PostgreSQL only). */
33+
private final List<String> returningColumns = new ArrayList<>();
34+
3235
/**
3336
* Sets the table to update.
3437
* @param table the table name
@@ -85,6 +88,20 @@ public UpdateBuilder whereGreaterThanOrEquals(String column, int value) {
8588
return this;
8689
}
8790

91+
/**
92+
* Specifies the columns to include in a {@code RETURNING} clause (PostgreSQL only).
93+
*
94+
* <p>The {@code RETURNING} clause is appended unconditionally to the SQL string;
95+
* it is the caller's responsibility to use a PostgreSQL connection.
96+
*
97+
* @param columns one or more column names; must not be {@code null} or empty
98+
* @return this builder instance for chaining
99+
*/
100+
public UpdateBuilder returning(final String... columns) {
101+
returningColumns.addAll(java.util.Arrays.asList(columns));
102+
return this;
103+
}
104+
88105
/**
89106
* Builds the SQL UPDATE statement.
90107
* @return the SQL result
@@ -123,6 +140,9 @@ public SqlResult build(SqlDialect dialect) {
123140
params.add(cond.getCondition().getValue());
124141
}
125142
}
143+
if (!returningColumns.isEmpty()) {
144+
sql.append(" RETURNING ").append(String.join(", ", returningColumns));
145+
}
126146

127147
return new SqlResult() {
128148
@Override

src/main/java/com/github/ezframework/javaquerybuilder/query/condition/Operator.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,21 @@ public enum Operator {
4444
* True SQL {@code NOT EXISTS (SELECT ...)} — value must be a
4545
* {@link com.github.ezframework.javaquerybuilder.query.Query}.
4646
*/
47-
NOT_EXISTS_SUBQUERY
47+
NOT_EXISTS_SUBQUERY,
48+
/**
49+
* PostgreSQL case-insensitive {@code ILIKE} substring match.
50+
*
51+
* <p>Only rendered correctly by
52+
* {@link com.github.ezframework.javaquerybuilder.query.sql.postgresql.PostgreSqlDialect};
53+
* using this operator with any other dialect produces no output.
54+
*/
55+
ILIKE,
56+
/**
57+
* PostgreSQL case-insensitive {@code NOT ILIKE} substring match.
58+
*
59+
* <p>Only rendered correctly by
60+
* {@link com.github.ezframework.javaquerybuilder.query.sql.postgresql.PostgreSqlDialect};
61+
* using this operator with any other dialect produces no output.
62+
*/
63+
NOT_ILIKE
4864
}

src/main/java/com/github/ezframework/javaquerybuilder/query/sql/AbstractSqlDialect.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
*
1818
* <p>Implements the standard (ANSI) SQL dialect and provides shared helpers for
1919
* rendering {@code WHERE} clauses. Subclasses may override
20-
* {@link #quoteIdentifier(String)} to apply dialect-specific identifier quoting
21-
* and {@link #supportsDeleteLimit()} to enable dialect-specific DELETE
22-
* {@code LIMIT} behaviour.
20+
* {@link #quoteIdentifier(String)} to apply dialect-specific identifier quoting,
21+
* {@link #supportsDeleteLimit()} to enable dialect-specific DELETE {@code LIMIT}
22+
* behaviour, and {@link #supportsReturning()} to enable a {@code RETURNING} clause.
2323
*
2424
* <p>Subquery support — parameter ordering contract:
2525
* <ol>
@@ -67,6 +67,9 @@ public SqlResult renderDelete(Query query) {
6767
if (supportsDeleteLimit() && query.getLimit() != null && query.getLimit() >= 0) {
6868
sql.append(" LIMIT ").append(query.getLimit());
6969
}
70+
if (supportsReturning() && !query.getReturningColumns().isEmpty()) {
71+
sql.append(" RETURNING ").append(String.join(", ", query.getReturningColumns()));
72+
}
7073

7174
final String sqlStr = sql.toString();
7275
final List<Object> paramsCopy = Collections.unmodifiableList(new ArrayList<>(params));
@@ -95,6 +98,17 @@ protected boolean supportsDeleteLimit() {
9598
return false;
9699
}
97100

101+
/**
102+
* Hook for dialects that support a {@code RETURNING} clause on DELETE statements
103+
* (for example, PostgreSQL). The default implementation returns {@code false}.
104+
* Subclasses that want to enable {@code RETURNING} should override this method.
105+
*
106+
* @return {@code true} if the dialect appends a {@code RETURNING} clause to DELETE statements
107+
*/
108+
protected boolean supportsReturning() {
109+
return false;
110+
}
111+
98112
@Override
99113
public SqlResult render(Query query) {
100114
final StringBuilder sql = new StringBuilder();

src/main/java/com/github/ezframework/javaquerybuilder/query/sql/SqlDialect.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
import com.github.ezframework.javaquerybuilder.query.Query;
44
import com.github.ezframework.javaquerybuilder.query.sql.mysql.MySqlDialect;
5+
import com.github.ezframework.javaquerybuilder.query.sql.postgresql.PostgreSqlDialect;
56
import com.github.ezframework.javaquerybuilder.query.sql.sqlite.SqliteDialect;
67

78
/**
89
* Strategy for rendering a {@link Query} to a SQL string.
910
*
1011
* <p>Use the built-in constants for the most common dialects:
11-
* {@link #STANDARD}, {@link #MYSQL}, or {@link #SQLITE}.
12+
* {@link #STANDARD}, {@link #MYSQL}, {@link #SQLITE}, or {@link #POSTGRESQL}.
1213
* For custom behaviour, extend {@link AbstractSqlDialect} and override
1314
* {@link AbstractSqlDialect#quoteIdentifier(String)}.
1415
*
@@ -26,6 +27,13 @@ public interface SqlDialect {
2627
/** SQLite dialect — identifiers are wrapped in double-quote characters. */
2728
SqlDialect SQLITE = new SqliteDialect();
2829

30+
/**
31+
* PostgreSQL dialect — identifiers are wrapped in double-quote characters,
32+
* with additional support for {@code ILIKE}/{@code NOT ILIKE} operators and
33+
* {@code RETURNING} clauses on DELETE statements.
34+
*/
35+
SqlDialect POSTGRESQL = new PostgreSqlDialect();
36+
2937
/**
3038
* Renders the given query to a parameterized {@link SqlResult}.
3139
*

0 commit comments

Comments
 (0)