Skip to content

Commit

Permalink
add new catalog and cache APIs (pgcentralfoundation#1545)
Browse files Browse the repository at this point in the history
  • Loading branch information
usamoi committed Aug 21, 2024
1 parent 2de7a4e commit ee15204
Show file tree
Hide file tree
Showing 17 changed files with 1,481 additions and 454 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pgrx-pg-sys/include/pg12.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
Expand Down
1 change: 1 addition & 0 deletions pgrx-pg-sys/include/pg13.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
Expand Down
1 change: 1 addition & 0 deletions pgrx-pg-sys/include/pg14.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
Expand Down
1 change: 1 addition & 0 deletions pgrx-pg-sys/include/pg15.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
Expand Down
1 change: 1 addition & 0 deletions pgrx-pg-sys/include/pg16.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
Expand Down
1 change: 1 addition & 0 deletions pgrx-pg-sys/include/pg17.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
Expand Down
1 change: 1 addition & 0 deletions pgrx-tests/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod memcxt_tests;
mod name_tests;
mod numeric_tests;
mod pg_cast_tests;
mod pg_catalog_tests;
mod pg_extern_tests;
mod pg_guard_tests;
mod pg_operator_tests;
Expand Down
135 changes: 135 additions & 0 deletions pgrx-tests/src/tests/pg_catalog_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use pgrx::prelude::*;

#[cfg(any(test, feature = "pg_test"))]
#[pg_schema]
mod tests {
use pgrx::pg_sys::Oid;
use std::ffi::CString;

#[allow(unused_imports)]
use crate as pgrx_tests;
use pgrx::prelude::*;

#[pg_test]
fn test_pg_catalog_pg_proc_boolin() {
use pgrx::pg_catalog::*;
let proname = CString::new("boolin").unwrap();
let proargtypes = /* cstring */ [pgrx::wrappers::regtypein("cstring")];
let pronamespace = /* pg_catalog */ Oid::from(11);
// search
let pg_proc = PgProc::search_procnameargsnsp(&proname, &proargtypes, pronamespace).unwrap();
let pg_proc = pg_proc.get().unwrap();
// getstruct, name
assert_eq!(pg_proc.proname(), proname.as_c_str());
// getstruct, primitive types
assert_eq!(pg_proc.pronamespace(), pronamespace);
assert_eq!(pg_proc.procost(), 1.0);
assert_eq!(pg_proc.prorows(), 0.0);
assert_eq!(pg_proc.provariadic(), Oid::INVALID);
// getstruct, regproc
assert_eq!(pg_proc.prosupport(), Oid::INVALID);
// getstruct, char
assert_eq!(pg_proc.prokind(), PgProcProkind::Function);
assert_eq!(pg_proc.prosecdef(), false);
assert_eq!(pg_proc.proleakproof(), false);
assert_eq!(pg_proc.proisstrict(), true);
assert_eq!(pg_proc.proretset(), false);
assert_eq!(pg_proc.provolatile(), PgProcProvolatile::Immutable);
assert_eq!(pg_proc.proparallel(), PgProcProparallel::Safe);
assert_eq!(pg_proc.pronargs(), 1);
assert_eq!(pg_proc.pronargdefaults(), 0);
assert_eq!(pg_proc.prorettype(), pgrx::pg_sys::BOOLOID);
// getstruct, oidvector
assert_eq!(pg_proc.proargtypes(), &proargtypes);
// getattr, null
assert!(pg_proc.proallargtypes().is_none());
assert!(pg_proc.proargmodes().is_none());
assert!(pg_proc.proargnames().is_none());
assert!(pg_proc.protrftypes().is_none());
// getattr, text
assert_eq!(pg_proc.prosrc(), "boolin");
assert!(pg_proc.probin().is_none());
assert!(pg_proc.proconfig().is_none());
}

#[pg_test]
fn test_pg_catalog_pg_proc_num_nulls() {
use pgrx::pg_catalog::*;
let proname = CString::new("num_nulls").unwrap();
let proargtypes = [pgrx::pg_sys::ANYOID];
let pronamespace = /* pg_catalog */ pgrx::pg_sys::Oid::from(11);
let pg_proc = PgProc::search_procnameargsnsp(&proname, &proargtypes, pronamespace).unwrap();
let pg_proc = pg_proc.get().unwrap();
assert_eq!(pg_proc.proname(), proname.as_c_str());
assert_eq!(pg_proc.pronamespace(), pronamespace);
assert_eq!(pg_proc.procost(), 1.0);
assert_eq!(pg_proc.prorows(), 0.0);
assert_eq!(pg_proc.provariadic(), pgrx::pg_sys::ANYOID);
assert_eq!(pg_proc.prosupport(), Oid::INVALID);
assert_eq!(pg_proc.prokind(), PgProcProkind::Function);
assert_eq!(pg_proc.prosecdef(), false);
assert_eq!(pg_proc.proleakproof(), false);
assert_eq!(pg_proc.proisstrict(), false);
assert_eq!(pg_proc.proretset(), false);
assert_eq!(pg_proc.provolatile(), PgProcProvolatile::Immutable);
assert_eq!(pg_proc.proparallel(), PgProcProparallel::Safe);
assert_eq!(pg_proc.pronargs(), 1);
assert_eq!(pg_proc.pronargdefaults(), 0);
assert_eq!(pg_proc.prorettype(), pgrx::pg_sys::INT4OID);
assert_eq!(pg_proc.proargtypes(), &proargtypes);
// getattr, oid[]
assert_eq!(
pg_proc.proallargtypes().map(|v| v.iter().collect()),
Some(vec![Some(pgrx::pg_sys::ANYOID)])
);
// getattr, char[]
assert_eq!(
pg_proc.proargmodes().map(|v| v.iter().collect()),
Some(vec![Some(PgProcProargmodes::Variadic)])
);
assert!(pg_proc.proargnames().is_none());
assert!(pg_proc.protrftypes().is_none());
assert_eq!(pg_proc.prosrc(), "pg_num_nulls");
assert!(pg_proc.probin().is_none());
assert!(pg_proc.proconfig().is_none());
}

#[pg_test]
fn test_pg_catalog_pg_proc_gcd() {
// skip this test for pg12
if pgrx::pg_sys::PG_VERSION_NUM < 130000 {
return;
}
use pgrx::pg_catalog::*;
let proname = CString::new("gcd").unwrap();
// search_list
let pg_proc = PgProc::search_list_procnameargsnsp_1(&proname).unwrap();
let mut int4gcd = false;
let mut int8gcd = false;
for i in 0..pg_proc.len() {
let pg_proc = pg_proc.get(i).unwrap();
if pg_proc.prosrc() == "int4gcd" {
int4gcd = true;
}
if pg_proc.prosrc() == "int8gcd" {
int8gcd = true;
}
}
assert!(int4gcd);
assert!(int8gcd);
}

#[pg_test]
fn test_pg_catalog_pg_class_pg_stats() {
use pgrx::pg_catalog::*;
let relname = CString::new("pg_stats").unwrap();
let relnamespace = /* pg_catalog */ pgrx::pg_sys::Oid::from(11);
let pg_class = PgClass::search_relnamensp(&relname, relnamespace).unwrap();
let pg_class = pg_class.get().unwrap();
// getattr, text[]
assert_eq!(
pg_class.reloptions().map(|v| v.iter().collect()),
Some(vec![Some("security_barrier=true".to_string())])
);
}
}
24 changes: 12 additions & 12 deletions pgrx-tests/tests/todo/composite-types-broken-on-spi.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -59,32 +59,32 @@ note: required by a bound in `pgrx::callconv::Args::<'a, 'fcx>::next_arg_uncheck
| ^^^^^^^^^^^^ required by this bound in `Args::<'a, 'fcx>::next_arg_unchecked`
= note: this error originates in the attribute macro `pg_extern` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
error[E0277]: `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
--> tests/todo/composite-types-broken-on-spi.rs:125:20
|
125 | for dog in dogs {
| ^^^^ `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
| ^^^^ `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
|
= help: the trait `IntoIterator` is not implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is not implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`

error[E0277]: `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
error[E0277]: `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
--> tests/todo/composite-types-broken-on-spi.rs:145:20
|
145 | for dog in dogs {
| ^^^^ `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
| ^^^^ `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
|
= help: the trait `IntoIterator` is not implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is not implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`

error[E0277]: `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
error[E0277]: `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
--> tests/todo/composite-types-broken-on-spi.rs:162:20
|
162 | for dog in dogs {
| ^^^^ `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
| ^^^^ `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
|
= help: the trait `IntoIterator` is not implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is not implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`

error[E0308]: mismatched types
--> tests/todo/composite-types-broken-on-spi.rs:11:5
Expand Down
24 changes: 12 additions & 12 deletions pgrx-tests/tests/todo/for-dog-in-dogs.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,29 @@ error[E0277]: `pgrx::VariadicArray<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::Allo
= help: the trait `IntoIterator` is not implemented for `pgrx::VariadicArray<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `pgrx::VariadicArray<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`

error[E0277]: `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
error[E0277]: `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
--> tests/todo/for-dog-in-dogs.rs:65:16
|
65 | for dog in dogs {
| ^^^^ `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
| ^^^^ `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
|
= help: the trait `IntoIterator` is not implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is not implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`

error[E0277]: `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
error[E0277]: `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
--> tests/todo/for-dog-in-dogs.rs:85:16
|
85 | for dog in dogs {
| ^^^^ `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
| ^^^^ `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
|
= help: the trait `IntoIterator` is not implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is not implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`

error[E0277]: `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
error[E0277]: `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
--> tests/todo/for-dog-in-dogs.rs:102:16
|
102 | for dog in dogs {
| ^^^^ `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
| ^^^^ `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>` is not an iterator
|
= help: the trait `IntoIterator` is not implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is not implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
= help: the trait `IntoIterator` is implemented for `pgrx::Array<'_, pgrx::prelude::PgHeapTuple<'_, pgrx::AllocatedByRust>>`
1 change: 1 addition & 0 deletions pgrx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ bitflags = "2.4.0" # BackgroundWorker
bitvec = "1.0" # processing array nullbitmaps
heapless = "0.8" # shmem and PgLwLock
libc.workspace = true # FFI type compat
paste = "1.0.14"
seahash = "4.1.0" # derive(PostgresHash)
serde.workspace = true # impls on pub types
serde_cbor = "0.11.2" # derive(PostgresType)
Expand Down
75 changes: 17 additions & 58 deletions pgrx/src/enum_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,26 @@
//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
//! Helper functions for working with Postgres `enum` types

use crate::pg_sys::GETSTRUCT;
use crate::pg_catalog::PgEnum;
use crate::{ereport, pg_sys, PgLogLevel, PgSqlErrorCode};

pub fn lookup_enum_by_oid(enumval: pg_sys::Oid) -> (String, pg_sys::Oid, f32) {
let tup = unsafe {
pg_sys::SearchSysCache(
pg_sys::SysCacheIdentifier::ENUMOID as i32,
pg_sys::Datum::from(enumval),
pg_sys::Datum::from(0),
pg_sys::Datum::from(0),
pg_sys::Datum::from(0),
)
};
if tup.is_null() {
let pg_enum = PgEnum::search_enumoid(enumval).unwrap();

let Some(pg_enum) = pg_enum.get() else {
ereport!(
PgLogLevel::ERROR,
PgSqlErrorCode::ERRCODE_INVALID_BINARY_REPRESENTATION,
format!("invalid internal value for enum: {enumval:?}")
);
}

let en = unsafe { GETSTRUCT(tup) } as pg_sys::Form_pg_enum;
let en = unsafe { en.as_ref() }.unwrap();
let result = (
unsafe {
core::ffi::CStr::from_ptr(en.enumlabel.data.as_ptr() as *const std::os::raw::c_char)
}
.to_str()
.unwrap()
.to_string(),
en.enumtypid,
en.enumsortorder as f32,
);

unsafe {
pg_sys::ReleaseSysCache(tup);
}
unreachable!()
};

result
(
pg_enum.enumlabel().to_str().unwrap().to_string(),
pg_enum.enumtypid(),
pg_enum.enumsortorder() as f32,
)
}

pub fn lookup_enum_by_label(typname: &str, label: &str) -> pg_sys::Datum {
Expand All @@ -57,35 +38,13 @@ pub fn lookup_enum_by_label(typname: &str, label: &str) -> pg_sys::Datum {
panic!("could not locate type oid for type: {typname}");
}

let tup = unsafe {
let label =
alloc::ffi::CString::new(label).expect("failed to convert enum typname to a CString");
pg_sys::SearchSysCache(
pg_sys::SysCacheIdentifier::ENUMTYPOIDNAME as i32,
pg_sys::Datum::from(enumtypoid),
pg_sys::Datum::from(label.as_ptr()),
pg_sys::Datum::from(0usize),
pg_sys::Datum::from(0usize),
)
};

if tup.is_null() {
panic!("could not find heap tuple for enum: {typname}.{label}, typoid={enumtypoid:?}");
}
let label = std::ffi::CString::new(label).expect("failed to convert enum typname to a CString");

// SAFETY: we know that `tup` is valid because we just got it from Postgres above
unsafe {
let oid = extract_enum_oid(tup);
pg_sys::ReleaseSysCache(tup);
pg_sys::Datum::from(oid)
}
}
let pg_enum = PgEnum::search_enumtypoidname(enumtypoid, &label).unwrap();

unsafe fn extract_enum_oid(tup: *mut pg_sys::HeapTupleData) -> pg_sys::Oid {
let en = {
// SAFETY: the caller has assured us that `tup` is a valid HeapTupleData pointer
GETSTRUCT(tup) as pg_sys::Form_pg_enum
let Some(pg_enum) = pg_enum.get() else {
panic!("could not find heap tuple for enum: {typname}.{label:?}, typoid={enumtypoid:?}");
};
let en = en.as_ref().unwrap();
en.oid

pg_sys::Datum::from(pg_enum.oid())
}
Loading

0 comments on commit ee15204

Please sign in to comment.