Skip to content

Commit a9d13d0

Browse files
committed
fix foreign nullable columns
1 parent 3486a16 commit a9d13d0

File tree

4 files changed

+100
-55
lines changed

4 files changed

+100
-55
lines changed

src/main/kotlin/model/sql/Table.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,25 @@ data class Table(
4949

5050
data class PrimaryKey(val keyName: String, val columnNames: List<ColumnName>)
5151

52-
data class ForeignKey(
53-
val name: String,
54-
val targetTable: SqlTableName,
55-
val references: Set<Reference>
56-
) {
57-
data class Reference(
52+
sealed interface ForeignKey {
53+
val name: String
54+
val targetTable: SqlTableName
55+
56+
data class KeyPair(
5857
val sourceColumn: ColumnName,
5958
val targetColumn: ColumnName,
6059
)
60+
61+
data class SingleKey(
62+
override val name: String,
63+
override val targetTable: SqlTableName,
64+
val reference: KeyPair,
65+
) : ForeignKey
66+
67+
data class MultiKey(
68+
override val name: String,
69+
override val targetTable: SqlTableName,
70+
val references: Set<KeyPair>,
71+
) : ForeignKey
6172
}
6273
}

src/main/kotlin/service/DbService.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,19 +216,27 @@ class DbService(
216216
name = resultSet.getString("target_table")!!,
217217
),
218218
)
219-
val ref = Table.ForeignKey.Reference(
219+
val ref = Table.ForeignKey.KeyPair(
220220
sourceColumn = Table.ColumnName(resultSet.getString("source_column")!!),
221221
targetColumn = Table.ColumnName(resultSet.getString("target_column")!!),
222222
)
223223
meta to ref
224224
}
225225
.groupBy({ it.first }, { it.second })
226226
.map { (meta, refs) ->
227-
meta.sourceTable to Table.ForeignKey(
228-
name = meta.name,
229-
targetTable = meta.targetTable,
230-
references = refs.toSet(),
231-
)
227+
val key = if (refs.size == 1)
228+
Table.ForeignKey.SingleKey(
229+
name = meta.name,
230+
targetTable = meta.targetTable,
231+
reference = refs.single(),
232+
)
233+
else
234+
Table.ForeignKey.MultiKey(
235+
name = meta.name,
236+
targetTable = meta.targetTable,
237+
references = refs.toSet(),
238+
)
239+
meta.sourceTable to key
232240
}.groupBy({ it.first }, { it.second })
233241
}
234242

src/main/kotlin/util/codegen/Column.kt

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,70 +38,83 @@ fun Table.Column.Type.getTypeName(): TypeName {
3838
}
3939

