question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Potential bug with references

See original GitHub issue

I 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/): image

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:closed
  • Created 3 years ago
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
vincentlauvlwjcommented, Apr 1, 2020

Reopen this issue because I will check identifiers’ length and print a warning if it’s too long in the next version.

0reactions
vincentlauvlwjcommented, Sep 19, 2020

Added the check in Ktorm 3.1.0.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Climate Change Impacts on the Potential Distribution and ...
Native to East Asia, this highly polyphagous insect is spreading rapidly worldwide.
Read more >
Potentially Dangerous Arachnids and Insects - BugGuide.Net
The purpose of this article is to assist you in determining whether the “bug” or spider you have found is potentially dangerous.
Read more >
Brown marmorated stink bug, Halyomorpha halys (Stål ...
Background. Halyomorpha halys (Stål), the brown marmorated stink bug, is a highly invasive insect species due in part to its exceptionally high ...
Read more >
Possible bug in Codelens references count, VS 2019 16.6.2
The reference count is 4, in all 3 files. So, in Form1.designer.vb, I do not believe there should be a reference to the...
Read more >
Population Growth Potential of the Bed Bug, Cimex lectularius L.
Experimental life tables were constructed and analyzed for three strains of the common bed bug: a pyrethroid-susceptible laboratory strain (HS), a highly ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found