|
515 | 515 |
|
516 | 516 | use std::sync::Arc; |
517 | 517 |
|
518 | | -use crate::value_converter::PythonDTO; |
| 518 | +use deadpool_postgres::Object; |
| 519 | +use pyo3::{pyclass, pymethods, Py, PyAny, PyErr, Python}; |
| 520 | + |
| 521 | +use crate::{ |
| 522 | + common::BaseDataBaseQuery, |
| 523 | + exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult}, |
| 524 | + query_result::PSQLDriverPyQueryResult, |
| 525 | +}; |
| 526 | + |
| 527 | +trait CursorObjectTrait { |
| 528 | + async fn cursor_start( |
| 529 | + &self, |
| 530 | + cursor_name: &String, |
| 531 | + scroll: &Option<bool>, |
| 532 | + querystring: &String, |
| 533 | + prepared: &Option<bool>, |
| 534 | + parameters: &Option<Py<PyAny>>, |
| 535 | + ) -> RustPSQLDriverPyResult<()>; |
| 536 | + |
| 537 | + async fn cursor_close(&self, closed: &bool, cursor_name: &String) |
| 538 | + -> RustPSQLDriverPyResult<()>; |
| 539 | +} |
519 | 540 |
|
520 | | -use super::transaction::Transaction; |
| 541 | +impl CursorObjectTrait for Object { |
| 542 | + async fn cursor_start( |
| 543 | + &self, |
| 544 | + cursor_name: &String, |
| 545 | + scroll: &Option<bool>, |
| 546 | + querystring: &String, |
| 547 | + prepared: &Option<bool>, |
| 548 | + parameters: &Option<Py<PyAny>>, |
| 549 | + ) -> RustPSQLDriverPyResult<()> { |
| 550 | + let mut cursor_init_query = format!("DECLARE {}", cursor_name); |
| 551 | + if let Some(scroll) = scroll { |
| 552 | + if *scroll { |
| 553 | + cursor_init_query.push_str(" SCROLL"); |
| 554 | + } else { |
| 555 | + cursor_init_query.push_str(" NO SCROLL"); |
| 556 | + } |
| 557 | + } |
| 558 | + |
| 559 | + cursor_init_query.push_str(format!(" CURSOR FOR {}", querystring).as_str()); |
| 560 | + |
| 561 | + self.psqlpy_query(cursor_init_query, parameters.clone(), *prepared) |
| 562 | + .await?; |
| 563 | + |
| 564 | + Ok(()) |
| 565 | + } |
| 566 | + |
| 567 | + async fn cursor_close( |
| 568 | + &self, |
| 569 | + closed: &bool, |
| 570 | + cursor_name: &String, |
| 571 | + ) -> RustPSQLDriverPyResult<()> { |
| 572 | + if *closed { |
| 573 | + return Err(RustPSQLDriverError::DataBaseCursorError( |
| 574 | + "Cursor is already closed".into(), |
| 575 | + )); |
| 576 | + } |
| 577 | + |
| 578 | + self.psqlpy_query( |
| 579 | + format!("CLOSE {}", cursor_name), |
| 580 | + Default::default(), |
| 581 | + Some(false), |
| 582 | + ) |
| 583 | + .await?; |
| 584 | + |
| 585 | + Ok(()) |
| 586 | + } |
| 587 | +} |
521 | 588 |
|
| 589 | +#[pyclass] |
522 | 590 | pub struct Cursor { |
523 | | - db_transaction: Arc<Transaction>, |
| 591 | + db_transaction: Arc<Object>, |
524 | 592 | querystring: String, |
525 | | - parameters: Vec<PythonDTO>, |
| 593 | + parameters: Option<pyo3::Py<pyo3::PyAny>>, |
526 | 594 | cursor_name: String, |
527 | 595 | fetch_number: usize, |
528 | 596 | scroll: Option<bool>, |
529 | | - prepared: bool, |
| 597 | + prepared: Option<bool>, |
530 | 598 | is_started: bool, |
531 | 599 | closed: bool, |
532 | 600 | } |
| 601 | + |
| 602 | +#[pymethods] |
| 603 | +impl Cursor { |
| 604 | + #[must_use] |
| 605 | + pub fn __aiter__(slf: Py<Self>) -> Py<Self> { |
| 606 | + slf |
| 607 | + } |
| 608 | + |
| 609 | + fn __await__(slf: Py<Self>) -> Py<Self> { |
| 610 | + slf |
| 611 | + } |
| 612 | + |
| 613 | + async fn __aenter__<'a>(slf: Py<Self>) -> RustPSQLDriverPyResult<Py<Self>> { |
| 614 | + let (db_transaction, cursor_name, scroll, querystring, prepared, parameters) = |
| 615 | + Python::with_gil(|gil| { |
| 616 | + let self_ = slf.borrow(gil); |
| 617 | + return ( |
| 618 | + self_.db_transaction.clone(), |
| 619 | + self_.cursor_name.clone(), |
| 620 | + self_.scroll, |
| 621 | + self_.querystring.clone(), |
| 622 | + self_.prepared, |
| 623 | + self_.parameters.clone(), |
| 624 | + ); |
| 625 | + }); |
| 626 | + db_transaction |
| 627 | + .cursor_start(&cursor_name, &scroll, &querystring, &prepared, ¶meters) |
| 628 | + .await?; |
| 629 | + Python::with_gil(|gil| { |
| 630 | + let mut self_ = slf.borrow_mut(gil); |
| 631 | + self_.is_started = true; |
| 632 | + }); |
| 633 | + Ok(slf) |
| 634 | + } |
| 635 | + |
| 636 | + #[allow(clippy::needless_pass_by_value)] |
| 637 | + async fn __aexit__<'a>( |
| 638 | + slf: Py<Self>, |
| 639 | + _exception_type: Py<PyAny>, |
| 640 | + exception: Py<PyAny>, |
| 641 | + _traceback: Py<PyAny>, |
| 642 | + ) -> RustPSQLDriverPyResult<()> { |
| 643 | + let (db_transaction, closed, cursor_name, is_exception_none, py_err) = |
| 644 | + pyo3::Python::with_gil(|gil| { |
| 645 | + let self_ = slf.borrow(gil); |
| 646 | + ( |
| 647 | + self_.db_transaction.clone(), |
| 648 | + self_.closed, |
| 649 | + self_.cursor_name.clone(), |
| 650 | + exception.is_none(gil), |
| 651 | + PyErr::from_value_bound(exception.into_bound(gil)), |
| 652 | + ) |
| 653 | + }); |
| 654 | + |
| 655 | + db_transaction.cursor_close(&closed, &cursor_name).await?; |
| 656 | + if !is_exception_none { |
| 657 | + return Err(RustPSQLDriverError::PyError(py_err)); |
| 658 | + } |
| 659 | + Ok(()) |
| 660 | + } |
| 661 | + |
| 662 | + pub async fn start(&mut self) -> RustPSQLDriverPyResult<()> { |
| 663 | + let db_transaction_arc = self.db_transaction.clone(); |
| 664 | + |
| 665 | + db_transaction_arc |
| 666 | + .cursor_start( |
| 667 | + &self.cursor_name, |
| 668 | + &self.scroll, |
| 669 | + &self.querystring, |
| 670 | + &self.prepared, |
| 671 | + &self.parameters, |
| 672 | + ) |
| 673 | + .await?; |
| 674 | + |
| 675 | + self.is_started = true; |
| 676 | + Ok(()) |
| 677 | + } |
| 678 | + |
| 679 | + /// Close the cursor. |
| 680 | + /// |
| 681 | + /// It executes CLOSE command to close cursor in the transaction. |
| 682 | + /// |
| 683 | + /// # Errors |
| 684 | + /// May return Err Result if cannot execute query. |
| 685 | + pub async fn close(&mut self) -> RustPSQLDriverPyResult<()> { |
| 686 | + let db_transaction_arc = self.db_transaction.clone(); |
| 687 | + |
| 688 | + db_transaction_arc |
| 689 | + .cursor_close(&self.closed, &self.cursor_name) |
| 690 | + .await?; |
| 691 | + |
| 692 | + self.closed = true; |
| 693 | + Ok(()) |
| 694 | + } |
| 695 | + |
| 696 | + /// Fetch data from cursor. |
| 697 | + /// |
| 698 | + /// It's possible to specify fetch number. |
| 699 | + /// |
| 700 | + /// # Errors |
| 701 | + /// May return Err Result if cannot execute query. |
| 702 | + pub async fn fetch<'a>( |
| 703 | + slf: Py<Self>, |
| 704 | + fetch_number: Option<usize>, |
| 705 | + ) -> RustPSQLDriverPyResult<PSQLDriverPyQueryResult> { |
| 706 | + let (db_transaction, inner_fetch_number, cursor_name) = Python::with_gil(|gil| { |
| 707 | + let self_ = slf.borrow(gil); |
| 708 | + return ( |
| 709 | + self_.db_transaction.clone(), |
| 710 | + self_.fetch_number, |
| 711 | + self_.cursor_name.clone(), |
| 712 | + ); |
| 713 | + }); |
| 714 | + |
| 715 | + let fetch_number = match fetch_number { |
| 716 | + Some(usize) => usize, |
| 717 | + None => inner_fetch_number, |
| 718 | + }; |
| 719 | + |
| 720 | + let result = db_transaction |
| 721 | + .psqlpy_query( |
| 722 | + format!("FETCH {fetch_number} FROM {}", cursor_name), |
| 723 | + None, |
| 724 | + Some(false), |
| 725 | + ) |
| 726 | + .await?; |
| 727 | + Ok(result) |
| 728 | + } |
| 729 | +} |
0 commit comments