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
106
107
108
109
110
111
112
113
114
115
116
use anyhow::Result;
use dbui_core::database::results::ResultSet;
use dbui_core::field_type::FieldType;
use dbui_core::Column;
use postgres::types;
use uuid::Uuid;

/// Creates a [ResultSet](dbui_core::database::results::ResultSet) from provided rows
pub fn from_rows(id: Uuid, ctx: &str, sql: &str, rows: Vec<postgres::row::Row>, duration_ms: i32) -> Result<ResultSet> {
  if rows.is_empty() {
    Ok(ResultSet::new(
      id,
      ctx.to_string(),
      Some(sql.to_string()),
      vec![],
      vec![],
      duration_ms
    ))
  } else {
    let columns: Vec<Column> = rows[0]
      .columns()
      .iter()
      .map(|col| Column::new(col.name().to_string(), from_type(col.type_())))
      .collect();
    let data: Vec<Vec<Option<String>>> = rows
      .iter()
      .map(|row| {
        columns
          .iter()
          .enumerate()
          .map(|(idx, col)| match crate::results::to_string::to_string(&row, idx, col.t()) {
            Ok(x) => x,
            Err(e) => Some(format!("Error: {}", e))
          })
          .collect::<Vec<Option<String>>>()
      })
      .collect();
    Ok(ResultSet::new(
      id,
      ctx.to_string(),
      Some(sql.to_string()),
      columns,
      data,
      duration_ms
    ))
  }
}

pub(crate) fn from_type(t: &types::Type) -> FieldType {
  match t.kind() {
    postgres::types::Kind::Simple => from_simple(t.name()),
    postgres::types::Kind::Enum(v) => FieldType::Enum {
      key: format!("{}: {:?}", t.name(), v)
    },
    postgres::types::Kind::Array(tx) => FieldType::List {
      t: Box::new(from_type(tx))
    },
    postgres::types::Kind::Range(tx) => FieldType::Range {
      t: Box::new(from_type(tx))
    },
    postgres::types::Kind::Domain(tx) => FieldType::Domain {
      t: Box::new(from_type(tx))
    },
    postgres::types::Kind::Composite(fields) => FieldType::Object {
      key: t.name().into(),
      fields: fields.iter().map(|f| format!("{:?}", f)).collect()
    },
    _ => FieldType::Unknown { t: "UNHANDLED".into() }
  }
}

fn from_simple(n: &str) -> FieldType {
  match n {
    "varchar" | "name" | "text" | "bpchar" => FieldType::String,

    "bool" => FieldType::Boolean,
    "char" => FieldType::Char,
    "int2" => FieldType::I16,
    "int4" => FieldType::I32,
    "oid" => FieldType::U32,
    "int8" => FieldType::I64,
    "float4" => FieldType::F32,
    "float8" => FieldType::F64,
    "money" | "numeric" => FieldType::Numeric,

    "date" => FieldType::Date,
    "time" => FieldType::Time,
    "timetz" | "time with time zone" => FieldType::TimeZoned,
    "timestamp" => FieldType::Timestamp,
    "timestamptz" | "timestamp with time zone" => FieldType::TimestampZoned,
    "interval" => FieldType::Interval,

    "uuid" => FieldType::Uuid,
    "json" | "jsonb" => FieldType::Json,
    "xml" => FieldType::Xml,
    "hstore" => FieldType::StringMap,

    "bytea" => FieldType::ByteArray,
    "bit" | "varbit" => FieldType::BitArray,

    "cidr" => FieldType::Cidr,
    "inet" => FieldType::InetAddr,
    "macaddr" => FieldType::MacAddr,

    "box" => FieldType::Box,
    "circle" => FieldType::Circle,
    "path" => FieldType::Path,
    "polygon" => FieldType::Polygon,
    "point" => FieldType::Point,

    "tsquery" => FieldType::TsQuery,
    "tsvector" => FieldType::TsVector,

    s => FieldType::Unknown { t: s.to_string() }
  }
}