WIP: Añade nueva extensión para dar soporte a bases de datos #12

Draft
manuelcillero wants to merge 12 commits from pagetop-seaorm-support into main
6 changed files with 92 additions and 31 deletions
Showing only changes of commit 796ae5ce81 - Show all commits

View file

@ -38,7 +38,8 @@ db_name = "my_app.db"
max_pool_size = 5
```
Para MySQL o PostgreSQL añade también `db_user`, `db_pass`, `db_host` y `db_port`.
Para MySQL o PostgreSQL añade también `db_user`, `db_pass` y `db_host`. El campo `db_port` es
opcional; si se omite se usa el puerto predeterminado del motor.
**Declara la extensión** en tu aplicación o en la extensión que la requiera:
@ -118,13 +119,13 @@ extensiones de PageTop, donde las migraciones deben ejecutarse durante la inicia
extensión. Los ficheros adaptados del original son:
| Archivos | Observaciones |
|----------------------------|--------------------------------------------------------------|
| `lib.rs` en `migration.rs` | Excluye módulos y exportaciones del CLI |
|-----------------------|--------------------------------------------------------------------------|
| `lib.rs` | Incluido en `migration.rs`, descarta módulos y exportaciones del CLI |
| `connection.rs` | Integración completa |
| `manager.rs` | Adapta *features* propias |
| `migrator.rs` | Adapta *features* propias y omite gestión de errores del CLI |
| `prelude.rs` | Excluye exportaciones del CLI |
| `schema.rs` | Integración ajustada con cambios menores |
| `schema.rs` | Integra con ajustes, original de [loco](https://github.com/loco-rs/loco) |
| `seaql_migrations.rs` | Integración completa |

View file

@ -34,7 +34,6 @@ include_config!(SETTINGS: Settings => [
"database.db_user" => "",
"database.db_pass" => "",
"database.db_host" => "localhost",
"database.db_port" => 0,
"database.max_pool_size" => 5,
]);
@ -57,8 +56,9 @@ pub struct Database {
pub db_pass: String,
/// Servidor de conexión a la base de datos (para mysql/postgres).
pub db_host: String,
/// Puerto de conexión a la base de datos, normalmente 3306 (para mysql) ó 5432 (para postgres).
pub db_port: u16,
/// Puerto de conexión a la base de datos (para mysql/postgres). Si es `None` se usa el puerto
/// predeterminado para el motor: 3306 para MySQL y 5432 para PostgreSQL.
pub db_port: Option<u16>,
/// Número máximo de conexiones habilitadas.
pub max_pool_size: u32,
}

View file

@ -1,7 +1,7 @@
use pagetop::core::TypeInfo;
use pagetop::trace;
pub use url::Url as DbUri;
pub(crate) use url::Url as DbUri;
pub use sea_orm::error::{DbErr, RuntimeErr};
pub use sea_orm::{DatabaseConnection as DbConn, ExecResult, QueryResult};
@ -16,7 +16,30 @@ mod migration;
pub use migration::prelude::*;
pub use migration::schema::*;
pub async fn query<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Vec<QueryResult>, DbErr> {
/// Ejecuta una consulta para devolver todas las filas resultantes.
///
/// Acepta cualquier tipo que implemente [`QueryStatementWriter`] (p. ej. [`SelectStatement`]) y
/// serializa la sentencia al dialecto de la base de datos configurada antes de ejecutarla. Cada
/// fila se devuelve como un [`QueryResult`] sin tipar; extrae los valores con
/// [`QueryResult::try_get`].
///
/// ```rust,no_run
/// use pagetop_seaorm::db::*;
///
/// async fn example() -> Result<(), DbErr> {
/// let mut stmt = Query::select()
/// .column(Asterisk)
/// .from(Alias::new("users"))
/// .to_owned();
/// let rows = fetch_all(&mut stmt).await?;
/// for row in rows {
/// let name: String = row.try_get("", "name")?;
/// println!("{name}");
/// }
/// Ok(())
/// }
/// ```
pub async fn fetch_all<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Vec<QueryResult>, DbErr> {
let dbconn = &*DBCONN;
let dbbackend = dbconn.get_database_backend();
dbconn
@ -31,7 +54,30 @@ pub async fn query<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Vec<QueryRes
.await
}
pub async fn exec<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Option<QueryResult>, DbErr> {
/// Ejecuta una consulta y devuelve sólo la primera fila, si existe.
///
/// Funciona igual que [`fetch_all`] pero detiene la ejecución tras la primera fila y devuelve
/// `None` si la consulta no produce resultados.
///
/// ```rust,no_run
/// use pagetop_seaorm::db::*;
///
/// async fn example() -> Result<(), DbErr> {
/// let mut stmt = Query::select()
/// .column(Asterisk)
/// .from(Alias::new("users"))
/// .and_where(Expr::col(Alias::new("id")).eq(1))
/// .to_owned();
/// if let Some(row) = fetch_one(&mut stmt).await? {
/// let name: String = row.try_get("", "name")?;
/// println!("{name}");
/// }
/// Ok(())
/// }
/// ```
pub async fn fetch_one<Q: QueryStatementWriter>(
stmt: &mut Q,
) -> Result<Option<QueryResult>, DbErr> {
let dbconn = &*DBCONN;
let dbbackend = dbconn.get_database_backend();
dbconn
@ -46,11 +92,27 @@ pub async fn exec<Q: QueryStatementWriter>(stmt: &mut Q) -> Result<Option<QueryR
.await
}
pub async fn exec_raw(stmt: String) -> Result<ExecResult, DbErr> {
/// Ejecuta una sentencia SQL en crudo (INSERT, UPDATE, DELETE…) y devuelve el resultado de
/// la operación.
///
/// A diferencia de [`fetch_all`] y [`fetch_one`], no construye la consulta, sino que la recibe como
/// cadena ya formada. Útil para sentencias avanzadas o para migraciones puntuales. El
/// [`ExecResult`] devuelto permite consultar las filas afectadas o el último ID insertado.
///
/// ```rust,no_run
/// use pagetop_seaorm::db::*;
///
/// async fn example() -> Result<(), DbErr> {
/// let result = execute("DELETE FROM sessions WHERE expired = 1").await?;
/// println!("Filas eliminadas: {}", result.rows_affected());
/// Ok(())
/// }
/// ```
pub async fn execute(stmt: impl Into<String>) -> Result<ExecResult, DbErr> {
let dbconn = &*DBCONN;
let dbbackend = dbconn.get_database_backend();
dbconn
.execute(Statement::from_string(dbbackend, stmt))
.execute(Statement::from_string(dbbackend, stmt.into()))
.await
}

View file

@ -35,10 +35,8 @@ pub static DBCONN: LazyLock<DbConn> = LazyLock::new(|| {
tmp_uri
.set_password(Some(config::SETTINGS.database.db_pass.as_str()))
.unwrap();
if config::SETTINGS.database.db_port != 0 {
tmp_uri
.set_port(Some(config::SETTINGS.database.db_port))
.unwrap();
if let Some(port) = config::SETTINGS.database.db_port {
tmp_uri.set_port(Some(port)).unwrap();
}
tmp_uri
}
@ -51,13 +49,10 @@ pub static DBCONN: LazyLock<DbConn> = LazyLock::new(|| {
.as_str(),
)
.unwrap(),
_ => {
trace::error!(
_ => panic!(
"Unrecognized database type \"{}\"",
&config::SETTINGS.database.db_type
);
DbUri::parse("").unwrap()
}
config::SETTINGS.database.db_type
),
};
run_now(Database::connect::<ConnectOptions>({

View file

@ -28,6 +28,8 @@ pub trait MigrationTrait: MigrationName + Send + Sync {
/// Define actions to perform when rolling back the migration
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
Err(DbErr::Migration("We Don't Do That Here".to_owned()))
Err(DbErr::Migration(
"Rollback not implemented for this migration".to_owned(),
))
}
}

View file

@ -39,7 +39,8 @@ db_name = "my_app.db"
max_pool_size = 5
```
Para MySQL o PostgreSQL añade también `db_user`, `db_pass`, `db_host` y `db_port`.
Para MySQL o PostgreSQL añade también `db_user`, `db_pass` y `db_host`. El campo `db_port` es
opcional; si se omite se usa el puerto predeterminado del motor.
**Declara la extensión** en tu aplicación o en la extensión que la requiera: