systemprompt_database/lib.rs
1//! # systemprompt-database
2//!
3//! `PostgreSQL` infrastructure for systemprompt.io: a thin `SQLx`-backed pool,
4//! generic repository traits, dynamic-query primitives for admin tooling, and
5//! lifecycle helpers (schema installation, extension migrations, validation).
6//!
7//! ## Public API surface
8//!
9//! - [`Database`] / [`DbPool`] — owned pool wrapper with optional split
10//! read/write providers.
11//! - [`DatabaseProvider`] — dyn-safe trait abstracting
12//! query/execute/transaction primitives across providers (currently only
13//! `PostgreSQL`).
14//! - [`PostgresProvider`] — the `PostgreSQL` implementation.
15//! - [`RepositoryError`] / [`DatabaseResult`] — canonical typed error/result
16//! returned from non-trait public APIs.
17//! - [`MigrationService`], [`install_extension_schemas`],
18//! [`install_extension_schemas_full`] — lifecycle helpers driving
19//! extension-supplied DDL.
20//! - [`DatabaseAdminService`], [`QueryExecutor`], [`AdminSql`],
21//! [`SafeIdentifier`] — admin/introspection layer used by the CLI.
22//! - [`resilience`] — domain-agnostic resilience primitives
23//! ([`resilience::ResilienceGuard`], [`resilience::CircuitBreaker`],
24//! [`resilience::Bulkhead`], [`resilience::retry_async`]) wrapping outbound
25//! calls; the crate's own connection and transaction retries run on them.
26//!
27//! ## Feature flags
28//!
29//! This crate currently has no Cargo features; everything compiles
30//! unconditionally. The `[package.metadata.docs.rs]` block is in place so
31//! `--all-features` documentation builds remain stable as features are added.
32//!
33//! ## sqlx allowlist
34//!
35//! Static SQL goes through the compile-time-verified `sqlx::query!` /
36//! `query_as!` / `query_scalar!` macros. Runtime/dynamic SQL is contained to
37//! two paths whose contract is dynamic SQL by design and that are documented in
38//! the workspace allowlist (`ci/check-sqlx.sh`, `instructions/prompt/rust.md`):
39//!
40//! - `src/admin/` — admin CLI surfaces (introspection, restricted query
41//! executor) where the SQL is the user input.
42//! - `src/services/postgres/` — the dyn-safe `DatabaseProvider` implementation,
43//! transaction wrapper, type-erased helpers and `PostgreSQL` schema
44//! introspection.
45//!
46//! Every other call site uses verified macros.
47
48pub mod admin;
49pub mod error;
50pub mod extension;
51pub mod lifecycle;
52pub mod models;
53#[macro_use]
54pub mod repository;
55pub mod resilience;
56pub mod services;
57
58pub use extension::DatabaseExtension;
59
60pub use models::{
61 ArtifactId, ClientId, ColumnInfo, ContentId, ContextId, DatabaseInfo, DatabaseQuery,
62 DatabaseTransaction, DbValue, ExecutionStepId, FileId, FromDatabaseRow, FromDbValue, IndexInfo,
63 JsonRow, LogId, QueryResult, QueryRow, QuerySelector, SessionId, SkillId, TableInfo, TaskId,
64 ToDbValue, TokenId, TraceId, UserId, parse_database_datetime,
65};
66
67pub use services::{
68 BoxFuture, Database, DatabaseCliDisplay, DatabaseExt, DatabaseProvider, DatabaseProviderExt,
69 DbPool, PostgresProvider, SqlExecutor, with_transaction, with_transaction_raw,
70 with_transaction_retry,
71};
72
73pub use error::{DatabaseResult, RepositoryError};
74pub use lifecycle::{
75 AppliedMigration, ChecksumDrift, ExtensionMigrationStatus, MigrationConfig, MigrationResult,
76 MigrationService, MigrationStatus, PendingMigration, SquashPlan, install_extension_schemas,
77 install_extension_schemas_full, install_extension_schemas_with_config, validate_column_exists,
78 validate_database_connection, validate_table_exists,
79};
80pub use repository::{
81 CleanupRepository, CreateServiceInput, DatabaseInfoRepository, PgDbPool, ServiceConfig,
82 ServiceRepository,
83};
84
85pub use admin::{
86 AdminSql, AdminSqlError, DEFAULT_READONLY_ROW_LIMIT, DatabaseAdminService, IdentifierError,
87 QueryExecutor, QueryExecutorError, SafeIdentifier,
88};
89pub use sqlx::types::Json;
90pub use sqlx::{PgPool, Pool, Postgres, Transaction};
91
92use systemprompt_traits::DatabaseHandle;
93
94impl DatabaseHandle for Database {
95 fn is_connected(&self) -> bool {
96 true
97 }
98
99 fn as_any(&self) -> &dyn std::any::Any {
100 self
101 }
102}