4040
context(CodeGenContext)
41-
fun PropertySpec.Builder.initializer(column: Table.Column) {
41+
fun PropertySpec.Builder.initializer(column: Table.Column, postFix: String, vararg postArgs: Any) {
4242
val columnName = column.name.value
4343
when (val type = column.type) {
44-
is Table.Column.Type.Array -> initializer("array<%T>(name = %S)", type.getTypeName(), columnName)
44+
is Table.Column.Type.Array -> initializer(
45+
"array<%T>(name = %S)$postFix",
46+
type.getTypeName(), columnName, *postArgs
47+
)
48+
4549
is Table.Column.Type.Enum -> initializer(
4650
"""
4751
customEnumeration(
4852
name = %S,
4953
sql = %S,
50-
fromDb = { %T(it as String) },
54+
fromDb = { %T<%T>(it as String) },
5155
toDb = { it.toPgObject() },
52-
)""".trimIndent(), columnName, type.name.name, typeNameGetPgEnumByLabel
56+
)$postFix""".trimIndent(),
57+
columnName,
58+
type.name.name,
59+
typeNameGetPgEnumByLabel,
60+
type.name.typeName,
61+
*postArgs
5362
)
5463

55-
Table.Column.Type.Int8 -> initializer("long(name = %S)", columnName)
56-
Table.Column.Type.Bool -> initializer("bool(name = %S)", columnName)
57-
Table.Column.Type.VarChar -> initializer("text(name = %S)", columnName)
58-
Table.Column.Type.Date -> initializer("%T(name = %S)", Poet.date, columnName)
59-
Table.Column.Type.Interval -> initializer("duration(name = %S)", columnName)
64+
Table.Column.Type.Int8 -> initializer("long(name = %S)$postFix", columnName, *postArgs)
65+
Table.Column.Type.Bool -> initializer("bool(name = %S)$postFix", columnName, *postArgs)
66+
Table.Column.Type.VarChar -> initializer("text(name = %S)$postFix", columnName, *postArgs)
67+
Table.Column.Type.Date -> initializer("%T(name = %S)$postFix", Poet.date, columnName, *postArgs)
68+
Table.Column.Type.Interval -> initializer("duration(name = %S)$postFix", columnName, *postArgs)
6069
Table.Column.Type.Int4Range -> initializer(
61-
"registerColumn(name = %S, type = %T())",
62-
columnName, typeNameInt4RangeColumnType
70+
"registerColumn(name = %S, type = %T())$postFix",
71+
columnName, typeNameInt4RangeColumnType, *postArgs
6372
)
6473

6574
Table.Column.Type.Int8Range -> initializer(
66-
"registerColumn(name = %S, type = %T())",
67-
columnName, typeNameInt8RangeColumnType
75+
"registerColumn(name = %S, type = %T())$postFix",
76+
columnName, typeNameInt8RangeColumnType, *postArgs
6877
)
6978

7079
Table.Column.Type.Int4MultiRange -> initializer(
71-
"registerColumn(name = %S, type = %T())",
72-
columnName, typeNameInt4MultiRangeColumnType
80+
"registerColumn(name = %S, type = %T())$postFix",
81+
columnName, typeNameInt4MultiRangeColumnType, *postArgs
7382
)
7483

7584
Table.Column.Type.Int8MultiRange -> initializer(
76-
"registerColumn(name = %S, type = %T())",
77-
columnName, typeNameInt8MultiRangeColumnType
85+
"registerColumn(name = %S, type = %T())$postFix",
86+
columnName, typeNameInt8MultiRangeColumnType, *postArgs
7887
)
7988

80-
Table.Column.Type.Int4 -> initializer("integer(name = %S)", columnName)
89+
Table.Column.Type.Int4 -> initializer("integer(name = %S)$postFix", columnName, *postArgs)
8190
Table.Column.Type.Json -> initializer(
82-
"%T<%T>(name = %S, serialize = %T)",
83-
Poet.jsonColumn, Poet.jsonElement, columnName, Poet.json,
91+
"%T<%T>(name = %S, serialize = %T)$postFix",
92+
Poet.jsonColumn, Poet.jsonElement, columnName, Poet.json, *postArgs
8493
)
8594

8695
Table.Column.Type.Jsonb -> initializer(
87-
"%T<%T>(name = %S, jsonConfig = %T)",
88-
Poet.jsonColumn, Poet.jsonElement, columnName, Poet.json,
96+
"%T<%T>(name = %S, jsonConfig = %T)$postFix",
97+
Poet.jsonColumn, Poet.jsonElement, columnName, Poet.json, *postArgs
8998
)
9099

91100
is Table.Column.Type.Numeric -> initializer(
92-
"decimal(name = %S, precision = ${type.precision}, scale = ${type.scale})",
93-
columnName,
101+
"decimal(name = %S, precision = ${type.precision}, scale = ${type.scale})$postFix",
102+
columnName, *postArgs
103+
)
104+
105+
Table.Column.Type.Int2 -> initializer("short(name = %S)$postFix", columnName, *postArgs)
106+
Table.Column.Type.Text -> initializer("text(name = %S)$postFix", columnName, *postArgs)
107+
Table.Column.Type.Time -> initializer("%T(name = %S)$postFix", Poet.time, columnName, *postArgs)
108+
Table.Column.Type.Timestamp -> initializer("%T(name = %S)$postFix", Poet.timestamp, columnName, *postArgs)
109+
Table.Column.Type.TimestampWithTimeZone -> initializer(
110+
"%T(name = %S)$postFix",
111+
Poet.timestampWithTimeZone, columnName, *postArgs
94112
)
95113

96-
Table.Column.Type.Int2 -> initializer("short(name = %S)", columnName)
97-
Table.Column.Type.Text -> initializer("text(name = %S)", columnName)
98-
Table.Column.Type.Time -> initializer("%T(name = %S)", Poet.time, columnName)
99-
Table.Column.Type.Timestamp -> initializer("%T(name = %S)", Poet.timestamp, columnName)
100-
Table.Column.Type.TimestampWithTimeZone -> initializer("%T(name = %S)", Poet.timestampWithTimeZone, columnName)
101-
Table.Column.Type.Uuid -> initializer("uuid(name = %S)", columnName)
114+
Table.Column.Type.Uuid -> initializer("uuid(name = %S)$postFix", columnName, *postArgs)
102115
Table.Column.Type.UnconstrainedNumeric -> initializer(
103-
"registerColumn(name = %S, type = %T())",
104-
columnName, typeNameUnconstrainedNumericColumnType
116+
"registerColumn(name = %S, type = %T())$postFix",
117+
columnName, typeNameUnconstrainedNumericColumnType, *postArgs
105118
)
106119
}
107120
}

src/main/kotlin/util/codegen/Table.kt

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import io.github.klahap.pgen.util.makeDifferent
1212

1313
context(CodeGenContext)
1414
internal fun Table.toTypeSpecInternal() = buildObject(this@toTypeSpecInternal.name.prettyName) {
15+
val foreignKeysSingle = this@toTypeSpecInternal.foreignKeys.filterIsInstance<Table.ForeignKey.SingleKey>()
16+
.associate { it.reference.sourceColumn to (it.targetTable to it.reference.targetColumn) }
1517
superclass(Poet.table)
1618
addSuperclassConstructorParameter("%S", this@toTypeSpecInternal.name.name)
1719
this@toTypeSpecInternal.columns.forEach { column ->
@@ -23,10 +25,19 @@ internal fun Table.toTypeSpecInternal() = buildObject(this@toTypeSpecInternal.na
2325
.parameterizedBy(column.type.getTypeName())
2426

2527
else -> column.type.getTypeName()
26-
}
28+
}.copy(nullable = column.isNullable),
2729
),
2830
) {
29-
initializer(column)
31+
val postArgs = mutableListOf<Any>()
32+
val postFix = buildString {
33+
foreignKeysSingle[column.name]?.let { foreignKey ->
34+
append(".references(%T.${foreignKey.second.pretty})")
35+
postArgs.add(foreignKey.first.typeName)
36+
}
37+
if (column.isNullable)
38+
append(".nullable()")
39+
}
40+
initializer(column, postFix = postFix, postArgs = postArgs.toTypedArray())
3041
}
3142
}
3243
if (this@toTypeSpecInternal.primaryKey != null) {
@@ -41,15 +52,17 @@ internal fun Table.toTypeSpecInternal() = buildObject(this@toTypeSpecInternal.na
4152
}
4253
}
4354

44-
addInitializerBlock {
45-
this@toTypeSpecInternal.foreignKeys.forEach { foreignKey ->
46-
val foreignKeyStrFormat = foreignKey.references.joinToString(", ") { ref ->
47-
"${ref.sourceColumn.pretty} to %T.${ref.targetColumn.pretty}"
55+
val foreignKeysMulti = this@toTypeSpecInternal.foreignKeys.filterIsInstance<Table.ForeignKey.MultiKey>()
56+
if (foreignKeysMulti.isNotEmpty())
57+
addInitializerBlock {
58+
foreignKeysMulti.forEach { foreignKey ->
59+
val foreignKeyStrFormat = foreignKey.references.joinToString(", ") { ref ->
60+
"${ref.sourceColumn.pretty} to %T.${ref.targetColumn.pretty}"
61+
}
62+
val foreignKeyStrValues = foreignKey.references.map {
63+
foreignKey.targetTable.typeName
64+
}.toTypedArray()
65+
addStatement("foreignKey($foreignKeyStrFormat)", *foreignKeyStrValues)
4866
}
49-
val foreignKeyStrValues = foreignKey.references.map {
50-
foreignKey.targetTable.typeName
51-
}.toTypedArray()
52-
addStatement("foreignKey($foreignKeyStrFormat)", *foreignKeyStrValues)
5367
}
54-
}
5568
}

0 commit comments

Comments
 (0)