// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// What a backend can do beyond the four operations of the `Driver` contract.
///
/// The base contract is deliberately the intersection every relational backend
/// offers. Everything else — a prepared handle, a server-side cursor, `COPY`, more
/// than one result set from one statement, an unsigned 64-bit column — is a
/// capability some backends have and others do not. A query layer asks once,
/// through `QueryExecutor::capabilities`, and lowers its plan instead of probing
/// by running a statement and interpreting the failure.
///
/// The presets below describe what the drivers in this repository implement, not
/// what the server software could theoretically do: `streaming_copy` is true only
/// where a driver actually exposes a streaming source and sink.
///
/// ```moonbit check
/// test "a preset names its own dialect" {
/// let pg = @moondb.Capabilities::for_dialect(@moondb.Dialect::Postgres)
/// assert_eq(pg.dialect.name(), "postgres")
/// assert_true(pg.streaming_copy)
/// assert_false(pg.unsigned_integers)
/// }
/// ```
#derive(Eq)
pub(all) struct Capabilities {
/// The dialect this report describes.
dialect : Dialect
/// A `Stmt` handle the server prepares once and re-executes with new values.
prepared_statements : Bool
/// `:name` parameters are rewritten by the driver before the statement is sent.
named_parameters : Bool
/// A cursor the server holds open and hands out in batches.
server_cursors : Bool
/// `COPY` in both directions is streamed chunk by chunk.
streaming_copy : Bool
/// Nested transactions roll back to a savepoint instead of the whole transaction.
savepoints : Bool
/// One statement can return more than one result set.
multiple_result_sets : Bool
/// An unsigned 64-bit column keeps its value above 2^63-1.
unsigned_integers : Bool
/// `INSERT`/`UPDATE`/`DELETE` accept a `RETURNING` clause.
returning : Bool
}
///|
/// The capabilities of the driver moondb ships for `dialect`.
pub fn Capabilities::for_dialect(dialect : Dialect) -> Capabilities {
match dialect {
Sqlite =>
{
dialect,
prepared_statements: true,
named_parameters: true,
server_cursors: false,
streaming_copy: false,
savepoints: true,
multiple_result_sets: false,
unsigned_integers: false,
returning: true,
}
Postgres =>
{
dialect,
prepared_statements: true,
named_parameters: true,
server_cursors: true,
streaming_copy: true,
savepoints: true,
multiple_result_sets: true,
unsigned_integers: false,
returning: true,
}
Mysql =>
{
dialect,
prepared_statements: true,
named_parameters: true,
server_cursors: false,
streaming_copy: false,
savepoints: true,
multiple_result_sets: true,
unsigned_integers: true,
returning: false,
}
}
}
///|
/// Whether this report describes `dialect`.
pub fn Capabilities::is_for(self : Capabilities, dialect : Dialect) -> Bool {
self.dialect == dialect
}
///|
/// The number of capabilities the backend has, which is the useful shape for a
/// feature matrix in a test: a preset that loses one silently stops adding up.
pub fn Capabilities::count(self : Capabilities) -> Int32 {
let flags = [
self.prepared_statements,
self.named_parameters,
self.server_cursors,
self.streaming_copy,
self.savepoints,
self.multiple_result_sets,
self.unsigned_integers,
self.returning,
]
let mut n = 0
for flag in flags {
if flag {
n += 1
}
}
n
}
///|
pub extend Capabilities with Eq::{not_equal, equal}