• 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

90.0
/src/main/java/io/github/torand/fastersql/statement/InsertBatchStatement.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.dialect.OracleDialect;
22

23
import java.util.Collection;
24
import java.util.List;
25
import java.util.Optional;
26
import java.util.function.Function;
27
import java.util.stream.Stream;
28

29
import static io.github.torand.fastersql.Command.INSERT;
30
import static io.github.torand.fastersql.util.collection.CollectionHelper.asList;
31
import static io.github.torand.fastersql.util.collection.CollectionHelper.concat;
32
import static io.github.torand.fastersql.util.collection.CollectionHelper.isEmpty;
33
import static io.github.torand.fastersql.util.collection.CollectionHelper.streamSafely;
34
import static io.github.torand.fastersql.util.contract.Requires.requireNonEmpty;
35
import static java.util.Objects.isNull;
36
import static java.util.Objects.requireNonNull;
37
import static java.util.stream.Collectors.joining;
38

39
public class InsertBatchStatement<T> extends PreparableStatement {
40
    private final Table<?> table;
41
    private final List<ColumnValueExtractor<? super T>> columnValueExtractors;
42
    private final List<? extends T> entities;
43

44
    InsertBatchStatement(Collection<? extends T> entities, Table<?> table, Collection<ColumnValueExtractor<? super T>> columnValueExtractors) {
2✔
45
        this.entities = asList(requireNonEmpty(entities, "No entities specified"));
8✔
46
        this.table = requireNonNull(table, "No table specified");
6✔
47
        this.columnValueExtractors = asList(columnValueExtractors);
4✔
48
    }
1✔
49

50
    public InsertBatchStatement<T> value(Column column, Function<? super T, Object> valueExtractor) {
51
        requireNonNull(column, "No column specified");
4✔
52
        requireNonNull(valueExtractor, "No value extractor specified");
4✔
53

54
        List<ColumnValueExtractor<? super T>> concatenated = concat(columnValueExtractors, new ColumnValueExtractor<>(column, valueExtractor));
14✔
55
        return new InsertBatchStatement<>(entities, table, concatenated);
9✔
56
    }
57

58
    @Override
59
    String sql(Context context) {
60
        final Context localContext = context.withCommand(INSERT);
4✔
61
        validate();
2✔
62

63
        if (context.getDialect() instanceof OracleDialect) {
4✔
64

65
            // INSERT ALL
66
            //   INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3')
67
            //   INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3')
68
            //   INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3')
69
            // SELECT 1 FROM DUAL;
70

71
            StringBuilder sb = new StringBuilder();
4✔
72
            sb.append("insert all");
4✔
73
            entities().forEach(e -> {
7✔
74
                sb.append(" into ").append(table.sql(localContext));
9✔
75
                sb.append(" (");
4✔
76
                sb.append(columnValueExtractors().map(cve -> cve.column().sql(localContext)).collect(joining(", ")));
17✔
77
                sb.append(") values (");
4✔
78
                sb.append(columnValueExtractors().map(cve -> cve.valueSql(localContext, e)).collect(joining(", ")));
18✔
79
                sb.append(")");
4✔
80
            });
1✔
81

82
            sb.append(" select 1 from DUAL");
4✔
83

84
            return sb.toString();
3✔
85
        } else {
86

87
            // INSERT INTO t
88
            //   (col1, col2, col3)
89
            // VALUES
90
            //   ('val1_1', 'val1_2', 'val1_3'),
91
            //   ('val2_1', 'val2_2', 'val2_3'),
92
            //   ('val3_1', 'val3_2', 'val3_3');
93

94
            StringBuilder sb = new StringBuilder();
4✔
95
            sb.append("insert into ").append(table.sql(context));
9✔
96
            sb.append(" (");
4✔
97
            sb.append(columnValueExtractors().map(cve -> cve.column().sql(localContext)).collect(joining(", ")));
17✔
98
            sb.append(") values ");
4✔
99
            sb.append(entities().map(e -> "("
12✔
100
                + columnValueExtractors().map(cve -> cve.valueSql(localContext, e)).collect(joining(", "))
15✔
101
                + ")")
102
                .collect(joining(", ")));
3✔
103

104
            return sb.toString();
3✔
105
        }
106
    }
107

108
    private Stream<? extends T> entities() {
109
        return streamSafely(entities);
4✔
110
    }
111

112
    private Stream<ColumnValueExtractor<? super T>> columnValueExtractors() {
113
        return streamSafely(columnValueExtractors);
4✔
114
    }
115

116
    @Override
117
    List<Object> params(Context context) {
118
        return entities()
5✔
119
            .flatMap(e -> columnValueExtractors()
6✔
120
                .map(cve -> cve.param(e))
6✔
121
                .flatMap(Optional::stream))
1✔
122
            .toList();
1✔
123
    }
124

125
    private void validate() {
126
        if (isNull(table)) {
4!
127
            throw new IllegalStateException("No table specified");
×
128
        }
129
        if (isEmpty(columnValueExtractors)) {
4!
130
            throw new IllegalStateException("No values specified");
×
131
        }
132
        validateColumnTableRelations(columnValueExtractors().map(ColumnValueExtractor::column));
6✔
133
    }
1✔
134

135
    private void validateColumnTableRelations(Stream<Column> columns) {
136
        columns
3✔
137
            .filter(c -> !table.name().equals(c.table().name()))
11!
138
            .findFirst()
2✔
139
            .ifPresent(c -> {
1✔
NEW
140
                throw new IllegalStateException("Column " + c.name() + " belongs to table " + c.table().name() + ", not the table specified by the INTO clause");
×
141
            });
142
    }
1✔
143
}
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