   }
   var targetPrimaryKeyName : String? {
     guard let pkeys = destinationEntity?.primaryKeyAttributeNames
-      else { return nil }
+     else { return nil }
     
     guard pkeys.count == 1 else { return nil }
     return pkeys[0]
   }
-}
-  
-// TODO: rename from info
-  
-protocol CodeRelationshipType: class {
-  // internal patch-helper since we can't refer to generic classes
-  var codeEntity : Entity? { get set }
-}
-extension CodeRelationship: CodeRelationshipType {}
+  
+  
+  // MARK: - Description
+
+  override open func appendJoinsToDescripton(_ ms: inout String) {
+    do {
+      let join = try calculateJoin()
+      if joinSemantic != .innerJoin { ms += " \(joinSemantic)" }
+      ms += " "
+      ms += join.shortDescription
+    }
+    catch {
+      ms += " JOIN-ERROR<\(error)>"
+    }
+  }
+}
+
+open class CodeRelationship<Target: DatabaseObject>
+             : CodeRelationshipBase<Target>, CodeRelationshipType
+{
+  public var codeEntity : Entity? = nil
+  override open var entity : Entity {
+    set {
+      codeEntity = newValue
+    }
+    get {
+      if let entity = codeEntity { return entity }
+      fatalError("code entity has not been resolved!")
+    }
+  }
+
+}
+
 
 fileprivate let fakeEntity = ModelEntity(name: "FAKE")
   

    }
  }

  // helpers
  
  var sourcePrimaryKeyName : String? {
    guard let pkeys = entity.primaryKeyAttributeNames else { return nil }

        if let sourceA = entity[attribute: sourceAttributeName] {
          // The primary key _column_ of the target, e.g. `company_id`
          // Do we have this as the column? (aka foreign key)?
          // But what if we name the columns 'id' in the database too. Say
          // Person.id and Address.id. We wouldn't want to match those?!
          
          let colname = sourceA.columnName ?? sourceA.name
          if let targetA = destinationEntity?[columnName: colname] {
            targetAttributeName = targetA.name
          }
          else {
            // OK, we hit this if both pkeys are named 'id'
            
            print("source attr is:", sourceA)
            throw JoinCalculationError.toManyCouldNotDeriveTargetAttribute(
              entityName: self.name, column: colname, sourceAttribute: sourceA,
              destinationEntity: destinationEntity
            )
          }
        }
        else {
          throw JoinCalculationError.toManyDidNotFindSourceAttribute(
                                       sourceAttributeName: sourceAttributeName)
        }
      }
    }
