1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
use std::collections::HashMap;
use std::sync::Arc;

use crate::postgres::protocol::{DataRow, TypeFormat};
use crate::postgres::type_info::SharedStr;
use crate::postgres::value::PgValue;
use crate::postgres::{PgTypeInfo, Postgres};
use crate::row::{ColumnIndex, Row};
use serde::de::DeserializeOwned;

// A statement has 0 or more columns being returned from the database

// For Postgres, each column has an OID and a format (binary or text)

// For simple (unprepared) queries, format will always be text

// For prepared queries, format will _almost_ always be binary

#[derive(Clone, Debug)]
pub(crate) struct Column {
    pub(crate) name: Option<SharedStr>,
    pub(crate) type_info: PgTypeInfo,
    pub(crate) format: TypeFormat,
    pub(crate) table_id: Option<u32>,
    pub(crate) column_id: i16,
}

// A statement description containing the column information used to

// properly decode data

#[derive(Default,Debug)]
pub(crate) struct Statement {
    // paramaters

    pub(crate) params: Box<[PgTypeInfo]>,

    // column name -> position

    pub(crate) names: HashMap<SharedStr, usize>,

    // all columns

    pub(crate) columns: Box<[Column]>,
}

#[derive(Debug)]
pub struct PgRow<'c> {
    pub(super) data: DataRow<'c>,

    // shared reference to the statement this row is coming from

    // allows us to get the column information on demand

    pub(super) statement: Arc<Statement>,
}

impl <'c>PgRow<'c>{
    pub fn json_decode_impl<T, I>(&self, index: I) -> crate::Result<T>
        where
            I: ColumnIndex<'c, Self>,
            T: DeserializeOwned
    {
        self.json_decode(index)
    }
}

impl crate::row::private_row::Sealed for PgRow<'_> {}

impl<'c> Row<'c> for PgRow<'c> {
    type Database = Postgres;

    #[inline]
    fn len(&self) -> usize {
        self.data.len()
    }

    #[doc(hidden)]
    fn try_get_raw<I>(&self, index: I) -> crate::Result<PgValue<'c>>
    where
        I: ColumnIndex<'c, Self>,
    {
        let index = index.index(self)?;
        let column = &self.statement.columns[index];
        let buffer = self.data.get(index);
        let value = match (column.format, buffer) {
            (_, None) => PgValue::null(),
            (TypeFormat::Binary, Some(buf)) => PgValue::bytes(column.type_info.clone(), buf),
            (TypeFormat::Text, Some(buf)) => PgValue::utf8(column.type_info.clone(), buf)?,
        };

        Ok(value)
    }
}

impl<'c> ColumnIndex<'c, PgRow<'c>> for usize {
    fn index(&self, row: &PgRow<'c>) -> crate::Result<usize> {
        let len = Row::len(row);

        if *self >= len {
            return Err(crate::Error::ColumnIndexOutOfBounds { len, index: *self });
        }

        Ok(*self)
    }
}

impl<'c> ColumnIndex<'c, PgRow<'c>> for str {
    fn index(&self, row: &PgRow<'c>) -> crate::Result<usize> {
        row.statement
            .names
            .get(self)
            .ok_or_else(|| crate::Error::ColumnNotFound((*self).into()))
            .map(|&index| index as usize)
    }
}