Skip to content

Commit 810eb4d

Browse files
committed
Replace column type overrides map with list
1 parent 9f350eb commit 810eb4d

File tree

11 files changed

+218
-21
lines changed

11 files changed

+218
-21
lines changed

query-engine-design-mode/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@
100100
<dependency>
101101
<groupId>org.mockito</groupId>
102102
<artifactId>mockito-core</artifactId>
103-
<version>5.17.0</version>
103+
<version>5.18.0</version>
104104
<scope>test</scope>
105105
</dependency>
106106
<dependency>
107107
<groupId>org.mockito</groupId>
108108
<artifactId>mockito-junit-jupiter</artifactId>
109-
<version>5.17.0</version>
109+
<version>5.18.0</version>
110110
<scope>test</scope>
111111
</dependency>
112112
<dependency>

query-engine/docs/query-engine-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ services:
5050
- 16686:16686
5151

5252
query-engine:
53-
image: ghcr.io/yaytay/query-engine-design-mode:0.0.70-2-main
53+
image: ghcr.io/yaytay/query-engine-design-mode:0.0.70-3-main
5454
ports:
5555
- 2000:8080
5656
volumes:

query-engine/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,13 @@
462462
<dependency>
463463
<groupId>org.mockito</groupId>
464464
<artifactId>mockito-core</artifactId>
465-
<version>5.17.0</version>
465+
<version>5.18.0</version>
466466
<scope>test</scope>
467467
</dependency>
468468
<dependency>
469469
<groupId>org.mockito</groupId>
470470
<artifactId>mockito-junit-jupiter</artifactId>
471-
<version>5.17.0</version>
471+
<version>5.18.0</version>
472472
<scope>test</scope>
473473
</dependency>
474474
<dependency>
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright (C) 2025 jtalbut
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
package uk.co.spudsoft.query.defn;
18+
19+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
20+
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
21+
import io.swagger.v3.oas.annotations.media.Schema;
22+
23+
/**
24+
* Override of the data type for a specific column.
25+
*
26+
* This facility is rarely required, but can be useful when a database does not provide adequate information for Query Engine to correctly identify the type of a field.
27+
*
28+
* This is known to be useful for boolean fields with MySQL.
29+
*
30+
* Setting a column to use a type that the result does not fit is going to cause problems (loss of data or errors) - so be sure you do this with care.
31+
*
32+
* @author jtalbut
33+
*/
34+
@JsonDeserialize(builder = ColumnTypeOverride.Builder.class)
35+
@Schema(description = """
36+
Override of the data type for a specific column.
37+
38+
This facility is rarely required, but can be useful when a database does not provide adequate information for Query Engine to correctly identify the type of a field.
39+
40+
This is known to be useful for boolean fields with MySQL.
41+
42+
Setting a column to use a type that the result does not fit is going to cause problems (loss of data or errors) - so be sure you do this with care.
43+
"""
44+
)
45+
public class ColumnTypeOverride {
46+
47+
private final String column;
48+
private final DataType type;
49+
50+
/**
51+
* Get the name of the column that is to have its data type set.
52+
* @return the name of the column that is to have its data type set.
53+
*/
54+
@Schema(description = """
55+
<P>The name of the column that is to have its data type set.</P>
56+
"""
57+
, maxLength = 100
58+
)
59+
public String getColumn() {
60+
return column;
61+
}
62+
63+
/**
64+
* Get the desired type of the column.
65+
* @return the desired type of the column.
66+
*/
67+
@Schema(description = """
68+
<P>The desired type of the column.</P>
69+
"""
70+
)
71+
public DataType getType() {
72+
return type;
73+
}
74+
75+
/**
76+
* Builder class for ColumnTypeOverride.
77+
*/
78+
@JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
79+
public static class Builder {
80+
81+
private String column;
82+
private DataType type;
83+
84+
private Builder() {
85+
}
86+
87+
/**
88+
* Set the {@link ColumnTypeOverride#column} value in the builder.
89+
* @param value The value for the {@link ColumnTypeOverride#column}.
90+
* @return this, so that this builder may be used in a fluent manner.
91+
*/
92+
public Builder column(final String value) {
93+
this.column = value;
94+
return this;
95+
}
96+
97+
/**
98+
* Set the {@link ColumnTypeOverride#type} value in the builder.
99+
* @param value The value for the {@link ColumnTypeOverride#type}.
100+
* @return this, so that this builder may be used in a fluent manner.
101+
*/
102+
public Builder type(final DataType value) {
103+
this.type = value;
104+
return this;
105+
}
106+
107+
/**
108+
* Create a new ColumnTypeOverride object.
109+
* @return a new ColumnTypeOverride object.
110+
*/
111+
public ColumnTypeOverride build() {
112+
return new uk.co.spudsoft.query.defn.ColumnTypeOverride(column, type);
113+
}
114+
}
115+
116+
/**
117+
* Create a new ColumnTypeOverride builder.
118+
* @return a new ColumnTypeOverride builder.
119+
*/
120+
public static ColumnTypeOverride.Builder builder() {
121+
return new ColumnTypeOverride.Builder();
122+
}
123+
124+
private ColumnTypeOverride(final String column, final DataType type) {
125+
this.column = column;
126+
this.type = type;
127+
}
128+
}

