• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

torand / FasterSQL / 13591799490

28 Feb 2025 03:59PM UTC coverage: 65.237% (-1.3%) from 66.563%
13591799490

push

github

web-flow
Merge pull request #14 from torand/having-support

feat: supporting the HAVING clause + IS NULL operator now supports an…

214 of 408 branches covered (52.45%)

Branch coverage included in aggregate %.

273 of 389 new or added lines in 69 files covered. (70.18%)

3 existing lines in 3 files now uncovered.

1079 of 1574 relevant lines covered (68.55%)

3.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.44
/src/main/java/io/github/torand/fastersql/statement/UpdateStatement.java
1
/*
2
 * Copyright (c) 2024 Tore Eide Andersen
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package io.github.torand.fastersql.statement;
17

18
import io.github.torand.fastersql.Column;
19
import io.github.torand.fastersql.Context;
20
import io.github.torand.fastersql.Table;
21
import io.github.torand.fastersql.expression.Expression;
22
import io.github.torand.fastersql.predicate.OptionalPredicate;
23
import io.github.torand.fastersql.predicate.Predicate;
24

25
import java.util.Collection;
26
import java.util.List;
27
import java.util.Optional;
28
import java.util.stream.Stream;
29

30
import static io.github.torand.fastersql.Command.UPDATE;
31
import static io.github.torand.fastersql.constant.Constants.$;
32
import static io.github.torand.fastersql.util.collection.CollectionHelper.asList;
33
import static io.github.torand.fastersql.util.collection.CollectionHelper.concat;
34
import static io.github.torand.fastersql.util.collection.CollectionHelper.isEmpty;
35
import static io.github.torand.fastersql.util.collection.CollectionHelper.nonEmpty;
36
import static io.github.torand.fastersql.util.collection.CollectionHelper.streamSafely;
37
import static io.github.torand.fastersql.util.contract.Requires.requireNonEmpty;
38
import static java.util.Objects.requireNonNull;
39
import static java.util.stream.Collectors.joining;
40

41
public class UpdateStatement extends PreparableStatement {
42
    private final Table<?> table;
43
    private final List<ColumnValue> columnValues;
44
    private final List<Predicate> predicates;
45

46
    UpdateStatement(Table<?> table, Collection<ColumnValue> columnValues, Collection<Predicate> predicates) {
2✔
47
        this.table = requireNonNull(table, "No table specified");
6✔
48
        this.columnValues = asList(columnValues);
4✔
49
        this.predicates = asList(predicates);
4✔
50
    }
1✔
51

52
    public UpdateStatement set(Column column, Expression expression) {
53
        requireNonNull(column, "No column specified");
4✔
54
        requireNonNull(expression, "No expression specified");
4✔
55

56
        List<ColumnValue> concatenated = concat(this.columnValues, new ColumnValue(column, expression));
14✔
57
        return new UpdateStatement(table, concatenated, predicates);
9✔
58
    }
59

60
    public UpdateStatement set(Column column, Object value) {
61
        requireNonNull(column, "No column specified");
4✔
62

63
        List<ColumnValue> concatenated = concat(this.columnValues, new ColumnValue(column, $(value)));
15✔
64
        return new UpdateStatement(table, concatenated, predicates);
9✔
65
    }
66

67
    public UpdateStatement set(Column column, Optional<?> maybeValue) {
68
        requireNonNull(column, "No column specified");
4✔
69
        requireNonNull(maybeValue, "No value specified");
4✔
70

71
        if (maybeValue.isPresent()) {
3✔
72
            List<ColumnValue> concatenated = concat(this.columnValues, new ColumnValue(column, $(maybeValue.get())));
16✔
73
            return new UpdateStatement(table, concatenated, predicates);
9✔
74
        } else {
75
            return this;
2✔
76
        }
77
    }
78

79
    public UpdateStatement where(Predicate... predicates) {
80
        requireNonEmpty(predicates, "No predicates specified");
6✔
81

82
        List<Predicate> concatenated = concat(this.predicates, predicates);
5✔
83
        return new UpdateStatement(table, columnValues, concatenated);
9✔
84
    }
85

86
    /**
87
     * Same as other method of same name, but only adds to the where clause predicates that are present.
88
     * @param maybePredicates the predicates that may be present or not
89
     * @return updated statement, for method chaining
90
     */
91
    public final UpdateStatement where(OptionalPredicate... maybePredicates) {
92
        requireNonEmpty(maybePredicates, "No optional predicates specified");
×
93

94
        List<Predicate> concatenated = concat(this.predicates, OptionalPredicate.unwrap(maybePredicates));
×
NEW
95
        return new UpdateStatement(table, columnValues, concatenated);
×
96
    }
97

98
    @Override
99
    String sql(Context context) {
100
        final Context localContext = context.withCommand(UPDATE);
4✔
101
        validate();
2✔
102

103
        StringBuilder sb = new StringBuilder();
4✔
104
        sb.append("update ");
4✔
105
        sb.append(table.sql(context));
7✔
106
        sb.append(" set ");
4✔
107
        sb.append(streamSafely(columnValues).map(cv -> cv.column().sql(localContext) + " = " + cv.valueSql(localContext)).collect(joining(", ")));
22✔
108
        if (nonEmpty(predicates)) {
4!
109
            sb.append(" where ");
4✔
110
            sb.append(streamSafely(predicates).map(p -> p.sql(localContext)).collect(joining(" and ")));
17✔
111
        }
112

113
        return sb.toString();
3✔
114
    }
115

116
    @Override
117
    List<Object> params(Context context) {
118
        final Context localContext = context.withCommand(UPDATE);
4✔
119
        return Stream.concat(
4✔
120
                streamSafely(columnValues).flatMap(cv -> cv.params(localContext)),
10✔
121
                streamSafely(predicates).flatMap(p -> p.params(localContext)))
8✔
122
            .toList();
1✔
123
    }
124

125
    private void validate() {
126
        if (isEmpty(columnValues)) {
4!
127
            throw new IllegalStateException("No values to set");
×
128
        }
129
        validateColumnTableRelations(streamSafely(columnValues).map(ColumnValue::column));
7✔
130
        validateColumnTableRelations(streamSafely(predicates).flatMap(Predicate::columnRefs));
7✔
131
    }
1✔
132

133
    private void validateColumnTableRelations(Stream<Column> columns) {
134
        columns
3✔
135
            .filter(c -> !table.name().equalsIgnoreCase(c.table().name()))
11!
136
            .findFirst()
2✔
137
            .ifPresent(c -> {
1✔
NEW
138
                throw new IllegalStateException("Column " + c.name() + " belongs to table " + c.table().name() + ", but is not specified in the UPDATE clause");
×
139
            });
140
    }
1✔
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc