// Copyright (c) 2023-2025 ParadeDB, Inc. // // This file is part of ParadeDB - Postgres for Search and Analytics // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . pub mod aggregate; pub mod builder_fns; pub mod config; pub mod operator; pub mod tokenize; use pgrx::{ direct_function_call, pg_cast, pg_sys, InOutFuncs, IntoDatum, PostgresType, StringInfo, }; pub use rustc_hash::FxHashMap as HashMap; pub use rustc_hash::FxHashSet as HashSet; use serde::{Deserialize, Serialize}; use std::ffi::CStr; use std::fmt::{Display, Formatter}; use tantivy::json_utils::split_json_path; #[macro_export] macro_rules! nodecast { ($type_:ident, $kind:ident, $node:expr) => {{ let node = $node; pgrx::is_a(node.cast(), pgrx::pg_sys::NodeTag::$kind) .then(|| node.cast::()) }}; ($type_:ident, $kind:ident, $node:expr, true) => {{ let node = $node; (node.is_null() || pgrx::is_a(node.cast(), pgrx::pg_sys::NodeTag::$kind)) .then(|| node.cast::()) }}; } // came to life in pg15 pub type Cardinality = f64; #[cfg(feature = "pg14")] pub type Varno = pgrx::pg_sys::Index; #[cfg(not(feature = "pg14"))] pub type Varno = i32; #[allow(dead_code)] pub trait AsBool { unsafe fn as_bool(&self) -> Option; } pub trait AsCStr { unsafe fn as_c_str(&self) -> Option<&std::ffi::CStr>; } #[cfg(feature = "pg14")] impl AsBool for *mut pgrx::pg_sys::Node { unsafe fn as_bool(&self) -> Option { let node = nodecast!(Value, T_Integer, *self)?; Some((*node).val.ival != 0) } } #[cfg(not(feature = "pg14"))] impl AsBool for *mut pgrx::pg_sys::Node { unsafe fn as_bool(&self) -> Option { let node = nodecast!(Boolean, T_Boolean, *self)?; Some((*node).boolval) } } #[cfg(feature = "pg14")] impl AsCStr for *mut pgrx::pg_sys::Node { unsafe fn as_c_str(&self) -> Option<&std::ffi::CStr> { let node = nodecast!(Value, T_String, *self)?; Some(std::ffi::CStr::from_ptr((*node).val.str_)) } } #[cfg(not(feature = "pg14"))] impl AsCStr for *mut pgrx::pg_sys::Node { unsafe fn as_c_str(&self) -> Option<&std::ffi::CStr> { let node = nodecast!(String, T_String, *self)?; Some(std::ffi::CStr::from_ptr((*node).sval)) } } /// A type used whenever our builder functions require a fieldname. #[derive( Debug, Clone, Ord, Eq, PartialOrd, PartialEq, Hash, Serialize, Deserialize, PostgresType, )] #[inoutfuncs] pub struct FieldName(String); impl Display for FieldName { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl AsRef for FieldName { fn as_ref(&self) -> &str { self } } impl std::ops::Deref for FieldName { type Target = str; fn deref(&self) -> &Self::Target { &self.0 } } impl From for FieldName where T: Into, { fn from(value: T) -> Self { FieldName(value.into()) } } impl InOutFuncs for FieldName { fn input(input: &CStr) -> Self where Self: Sized, { FieldName(input.to_str().unwrap().to_owned()) } fn output(&self, buffer: &mut StringInfo) { buffer.push_str(&self.0); } } impl FieldName { #[inline(always)] pub fn into_inner(self) -> String { self.0 } pub fn root(&self) -> String { let json_path = split_json_path(self.0.as_str()); if json_path.len() == 1 { self.0.clone() } else { json_path[0].clone() } } pub fn path(&self) -> Option { let json_path = split_json_path(self.0.as_str()); if json_path.len() == 1 { None } else { Some(json_path[1..].join(".")) } } pub fn is_ctid(&self) -> bool { self.root() == "ctid" } } #[pg_cast(implicit)] fn text_to_fieldname(field: String) -> FieldName { FieldName(field) } #[allow(unused)] pub fn fieldname_typoid() -> pg_sys::Oid { unsafe { let oid = direct_function_call::( pg_sys::regtypein, &[c"paradedb.FieldName".into_datum()], ) .expect("type `paradedb.FieldName` should exist"); if oid == pg_sys::Oid::INVALID { panic!("type `paradedb.FieldName` should exist"); } oid } }