parsql-postgres 0.4.0

Parsql için postgresql entegrasyonunu sağlayan küfedir.
Documentation
# parsql-postgres

Parsql için PostgreSQL entegrasyon küfesidir. Bu paket, parsql'in PostgreSQL veritabanlarıyla çalışmasını sağlayan **senkron** API'leri içerir.

## Özellikler

- Senkron PostgreSQL işlemleri
- Otomatik SQL sorgu oluşturma 
- Güvenli parametre yönetimi
- Generic CRUD işlemleri (get, insert, update, delete)
- Client nesnesi için extension metotları
- Transaction desteği
- Veritabanı satırlarını struct'lara dönüştürme
- SQL Injection saldırılarına karşı otomatik koruma

## Güvenlik Özellikleri

### SQL Injection Koruması

parsql-postgres, SQL Injection saldırılarına karşı güvenli bir şekilde tasarlanmıştır:

- Tüm kullanıcı girdileri otomatik olarak parametrize edilir
- PostgreSQL'in "$1, $2, ..." parametrelendirme yapısı otomatik olarak kullanılır
- Makrolar, SQL parametrelerini güvenli bir şekilde işleyerek injection saldırılarına karşı koruma sağlar
- Parametrelerin doğru sırada ve tipte gönderilmesi otomatik olarak yönetilir
- `#[where_clause]` ve diğer SQL bileşenlerinde kullanıcı girdileri her zaman parametrize edilir

```rust
// 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(&conn, query)?;
```

## Kurulum

Cargo.toml dosyanıza şu şekilde ekleyin:

```toml
[dependencies]
parsql = { version = "0.4.0", features = ["postgres"] }
```

## Kullanım

parsql-postgres ile çalışmak için iki farklı yaklaşım kullanabilirsiniz:

### 1. Fonksiyon Tabanlı Yaklaşım

```rust
use parsql::postgres::{
    macros::{Insertable, SqlParams, Queryable, FromRow},
    traits::{SqlParams, SqlQuery},
};
use postgres::{Client, NoTls, types::ToSql};

#[derive(Insertable, SqlParams)]
#[table("users")]
struct InsertUser {
    name: String,
    email: String,
}

#[derive(Queryable, FromRow, SqlParams)]
#[table("users")]
#[where_clause("id = $")]
struct GetUser {
    id: i32,
    name: String,
    email: String,
}

fn main() -> Result<(), postgres::Error> {
    let mut client = Client::connect("host=localhost user=postgres", NoTls)?;
    
    // Fonksiyon yaklaşımı ile kullanıcı ekleme
    let insert_user = InsertUser {
        name: "John".to_string(),
        email: "john@example.com".to_string(),
    };
    let inserted_record_id = client.insert::<InsertUser, i64>(insert_user)?;
    
    // Fonksiyon yaklaşımı ile kullanıcı getirme
    let get_user = GetUser {
        id: 1,
        name: String::new(),
        email: String::new(),
    };
    let user = client.fetch(&get_user)?;
    
    println!("Kullanıcı: {:?}", user);
    Ok(())
}
```

### 2. Extension Metot Yaklaşımı (CrudOps Trait)

Bu yaklaşımda, `CrudOps` trait'i sayesinde CRUD işlemlerini doğrudan `Client` nesnesi üzerinden çağırabilirsiniz:

```rust
use parsql::postgres::{
    CrudOps,
    macros::{Insertable, SqlParams, Queryable, FromRow},
    traits::{SqlParams, SqlQuery},
};
use postgres::{Client, NoTls, types::ToSql};

#[derive(Insertable, SqlParams)]
#[table("users")]
struct InsertUser {
    name: String,
    email: String,
}

#[derive(Queryable, FromRow, SqlParams)]
#[table("users")]
#[where_clause("id = $1")]
struct GetUser {
    id: i32,
    name: String,
    email: String,
}

fn main() -> Result<(), postgres::Error> {
    let mut client = Client::connect("host=localhost user=postgres", NoTls)?;
    
    // Extension metot yaklaşımı ile kullanıcı ekleme
    let insert_user = InsertUser {
        name: "John".to_string(),
        email: "john@example.com".to_string(),
    };
    let inserted_record_id = client.insert::<InsertUser, i64>(insert_user)?;
    
    // Extension metot yaklaşımı ile kullanıcı getirme
    let get_user = GetUser {
        id: 1,
        name: String::new(),
        email: String::new(),
    };
    let user = client.get(&get_user)?;
    
    println!("Kullanıcı: {:?}", user);
    Ok(())
}
```

Extension metot yaklaşımı, özellikle birden fazla CRUD işleminin aynı anda yapıldığı durumlarda kodunuzu daha okunabilir ve akıcı hale getirir.

## Temel Kullanım

Bu paket, PostgreSQL veritabanı ile çalışırken **senkron işlemler** kullanır. Bu, tokio-postgres gibi async/await kullanımı gerektirmediği anlamına gelir.

### Bağlantı Kurma

```rust
use postgres::{Client, NoTls, Error};

fn main() -> Result<(), Error> {
    // PostgreSQL bağlantısı oluşturma
    let mut client = Client::connect(
        "host=localhost user=postgres password=postgres dbname=test",
        NoTls,
    )?;
    
    // Ö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
        )",
        &[],
    )?;
    
    // ...
    
    Ok(())
}
```

