def string(s: String): Source =
Library.string_builder(hint = s.length + 10) { result => val q = '\''
result += q for (c <- s.iterator) { if (c == '\u0000') error("Illegal NUL character in SQL string literal") if (c == q) result += q
result += c
}
result += q
}
class Batch_Error(val results: List[Int]) extends SQLException
class Statement private[SQL](val db: Database, val rep: PreparedStatement) extends AutoCloseable {
stmt =>
object bool { def update(i: Int, x: Boolean): Unit = rep.setBoolean(i, x) def update(i: Int, x: Option[Boolean]): Unit = { if (x.isDefined) update(i, x.get) else rep.setNull(i, java.sql.Types.BOOLEAN)
}
} object int { def update(i: Int, x: Int): Unit = rep.setInt(i, x) def update(i: Int, x: Option[Int]): Unit = { if (x.isDefined) update(i, x.get) else rep.setNull(i, java.sql.Types.INTEGER)
}
} object long { def update(i: Int, x: Long): Unit = rep.setLong(i, x) def update(i: Int, x: Option[Long]): Unit = { if (x.isDefined) update(i, x.get) else rep.setNull(i, java.sql.Types.BIGINT)
}
} object double { def update(i: Int, x: Double): Unit = rep.setDouble(i, x) def update(i: Int, x: Option[Double]): Unit = { if (x.isDefined) update(i, x.get) else rep.setNull(i, java.sql.Types.DOUBLE)
}
} object string { def update(i: Int, x: String): Unit = rep.setString(i, x) def update(i: Int, x: Option[String]): Unit = update(i, x.orNull)
} object bytes { def update(i: Int, bytes: Bytes): Unit = { if (bytes == null) rep.setBytes(i, null) elseif (bytes.size > Int.MaxValue) thrownew IllegalArgumentException else rep.setBinaryStream(i, bytes.stream(), bytes.size.toInt)
} def update(i: Int, bytes: Option[Bytes]): Unit = update(i, bytes.orNull)
} object date { def update(i: Int, date: Date): Unit = db.update_date(stmt, i, date) def update(i: Int, date: Option[Date]): Unit = update(i, date.orNull)
}
def execute(): Boolean = rep.execute()
def execute_batch(batch: IterableOnce[Statement => Unit]): Unit = { val it = batch.iterator if (it.nonEmpty) { for (body <- it) { body(this); rep.addBatch() } val res = rep.executeBatch() if (!res.forall(i => i >= 0 || i == java.sql.Statement.SUCCESS_NO_INFO)) { thrownew Batch_Error(res.toList)
}
}
}
def execute_query(): Result = new Result(this, rep.executeQuery())
overridedef close(): Unit = rep.close()
}
/* results */
class Result private[SQL](val stmt: Statement, val rep: ResultSet) extends AutoCloseable {
res =>
def next(): Boolean = rep.next()
def iterator[A](get: Result => A): Iterator[A] = new Iterator[A] { privatevar _next: Boolean = res.next() def hasNext: Boolean = _next def next(): A = { val x = get(res); _next = res.next(); x }
}
def bool(column: Column): Boolean = rep.getBoolean(column.name) def int(column: Column): Int = rep.getInt(column.name) def long(column: Column): Long = rep.getLong(column.name) def double(column: Column): Double = rep.getDouble(column.name) def string(column: Column): String = { val s = rep.getString(column.name) if (s == null) ""else s
} def bytes(column: Column): Bytes = { val bs = rep.getBytes(column.name) if (bs == null) Bytes.empty else Bytes(bs)
} def date(column: Column): Date = stmt.db.date(res, column)
def vacuum(tables: List[SQL.Table] = Nil): Unit = if (is_sqlite) execute_statement("VACUUM") // always FULL elseif (tables.isEmpty) execute_statement("VACUUM FULL") elseif (postgresql_major_version.get <= 10) { for (t <- tables) execute_statement("VACUUM " + t.ident)
} else execute_statement("VACUUM" + commas(tables.map(_.ident)))
def now(): Date
/* types */
def sql_type(T: Type): Source
/* connection */
def connection: Connection
def sqlite_connection: Option[JDBC4Connection] =
connection match { case conn: JDBC4Connection => Some(conn) case _ => None }
def postgresql_connection: Option[PGConnection] =
connection match { case conn: PGConnection => Some(conn) case _ => None }
def the_sqlite_connection: JDBC4Connection =
sqlite_connection getOrElse
error("SQLite connection expected, but found " + connection.getClass.getName)
def the_postgresql_connection: PGConnection =
postgresql_connection getOrElse
error("PostgreSQL connection expected, but found " + connection.getClass.getName)
def postgresql_major_version: Option[Int] = if (is_postgresql) { def err(s: String): Nothing = error("Bad PostgreSQL version " + s)
the_postgresql_connection.getParameterStatus("server_version") match { casenull => err("null") case str =>
str.iterator.takeWhile(Symbol.is_ascii_digit).mkString match { case Value.Int(m) => Some(m) case _ => err(quote(str))
}
}
} else None
overridedef close(): Unit = connection.close()
def transaction[A](body: => A): A = connection.synchronized {
require(connection.getAutoCommit(), "transaction already active") try {
connection.setAutoCommit(false) try { val result = body
connection.commit()
result
} catch { case exn: Throwable => connection.rollback(); throw exn }
} finally { connection.setAutoCommit(true) }
}
def transaction_lock[A](
tables: Tables,
create: Boolean = false,
label: String = "",
log: Logger = transaction_logger()
)(body: => A): A = { val trace_count = - SQL.transaction_count() val trace_start = Time.now() var trace_nl = false
def name_pattern(name: String): String = { val escape = connection.getMetaData.getSearchStringEscape
name.iterator.map(c =>
if_proper(c == '_' || c == '%' || c == escape(0), escape) + c).mkString
}
def get_tables(pattern: String = "%"): List[String] = { val result = new mutable.ListBuffer[String] val rs = connection.getMetaData.getTables(null, null, pattern, null) while (rs.next) { result += rs.getString(3) }
result.toList
}
def get_table_columns(
table_pattern: String = "%",
pattern: String = "%"
): List[(String, String)] = { val result = new mutable.ListBuffer[(String, String)] val rs = connection.getMetaData.getColumns(null, null, table_pattern, pattern) while (rs.next) { result += (rs.getString(3) -> rs.getString(4)) }
result.toList
}
lazyval init_jdbc: Unit = { val lib_path = Path.explode("$ISABELLE_SQLITE_HOME/" + Platform.jvm_platform) val lib_name = File.get_file(lib_path).file_name
def date(res: SQL.Result, column: SQL.Column): Date =
proper_string(res.string(column)) match { case None => null case Some(s) => date_format.parse(s)
}
if (user.isEmpty) error("Undefined database user") if (server.host.isEmpty) error("Undefined database server host") if (server.port <= 0) error("Undefined database server port")
val name = proper_string(database) getOrElse user val url = "jdbc:postgresql://" + server.host + ":" + server.port + "/" + name val ssh = server.ssh_system.ssh_session val print = "server " + quote(user + "@" + server + "/" + name) +
if_proper(ssh, " via ssh " + quote(ssh.get.toString))
val connection = DriverManager.getConnection(url, user, password) val db = new Database(connection, print, server, server_close, receiver_delay)
class Database private[PostgreSQL]( val connection: Connection,
print: String,
server: SSH.Server,
server_close: Boolean,
receiver_delay: Time
) extends SQL.Database { overridedef toString: String = print
overridedef now(): Date = { val now = SQL.Column.date("now")
execute_query_statementO[Date]("SELECT NOW() as " + now.ident, res => res.date(now))
.getOrElse(error("Failed to get current date/time from database server " + toString))
}
/* notifications: IPC via database server */ /* - see https://www.postgresql.org/docs/14/sql-notify.html - self-notifications and repeated notifications are suppressed - notifications are sorted by local system time (nano seconds) - receive() == None means that IPC is inactive or unavailable (SQLite)
*/
try { while (true) {
Isabelle_Thread.interruptible { receiver_delay.sleep(); Option(conn.getNotifications())} match { case Some(array) if array.nonEmpty =>
synchronized { var received = _receiver_buffer.getOrElse(Map.empty) for (a <- array.iterator if a.getPID != self_pid) { val msg = SQL.Notification(a.getName, a.getParameter) if (!received.isDefinedAt(msg)) { val stamp = System.nanoTime()
received = received + (msg -> stamp)
}
}
_receiver_buffer = Some(received)
} case _ =>
}
}
} catch { case Exn.Interrupt() => }
}
privatedef receiver_shutdown(): Unit = synchronized { if (_receiver_buffer.isDefined) {
_receiver_thread.interrupt()
Some(_receiver_thread)
} else None
}.foreach(_.join())
privatedef synchronized_receiver[A](body: => A): A = synchronized { if (_receiver_buffer.isEmpty) {
_receiver_buffer = Some(Map.empty)
_receiver_thread
}
body
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.