query-engine/src/main/java/uk/co/spudsoft/query/defn/SourceSql.java

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@
1818

1919
import com.fasterxml.jackson.annotation.JsonFormat;
2020
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
21+
import com.fasterxml.jackson.annotation.JsonIgnore;
2122
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2223
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
2324
import com.google.common.base.Strings;
25+
import com.google.common.collect.ImmutableList;
2426
import com.google.common.collect.ImmutableMap;
2527
import io.swagger.v3.oas.annotations.media.Schema;
2628
import io.vertx.core.Context;
2729
import io.vertx.core.Vertx;
2830
import java.time.Duration;
31+
import java.util.List;
2932
import java.util.Map;
3033
import uk.co.spudsoft.query.exec.SharedMap;
3134
import uk.co.spudsoft.query.exec.SourceInstance;
@@ -72,7 +75,8 @@ public SourceInstance createInstance(Vertx vertx, Context context, SharedMap sha
7275
private final Duration idleTimeout;
7376
private final Duration connectionTimeout;
7477
private final Boolean replaceDoubleQuotes;
75-
private final ImmutableMap<String, DataType> columnTypeOverrides;
78+
private final ImmutableList<ColumnTypeOverride> columnTypeOverrides;
79+
private final ImmutableMap<String, DataType> columnTypeOverrideMap;
7680

7781

7882
@Override
@@ -427,10 +431,19 @@ This is a map of column names (from the results for this query) to the Query Eng
427431
Setting a column to use a type that the result does not fit is going to cause problems (loss of data or errors) - so be sure you do this with care.
428432
"""
429433
)
430-
public Map<String, DataType> getColumnTypeOverrides() {
434+
public List<ColumnTypeOverride> getColumnTypeOverrides() {
431435
return columnTypeOverrides;
432436
}
433437

438+
/**
439+
* Get the defined {@link #columnTypeOverrides} as a map.
440+
* @return the defined {@link #columnTypeOverrides} as a map.
441+
*/
442+
@JsonIgnore
443+
public Map<String, DataType> getColumnTypeOverrideMap() {
444+
return columnTypeOverrideMap;
445+
}
446+
434447
/**
435448
* Builder class for SourceSql.
436449
*/
@@ -449,7 +462,7 @@ public static class Builder {
449462
private Duration idleTimeout;
450463
private Duration connectionTimeout;
451464
private Boolean replaceDoubleQuotes;
452-
private ImmutableMap<String, DataType> columnTypeOverrides;
465+
private ImmutableList<ColumnTypeOverride> columnTypeOverrides;
453466

454467
private Builder() {
455468
}
@@ -579,7 +592,7 @@ public Builder replaceDoubleQuotes(final Boolean value) {
579592
* @param value The value for the {@link SourceSql#replaceDoubleQuotes}.
580593
* @return this, so that this builder may be used in a fluent manner.
581594
*/
582-
public Builder columnTypeOverrides(final Map<String, DataType> value) {
595+
public Builder columnTypeOverrides(final List<ColumnTypeOverride> value) {
583596
this.columnTypeOverrides = ImmutableCollectionTools.copy(value);
584597
return this;
585598
}
@@ -635,7 +648,7 @@ public SourceSql(final SourceType type
635648
, final Duration idleTimeout
636649
, final Duration connectionTimeout
637650
, final Boolean replaceDoubleQuotes
638-
, final Map<String, DataType> columnTypeOverrides
651+
, final List<ColumnTypeOverride> columnTypeOverrides
639652
) {
640653
validateType(SourceType.SQL, type);
641654
this.type = type;
@@ -650,8 +663,16 @@ public SourceSql(final SourceType type
650663
this.idleTimeout = idleTimeout;
651664
this.connectionTimeout = connectionTimeout;
652665
this.replaceDoubleQuotes = replaceDoubleQuotes;
653-
this.columnTypeOverrides = ImmutableCollectionTools.copy(columnTypeOverrides);
666+
if (columnTypeOverrides == null || columnTypeOverrides.isEmpty()) {
667+
this.columnTypeOverrides = null;
668+
this.columnTypeOverrideMap = null;
669+
} else {
670+
ImmutableMap.Builder<String, DataType> builder = ImmutableMap.<String, DataType>builder();
671+
columnTypeOverrides.forEach(cto -> {
672+
builder.put(cto.getColumn(), cto.getType());
673+
});
674+
this.columnTypeOverrides = ImmutableCollectionTools.copy(columnTypeOverrides);
675+
this.columnTypeOverrideMap = builder.build();
676+
}
654677
}
655-
656-
657678
}

query-engine/src/main/java/uk/co/spudsoft/query/exec/sources/sql/RowStreamWrapper.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,7 @@ public RowStreamWrapper(SourceNameTracker sourceNameTracker, SqlConnection conne
8080
if (columnTypeOverrides != null && columnTypeOverrides.containsKey(cd.name())) {
8181
types.putIfAbsent(cd.name(), columnTypeOverrides.get(cd.name()));
8282
} else {
83-
try {
84-
types.putIfAbsent(cd.name(), DataType.fromJdbcType(cd.jdbcType()));
85-
} catch (Throwable ex) {
86-
logger.warn("Failed to process column {}: ", cd.name(), ex);
87-
}
83+
types.putIfAbsent(cd.name(), DataType.fromJdbcType(cd.jdbcType()));
8884
}
8985
}
9086
logger.debug("Got types: {}", types);

query-engine/src/main/java/uk/co/spudsoft/query/exec/sources/sql/SourceSqlStreamingInstance.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public Future<ReadStreamWithTypes> initialize(PipelineExecutor executor, Pipelin
203203
transaction = tran;
204204
logger.debug("Executing SQL stream on {} with {}", connection, args);
205205
MetadataRowStreamImpl rowStream = new MetadataRowStreamImpl(preparedStatement, context, definition.getStreamingFetchSize(), args);
206-
rowStreamWrapper = new RowStreamWrapper(this, connection, transaction, rowStream, definition.getColumnTypeOverrides());
206+
rowStreamWrapper = new RowStreamWrapper(this, connection, transaction, rowStream, definition.getColumnTypeOverrideMap());
207207
return rowStreamWrapper.ready();
208208
})
209209
.recover(ex -> {

query-engine/src/main/java/uk/co/spudsoft/query/main/Version.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public final class Version {
3434
/**
3535
* The project version, as set in the Maven pom.xml.
3636
*/
37-
public static final String MAVEN_PROJECT_VERSION = "0.0.70-2-main";
37+
public static final String MAVEN_PROJECT_VERSION = "0.0.70-3-main";
3838

3939
private Version() {
4040
}

query-engine/src/main/resources/samples/sub1/sub2/AllDynamicIT.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ source:
6363
d."dataId" <= :maxId
6464
order by
6565
d."dataId"
66+
columnTypeOverrides:
67+
- column: LongField
68+
type: Double
69+
6670
processors:
6771
- type: GROUP_CONCAT
6872
delimiter: ","
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (C) 2025 njt
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
package uk.co.spudsoft.query.defn;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertNotNull;
21+
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import org.junit.jupiter.api.Test;
23+
24+
/**
25+
*
26+
* @author njt
27+
*/
28+
public class ColumnTypeOverrideTest {
29+
30+
@Test
31+
public void testGetColumn() {
32+
ColumnTypeOverride cto = ColumnTypeOverride.builder().build();
33+
assertNotNull(cto);
34+
assertNull(cto.getColumn());
35+
cto = ColumnTypeOverride.builder().column("col").build();
36+
assertEquals("col", cto.getColumn());
37+
}
38+
39+
@Test
40+
public void testGetType() {
41+
ColumnTypeOverride cto = ColumnTypeOverride.builder().build();
42+
assertNotNull(cto);
43+
assertNull(cto.getType());
44+
cto = ColumnTypeOverride.builder().type(DataType.Double).build();
45+
assertEquals(DataType.Double, cto.getType());
46+
}
47+
48+
}

0 commit comments

Comments
 (0)