## CRUD İşlemleri

### Veri Okuma (Get) İşlemi

```rust
use parsql::postgres::{
    macros::{Queryable, SqlParams, FromRow},
    traits::{FromRow, SqlParams, SqlQuery},
};
use postgres::{types::ToSql, Error, 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(),
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Client::connect(
        "host=localhost user=postgres password=postgres dbname=test",
        NoTls,
    )?;
    
    // Kullanımı
    let get_user = GetUser::new(1);
    let get_result = client.fetch(get_user)?;
    
    println!("Kullanıcı: {:?}", get_result);
    Ok(())
}
```

### Veri Ekleme (Insert) İşlemi

```rust
use parsql::{
    core::Insertable,
    macros::{Insertable, SqlParams},
    postgres::{SqlParams, insert},
};
use postgres::types::ToSql;

#[derive(Insertable)]
#[table("users")]
pub struct InsertUser {
    pub name: String,
    pub email: String,
    pub state: i16,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Client::connect(
        "host=localhost user=postgres password=postgres dbname=test",
        NoTls,
    )?;
    
    let insert_user = InsertUser {
        name: "Ali".to_string(),
        email: "ali@parsql.com".to_string(),
        state: 1,
    };
    
    let insert_result = insert(&mut client, insert_user)?;
    println!("Eklenen kayıt ID: {}", insert_result);
    
    Ok(())
}
```

### Veri Güncelleme (Update) İşlemi

```

## Transaction İşlemleri

parsql-postgres ile transaction işlemlerini iki farklı şekilde gerçekleştirebilirsiniz:

### 1. CrudOps Trait'i ile Transaction Kullanımı

Bu yaklaşımda, `CrudOps` trait'i `Transaction` struct'ı için de implemente edilmiştir, böylece doğrudan transaction nesnesi üzerinden CRUD işlemlerini yapabilirsiniz:

```rust
use postgres::{Client, NoTls};
use parsql::postgres::CrudOps;  // CrudOps trait'ini içe aktar
use parsql::macros::{Insertable, SqlParams, Updateable, UpdateParams};

#[derive(Insertable, SqlParams)]
#[table("users")]
struct InsertUser {
    name: String,
    email: String,
}

#[derive(Updateable, UpdateParams)]
#[table("users")]
#[update("email")]
#[where_clause("id = $")]
struct UpdateUser {
    id: i32,
    email: String,
}

fn main() -> Result<(), postgres::Error> {
    let mut client = Client::connect("host=localhost user=postgres", NoTls)?;
    
    // Transaction başlat
    let mut tx = client.transaction()?;
    
    // Transaction üzerinde CrudOps metotlarını kullan
    let insert_user = InsertUser {
        name: "Ali".to_string(),
        email: "ali@example.com".to_string(),
    };
    let inserted_record_id = tx.insert(insert_user)?;
    
    let update_user = UpdateUser {
        id: 1,
        email: "ali.updated@example.com".to_string(),
    };
    let rows_updated = tx.update(update_user)?;
    
    // Transaction'ı tamamla
    tx.commit()?;
    Ok(())
}
```

### 2. Transaction Yardımcı Fonksiyonları ile Kullanım

Bu yaklaşımda, `transactional` modülündeki yardımcı fonksiyonları kullanarak method chaining yaklaşımıyla işlemlerinizi gerçekleştirebilirsiniz:

```rust
use parsql::postgres::{
    macros::{FromRow, Insertable, Queryable, SqlParams, UpdateParams, Updateable},
    traits::{CrudOps, FromRow, SqlParams, SqlQuery, UpdateParams},
    transactional::{begin, tx_insert, tx_update},
};
use postgres::{types::ToSql, Client, Error, Row};

#[derive(Insertable, SqlParams)]
#[table("users")]
struct InsertUser {
    name: String,
    email: String,
}

#[derive(Updateable, UpdateParams)]
#[table("users")]
#[update("email")]
#[where_clause("id = $")]
struct UpdateUser {
    id: i32,
    email: String,
}

fn main() -> Result<(), postgres::Error> {
    let mut client = Client::connect("host=localhost user=postgres", NoTls)?;
    
    // Transaction başlat
    let tx = begin(&mut client)?;
    
    // Transaction işlemlerini zincirleme (method chaining) şeklinde gerçekleştir
    let insert_user = InsertUser {
        name: "Ali".to_string(),
        email: "ali@example.com".to_string(),
    };
    
    let (tx, _) = tx.tx_insert(tx, insert_user)?;
    
    let update_user = UpdateUser {
        id: 1,
        email: "ali.updated@example.com".to_string(),
    };
    
    let (tx, _) = tx.tx_update(tx, update_user)?;
    
    // Transaction'ı tamamla
    tx.commit()?;
    Ok(())
}
```

Bu yaklaşım, özellikle transaction içinde birden fazla işlem gerçekleştirirken, transaction nesnesinin sürekli olarak elde edilebilmesini sağlar ve kod okunabilirliğini artırır.