Skip to content

Commit e843c6f

Browse files
authored
Merge pull request #49 from hasura/codedmart/oracle-dates
Add date/timestamp casts explicitly to `where` predicates
2 parents 4b16fe1 + 58550c5 commit e843c6f

File tree

2 files changed

+75
-12
lines changed

2 files changed

+75
-12
lines changed

ndc-connector-oracle/src/main/kotlin/io/hasura/oracle/JSONGenerator.kt

+15-4
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,23 @@ object JsonQueryGenerator : BaseQueryGenerator() {
117117
(request.query.fields ?: emptyMap()).map { (alias, field) ->
118118
when (field) {
119119
is ColumnField -> {
120+
val field = DSL.field(
121+
DSL.name(field.column),
122+
columnTypeTojOOQType(request.collection, field)
123+
)
124+
120125
DSL.jsonEntry(
121126
alias,
122-
DSL.field(
123-
DSL.name(field.column),
124-
columnTypeTojOOQType(request.collection, field)
125-
)
127+
// Oracle JSON functions convert DATE to ISO8601 format, which includes a timestamp
128+
// We need to convert it to a date-only format to preserve the actual date value
129+
//
130+
// SEE: https://docs.oracle.com/en/database/oracle/oracle-database/23/adjsn/overview-json-generation.html
131+
// (Section: "Result Returned by SQL/JSON Generation Functions")
132+
when (field.dataType) {
133+
SQLDataType.DATE -> DSL.toChar(field, DSL.inline("YYYY-MM-DD"))
134+
SQLDataType.TIMESTAMP -> DSL.toChar(field, DSL.inline("YYYY-MM-DD HH24:MI:SS"))
135+
else -> field
136+
}
126137
)
127138
}
128139

ndc-sqlgen/src/main/kotlin/io/hasura/ndc/sqlgen/BaseGenerator.kt

+60-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package io.hasura.ndc.sqlgen
22

3+
import io.hasura.ndc.common.ConnectorConfiguration
4+
import io.hasura.ndc.common.NDCScalar
35
import io.hasura.ndc.ir.*
46
import org.jooq.Condition
57
import org.jooq.impl.DSL
8+
import org.jooq.impl.SQLDataType
69
import org.jooq.Field
710

811
sealed interface BaseGenerator {
@@ -28,7 +31,8 @@ sealed interface BaseGenerator {
2831
fun buildComparison(
2932
col: Field<Any>,
3033
operator: ApplyBinaryComparisonOperator,
31-
listVal: List<Field<Any>>
34+
listVal: List<Field<Any>>,
35+
columnType: NDCScalar?
3236
): Condition {
3337
if (operator != ApplyBinaryComparisonOperator.IN && listVal.size != 1) {
3438
error("Only the IN operator supports multiple values")
@@ -37,20 +41,67 @@ sealed interface BaseGenerator {
3741
// unwrap single value for use in all but the IN operator
3842
// OR return falseCondition if listVal is empty
3943
val singleVal = listVal.firstOrNull() ?: return DSL.falseCondition()
44+
val castedValue = castValue(singleVal, columnType)
4045

4146
return when (operator) {
42-
ApplyBinaryComparisonOperator.EQ -> col.eq(singleVal)
43-
ApplyBinaryComparisonOperator.GT -> col.gt(singleVal)
44-
ApplyBinaryComparisonOperator.GTE -> col.ge(singleVal)
45-
ApplyBinaryComparisonOperator.LT -> col.lt(singleVal)
46-
ApplyBinaryComparisonOperator.LTE -> col.le(singleVal)
47-
ApplyBinaryComparisonOperator.IN -> col.`in`(listVal)
47+
ApplyBinaryComparisonOperator.EQ -> col.eq(castedValue)
48+
ApplyBinaryComparisonOperator.GT -> col.gt(castedValue)
49+
ApplyBinaryComparisonOperator.GTE -> col.ge(castedValue)
50+
ApplyBinaryComparisonOperator.LT -> col.lt(castedValue)
51+
ApplyBinaryComparisonOperator.LTE -> col.le(castedValue)
52+
ApplyBinaryComparisonOperator.IN -> col.`in`(listVal.map { castValue(it, columnType) })
4853
ApplyBinaryComparisonOperator.IS_NULL -> col.isNull
4954
ApplyBinaryComparisonOperator.LIKE -> col.like(singleVal as Field<String>)
5055
ApplyBinaryComparisonOperator.CONTAINS -> col.contains(singleVal as Field<String>)
5156
}
5257
}
5358

59+
fun castValue(value: Any, scalarType: NDCScalar?): Any {
60+
return when (scalarType) {
61+
NDCScalar.TIMESTAMPTZ -> DSL.cast(value, SQLDataType.TIMESTAMPWITHTIMEZONE)
62+
NDCScalar.TIMESTAMP -> DSL.cast(value, SQLDataType.TIMESTAMP)
63+
NDCScalar.DATE -> DSL.cast(value, SQLDataType.DATE)
64+
else -> value
65+
}
66+
}
67+
68+
fun getColumnType(col: ComparisonTarget, request: QueryRequest): NDCScalar? {
69+
val connectorConfig = ConnectorConfiguration.Loader.config
70+
val collection = getCollectionForCompCol(col, request)
71+
val collectionIsTable = connectorConfig.tables.any { it.tableName == collection }
72+
val collectionIsNativeQuery = connectorConfig.nativeQueries.containsKey(collection)
73+
val columnType = when {
74+
collectionIsTable -> {
75+
val table = connectorConfig.tables.find { it.tableName == collection }
76+
?: error("Table $collection not found in connector configuration")
77+
78+
val column = table.columns.find { it.name == col.name }
79+
?: error("Column ${col.name} not found in table $collection")
80+
81+
column.type
82+
}
83+
84+
collectionIsNativeQuery -> {
85+
val nativeQuery = connectorConfig.nativeQueries[collection]
86+
?: error("Native query $collection not found in connector configuration")
87+
88+
val column = nativeQuery.columns[col.name]
89+
?: error("Column ${col.name} not found in native query $collection")
90+
91+
Type.extractBaseType(column)
92+
}
93+
94+
else -> error("Collection $collection not found in connector configuration")
95+
}
96+
97+
return when {
98+
columnType == "DATE" -> NDCScalar.DATE
99+
columnType.contains("TIMESTAMP") && !columnType.contains("TIME ZONE") -> NDCScalar.TIMESTAMP
100+
columnType.contains("TIMESTAMP") && columnType.contains("TIME ZONE") -> NDCScalar.TIMESTAMPTZ
101+
else -> null
102+
}
103+
}
104+
54105
private fun getCollectionForCompCol(
55106
col: ComparisonTarget,
56107
request: QueryRequest
@@ -124,6 +175,7 @@ sealed interface BaseGenerator {
124175
}
125176

126177
is Expression.ApplyBinaryComparison -> {
178+
val columnType = getColumnType(e.column, request)
127179
val column = DSL.field(
128180
DSL.name(
129181
splitCollectionName(getCollectionForCompCol(e.column, request)) + e.column.name
@@ -143,7 +195,7 @@ sealed interface BaseGenerator {
143195

144196
is ComparisonValue.VariableComp -> listOf(DSL.field(DSL.name(listOf("vars", v.name))))
145197
}
146-
return buildComparison(column, e.operator, comparisonValue)
198+
return buildComparison(column, e.operator, comparisonValue, columnType)
147199
}
148200

149201
is Expression.ApplyUnaryComparison -> {

0 commit comments

Comments
 (0)