extract str functions into separate modules.
add first sql based unit test.
This commit is contained in:
parent
01d79841f2
commit
6d2779e6ec
|
@ -38,6 +38,7 @@ pulldown-cmark = "0.9.1"
|
||||||
any_ascii = "0.3.0"
|
any_ascii = "0.3.0"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
uuid = "1.2.2"
|
uuid = "1.2.2"
|
||||||
|
regex = "1.10.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pgrx-tests = "=0.11.2"
|
pgrx-tests = "=0.11.2"
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -1,10 +1,10 @@
|
||||||
PACKAGE_VERSION=0.3.0
|
PACKAGE_VERSION=0.3.1
|
||||||
PACKAGE_NAME=pg_str
|
PACKAGE_NAME=pg_str
|
||||||
PG_VERSION=15
|
PG_VERSION=15
|
||||||
PREFIX=target/release/$(PACKAGE_NAME)-pg$(PG_VERSION)
|
PREFIX=target/release/$(PACKAGE_NAME)-pg$(PG_VERSION)
|
||||||
|
|
||||||
build:
|
build:
|
||||||
cargo pgx package
|
cargo pgrx package
|
||||||
|
|
||||||
install: $(PREFIX)
|
install: $(PREFIX)
|
||||||
cp -f $(PREFIX)/usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME)--$(PACKAGE_VERSION).sql /usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME)--$(PACKAGE_VERSION).sql
|
cp -f $(PREFIX)/usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME)--$(PACKAGE_VERSION).sql /usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME)--$(PACKAGE_VERSION).sql
|
||||||
|
|
24
README.md
24
README.md
|
@ -1,28 +1,30 @@
|
||||||
# Postgresql String Extension
|
# pg_str: the postgresql extension for strings
|
||||||
|
|
||||||
A better way of handling string manipulation and transformations in Postgresql.
|
add some good default string manipulation functions to postgresql. build using the rust library pgrx: [https://github.com/pgcentralfoundation/pgrx](https://github.com/pgcentralfoundation/pgrx).
|
||||||
|
|
||||||
Function api and behavior is inspired by those available in the Laravel web framework: https://laravel.com/docs/8.x/helpers#strings-method-list
|
|
||||||
|
|
||||||
## Installation
|
function api and behavior is inspired by the laravel web framework: [https://laravel.com/docs/10.x/strings](https://laravel.com/docs/10.x/strings)
|
||||||
|
|
||||||
|
## installation
|
||||||
```
|
```
|
||||||
git clone git@github.com:abumni/pg_str
|
git clone https://gitea.publicmatt.com/public/pg_str.git
|
||||||
cd pg_str
|
cd pg_str
|
||||||
cargo pgx package # run cargo install pgx first
|
cargo pgx package # run cargo install pgx first
|
||||||
sudo make install # adjust Makefile if using different version of postgresql than 13.
|
sudo make install # adjust Makefile if using different version of postgresql than 13.
|
||||||
```
|
```
|
||||||
This puts the binaries and sql into the right folder location. Next you need to create the extension in postgresql:
|
this puts the binaries and sql into the right folder location. next you need to create the extension in postgresql:
|
||||||
|
|
||||||
```
|
```
|
||||||
psql
|
psql
|
||||||
> create extension pg_str; # installs functions in a schema named 'str'
|
> create extension pg_str; # installs functions in a 'public' schema.
|
||||||
> select str.markdown('# Hello '
|
> select str_markdown('# Hello '
|
||||||
|| str.snake('pg str')
|
|| str.snake('pg str')
|
||||||
|| '- ~~using programming language for str manipulations~~
|
|| '- ~~using programming language for str manipulations~~
|
||||||
- **do it all in postgresql** ');
|
- **do it all in postgresql** ');
|
||||||
```
|
```
|
||||||
|
|
||||||
## API
|
## api thus far:
|
||||||
|
|
||||||
- [x] after
|
- [x] after
|
||||||
- [] afterLast
|
- [] afterLast
|
||||||
- [x] ascii
|
- [x] ascii
|
||||||
|
@ -60,10 +62,10 @@ psql
|
||||||
- [x] singular
|
- [x] singular
|
||||||
- [x] slug
|
- [x] slug
|
||||||
- [x] snake
|
- [x] snake
|
||||||
- [] start
|
- [x] start
|
||||||
- [] startsWith
|
- [] startsWith
|
||||||
- [x] studly
|
- [x] studly
|
||||||
- [] substr
|
- [x] substr
|
||||||
- [] substrCount
|
- [] substrCount
|
||||||
- [] substrReplace
|
- [] substrReplace
|
||||||
- [x] title
|
- [x] title
|
||||||
|
|
178
src/lib.rs
178
src/lib.rs
|
@ -1,149 +1,39 @@
|
||||||
|
pub mod modules {
|
||||||
|
pub mod after;
|
||||||
|
pub mod ascii;
|
||||||
|
pub mod before;
|
||||||
|
pub mod case;
|
||||||
|
pub mod contains;
|
||||||
|
pub mod length;
|
||||||
|
pub mod markdown;
|
||||||
|
pub mod random;
|
||||||
|
pub mod split;
|
||||||
|
pub mod start;
|
||||||
|
pub mod substr;
|
||||||
|
pub mod uuid;
|
||||||
|
}
|
||||||
|
|
||||||
use pgrx::prelude::*;
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
use rand::distributions::{Alphanumeric, DistString};
|
pub use modules::after::*;
|
||||||
|
pub use modules::ascii::*;
|
||||||
use any_ascii::any_ascii;
|
pub use modules::before::*;
|
||||||
use inflector::cases::{
|
pub use modules::case::*;
|
||||||
camelcase, kebabcase, pascalcase, screamingsnakecase, snakecase, titlecase,
|
pub use modules::contains::*;
|
||||||
};
|
pub use modules::length::*;
|
||||||
use inflector::string::{pluralize, singularize};
|
pub use modules::markdown::*;
|
||||||
use pulldown_cmark::{html, Options, Parser};
|
pub use modules::random::*;
|
||||||
use str_slug::StrSlug;
|
pub use modules::split::*;
|
||||||
use uuid::Uuid;
|
pub use modules::start::*;
|
||||||
|
pub use modules::substr::*;
|
||||||
|
pub use modules::uuid::*;
|
||||||
|
|
||||||
pgrx::pg_module_magic!();
|
pgrx::pg_module_magic!();
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_random(length: i32) -> String {
|
|
||||||
Alphanumeric.sample_string(&mut rand::thread_rng(), length as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_length(input: &str) -> i32 {
|
|
||||||
input.len() as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_after<'a>(input: &'a str, search: &str) -> &'a str {
|
|
||||||
let matches: Vec<_> = input.match_indices(search).collect();
|
|
||||||
match matches.first() {
|
|
||||||
None => input,
|
|
||||||
Some(x) => &input[x.1.len()..],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #[pg_extern]
|
|
||||||
// fn str_after_last<'a>(input: &'a str, search: &str) -> &'a str {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn str_before<'a>(input: &'a str, search: &str) -> &'a str {
|
|
||||||
// }
|
|
||||||
// fn str_beforeLast<'a>(input: &'a str, search: &str) -> &'a str {
|
|
||||||
// }
|
|
||||||
// fn str_between<'a>(input: &'a str, search: &str) -> &'a str {
|
// fn str_between<'a>(input: &'a str, search: &str) -> &'a str {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[pg_extern]
|
#[pg_extern]
|
||||||
fn str_uuid() -> String {
|
fn str_replace<'a>(input: &'a str, old: &'a str, new: &'a str) -> String {
|
||||||
Uuid::new_v4().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_ascii(input: &str) -> String {
|
|
||||||
any_ascii(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_is_ascii(input: &str) -> bool {
|
|
||||||
input.is_ascii()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_contains(input: &str, search: &str) -> bool {
|
|
||||||
input.contains(search)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_contains_all(input: &str, search: Vec<&str>) -> bool {
|
|
||||||
search.iter().all(|s| input.contains(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_lower(input: &str) -> String {
|
|
||||||
input.to_lowercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_upper(input: &str) -> String {
|
|
||||||
input.to_uppercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_slug(input: &str, sep: char) -> String {
|
|
||||||
let mut slug = StrSlug::new();
|
|
||||||
slug.separator = sep;
|
|
||||||
slug.slug(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_singular(input: &str) -> String {
|
|
||||||
singularize::to_singular(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_plural(input: &str) -> String {
|
|
||||||
pluralize::to_plural(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_title(input: &str) -> String {
|
|
||||||
titlecase::to_title_case(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_camel(input: &str) -> String {
|
|
||||||
camelcase::to_camel_case(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_kebab(input: &str) -> String {
|
|
||||||
kebabcase::to_kebab_case(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_snake(input: &str) -> String {
|
|
||||||
snakecase::to_snake_case(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_studly(input: &str) -> String {
|
|
||||||
pascalcase::to_pascal_case(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_scream(input: &str) -> String {
|
|
||||||
screamingsnakecase::to_screaming_snake_case(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_markdown(input: &str) -> String {
|
|
||||||
// Set up options and parser. Strikethroughs are not part of the CommonMark standard
|
|
||||||
// and we therefore must enable it explicitly.
|
|
||||||
let mut options = Options::empty();
|
|
||||||
options.insert(Options::ENABLE_STRIKETHROUGH);
|
|
||||||
let parser = Parser::new_ext(input, options);
|
|
||||||
|
|
||||||
// Write to String buffer.
|
|
||||||
let mut html_output: String = String::with_capacity(input.len() * 3 / 2);
|
|
||||||
html::push_html(&mut html_output, parser);
|
|
||||||
html_output
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_substr(input: &str, start: i32, end: i32) -> &str {
|
|
||||||
&input[start as usize..end as usize]
|
|
||||||
}
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_replace(input: &'static str, old: &'static str, new: &'static str) -> String {
|
|
||||||
input.replace(old, new)
|
input.replace(old, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,18 +43,8 @@ fn str_append(mut input: String, extra: &str) -> String {
|
||||||
input
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_split(input: &'static str, pattern: &str) -> Vec<&'static str> {
|
|
||||||
input.split_terminator(pattern).into_iter().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern]
|
|
||||||
fn str_split_set<'a>(input: &'a str, pattern: &'a str) -> SetOfIterator<'a, &'a str> {
|
|
||||||
SetOfIterator::new(input.split_terminator(pattern).into_iter())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "pg_test"))]
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
#[pg_schema]
|
#[pgrx::pg_schema]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
// #[pg_test]
|
// #[pg_test]
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_after<'a>(input: &'a str, search: &str) -> &'a str {
|
||||||
|
let matches: Vec<_> = input.match_indices(search).collect();
|
||||||
|
match matches.first() {
|
||||||
|
None => input,
|
||||||
|
Some(x) => &input[x.1.len()..],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #[pg_extern]
|
||||||
|
// fn str_after_last<'a>(input: &'a str, search: &str) -> &'a str {
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
use any_ascii::any_ascii;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_ascii(input: &str) -> String {
|
||||||
|
any_ascii(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_is_ascii(input: &str) -> bool {
|
||||||
|
input.is_ascii()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
// fn str_before<'a>(input: &'a str, search: &str) -> &'a str {
|
||||||
|
// }
|
||||||
|
// fn str_beforeLast<'a>(input: &'a str, search: &str) -> &'a str {
|
||||||
|
// }
|
|
@ -0,0 +1,71 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
use inflector::cases::{
|
||||||
|
camelcase, kebabcase, pascalcase, screamingsnakecase, snakecase, titlecase,
|
||||||
|
};
|
||||||
|
use inflector::string::{pluralize, singularize};
|
||||||
|
use str_slug::StrSlug;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_lower(input: &str) -> String {
|
||||||
|
input.to_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_upper(input: &str) -> String {
|
||||||
|
input.to_uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_slug(input: &str, sep: char) -> String {
|
||||||
|
let mut slug = StrSlug::new();
|
||||||
|
slug.separator = sep;
|
||||||
|
slug.slug(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_singular(input: &str) -> String {
|
||||||
|
singularize::to_singular(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_plural(input: &str) -> String {
|
||||||
|
pluralize::to_plural(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_title(input: &str) -> String {
|
||||||
|
titlecase::to_title_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_camel(input: &str) -> String {
|
||||||
|
camelcase::to_camel_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_kebab(input: &str) -> String {
|
||||||
|
kebabcase::to_kebab_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_snake(input: &str) -> String {
|
||||||
|
snakecase::to_snake_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_studly(input: &str) -> String {
|
||||||
|
pascalcase::to_pascal_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_scream(input: &str) -> String {
|
||||||
|
screamingsnakecase::to_screaming_snake_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_contains(input: &str, search: &str) -> bool {
|
||||||
|
input.contains(search)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_contains_all(input: &str, search: Vec<&str>) -> bool {
|
||||||
|
search.iter().all(|s| input.contains(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn str_length(input: &str) -> i32 {
|
||||||
|
input.len() as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
use pulldown_cmark::{html, Options, Parser};
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_markdown(input: &str) -> String {
|
||||||
|
// Set up options and parser. Strikethroughs are not part of the CommonMark standard
|
||||||
|
// and we therefore must enable it explicitly.
|
||||||
|
let mut options = Options::empty();
|
||||||
|
options.insert(Options::ENABLE_STRIKETHROUGH);
|
||||||
|
let parser = Parser::new_ext(input, options);
|
||||||
|
|
||||||
|
// Write to String buffer.
|
||||||
|
let mut html_output: String = String::with_capacity(input.len() * 3 / 2);
|
||||||
|
html::push_html(&mut html_output, parser);
|
||||||
|
html_output
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
use rand::distributions::{Alphanumeric, DistString};
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_random(length: i32) -> String {
|
||||||
|
Alphanumeric.sample_string(&mut rand::thread_rng(), length as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_split<'a>(input: &'a str, pattern: &str) -> Vec<&'a str> {
|
||||||
|
input.split_terminator(pattern).into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_split_set<'a>(input: &'a str, pattern: &'a str) -> SetOfIterator<'a, &'a str> {
|
||||||
|
SetOfIterator::new(input.split_terminator(pattern).into_iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_start(value: &str, prefix: &str) -> String {
|
||||||
|
let quoted = regex::escape(prefix);
|
||||||
|
let re = Regex::new(&format!("^(?:{})+", quoted)).unwrap();
|
||||||
|
|
||||||
|
format!("{}{}", prefix, re.replace(value, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
#[pgrx::pg_schema]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
#[pg_test]
|
||||||
|
fn test_no_slash_prefix() {
|
||||||
|
let result = Spi::get_one::<String>("SELECT public.str_start('path/to/file', '/')");
|
||||||
|
assert_eq!(result, Ok(Some("/path/to/file".to_string())));
|
||||||
|
}
|
||||||
|
#[pg_test]
|
||||||
|
fn test_slash_prefix() {
|
||||||
|
let result = Spi::get_one::<String>("SELECT public.str_start('/path/to/file', '/')");
|
||||||
|
assert_eq!(result, Ok(Some("/path/to/file".to_string())));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_substr(input: &str, start: i32, end: i32) -> &str {
|
||||||
|
&input[start as usize..end as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
pub fn str_uuid() -> String {
|
||||||
|
Uuid::new_v4().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
use pgrx::prelude::*;
|
||||||
|
}
|
Loading…
Reference in New Issue