0
votes

I'm trying to simple insert an element into a databse without having an autoinc flag on the table.

Here is the table definition, I have all of the appropriate mappers defined:

class BlockHeaderTable(tag: Tag) extends Table[BlockHeader](tag,"block_headers")  {

  def hash = column[DoubleSha256Digest]("hash", O.PrimaryKey)

  def version = column[UInt32]("version")

  def previousBlockHash = column[DoubleSha256Digest]("previous_block_hash")

  def merkleRootHash = column[DoubleSha256Digest]("merkle_root_hash")

  def time = column[UInt32]("time")

  def nBits = column[UInt32]("n_bits")

  def nonce = column[UInt32]("nonce")

  def * = (hash, version, previousBlockHash, merkleRootHash, time, nBits, nonce).<>[BlockHeader,
    (DoubleSha256Digest, UInt32, DoubleSha256Digest, DoubleSha256Digest, UInt32, UInt32, UInt32)](blockHeaderApply,blockHeaderUnapply)
}

I'm trying to create a simple insert function for this table. Here is my attempt:

  override val table = TableQuery[BlockHeaderTable]

  def create(blockHeader: BlockHeader): Future[BlockHeader] = {
    val insertAction = table += blockHeader
    database.run(insertAction)
  }

and I'm getting this error for the types:

chris@chris-870Z5E-880Z5E-680Z5E:~/dev/bitcoins-spv-node$ sbt compile 
[info] Loading project definition from /home/chris/dev/bitcoins-spv-node/project
[info] Set current project to bitcoins-spv-node (in build file:/home/chris/dev/bitcoins-spv-node/)
[info] Compiling 1 Scala source to /home/chris/dev/bitcoins-spv-node/target/scala-2.11/classes...
[error] /home/chris/dev/bitcoins-spv-node/src/main/scala/org/bitcoins/spvnode/models/BlockHeaderDAO.scala:30: type mismatch;
[error]  found   : slick.profile.FixedSqlAction[Boolean,slick.dbio.NoStream,slick.dbio.Effect.Write]
[error]  required: slick.dbio.DBIOAction[org.bitcoins.core.protocol.blockchain.BlockHeader,slick.dbio.NoStream,Nothing]
[error]     database.run(insertAction)
[error]                  ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 3 s, completed Sep 8, 2016 2:11:32 PM
2
Where are you defining your insertAction? It looks like it's not inheriting from DBIOAction? - Nathaniel Ford
table is just a TableQuery object, blockHeader is an instance of an object that table stores. The += is defined inside of SimpleInsertActionComposer - Chris Stewart
I guess the major question is if an an insert statement can be used outside of DBIO.seq, every test case they have using it uses that function: github.com/slick/slick/blob/… - Chris Stewart
Not sure about this, but inserting usually returns the number of inserted rows. Try to change table += blockHeader to (table += blockHeader).andThen(DBIO.successful(blockHeader)) if you want to return the just inserted object. - Roman
@Roman this doesn't result in a Future[BlockHeader] -- there has to be a better way to do this - Chris Stewart

2 Answers

1
votes

By default += returns the number of affected rows, which is usually 1. However to satisfy the signature of your create function, you will need to return a BlockHeader. You can do this by simply chain a DBIO.successful() action that returns your "insert" value:

def create(blockHeader: BlockHeader): Future[BlockHeader] = {
  val insertion: DBIO[BlockHeader] = (table += blockHeader).andThen(DBIO.successful(blockHeader))
  database.run(insertAction)
}

andThen is executed just after the first action suceeded, and will return the value of the second action.

Do not confuse with returning. With returningyou can change the value that += and ++= returns. Said this, most of DBMS only allow to return a single, auto incremented primary key. For example if hash was an auto incremented value, you can return it like this:

val hash = (table returning table.map(_.hash)) += blockHeader

And to go a little further, you can also insert that value in your BlockHeaderobject using into:

val blockHeaderWithHash = (table returning table.map(_.hash) into ((blockHeader, hash) => blockHeader.copy(hash = hash))) += blockHeader
0
votes

Your insertAction returns a number of rows that were inserted, so you should map on that value and check if the operation was successful:

def create(blockHeader: BlockHeader): Future[BlockHeader] = {
  val insertAction = (table += blockHeader).flatMap {
    case 0 => DBIO.failed(new Exception("Failed to insert `BlockHeader` object"))
    case _ => DBIO.successful(blockHeader)
  }

  db.run(insertAction)
}