Potential bug with references
See original GitHub issueI think I found a bug with entity references/foreign keys. So, I have the following database schema (specified with DBML https://www.dbml.org/home/):
Table users {
id integer [pk, unique, increment, not null]
username varchar(10) [not null, unique]
full_name varchar(20) [not null, unique]
email varchar(20) [not null, unique]
password_hash varchar(60) [not null]
}
Table projects {
id integer [pk, unique, increment, not null]
name varchar(30) [not null, unique]
description text [not null]
project_owner_id integer [ref: - users.id, not null]
}
Table project_membership {
id integer [pk, unique, increment, not null]
user_id integer [not null]
project_id integer [not null]
}
Ref: users.id - project_membership.user_id [delete: cascade]
Ref: projects.id - project_membership.project_id [delete: cascade]
Table variables {
id integer [pk, unique, increment, not null]
name varchar(20) [not null]
type variable_type [not null]
unit varchar(5) [not null]
dimension text [not null]
objective objective [not null]
exogenous boolean [not null]
system_descriptive boolean [not null]
project_id integer [not null]
}
enum variable_type {
EFFORT
FLOW
CONNECTING
DISPLACEMENT
MOMENTUM
TYPELESS
}
Ref: projects.id - variables.project_id [delete: cascade]
Table component_specifications {
id integer [pk, unique, increment, not null]
name varchar(20) [unique, not null]
type component_type [not null]
project_id integer [not null]
}
enum component_type {
RESISTOR
CAPACITOR
INERTIA
SOURCE_OF_FLOW
SOURCE_OF_EFFORT
TRANSFORMER
GYRATOR
FLOW_JUNCTION
EFFORT_JUNCTION
SINK
}
Ref: projects.id - component_specifications.project_id [delete: cascade]
Table component_specification_internal_variables {
id integer [pk, unique, increment, not null]
component_id integer [not null]
variable_id integer [not null]
}
Ref: component_specifications.id - component_specification_internal_variables.component_id [delete: cascade]
Ref: variables.id - component_specification_internal_variables.variable_id [delete: cascade]
Table component_specification_additional_internal_variables {
id integer [pk, unique, increment, not null]
component_id integer [not null]
variable_id integer [not null]
}
Ref: component_specifications.id - component_specification_additional_internal_variables.component_id [delete: cascade]
Ref: variables.id - component_specification_additional_internal_variables.variable_id [delete: cascade]
The component_specification_internal_variables
and component_specification_additional_internal_variables
tables are mapping tables for many-to-many references between a component specification’s additional internal and internal variables.
Both tables are virtually identical, with only the name being different, so I would expect them to behave identically in Ktorm.
Here is the corresponding Ktorm schema:
interface User : Entity<User> {
companion object : Entity.Factory<User>()
val id: Int
var username: String
var fullName: String
var email: String
var passwordHash: String
}
object Users : Table<User>("users") {
val id by int("id").primaryKey().bindTo { it.id }
val username by varchar("username").bindTo { it.username }
val fullName by varchar("full_name").bindTo { it.fullName }
val email by varchar("email").bindTo { it.email }
val passwordHash by varchar("password_hash").bindTo { it.passwordHash }
}
interface Project : Entity<Project> {
companion object : Entity.Factory<Project>()
val id: Int
var name: String
var description: String
var owner: User
}
object Projects : Table<Project>("projects") {
val id by int("id").primaryKey().bindTo { it.id }
val name by varchar("name").bindTo { it.name }
val description by text("description").bindTo { it.description }
val projectOwnerId by int("project_owner_id").references(Users) { it.owner }
}
interface ProjectMembershipMapping : Entity<ProjectMembershipMapping> {
companion object : Entity.Factory<ProjectMembershipMapping>()
val id: Int
var user: User
var project: Project
}
object ProjectMembership : Table<ProjectMembershipMapping>("project_membership") {
val id by int("id").primaryKey().bindTo { it.id }
val userId by int("user_id").references(Users) { it.user }
val projectId by int("project_id").references(Projects) { it.project }
}
interface ComponentSpecificationEntity : Entity<ComponentSpecificationEntity> {
companion object : Entity.Factory<ComponentSpecificationEntity>()
val id: Int
var name: String
var type: ComponentType
var project: Project
}
object ComponentSpecifications : Table<ComponentSpecificationEntity>("component_specifications") {
val id by int("id").primaryKey().bindTo { it.id }
val name by varchar("name").bindTo { it.name }
val type by componentType("type").bindTo { it.type }
val projectId by int("project_id").references(Projects) { it.project }
}
interface VariableEntity : Entity<VariableEntity> {
companion object : Entity.Factory<VariableEntity>()
val id: Int
var name: String
var type: VariableType
var unit: String
var dimension: String
var objective: Objective
var exogenous: Boolean
var systemDescriptive: Boolean
var project: Project
}
object Variables : Table<VariableEntity>("variables") {
val id by int("id").primaryKey().bindTo { it.id }
val name by varchar("name").bindTo { it.name }
val type by variableType("type").bindTo { it.type }
val unit by varchar("unit").bindTo { it.unit }
val dimension by varchar("dimension").bindTo { it.dimension }
val objective by objective("objective").bindTo { it.objective }
val exogenous by boolean("exogenous").bindTo { it.exogenous }
val systemDescriptive by boolean("system_descriptive").bindTo { it.systemDescriptive }
val projectId by int("project_id").references(Projects) { it.project }
}
interface ComponentSpecificationInternalVariableMapping : Entity<ComponentSpecificationInternalVariableMapping> {
companion object : Entity.Factory<ComponentSpecificationInternalVariableMapping>()
val id: Int
var component: ComponentSpecificationEntity
var variable: VariableEntity
}
object ComponentSpecificationInternalVariables :
Table<ComponentSpecificationInternalVariableMapping>("component_specification_internal_variables") {
val id by int("id").primaryKey().bindTo { it.id }
val componentId by int("component_id").references(ComponentSpecifications) { it.component }
val variableId by int("variable_id").references(Variables) { it.variable }
}
interface ComponentSpecificationAdditionalInternalVariableMapping :
Entity<ComponentSpecificationAdditionalInternalVariableMapping> {
companion object : Entity.Factory<ComponentSpecificationAdditionalInternalVariableMapping>()
val id: Int
var component: ComponentSpecificationEntity
var variable: VariableEntity
}
object ComponentSpecificationAdditionalInternalVariables :
Table<ComponentSpecificationAdditionalInternalVariableMapping>("component_specification_additional_internal_variables") {
val id by int("id").primaryKey().bindTo { it.id }
val componentId by int("component_id").references(ComponentSpecifications) { it.component }
val variableId by int("variable_id").references(Variables) { it.variable }
}
So, for the following test code:
database.useTransaction {
database.sequenceOf(Users).add(
User {
username = "testuser"
fullName = "Test Tester"
email = "test@test.com"
passwordHash = passwordHashForTestUsers
}
)
val ownerUser: User =
database.sequenceOf(Users).find { it.username eq "testuser" }
?: fail("Couldn't find recently created user")
database.sequenceOf(Projects).add(
Project {
name = "Test Project"
description = "Some description"
owner = ownerUser
}
)
val project: Project =
database.sequenceOf(Projects).find { it.name eq "Test Project" }
?: fail("Couldn't find recently created project")
database.sequenceOf(Variables).add(
VariableEntity {
name = "T1"
type = VariableType.EFFORT
unit = "T^2"
dimension = "T.T"
objective = Objective.NONE
exogenous = false
systemDescriptive = false
this.project = project
}
)
val variable: VariableEntity =
database.sequenceOf(Variables).find { it.name eq "T1" }
?: fail("Couldn't find recently created variable")
database.sequenceOf(ComponentSpecifications).add(
ComponentSpecificationEntity {
name = "SOE"
type = ComponentType.SOURCE_OF_EFFORT
this.project = project
}
)
database.sequenceOf(ComponentSpecifications).add(
ComponentSpecificationEntity {
name = "SNK"
type = ComponentType.SINK
this.project = project
}
)
val sourceOfEffortComponent: ComponentSpecificationEntity =
database.sequenceOf(ComponentSpecifications).find { it.name eq "SOE" }
?: fail("Couldn't find recently created component specification")
val sinkComponent: ComponentSpecificationEntity =
database.sequenceOf(ComponentSpecifications).find { it.name eq "SNK" }
?: fail("Couldn't find recently created component specification")
database.sequenceOf(ComponentSpecificationInternalVariables).add(
ComponentSpecificationInternalVariableMapping {
component = sourceOfEffortComponent
this.variable = variable
}
)
database.sequenceOf(ComponentSpecificationAdditionalInternalVariables).add(
ComponentSpecificationAdditionalInternalVariableMapping {
component = sinkComponent
this.variable = variable
}
)
val internalVariableMapping: ComponentSpecificationInternalVariableMapping =
database
.sequenceOf(ComponentSpecificationInternalVariables)
.find { it.componentId eq sourceOfEffortComponent.id }
?: fail("Couldn't find recently created internal variable mapping")
val additionalInternalVariableMapping: ComponentSpecificationAdditionalInternalVariableMapping =
database
.sequenceOf(ComponentSpecificationAdditionalInternalVariables)
.find { it.componentId eq sinkComponent.id }
?: fail("Couldn't find recently created additional internal variable mapping")
println("Internal variable name: ${internalVariableMapping.variable.name}")
println("Additional internal variable name: ${additionalInternalVariableMapping.variable.name}")
}
I get the following results:
Internal variable name: T1
Additional internal variable name:
As you can see, the variable loses it’s name information, when referenced through additional internal variables, even though both tables and the Ktorm schema for them is virtually identical.
Here are the SQL queries generated by ktorm:
For internal variables:
select component_specification_internal_variables.id as component_specification_internal_variables_id, component_specification_internal_variables.component_id as component_specification_internal_variables_component_id, component_specification_internal_variables.variable_id as component_specification_internal_variables_variable_id, _ref0.id as _ref0_id, _ref0.name as _ref0_name, _ref0.type as _ref0_type, _ref0.project_id as _ref0_project_id, _ref1.id as _ref1_id, _ref1.name as _ref1_name, _ref1.description as _ref1_description, _ref1.project_owner_id as _ref1_project_owner_id, _ref2.id as _ref2_id, _ref2.username as _ref2_username, _ref2.full_name as _ref2_full_name, _ref2.email as _ref2_email, _ref2.password_hash as _ref2_password_hash, _ref3.id as _ref3_id, _ref3.name as _ref3_name, _ref3.type as _ref3_type, _ref3.unit as _ref3_unit, _ref3.dimension as _ref3_dimension, _ref3.objective as _ref3_objective, _ref3.exogenous as _ref3_exogenous, _ref3.system_descriptive as _ref3_system_descriptive, _ref3.project_id as _ref3_project_id, _ref4.id as _ref4_id, _ref4.name as _ref4_name, _ref4.description as _ref4_description, _ref4.project_owner_id as _ref4_project_owner_id, _ref5.id as _ref5_id, _ref5.username as _ref5_username, _ref5.full_name as _ref5_full_name, _ref5.email as _ref5_email, _ref5.password_hash as _ref5_password_hash from component_specification_internal_variables left join component_specifications _ref0 on component_specification_internal_variables.component_id = _ref0.id left join projects _ref1 on _ref0.project_id = _ref1.id left join users _ref2 on _ref1.project_owner_id = _ref2.id left join variables _ref3 on component_specification_internal_variables.variable_id = _ref3.id left join projects _ref4 on _ref3.project_id = _ref4.id left join users _ref5 on _ref4.project_owner_id = _ref5.id where component_specification_internal_variables.component_id = ? limit ?
For additional internal variables:
select component_specification_additional_internal_variables.id as component_specification_additional_internal_variables_id, component_specification_additional_internal_variables.component_id as component_specification_additional_internal_variables_component_id, component_specification_additional_internal_variables.variable_id as component_specification_additional_internal_variables_variable_id, _ref0.id as _ref0_id, _ref0.name as _ref0_name, _ref0.type as _ref0_type, _ref0.project_id as _ref0_project_id, _ref1.id as _ref1_id, _ref1.name as _ref1_name, _ref1.description as _ref1_description, _ref1.project_owner_id as _ref1_project_owner_id, _ref2.id as _ref2_id, _ref2.username as _ref2_username, _ref2.full_name as _ref2_full_name, _ref2.email as _ref2_email, _ref2.password_hash as _ref2_password_hash, _ref3.id as _ref3_id, _ref3.name as _ref3_name, _ref3.type as _ref3_type, _ref3.unit as _ref3_unit, _ref3.dimension as _ref3_dimension, _ref3.objective as _ref3_objective, _ref3.exogenous as _ref3_exogenous, _ref3.system_descriptive as _ref3_system_descriptive, _ref3.project_id as _ref3_project_id, _ref4.id as _ref4_id, _ref4.name as _ref4_name, _ref4.description as _ref4_description, _ref4.project_owner_id as _ref4_project_owner_id, _ref5.id as _ref5_id, _ref5.username as _ref5_username, _ref5.full_name as _ref5_full_name, _ref5.email as _ref5_email, _ref5.password_hash as _ref5_password_hash from component_specification_additional_internal_variables left join component_specifications _ref0 on component_specification_additional_internal_variables.component_id = _ref0.id left join projects _ref1 on _ref0.project_id = _ref1.id left join users _ref2 on _ref1.project_owner_id = _ref2.id left join variables _ref3 on component_specification_additional_internal_variables.variable_id = _ref3.id left join projects _ref4 on _ref3.project_id = _ref4.id left join users _ref5 on _ref4.project_owner_id = _ref5.id where component_specification_additional_internal_variables.component_id = ? limit ?
Sorry for this issue being so long, but I don’t really know how else to convey the information about the potential bug.
Issue Analytics
- State:
- Created 3 years ago
- Comments:13 (7 by maintainers)
Top GitHub Comments
Reopen this issue because I will check identifiers’ length and print a warning if it’s too long in the next version.
Added the check in Ktorm 3.1.0.