ToB企服应用市场:ToB评测及商务社交产业平台

标题: 利用pgrx开发postgre插件 [打印本页]

作者: 悠扬随风    时间: 2024-11-12 16:33
标题: 利用pgrx开发postgre插件
pg插件开发发起在linux情况下进行, windows可以采用虚拟机的方式开发.
安装虚拟机情况

hello pgrx

执行命令新建pg扩展项目: cargo pgrx new ​
利用天生的项目模板进行插件开发
调试

假如有需要, 可以修改自己要用的pg版本配置文件来方便调试, 例如要用pg16就改.pgrx/data-16
运行默认模板插件的函数

进入pgrx新建的项目目录后, 在控制台执行cargo pgrx run pg16表示在postgre16版本运行该插件.
默认端口起始是28800, 因此pg16开放的端口是28816(pg17就是28817),假如开放允许外部连接可以利用这个端口进去(pgrx run命令会自动接入到pg控制台模式)
执行sql安装pg扩展:
  1. create extension <project_name>;
  2. SELECT extname AS "Extension Name", extversion AS "Version"
  3. FROM pg_extension;
复制代码
的来源是自己建立的项目名称.
调试用的宏

例: use pgrx::{debug1, debug5}
按照pg级别利用, 有:
自定义类型

see: 自定义类型官方示例
自定义类型需要实现序列化和反序列化:
  1. #[derive(PostgresType, Deserialize, Serialize)]
  2. pub struct MyType {}
复制代码
重载运算符

see: 重载运算符官方示例
  1. #[pg_operator(immutable, parallel_safe)]
  2. #[opname(||)]
  3. fn mytype_concact(mut left: MyType, right: MyType) -> MyType {
  4.     left.0.push_str(&right.0);
  5.     left
  6. }
复制代码
处理panic

官方提供了一个叫做PgTryBuilder的实现, 用来捕获rust中的panic.
  1. #[pg_extern]
  2. fn maybe_panic(panic: bool, trap_it: bool, message: &str) {
  3.     PgTryBuilder::new(|| {
  4.         if panic {
  5.             panic!("panic says: {}", message)
  6.             // std::panic::panic_any(42)
  7.         }
  8.     })
  9.     .catch_rust_panic(|cause| {
  10.         // we can catch general Rust panics.  The `error` argument is a pg_sys::panic::CaughtError::RustPanic
  11.         // enum variant with the payload from the originating panic
  12.         if trap_it {
  13.             if let CaughtError::RustPanic { ereport, payload } = &cause {
  14.                 warning!("{:#?}", ereport);
  15.                 if let Some(s) = payload.downcast_ref::<String>() {
  16.                     // we have access to the panic!() message
  17.                     warning!("{}", s);
  18.                     return;
  19.                 } else {
  20.                     // this won't happen with this example, but say the `panic_any(42)` was used
  21.                     // instead.  Then we'd be here, and we can just raise another `panic!()`, which
  22.                     // will be what's ultimately reported to Postgres.
  23.                     //
  24.                     // In this case, Postgres' LOCATION error slot will be this line, and the CONTEXT
  25.                     // slot will show the line number of the original `panic_any(42)` above
  26.                     panic!("panic payload not a `String`");
  27.                 }
  28.             }
  29.             unreachable!("internal error:  `CaughtError` not a `::RustPanic`");
  30.         } else {
  31.             cause.rethrow()
  32.         }
  33.     })
  34.     // finally block always runs after the catch handlers finish (even if they rethrow or raise
  35.     // their own panic, like in this case)
  36.     .finally(|| warning!("FINALLY!"))
  37.     .execute()
  38. }
复制代码
在插件中执行SQL

see: 官方spi示例
see: hook列表(没有试过)
执行SQL需要用到pgrx提供的spi接口
这是一个在hook中执行sql的示例:
  1. #[pg_guard]
  2. pub unsafe extern "C" fn _PG_init() {
  3.         PgTryBuilder::new(|| {
  4.             let vec = generate_rsa_key().unwrap();
  5.             Spi::connect(move |mut client| {
  6.                 let init_schema = format!(
  7.                     r#"
  8.                     create schema if not exists  {};
  9.                 "#,
  10.                     PROJECT_SCHEMA
  11.                 );
  12.                 match client.update(init_schema.as_str(), None, None) {
  13.                     Ok(_) => {}
  14.                     Err(_) => {
  15.                         return;
  16.                     }
  17.                 };
  18.                 let init_sql = format!(
  19.                     r#"
  20.                 create table if not exists  {}.t_rsa_key(
  21.                     id serial8 not null primary key,
  22.                     public_key text,
  23.                     private_key text
  24.                 );
  25.             "#,
  26.                     PROJECT_SCHEMA
  27.                 );
  28.                 client.update(init_sql.as_str(), None, None).unwrap();
  29.                 let insert_sql = format!(
  30.                     r#"
  31.             insert into {}.t_rsa_key (public_key,private_key) values ('{}','{}');
  32.             "#,
  33.                     PROJECT_SCHEMA, vec[0], vec[1]
  34.                 );
  35.                 client.update(insert_sql.as_str(), None, None).unwrap();
  36.             })
  37.         })
  38.         .execute();
  39. }
复制代码
关于_PG_init被调用多次的解决方式

https://stackoverflow.com/questions/28037300/in-postgresql-why-is-pg-init-called-twice
https://github.com/pgcentralfoundation/pgrx/blob/develop/pgrx-examples/shmem/README.md
(TODO: 实在我也没行止理这个问题)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4