Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new catalog and cache APIs #1545

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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-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
Loading