more operations;
authorwenzelm
Sun, 04 Sep 2016 17:38:22 +0200
changeset 63779 9da65bc75610
parent 63778 e06e899b78d0
child 63780 163244cefb4e
more operations;
src/Pure/General/bytes.scala
src/Pure/Tools/sql.scala
src/Pure/Tools/sqlite.scala
--- a/src/Pure/General/bytes.scala	Sun Sep 04 15:44:20 2016 +0200
+++ b/src/Pure/General/bytes.scala	Sun Sep 04 17:38:22 2016 +0200
@@ -25,6 +25,8 @@
     }
   }
 
+  def apply(a: Array[Byte]): Bytes = apply(a, 0, a.length)
+
   def apply(a: Array[Byte], offset: Int, length: Int): Bytes =
     if (length == 0) empty
     else {
--- a/src/Pure/Tools/sql.scala	Sun Sep 04 15:44:20 2016 +0200
+++ b/src/Pure/Tools/sql.scala	Sun Sep 04 17:38:22 2016 +0200
@@ -1,12 +1,15 @@
 /*  Title:      Pure/Tools/sql.scala
     Author:     Makarius
 
-Support for SQL.
+Generic support for SQL.
 */
 
 package isabelle
 
 
+import java.sql.ResultSet
+
+
 object SQL
 {
   /* concrete syntax */
@@ -28,5 +31,71 @@
   def quote_string(s: String): String =
     quote(s.map(quote_char(_)).mkString)
 
-  def quote_ident(s: String): String = "`" + s + "`"
+  def quote_ident(s: String): String =
+  {
+    require(!s.contains('`'))
+    "`" + s + "`"
+  }
+
+
+  /* columns */
+
+  object Column
+  {
+    def int(name: String, strict: Boolean = true): Column[Int] = new Column_Int(name, strict)
+    def long(name: String, strict: Boolean = true): Column[Long] = new Column_Long(name, strict)
+    def double(name: String, strict: Boolean = true): Column[Double] = new Column_Double(name, strict)
+    def string(name: String, strict: Boolean = true): Column[String] = new Column_String(name, strict)
+    def bytes(name: String, strict: Boolean = true): Column[Bytes] = new Column_Bytes(name, strict)
+  }
+
+  abstract class Column[A] private[SQL](val name: String, val strict: Boolean)
+  {
+    def sql_name: String = quote_ident(name)
+    def sql_type: String
+    def sql_decl: String = sql_name + " " + sql_type + (if (strict) " NOT NULL" else "")
+    def result(rs: ResultSet): A
+    def result_string(rs: ResultSet): String = rs.getString(name)
+
+    override def toString: String = sql_decl
+  }
+
+  class Column_Int private[SQL](name: String, strict: Boolean)
+    extends Column[Int](name, strict)
+  {
+    def sql_type: String = "INTEGER"
+    def result(rs: ResultSet): Int = rs.getInt(name)
+  }
+
+  class Column_Long private[SQL](name: String, strict: Boolean)
+    extends Column[Long](name, strict)
+  {
+    def sql_type: String = "INTEGER"
+    def result(rs: ResultSet): Long = rs.getLong(name)
+  }
+
+  class Column_Double private[SQL](name: String, strict: Boolean)
+    extends Column[Double](name, strict)
+  {
+    def sql_type: String = "REAL"
+    def result(rs: ResultSet): Double = rs.getDouble(name)
+  }
+
+  class Column_String private[SQL](name: String, strict: Boolean)
+    extends Column[String](name, strict)
+  {
+    def sql_type: String = "TEXT"
+    def result(rs: ResultSet): String = rs.getString(name)
+  }
+
+  class Column_Bytes private[SQL](name: String, strict: Boolean)
+    extends Column[Bytes](name, strict)
+  {
+    def sql_type: String = "BLOB"
+    def result(rs: ResultSet): Bytes =
+    {
+      val bs = rs.getBytes(name)
+      if (bs == null) null else Bytes(bs)
+    }
+  }
 }
--- a/src/Pure/Tools/sqlite.scala	Sun Sep 04 15:44:20 2016 +0200
+++ b/src/Pure/Tools/sqlite.scala	Sun Sep 04 17:38:22 2016 +0200
@@ -19,6 +19,21 @@
     override def toString: String = path.toString
 
     def close { connection.close }
+
+    def transaction[A](body: => A): A =
+    {
+      val auto_commit = connection.getAutoCommit
+      val savepoint = connection.setSavepoint
+
+      try {
+        connection.setAutoCommit(false)
+        val result = body
+        connection.commit
+        result
+      }
+      catch { case exn: Throwable => connection.rollback(savepoint); throw exn }
+      finally { connection.setAutoCommit(auto_commit) }
+    }
   }
 
   def open_database(path: Path): Database =