use crate::errors::{GdalError, Result};
use crate::Dataset;
use gdal_sys::OGRErr;
use std::ops::{Deref, DerefMut};
#[derive(Debug)]
pub struct Transaction<'a> {
dataset: &'a mut Dataset,
rollback_on_drop: bool,
}
impl<'a> Transaction<'a> {
fn new(dataset: &'a mut Dataset) -> Self {
Transaction {
dataset,
rollback_on_drop: true,
}
}
#[deprecated = "Transaction now implements Deref<Target = Dataset>, so you can call Dataset methods on it directly. Use .deref() if you need a reference to the underlying Dataset."]
pub fn dataset(&self) -> &Dataset {
self.dataset
}
#[deprecated = "Transaction now implements DerefMut<Target = Dataset>, so you can call Dataset methods on it directly. Use .deref_mut() if you need a mutable reference to the underlying Dataset."]
pub fn dataset_mut(&mut self) -> &mut Dataset {
self.dataset
}
pub fn commit(mut self) -> Result<()> {
let rv = unsafe { gdal_sys::GDALDatasetCommitTransaction(self.dataset.c_dataset()) };
self.rollback_on_drop = false;
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "GDALDatasetCommitTransaction",
});
}
Ok(())
}
pub fn rollback(mut self) -> Result<()> {
let rv = unsafe { gdal_sys::GDALDatasetRollbackTransaction(self.dataset.c_dataset()) };
self.rollback_on_drop = false;
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "GDALDatasetRollbackTransaction",
});
}
Ok(())
}
}
impl Deref for Transaction<'_> {
type Target = Dataset;
fn deref(&self) -> &Self::Target {
self.dataset
}
}
impl DerefMut for Transaction<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.dataset
}
}
impl Drop for Transaction<'_> {
fn drop(&mut self) {
if self.rollback_on_drop {
unsafe { gdal_sys::GDALDatasetRollbackTransaction(self.dataset.c_dataset()) };
}
}
}
impl Dataset {
pub fn start_transaction(&mut self) -> Result<Transaction<'_>> {
let force = 1;
let rv = unsafe { gdal_sys::GDALDatasetStartTransaction(self.c_dataset(), force) };
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "GDALDatasetStartTransaction",
});
}
Ok(Transaction::new(self))
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::{fixture, open_gpkg_for_update};
use crate::vector::{Geometry, LayerAccess};
use crate::Dataset;
fn polygon() -> Geometry {
Geometry::from_wkt("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))").unwrap()
}
#[test]
fn test_start_transaction() {
let (_temp_path, mut ds) = open_gpkg_for_update(&fixture("poly.gpkg"));
let txn = ds.start_transaction();
assert!(txn.is_ok());
}
#[test]
fn test_transaction_commit() {
let (_temp_path, mut ds) = open_gpkg_for_update(&fixture("poly.gpkg"));
let orig_feature_count = ds.layer(0).unwrap().feature_count();
let txn = ds.start_transaction().unwrap();
let mut layer = txn.layer(0).unwrap();
layer.create_feature(polygon()).unwrap();
assert!(txn.commit().is_ok());
assert_eq!(ds.layer(0).unwrap().feature_count(), orig_feature_count + 1);
}
#[test]
fn test_transaction_rollback() {
let (_temp_path, mut ds) = open_gpkg_for_update(&fixture("poly.gpkg"));
let orig_feature_count = ds.layer(0).unwrap().feature_count();
let txn = ds.start_transaction().unwrap();
let mut layer = txn.layer(0).unwrap();
layer.create_feature(polygon()).unwrap();
assert!(txn.rollback().is_ok());
assert_eq!(ds.layer(0).unwrap().feature_count(), orig_feature_count);
}
#[test]
fn test_transaction_implicit_rollback() {
let (_temp_path, mut ds) = open_gpkg_for_update(&fixture("poly.gpkg"));
let orig_feature_count = ds.layer(0).unwrap().feature_count();
{
let txn = ds.start_transaction().unwrap();
let mut layer = txn.layer(0).unwrap();
layer.create_feature(polygon()).unwrap();
}
assert_eq!(ds.layer(0).unwrap().feature_count(), orig_feature_count);
}
#[test]
fn test_start_transaction_unsupported() {
let mut ds = Dataset::open(fixture("roads.geojson")).unwrap();
assert!(ds.start_transaction().is_err());
}
}