Uploaded image for project: 'Corda'
  1. CORDA-1297

Columns in the Corda database schema should have correct NULL/NOT NULL constraints.


    • Severity:
    • Feature Team:
      Corda Core
    • Sprint:


      The Hibernate @Column annotation has the following default values:

      • nullable = true
      • insertable = true
      • updateable = true

      So Hibernate expects to be able to insert NULL values into the database by default. However, Corda's Kotlin entity classes do not always set nullable=false on their columns, even when the Kotlin type explicitly disallows null values. E.g. the VaultStates entity:

      @Table(name = "vault_states",
             indexes = arrayOf(Index(name = "state_status_idx", columnList = "state_status"),
                      Index(name = "lock_id_idx", columnList = "lock_id, state_status")))
      class VaultStates(
          /** NOTE: serialized transaction state (including contract state) is now resolved from transaction store */
          // TODO: create a distinct table to hold serialized state data (once DBTransactionStore is encrypted)
          /** refers to the X500Name of the notary a state is attached to */
          @Column(name = "notary_name")
          var notary: Party,
          /** references a concrete ContractState that is [QueryableState] and has a [MappedSchema] */
          @Column(name = "contract_state_class_name")
          var contractStateClassName: String,
          /** state lifecycle: unconsumed, consumed */
          @Column(name = "state_status")
          var stateStatus: Vault.StateStatus,
          /** refers to timestamp recorded upon entering UNCONSUMED state */
          @Column(name = "recorded_timestamp")
          var recordedTime: Instant,
          /** refers to timestamp recorded upon entering CONSUMED state */
          @Column(name = "consumed_timestamp", nullable = true)
          var consumedTime: Instant? = null,
          /** used to denote a state has been soft locked (to prevent double spend)
           *  will contain a temporary unique [UUID] obtained from a flow session */
          @Column(name = "lock_id", nullable = true)
          var lockId: String? = null,
          /** refers to the last time a lock was taken (reserved) or updated (released, re-reserved) */
          @Column(name = "lock_timestamp", nullable = true)
          var lockUpdateTime: Instant? = null
      ) : PersistentState()

      This entity seems to expect @Colum(nullable=false) by default as it never sets this flag when it needs to and only sets it when it doesn't.

      Similarly, AbstractCashSelection.attemptSpend() is clearly (and incorrectly) assuming that it can never read NULL values from the database:

      while (rs.next()) {
          val txHash = SecureHash.parse(rs.getString(1))
          val index = rs.getInt(2)
          val pennies = rs.getLong(3)
          totalPennies = rs.getLong(4)
          val rowLockId = rs.getString(5)

      because getLong() and getInt() both return Java primitive types that are zero for NULL values. Developers are expected to call wasNull() to discover if the column was NULL or not.

      Our Hibernate entities need to impose the correct NULL/NOT NULL constraints on the database - whatever these correct values actually are.




            • Assignee:
              Maksymilian Pawlak (Inactive)
              Chris Rankin
            • Votes:
              0 Vote for this issue
              4 Start watching this issue


              • Created:

                PagerDuty Incident