use std::{mem, ptr};
use pgrx_sql_entity_graph::metadata::{
ArgumentError, ReturnsError, ReturnsRef, SqlMappingRef, SqlTranslatable, TypeOrigin,
};
use crate::{FromDatum, IntoDatum, PgMemoryContexts, pg_sys, set_varsize_4b};
pub type Box = pg_sys::BOX;
impl FromDatum for Box {
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_: pg_sys::Oid,
) -> Option<Self>
where
Self: Sized,
{
if is_null {
None
} else {
let the_box = datum.cast_mut_ptr::<Self>();
Some(the_box.read())
}
}
}
impl IntoDatum for Box {
fn into_datum(mut self) -> Option<pg_sys::Datum> {
unsafe {
let ptr = PgMemoryContexts::CurrentMemoryContext
.copy_ptr_into(&mut self, mem::size_of::<Self>());
Some(ptr.into())
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::BOXOID
}
}
pub type Line = pg_sys::LINE;
impl FromDatum for Line {
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_: pg_sys::Oid,
) -> Option<Self>
where
Self: Sized,
{
if is_null {
None
} else {
let line = datum.cast_mut_ptr::<Self>();
Some(line.read())
}
}
}
impl IntoDatum for Line {
fn into_datum(mut self) -> Option<pg_sys::Datum> {
unsafe {
let ptr = PgMemoryContexts::CurrentMemoryContext
.copy_ptr_into(&mut self, mem::size_of::<Self>());
Some(ptr.into())
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::LINEOID
}
}
pub type LineSegment = pg_sys::LSEG;
impl FromDatum for LineSegment {
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_: pg_sys::Oid,
) -> Option<Self>
where
Self: Sized,
{
if is_null {
None
} else {
let lseg = datum.cast_mut_ptr::<Self>();
Some(lseg.read())
}
}
}
impl IntoDatum for LineSegment {
fn into_datum(mut self) -> Option<pg_sys::Datum> {
unsafe {
let ptr = PgMemoryContexts::CurrentMemoryContext
.copy_ptr_into(&mut self, mem::size_of::<Self>());
Some(ptr.into())
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::LSEGOID
}
}
pub type Point = pg_sys::Point;
impl FromDatum for Point {
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_: pg_sys::Oid,
) -> Option<Self>
where
Self: Sized,
{
if is_null {
None
} else {
let point: *mut Self = datum.cast_mut_ptr();
Some(point.read())
}
}
}
impl IntoDatum for Point {
fn into_datum(mut self) -> Option<pg_sys::Datum> {
unsafe {
let copy = PgMemoryContexts::CurrentMemoryContext
.copy_ptr_into(&mut self, mem::size_of::<Self>());
Some(copy.into())
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::POINTOID
}
}
pub type Circle = pg_sys::CIRCLE;
impl FromDatum for Circle {
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_: pg_sys::Oid,
) -> Option<Self>
where
Self: Sized,
{
if is_null {
None
} else {
let circle = datum.cast_mut_ptr::<Self>();
Some(circle.read())
}
}
}
impl IntoDatum for Circle {
fn into_datum(mut self) -> Option<pg_sys::Datum> {
unsafe {
let ptr = PgMemoryContexts::CurrentMemoryContext
.copy_ptr_into(&mut self, mem::size_of::<Self>());
Some(ptr.into())
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::CIRCLEOID
}
}
#[derive(Debug, Clone, Default)]
pub struct Path {
points: Vec<Point>,
closed: bool,
}
impl Path {
pub fn new(points: Vec<pg_sys::Point>, closed: bool) -> Self {
Self { points, closed }
}
pub fn points(&self) -> &[Point] {
&self.points
}
pub fn closed(&self) -> bool {
self.closed
}
}
impl IntoDatum for Path {
fn into_datum(self) -> Option<pg_sys::Datum> {
let num_points = self.points.len();
let (total_size, varlena_size) =
geo_varlena_size(num_points, mem::size_of::<pg_sys::PATH>());
unsafe {
let path =
PgMemoryContexts::CurrentMemoryContext.palloc(total_size).cast::<pg_sys::PATH>();
set_varsize_4b(path.cast(), varlena_size);
(*path).npts = num_points as i32;
(*path).closed = self.closed as i32;
(*path).dummy = 0;
let points = (*path).p.as_mut_slice(num_points);
for (i, point) in self.points.iter().enumerate() {
points[i] = *point;
}
Some(pg_sys::Datum::from(path))
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::PATHOID
}
}
impl FromDatum for Path {
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_: pg_sys::Oid,
) -> Option<Self>
where
Self: Sized,
{
if is_null {
None
} else {
let data = pg_sys::pg_detoast_datum(datum.cast_mut_ptr()).cast::<pg_sys::PATH>();
let closed = (*data).closed != 0;
let points = ptr::addr_of!((*data).p);
let points = (*points).as_slice((*data).npts as usize).to_vec();
if data != datum.cast_mut_ptr() {
pg_sys::pfree(data.cast());
}
Some(Path { points, closed })
}
}
}
unsafe impl SqlTranslatable for Path {
const TYPE_IDENT: &'static str = crate::pgrx_resolved_type!(Path);
const TYPE_ORIGIN: TypeOrigin = TypeOrigin::External;
const ARGUMENT_SQL: Result<SqlMappingRef, ArgumentError> = Ok(SqlMappingRef::literal("path"));
const RETURN_SQL: Result<ReturnsRef, ReturnsError> =
Ok(ReturnsRef::One(SqlMappingRef::literal("path")));
}
#[derive(Debug, Clone, Default)]
pub struct Polygon {
points: Vec<Point>,
boundbox: Box,
}
unsafe impl SqlTranslatable for Polygon {
const TYPE_IDENT: &'static str = crate::pgrx_resolved_type!(Polygon);
const TYPE_ORIGIN: TypeOrigin = TypeOrigin::External;
const ARGUMENT_SQL: Result<SqlMappingRef, ArgumentError> =
Ok(SqlMappingRef::literal("polygon"));
const RETURN_SQL: Result<ReturnsRef, ReturnsError> =
Ok(ReturnsRef::One(SqlMappingRef::literal("polygon")));
}
impl Polygon {
pub fn new(points: Vec<pg_sys::Point>) -> Self {
let Some(boundbox) = points.iter().fold(None, |acc, point| {
let boundbox = acc.unwrap_or_else(|| Box { high: *point, low: *point });
Some(Box {
high: Point { x: boundbox.high.x.max(point.x), y: boundbox.high.y.max(point.y) },
low: Point { x: boundbox.low.x.min(point.x), y: boundbox.low.y.min(point.y) },
})
}) else {
return Self::default();
};
Self { points, boundbox }
}
pub fn points(&self) -> &[Point] {
&self.points
}
pub fn boundbox(&self) -> Box {
self.boundbox
}
}
impl IntoDatum for Polygon {
fn into_datum(self) -> Option<pg_sys::Datum> {
let num_points = self.points.len();
let (total_size, varlena_size) =
geo_varlena_size(num_points, mem::size_of::<pg_sys::POLYGON>());
unsafe {
let polygon =
PgMemoryContexts::CurrentMemoryContext.palloc(total_size).cast::<pg_sys::POLYGON>();
set_varsize_4b(polygon.cast(), varlena_size);
(*polygon).npts = num_points as i32;
let points = (*polygon).p.as_mut_slice(num_points);
for (i, point) in self.points.iter().enumerate() {
points[i] = *point;
}
(*polygon).boundbox = self.boundbox;
Some(pg_sys::Datum::from(polygon))
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::POLYGONOID
}
}
impl FromDatum for Polygon {
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_: pg_sys::Oid,
) -> Option<Self>
where
Self: Sized,
{
if is_null {
None
} else {
let data = pg_sys::pg_detoast_datum(datum.cast_mut_ptr()).cast::<pg_sys::POLYGON>();
let points = ptr::addr_of!((*data).p);
let points = (*points).as_slice((*data).npts as usize).to_vec();
let boundbox = (*data).boundbox;
if data != datum.cast_mut_ptr() {
pg_sys::pfree(data.cast());
}
Some(Polygon { points, boundbox })
}
}
}
fn geo_varlena_size(num_points: usize, header_size: usize) -> (usize, i32) {
const VARLENA_4B_MAX_SIZE: usize = 0x3fff_ffff;
let points_size =
num_points.checked_mul(mem::size_of::<Point>()).expect("geometric datum is too large");
let total_size = header_size.checked_add(points_size).expect("geometric datum is too large");
if total_size > VARLENA_4B_MAX_SIZE {
panic!("geometric datum is too large");
}
let varlena_size = total_size as i32;
(total_size, varlena_size)
}