| Crates.io | parsql-tokio-postgres |
| lib.rs | parsql-tokio-postgres |
| version | 0.5.0 |
| created_at | 2024-12-23 02:02:50.697395+00 |
| updated_at | 2025-08-01 05:21:48.940298+00 |
| description | Parsql için postgresql entegrasyonunu, tokio runtime ile birlikte sağlayan küfedir. |
| homepage | |
| repository | https://github.com/yazdostum-nettr/parsql |
| max_upload_size | |
| id | 1492626 |
| size | 131,151 |
Parsql için Tokio PostgreSQL entegrasyon küfesidir. Bu paket, parsql'in tokio-postgres ve deadpool-postgres kütüphaneleriyle asenkron çalışmasını sağlayan API'leri içerir.
parsql-tokio-postgres, SQL Injection saldırılarına karşı güvenli bir şekilde tasarlanmıştır:
#[where_clause] ve diğer SQL bileşenlerinde kullanıcı girdileri her zaman parametrize edilir// SQL injection koruması örneği
#[derive(Queryable, FromRow, SqlParams)]
#[table("users")]
#[where_clause("username = $ AND status = $")]
struct UserQuery {
username: String,
status: i32,
}
// Kullanıcı girdisi (potansiyel olarak zararlı) güvenle kullanılır
let query = UserQuery {
username: kullanici_girdisi, // Bu değer direkt SQL'e eklenmez, parametrize edilir
status: 1,
};
// Oluşturulan sorgu: "SELECT * FROM users WHERE username = $1 AND status = $2"
// Parametreler güvenli bir şekilde: [kullanici_girdisi, 1] olarak gönderilir
let user = get(&client, query).await?;
Cargo.toml dosyanıza şu şekilde ekleyin:
[dependencies]
# Tokio PostgreSQL için
parsql = { version = "0.5.0", features = ["tokio-postgres"] }
Bu paket, PostgreSQL veritabanı ile çalışırken asenkron işlemler kullanır. Bu, async/await kullanımı gerektirdiği anlamına gelir.
use tokio_postgres::{NoTls, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
// PostgreSQL bağlantısı oluşturma
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
// Bağlantıyı arka planda çalıştır
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
// Örnek tablo oluşturma
client.execute(
"CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
state SMALLINT NOT NULL
)",
&[],
).await?;
// ...
Ok(())
}
use deadpool_postgres::{Config, Client, Pool};
use tokio_postgres::NoTls;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Deadpool konfigürasyonu
let mut cfg = Config::new();
cfg.host = Some("localhost".to_string());
cfg.user = Some("postgres".to_string());
cfg.password = Some("postgres".to_string());
cfg.dbname = Some("test".to_string());
// Bağlantı havuzu oluşturma
let pool = cfg.create_pool(None, NoTls)?;
// Havuzdan bağlantı alma
let client: Client = pool.get().await?;
// ...
Ok(())
}
Veritabanı işlemlerini gerçekleştirmek için iki yaklaşım sunulmaktadır:
get, insert, update, vb.)client.get(), client.insert(), vb.) - CrudOps traitv0.5.0'dan itibaren tüm gerekli import'lar için prelude kullanabilirsiniz:
use parsql::prelude::*;
#[derive(Queryable, FromRow, SqlParams, Debug)]
#[table("users")]
#[where_clause("id = $")]
pub struct GetUser {
pub id: i64,
pub name: String,
pub email: String,
}
impl GetUser {
pub fn new(id: i64) -> Self {
Self {
id,
name: Default::default(),
email: Default::default(),
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
// Extension metot kullanımı
let get_user = GetUser::new(1);
let user = client.get(get_user).await?;
println!("Kullanıcı: {:?}", user);
Ok(())
}
use parsql::tokio_postgres::{
macros::{FromRow, Queryable, SqlParams},
traits::{FromRow, SqlParams, SqlQuery},
};
use tokio_postgres::{types::ToSql, Row};
#[derive(Queryable, FromRow, SqlParams, Debug)]
#[table("users")]
#[where_clause("id = $")]
pub struct GetUser {
pub id: i64,
pub name: String,
pub email: String,
pub state: i16,
}
impl GetUser {
pub fn new(id: i64) -> Self {
Self {
id,
name: Default::default(),
email: Default::default(),
state: Default::default(),
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
// Kullanımı (fonksiyon yaklaşımı)
let get_user = GetUser::new(1);
let get_result = get(&client, get_user).await?;
// veya (extension metot yaklaşımı)
let get_user = GetUser::new(1);
let get_result = client.get(get_user).await?;
println!("Kullanıcı: {:?}", get_result);
Ok(())
}
use parsql::tokio_postgres::{
macros::{Insertable, SqlParams},
traits::{SqlParams, SqlQuery},
};
use tokio_postgres::types::ToSql;
#[derive(Insertable, SqlParams)]
#[table("users")]
pub struct InsertUser {
pub name: String,
pub email: String,
pub state: i16,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
let insert_user = InsertUser {
name: "Ali".to_string(),
email: "ali@parsql.com".to_string(),
state: 1,
};
// Fonksiyon yaklaşımı
let insert_result = insert(&client, insert_user).await?;
// veya extension metot yaklaşımı
let insert_user = InsertUser {
name: "Mehmet".to_string(),
email: "mehmet@parsql.com".to_string(),
state: 1,
};
let insert_result = client.insert(insert_user).await?;
println!("Eklenen kayıt ID: {}", insert_result);
Ok(())
}
use parsql::tokio_postgres::{
macros::{UpdateParams, Updateable},
traits::{SqlQuery, UpdateParams},
};
use tokio_postgres::types::ToSql;
#[derive(Updateable, UpdateParams)]
#[table("users")]
#[update("name, email")]
#[where_clause("id = $")]
pub struct UpdateUser {
pub id: i64,
pub name: String,
pub email: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
let update_user = UpdateUser {
id: 1,
name: "Ali Yılmaz".to_string(),
email: "ali.yilmaz@parsql.com".to_string(),
};
// Fonksiyon yaklaşımı
let update_result = update(&client, update_user).await?;
// veya extension metot yaklaşımı
let update_user = UpdateUser {
id: 2,
name: "Aslan Kaya".to_string(),
email: "aslan.kaya@parsql.com".to_string(),
};
let update_result = client.update(update_user).await?;
println!("Güncelleme başarılı: {}", update_result);
Ok(())
}
use parsql::tokio_postgres::{
macros::{Deletable, SqlParams},
traits::{SqlParams, SqlQuery},
};
use tokio_postgres::{types::ToSql, Client};
#[derive(Deletable, SqlParams)]
#[table("users")]
#[where_clause("id = $")]
pub struct DeleteUser {
pub id: i64,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
let delete_user = DeleteUser { id: 1 };
// Fonksiyon yaklaşımı
let delete_result = delete(&client, delete_user).await?;
// veya extension metot yaklaşımı
let delete_user = DeleteUser { id: 2 };
let delete_result = client.delete(delete_user).await?;
println!("Silinen kayıt sayısı: {}", delete_result);
Ok(())
}
Bazen standart CRUD işlemleri yetersiz kalabilir. Özel sorguları kolayca çalıştırmak için select ve select_all işlevleri sağlanmıştır. Bunlar da hem fonksiyon hem de extension metot olarak sunulmaktadır:
use parsql::{
core::Queryable,
macros::{Queryable, SqlParams},
tokio_postgres::{SqlParams, select, select_all, FromRow, CrudOps},
};
use tokio_postgres::Row;
#[derive(Queryable, SqlParams)]
#[table("users")]
#[select("SELECT u.*, p.role FROM users u JOIN profiles p ON u.id = p.user_id")]
#[where_clause("u.state = $")]
pub struct UserWithRole {
pub id: i64,
pub name: String,
pub email: String,
pub state: i16,
pub role: String,
}
// FromRow trait'ini manuel olarak uygulama
impl FromRow for UserWithRole {
fn from_row(row: &Row) -> Result<Self, tokio_postgres::Error> {
Ok(Self {
id: row.try_get("id")?,
name: row.try_get("name")?,
email: row.try_get("email")?,
state: row.try_get("state")?,
role: row.try_get("role")?,
})
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
let query = UserWithRole {
id: 0,
name: String::new(),
email: String::new(),
state: 1, // Aktif kullanıcılar
role: String::new(),
};
// Fonksiyon yaklaşımı - Tek bir sonuç almak için
let user = select(&client, query.clone(), |row| UserWithRole::from_row(row)).await?;
// Extension metot yaklaşımı - Tek bir sonuç almak için
let user = client.select(query.clone(), |row| UserWithRole::from_row(row)).await?;
println!("Kullanıcı: {:?}", user);
// Fonksiyon yaklaşımı - Tüm sonuçları almak için
let users = select_all(&client, query.clone(), |row| {
UserWithRole::from_row(row).unwrap()
}).await?;
// Extension metot yaklaşımı - Tüm sonuçları almak için
let users = client.select_all(query, |row| {
UserWithRole::from_row(row).unwrap()
}).await?;
println!("Kullanıcı sayısı: {}", users.len());
Ok(())
}
Deadpool bağlantı havuzu, çok sayıda eşzamanlı veritabanı işlemi için bağlantıları etkin şekilde yönetmenizi sağlar. Bunu kullanmak için deadpool-postgres özelliğini etkinleştirin:
use parsql::{
tokio_postgres::{get, FromRow, SqlParams},
macros::{FromRow, Queryable, SqlParams},
};
use deadpool_postgres::{Config, Client, Pool};
use tokio_postgres::NoTls;
#[derive(Queryable, FromRow, SqlParams, Debug)]
#[table("users")]
#[where_clause("state = $")]
pub struct ActiveUsers {
pub id: i64,
pub name: String,
pub email: String,
pub state: i16,
}
impl ActiveUsers {
pub fn new() -> Self {
Self {
id: 0,
name: String::new(),
email: String::new(),
state: 1, // Aktif kullanıcılar
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Deadpool konfigürasyonu
let mut cfg = Config::new();
cfg.host = Some("localhost".to_string());
cfg.user = Some("postgres".to_string());
cfg.password = Some("postgres".to_string());
cfg.dbname = Some("test".to_string());
// Bağlantı havuzu oluşturma
let pool = cfg.create_pool(None, NoTls)?;
// Havuzdan bağlantı alma
let client: Client = pool.get().await?;
// Sorgu oluşturma
let query = ActiveUsers::new();
// Aktif kullanıcıları getirme
let active_users = get(&client, query).await?;
println!("Aktif kullanıcı: {:?}", active_users);
Ok(())
}
Hata ayıklama amacıyla, çalıştırılan SQL sorgularını izlemek için PARSQL_TRACE çevre değişkenini kullanabilirsiniz:
PARSQL_TRACE=1 cargo run
Bu, çalıştırılan tüm SQL sorgularını konsola yazdıracaktır.
Makrolar, SQL oluşturmada esneklik sağlamak için çeşitli özellikler sunar:
#[derive(Queryable, FromRow, SqlParams)]
#[table("users")]
#[select("SELECT * FROM users")] // İsteğe bağlı özel SQL
#[where_clause("id = $ AND state = $")] // Koşullar
#[order_by("id DESC")] // Sıralama
#[limit(10)] // Limit
#[offset(5)] // Offset
struct UserQuery {
// ...
}
#[derive(Insertable, SqlParams)]
#[table("users")]
#[returning("id")] // INSERT...RETURNING için
struct NewUser {
// ...
}
#[derive(Updateable, UpdateParams)]
#[table("users")]
#[update("name, email")] // Yalnızca belirli alanları güncelle
#[where_clause("id = $")]
struct UpdateUser {
// ...
}
get_all yerine sayfalama (limit ve offset) kullanınasync fn handle_database() -> Result<(), Box<dyn std::error::Error>> {
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres password=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
let result = match get(&client, user_query).await {
Ok(user) => {
println!("Kullanıcı bulundu: {:?}", user);
// İşlem başarılı
Ok(())
},
Err(e) => match e.code() {
// Belirli PostgreSQL hata kodlarını işleme
Some(code) if code == &tokio_postgres::error::SqlState::UNIQUE_VIOLATION => {
println!("Benzersizlik ihlali: {}", e);
Err(e.into())
},
Some(code) if code == &tokio_postgres::error::SqlState::FOREIGN_KEY_VIOLATION => {
println!("Yabancı anahtar ihlali: {}", e);
Err(e.into())
},
_ => {
println!("Genel veritabanı hatası: {}", e);
Err(e.into())
}
},
};
result
}
Bu kütüphane MIT veya Apache-2.0 lisansı altında lisanslanmıştır.
Transaction işlemleri için iki farklı yaklaşım kullanabilirsiniz:
CrudOps trait üzerinden Transaction nesnesiyle çalışmaTransaction struct'ı için CrudOps trait'i implement edilmiştir, bu sayede Client nesnesi üzerinde kullanılan extension methodlarını doğrudan Transaction nesnesi üzerinde de kullanabilirsiniz:
use tokio_postgres::{NoTls, Error};
use parsql::tokio_postgres::{CrudOps, transactional};
use parsql::macros::{Insertable, Updateable, SqlParams};
#[derive(Insertable, SqlParams)]
#[table("users")]
struct InsertUser {
name: String,
email: String,
state: i16,
}
#[derive(Updateable, SqlParams)]
#[table("users")]
#[where_clause("id = $")]
struct ActivateUser {
id: i64,
state: i16,
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let (mut client, connection) = tokio_postgres::connect(
"host=localhost user=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
// Transaction başlat
let transaction = client.transaction().await?;
// Kullanıcı ekle - CrudOps trait methodunu kullanarak
let user = InsertUser {
name: "John".to_string(),
email: "john@example.com".to_string(),
state: 0, // pasif
};
// Transaction içinde doğrudan insert işlemi
let rows = transaction.insert(user).await?;
println!("Eklenen satır sayısı: {}", rows);
// Kullanıcıyı aktifleştir - CrudOps trait methodunu kullanarak
let activate = ActivateUser {
id: 1, // Eklenen kullanıcının ID'si
state: 1, // aktif
};
// Transaction içinde doğrudan update işlemi
let updated = transaction.update(activate).await?;
println!("Güncelleme başarılı: {}", updated);
// Transaction'ı commit et
transaction.commit().await?;
Ok(())
}
transactional modülü ile çalışmatransactional modülündeki yardımcı fonksiyonları kullanarak işlemlerinizi gerçekleştirebilirsiniz:
use tokio_postgres::{NoTls, Error};
use parsql::tokio_postgres::transactional;
use parsql::macros::{Insertable, Updateable, SqlParams};
#[derive(Insertable, SqlParams)]
#[table("users")]
struct InsertUser {
name: String,
email: String,
state: i16,
}
#[derive(Updateable, SqlParams)]
#[table("users")]
#[where_clause("id = $")]
struct ActivateUser {
id: i64,
state: i16,
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let (mut client, connection) = tokio_postgres::connect(
"host=localhost user=postgres dbname=test",
NoTls,
).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Bağlantı hatası: {}", e);
}
});
// Transaction başlat
let tx = transactional::begin(&mut client).await?;
// Kullanıcı ekle
let user = InsertUser {
name: "John".to_string(),
email: "john@example.com".to_string(),
state: 0, // pasif
};
// Transaction içinde insert işlemi
let (tx, rows) = transactional::tx_insert(tx, user).await?;
println!("Eklenen satır sayısı: {}", rows);
// Kullanıcıyı aktifleştir
let activate = ActivateUser {
id: 1, // Eklenen kullanıcının ID'si
state: 1, // aktif
};
// Transaction içinde update işlemi
let (tx, updated) = transactional::tx_update(tx, activate).await?;
println!("Güncelleme başarılı: {}", updated);
// Transaction'ı commit et
tx.commit().await?;
Ok(())
}
transactional modülü aşağıdaki fonksiyonları sağlar:
begin(&mut client): Yeni bir transaction başlatırtx_insert(transaction, entity): Transaction içinde insert işlemi yapar ve transaction ile etkilenen satır sayısını döndürürtx_update(transaction, entity): Transaction içinde update işlemi yapar ve transaction ile güncelleme durumunu döndürürtx_delete(transaction, entity): Transaction içinde delete işlemi yapar ve transaction ile silinen satır sayısını döndürürtx_get(transaction, params): Transaction içinde tek bir kayıt getirir ve transaction ile kaydı döndürürtx_get_all(transaction, params): Transaction içinde birden fazla kayıt getirir ve transaction ile kayıtları döndürürtx_select(transaction, entity, to_model): Transaction içinde özel bir sorgu çalıştırır ve belirtilen dönüşüm fonksiyonunu kullanarak sonuçları dönüştürürtx_select_all(transaction, entity, to_model): Transaction içinde özel bir sorgu çalıştırır ve tüm sonuçları belirtilen dönüşüm fonksiyonunu kullanarak dönüştürürHer fonksiyon, transaction nesnesini geri döndürür, böylece zincirleme işlemler yapabilirsiniz:
let tx = transactional::begin(&mut client).await?;
let (tx, _) = transactional::tx_insert(tx, user1).await?;
let (tx, _) = transactional::tx_insert(tx, user2).await?;
let (tx, _) = transactional::tx_update(tx, activate_user).await?;
tx.commit().await?;