Skip to content

Commit 7308941

Browse files
committed
cleanup + tests
1 parent 218c25c commit 7308941

File tree

7 files changed

+397
-22
lines changed

7 files changed

+397
-22
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Thank you to all who have contributed!
2929
- **EXPERIMENTAL** add `metas` map for `PType`
3030
- partiql-planner: `ROW` or collection types with excluded fields resulting from `RelExclude` will include a meta
3131
`CONTAINS_EXCLUDED_FIELD` mapping to `true`
32+
- Static factories for creating `PType.decimal` and `PType.numeric` types with a precision and default scale
33+
- Metas attached to `PType`s indicating if a parameter (e.g. precision, scale, length) was specified
3234

3335
### Changed
3436

partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ internal object RexConverter {
111111
private const val INTERVAL_DEFAULT_PRECISON = 2
112112
private const val INTERVAL_DEFAULT_FRACTIONAL_PRECISION = 6
113113

114+
private const val UNSPECIFIED_LENGTH = "UNSPECIFIED_LENGTH"
115+
private const val UNSPECIFIED_PRECISION = "UNSPECIFIED_PRECISION"
116+
private const val UNSPECIFIED_SCALE = "UNSPECIFIED_SCALE"
117+
118+
private fun PType.setUnspecifiedLengthMeta() {
119+
this.metas[UNSPECIFIED_LENGTH] = true
120+
}
121+
122+
private fun PType.setUnspecifiedPrecisionMeta() {
123+
this.metas[UNSPECIFIED_PRECISION] = true
124+
}
125+
126+
private fun PType.setUnspecifiedScaleMeta() {
127+
this.metas[UNSPECIFIED_SCALE] = true
128+
}
129+
114130
internal fun apply(expr: Expr, context: Env): Rex = ToRex.visitExprCoerce(expr, context)
115131

116132
internal fun applyRel(expr: Expr, context: Env): Rex = expr.accept(ToRex, context)
@@ -1058,17 +1074,33 @@ internal object RexConverter {
10581074
// TODO CHAR_VARYING, CHARACTER_LARGE_OBJECT, CHAR_LARGE_OBJECT
10591075
DataType.CHARACTER, DataType.CHAR -> {
10601076
val length = type.length ?: 1
1061-
assertGtZeroAndCreate(PType.CHAR, "length", length, PType::character)
1077+
assertGtZeroAndCreate(PType.CHAR, "length", length, PType::character).also {
1078+
if (type.length == null) {
1079+
it.setUnspecifiedLengthMeta()
1080+
}
1081+
}
10621082
}
10631083
DataType.CHARACTER_VARYING, DataType.VARCHAR -> {
10641084
val length = type.length ?: 1
1065-
assertGtZeroAndCreate(PType.VARCHAR, "length", length, PType::varchar)
1085+
assertGtZeroAndCreate(PType.VARCHAR, "length", length, PType::varchar).also {
1086+
if (type.length == null) {
1087+
it.setUnspecifiedLengthMeta()
1088+
}
1089+
}
1090+
}
1091+
DataType.CLOB -> assertGtZeroAndCreate(PType.CLOB, "length", type.length ?: Int.MAX_VALUE, PType::clob).also {
1092+
if (type.length == null) {
1093+
it.setUnspecifiedLengthMeta()
1094+
}
10661095
}
1067-
DataType.CLOB -> assertGtZeroAndCreate(PType.CLOB, "length", type.length ?: Int.MAX_VALUE, PType::clob)
10681096
DataType.STRING -> PType.string()
10691097
// <binary large object string type>
10701098
// TODO BINARY_LARGE_OBJECT
1071-
DataType.BLOB -> assertGtZeroAndCreate(PType.BLOB, "length", type.length ?: Int.MAX_VALUE, PType::blob)
1099+
DataType.BLOB -> assertGtZeroAndCreate(PType.BLOB, "length", type.length ?: Int.MAX_VALUE, PType::blob).also {
1100+
if (type.length == null) {
1101+
it.setUnspecifiedLengthMeta()
1102+
}
1103+
}
10721104
// <bit string type>
10731105
DataType.BIT -> error("BIT is not supported yet.")
10741106
DataType.BIT_VARYING -> error("BIT VARYING is not supported yet.")
@@ -1077,7 +1109,10 @@ internal object RexConverter {
10771109
val p = type.precision
10781110
val s = type.scale
10791111
when {
1080-
p == null && s == null -> PType.decimal(38, 0)
1112+
p == null && s == null -> PType.decimal(38, 0).also {
1113+
it.setUnspecifiedPrecisionMeta()
1114+
it.setUnspecifiedScaleMeta()
1115+
}
10811116
p != null && s != null -> {
10821117
assertParamCompToZero(PType.NUMERIC, "precision", p, false)
10831118
assertParamCompToZero(PType.NUMERIC, "scale", s, true)
@@ -1088,7 +1123,7 @@ internal object RexConverter {
10881123
}
10891124
p != null && s == null -> {
10901125
assertParamCompToZero(PType.NUMERIC, "precision", p, false)
1091-
PType.decimal(p, 0)
1126+
PType.decimal(p, 0).also { it.setUnspecifiedScaleMeta() }
10921127
}
10931128
else -> error("Precision can never be null while scale is specified.")
10941129
}
@@ -1097,7 +1132,10 @@ internal object RexConverter {
10971132
val p = type.precision
10981133
val s = type.scale
10991134
when {
1100-
p == null && s == null -> PType.decimal(38, 0)
1135+
p == null && s == null -> PType.decimal(38, 0).also {
1136+
it.setUnspecifiedPrecisionMeta()
1137+
it.setUnspecifiedScaleMeta()
1138+
}
11011139
p != null && s != null -> {
11021140
assertParamCompToZero(PType.DECIMAL, "precision", p, false)
11031141
assertParamCompToZero(PType.DECIMAL, "scale", s, true)
@@ -1108,7 +1146,7 @@ internal object RexConverter {
11081146
}
11091147
p != null && s == null -> {
11101148
assertParamCompToZero(PType.DECIMAL, "precision", p, false)
1111-
PType.decimal(p, 0)
1149+
PType.decimal(p, 0).also { it.setUnspecifiedScaleMeta() }
11121150
}
11131151
else -> error("Precision can never be null while scale is specified.")
11141152
}
@@ -1125,25 +1163,41 @@ internal object RexConverter {
11251163
DataType.BOOL -> PType.bool()
11261164
// <datetime type>
11271165
DataType.DATE -> PType.date()
1128-
DataType.TIME -> assertGtEqZeroAndCreate(PType.TIME, "precision", type.precision ?: 0, PType::time)
1166+
DataType.TIME -> assertGtEqZeroAndCreate(PType.TIME, "precision", type.precision ?: 0, PType::time).also {
1167+
if (type.precision == null) {
1168+
it.setUnspecifiedPrecisionMeta()
1169+
}
1170+
}
11291171
DataType.TIME_WITH_TIME_ZONE -> assertGtEqZeroAndCreate(
11301172
PType.TIMEZ,
11311173
"precision",
11321174
type.precision ?: 0,
11331175
PType::timez
1134-
)
1176+
).also {
1177+
if (type.precision == null) {
1178+
it.setUnspecifiedPrecisionMeta()
1179+
}
1180+
}
11351181
DataType.TIMESTAMP -> assertGtEqZeroAndCreate(
11361182
PType.TIMESTAMP,
11371183
"precision",
11381184
type.precision ?: 6,
11391185
PType::timestamp
1140-
)
1186+
).also {
1187+
if (type.precision == null) {
1188+
it.setUnspecifiedPrecisionMeta()
1189+
}
1190+
}
11411191
DataType.TIMESTAMP_WITH_TIME_ZONE -> assertGtEqZeroAndCreate(
11421192
PType.TIMESTAMPZ,
11431193
"precision",
11441194
type.precision ?: 6,
11451195
PType::timestampz
1146-
)
1196+
).also {
1197+
if (type.precision == null) {
1198+
it.setUnspecifiedPrecisionMeta()
1199+
}
1200+
}
11471201
// <container type>
11481202
DataType.STRUCT -> PType.struct()
11491203
DataType.TUPLE -> PType.struct()
@@ -1154,6 +1208,7 @@ internal object RexConverter {
11541208
DataType.USER_DEFINED -> TODO("Custom type not supported ")
11551209
// <interval type>
11561210
DataType.INTERVAL -> {
1211+
// TODO meta for interval when precision is unspecified
11571212
when (val q = type.intervalQualifier) {
11581213
is IntervalQualifier.Single -> {
11591214
val precision = q.precision ?: INTERVAL_DEFAULT_PRECISON

partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/CompilerType.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ internal class CompilerType(
1818
// Note: This is an experimental property.
1919
internal val isMissingValue: Boolean = false
2020
) : PType(_delegate.code()) {
21+
init {
22+
this.metas = HashMap(_delegate.metas)
23+
}
24+
2125
fun getDelegate(): PType = _delegate
2226
override fun getFields(): MutableCollection<PTypeField> {
2327
return _delegate.fields.map { field ->
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package org.partiql.planner.internal.typer
2+
3+
import org.junit.jupiter.api.Assertions.assertEquals
4+
import org.junit.jupiter.api.Test
5+
import org.partiql.ast.Ast.exprCast
6+
import org.partiql.ast.Ast.exprLit
7+
import org.partiql.ast.Ast.query
8+
import org.partiql.ast.DataType
9+
import org.partiql.ast.Literal
10+
import org.partiql.ast.Statement
11+
import org.partiql.plan.Action
12+
import org.partiql.plan.rex.RexCast
13+
import org.partiql.planner.PartiQLPlanner
14+
import org.partiql.spi.catalog.Session
15+
import org.partiql.spi.types.PType
16+
17+
private const val UNSPECIFIED_LENGTH = "UNSPECIFIED_LENGTH"
18+
private const val UNSPECIFIED_PRECISION = "UNSPECIFIED_PRECISION"
19+
private const val UNSPECIFIED_SCALE = "UNSPECIFIED_SCALE"
20+
21+
/**
22+
* Tests that PType metas are available in the plan after AST -> plan conversion and type inference.
23+
*/
24+
class PTypeMetaInPlan {
25+
private val planner = PartiQLPlanner.standard()
26+
private fun PType.assertHasLengthMeta() {
27+
assertEquals(this.metas[UNSPECIFIED_LENGTH], true)
28+
}
29+
30+
private fun PType.assertHasPrecisionMeta() {
31+
assertEquals(this.metas[UNSPECIFIED_PRECISION], true)
32+
}
33+
34+
private fun PType.assertHasScaleMeta() {
35+
assertEquals(this.metas[UNSPECIFIED_SCALE], true)
36+
}
37+
38+
private fun createCastStatement(dt: DataType): Statement {
39+
return query(exprCast(exprLit(Literal.nul()), dt))
40+
}
41+
42+
private fun getRexCast(dataType: DataType): RexCast {
43+
val castStatement = createCastStatement(dataType)
44+
val result = planner.plan(castStatement, Session.empty())
45+
val rex = (result.plan.action as Action.Query).rex
46+
rex as RexCast
47+
return rex
48+
}
49+
50+
@Test
51+
fun `test decimal has no scale and no precision`() {
52+
val decimal = getRexCast(DataType.DECIMAL()).type.pType
53+
decimal.assertHasPrecisionMeta()
54+
decimal.assertHasScaleMeta()
55+
}
56+
57+
@Test
58+
fun `test decimal has no scale`() {
59+
val decimal = getRexCast(DataType.DECIMAL(10)).type.pType
60+
decimal.assertHasScaleMeta()
61+
}
62+
63+
@Test
64+
fun `test dec has no scale and no precision`() {
65+
val decimal = getRexCast(DataType.DEC()).type.pType
66+
decimal.assertHasPrecisionMeta()
67+
decimal.assertHasScaleMeta()
68+
}
69+
70+
@Test
71+
fun `test dec has no scale`() {
72+
val decimal = getRexCast(DataType.DEC(10)).type.pType
73+
decimal.assertHasScaleMeta()
74+
}
75+
76+
@Test
77+
fun `test numeric has no scale and no precision`() {
78+
val numeric = getRexCast(DataType.NUMERIC()).type.pType
79+
numeric.assertHasPrecisionMeta()
80+
numeric.assertHasScaleMeta()
81+
}
82+
83+
@Test
84+
fun `test numeric has no scale`() {
85+
val numeric = getRexCast(DataType.NUMERIC(10)).type.pType
86+
numeric.assertHasScaleMeta()
87+
}
88+
89+
@Test
90+
fun `test varchar has no length`() {
91+
val varchar = getRexCast(DataType.VARCHAR()).type.pType
92+
varchar.assertHasLengthMeta()
93+
}
94+
95+
@Test
96+
fun `test character_varying has no length`() {
97+
val varchar = getRexCast(DataType.CHARACTER_VARYING()).type.pType
98+
varchar.assertHasLengthMeta()
99+
}
100+
101+
@Test
102+
fun `test character has no length`() {
103+
val char = getRexCast(DataType.CHARACTER()).type.pType
104+
char.assertHasLengthMeta()
105+
}
106+
107+
@Test
108+
fun `test char has no length`() {
109+
val char = getRexCast(DataType.CHAR()).type.pType
110+
char.assertHasLengthMeta()
111+
}
112+
113+
@Test
114+
fun `test clob has no length`() {
115+
val clob = getRexCast(DataType.CLOB()).type.pType
116+
clob.assertHasLengthMeta()
117+
}
118+
119+
@Test
120+
fun `test blob has no length`() {
121+
val blob = getRexCast(DataType.BLOB()).type.pType
122+
blob.assertHasLengthMeta()
123+
}
124+
125+
@Test
126+
fun `test time has no precision`() {
127+
val time = getRexCast(DataType.TIME()).type.pType
128+
time.assertHasPrecisionMeta()
129+
}
130+
131+
@Test
132+
fun `test timez has no precision`() {
133+
val timez = getRexCast(DataType.TIME_WITH_TIME_ZONE()).type.pType
134+
timez.assertHasPrecisionMeta()
135+
}
136+
137+
@Test
138+
fun `test timestamp has no precision`() {
139+
val timestamp = getRexCast(DataType.TIMESTAMP()).type.pType
140+
timestamp.assertHasPrecisionMeta()
141+
}
142+
143+
@Test
144+
fun `test timestampz has no precision`() {
145+
val timestampz = getRexCast(DataType.TIMESTAMP_WITH_TIME_ZONE()).type.pType
146+
timestampz.assertHasPrecisionMeta()
147+
}
148+
}

partiql-spi/api/partiql-spi.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ public abstract class org/partiql/spi/types/PType : org/partiql/spi/Enum {
506506
public static final field VARIANT I
507507
public field metas Ljava/util/Map;
508508
protected fun <init> (I)V
509+
protected fun <init> (ILjava/util/Map;)V
509510
public static fun array ()Lorg/partiql/spi/types/PType;
510511
public static fun array (Lorg/partiql/spi/types/PType;)Lorg/partiql/spi/types/PType;
511512
public static fun bag ()Lorg/partiql/spi/types/PType;
@@ -521,6 +522,7 @@ public abstract class org/partiql/spi/types/PType : org/partiql/spi/Enum {
521522
public static fun codes ()[I
522523
public static fun date ()Lorg/partiql/spi/types/PType;
523524
public static fun decimal ()Lorg/partiql/spi/types/PType;
525+
public static fun decimal (I)Lorg/partiql/spi/types/PType;
524526
public static fun decimal (II)Lorg/partiql/spi/types/PType;
525527
public static fun doublePrecision ()Lorg/partiql/spi/types/PType;
526528
public static fun dynamic ()Lorg/partiql/spi/types/PType;
@@ -547,6 +549,7 @@ public abstract class org/partiql/spi/types/PType : org/partiql/spi/Enum {
547549
public static fun intervalYearMonth (I)Lorg/partiql/spi/types/PType;
548550
public fun name ()Ljava/lang/String;
549551
public static fun numeric ()Lorg/partiql/spi/types/PType;
552+
public static fun numeric (I)Lorg/partiql/spi/types/PType;
550553
public static fun numeric (II)Lorg/partiql/spi/types/PType;
551554
public static fun of (I)Lorg/partiql/spi/types/PType;
552555
public static fun real ()Lorg/partiql/spi/types/PType;

0 commit comments

Comments
 (0)