[PostgreSQL] PL/Rust

[์›๋ณธ ๋งํฌ]

PostgreSQL์—์„œ๋Š” Rust๋„ ํ™•์žฅ์˜ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ธฐ๋ณธ์€ ์•„๋‹ˆ๊ณ  ์„œ๋“œํŒŒํ‹ฐ๋‹ค.
๊ตณ์ด ์ด๋ ‡๊ฒŒ๊นŒ์ง€ ์“ธ ์ผ์ด ์ž˜ ์žˆ์œผ๋ ค๋‚˜ ์‹ถ๊ธด ํ•œ๋ฐ...

ํ˜„ ์‹œ์ ์—์„œ๋Š” ์•„์ง PG17๊นŒ์ง€๋งŒ ์ง€์›๋˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

rust๋กœ ๊ณ ์„ฑ๋Šฅ ํ•ด์‹œ ์ฒ˜๋ฆฌ, JSON ์ฒ˜๋ฆฌ, ์ •๊ทœ์‹, ๋น…๋„˜๋ฒ„ ์—ฐ์‚ฐ ๋“ฑ์„ ์ตœ์ ํ™”ํ•  ์ผ์ด ์žˆ๋‹ค๋ฉด ์“ธ๋งŒ์€ ํ•˜๋‹ค.

๊ทผ๋ฐ ๋‹จ์ ๋„ ๋งŽ๋‹ค. ๋ชจ๋“  crate์„ ์‚ฌ์šฉํ•˜๊ธฐ๋Š” ์–ด๋ ต๊ณ , Rust ๋ฒ„์ „ ์ง€์›๋„ ๋‚ฎ๋‹ค. ์ตœ์‹ ๋ฒ„์ „์„ ์“ฐ๊ธฐ๋Š” ์–ด๋ ต๋‹ค.
์†๋ฐœ ๋‹ค ๋ฌถ์ธ ์ฑ„๋กœ ์“ฐ๋Š”๊ฑฐ๋ผ Rust์˜ ์žฅ์ ์„ ์ œ๋Œ€๋กœ ๋Œ์–ด๋‚ด๊ธฐ๋Š” ์š”์›ํ•˜๋‹ค.




์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํ™•์ธ

ํ˜„์žฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชฉ๋ก์— ์žˆ๋Š”์ง€๋ถ€ํ„ฐ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ ์žˆ๋‹ค๋ฉด ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๊ณ , ์—†๋‹ค๋ฉด ์„ค์น˜๋ฅผ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

SHOW shared_preload_libraries;

์ด๋Ÿฌ๋ฉด ์—†๋Š”๊ฑฐ๊ณ 


์ด๋Ÿฌ๋ฉด ์žˆ๋Š”๊ฑฐ๋‹ค.




์„ค์น˜ (Self Hosted)

๋‹ค๋ฅธ ํ™•์žฅ๋“ค๊ณผ ๋น„๊ตํ•ด์„œ๋„ ์„ค์น˜๊ฐ€ ๋งค์šฐ ๋ฒˆ๊ฑฐ๋กœ์šด ํŽธ์ด๋‹ค.

๊ถŒํ•œ์„ค์ •๊ฐ™์€๊ฑฐ ํ•ด์ฃผ๊ณ , Rust๋„ ๊น”๊ณ  ์ด๊ฒƒ์ €๊ฒƒ ์…‹์—…์„ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.
Rust ์ •๋„๋Š” ๊น”๋ ค์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.

sudo chown postgres -R $(pg_config --sharedir)/extension/
sudo chown postgres -R $(pg_config --sharedir)/lib/

sudo su - postgres
source ~/.cargo/env
rustup default stable

Rust ์ปดํฌ๋„ŒํŠธ์™€ ํƒ€๊ฒŸ์„ ์ถ”๊ฐ€ํ•˜๊ณ 

rustup component add rustc-dev llvm-tools-preview
rustup target install x86_64-unknown-linux-gnu

Postgres ๋ฒ„์ „์„ ํ™•์ธํ•œ ๋‹ค์Œ

pg_config --version

๋ฒ„์ „์— ๋งž๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ณ  init์„ ๋Œ๋ฆฌ๋ฉด ๋œ๋‹ค.

git clone https://github.com/pgcentralfoundation/plrust.git

cd ~/plrust/plrustc
./build.sh
mv ~/plrust/build/bin/plrustc ~/.cargo/bin/

cd ~/plrust/plrust
PG_VER=17 STD_TARGETS="x86_64-postgres-linux-gnu " ./build

mkdir -p ~/.pgrx
export PGRX_HOME=~/.pgrx
cargo pgrx init --pg17 $(which pg_config)

cd ~/plrust/plrust
cargo pgrx install --release --features trusted -c $(which pg_config)

๊ทธ๋ฆฌ๊ณ  ์˜ค๋ฅ˜ ์—†์ด ๋‹ค ํ†ต๊ณผ๋˜๋ฉด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ด์งˆ ๊ฒƒ์ด๋‹ค.




AWS RDS์—์„œ

๋‹คํ–‰ํžˆ, ์ƒ์šฉ ํด๋ผ์šฐ๋“œ๋“ค์—์„œ๋Š” ๊ธฐ๋ณธ ํ™•์žฅ ์„ธํŠธ์— ํฌํ•จ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ”ํ•˜๋‹ค.
13๋ฒ„์ „๊ณผ 17๋ฒ„์ „๊นŒ์ง€๋Š” shared_preload_libraries์— plrust๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ฆฌ๋กœ๋“œ๋งŒ ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์„ค์น˜๋œ๋‹ค.

๋‹ค๋งŒ, Aurora ๊ณ„์—ด์—๋Š” ์—†๋Š” ๋ฒ„์ „์ด ๋” ๋งŽ์€ ๊ฒƒ ๊ฐ™๋‹ค.

์—†๋‹ค๋ฉด ์ถ”๊ฐ€ํ•˜๊ณ  ์žฌ๋ถ€ํŒ…๊นŒ์ง€ ํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์šดํƒ€์ž„์ด ํ•„์ˆ˜์ ์ด๋‹ค.

์ดํ›„์— CREATE EXTENTION๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.




ํ™•์žฅ ์‚ฌ์šฉํ•˜๊ธฐ

์„ค์น˜๊ฐ€ ์ž˜ ๋˜์–ด์žˆ๋‹ค๋ฉด, CREATE EXTENSION ๋ช…๋ น ํ•˜๋‚˜๋กœ ์ฆ‰์‹œ ํ™œ์„ฑํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

CREATE EXTENSION plrust;


CREATE FUNCTION hello()
RETURNS text
LANGUAGE plrust
AS $$
    Ok(Some("hello".to_string()))
$$;

SELECT hello();

๊ทธ๋ฆฌ๊ณ  ๋ญ ๊ผฌ์ด๋ฉด rustyํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋‚œ๋‹ค.

์‹ค์ œ๋กœ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ๊ทธ๋Œ€๋กœ ๋Œ๋ฆฌ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํŒŒ๋ผ๋ฏธํ„ฐ๋‚˜ ๋ฐ˜ํ™˜๊ฐ’ ์ฒ˜๋ฆฌ๋„ ๋œ๋‹ค.
๋‹ค๋งŒ ์—ฌ๊ธฐ์—๋Š” ์•”์‹œ์ ์ธ ๊ทœ์น™์ด ๋ช‡๊ฐ€์ง€ ์žˆ๋Š”๋ฐ

SQL ํƒ€์ž…์ด ์•”์‹œ์ ์œผ๋กœ ๋ณ€ํ™˜๋œ๋‹ค๋Š” ๊ฒƒ (์˜ˆ: int => i32)
๋ชจ๋“  ๋ฐ˜ํ™˜๊ฐ’์€ Result๋กœ ๊ฐ์‹ธ์ ธ์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ (Ok ๋ฐ˜ํ™˜)
Nullable์€ Option์œผ๋กœ ํ‘œํ˜„๋œ๋‹ค๋Š” ๊ฒƒ ์ •๋„๋‹ค.


์•„๋ฌดํŠผ ์“ฐ๋Š”๋ฐ๋Š” ๋ณ„๋ฐ˜ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.




์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ

Err์— ๋ฌธ์ž์—ด ์ ๋‹นํžˆ ๋„ฃ์–ด์„œ ์˜๋ฉด ํ‘œ์ค€ Postgres ํ˜•์‹๋Œ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜๊ฐ„๋‹ค.
๋‹น์—ฐํžˆ ์ด๊ฑด try catch๊ฐ™์€๊ฒŒ ์—†์œผ๋ฏ€๋กœ ์ฆ‰์‹œ ์˜ค๋ฅ˜๋กœ ๋ฐœ์‚ฌ๋œ๋‹ค.




crate ์‚ฌ์šฉ

์™ธ๋ถ€ crate๋„ ์ œํ•œ์ ์ด์ง€๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
๋ณด์•ˆ ๋ฌธ์ œ ๋•Œ๋ฌธ์— RDS ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ์ผ๋ถ€ ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” crate์— ํ•œํ•ด์„œ๋งŒ ์‚ฌ์šฉ์ด ํ—ˆ์šฉ๋œ๋‹ค.

url, regex, serde, serde_json, num-bigint ๊ฐ™์€ ๊ฒƒ๋“ค์ด ๊ทธ๊ฒƒ์ด๋‹ค. tokio ๊ฐ™์€๊ฑด ์•ˆ๋œ๋‹ค.

์ด๊ฑด ๊ฐ์ž ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜๋Š” ํŽธ์ด ์ข‹๋‹ค.
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL.Concepts.General.Using.PL_Rust.html?utm_source=chatgpt.com

๊ทธ๋ฆฌ๊ณ  ์ด๊ฑด PL/Rust๋งˆ๋‹ค ๋ฌธ๋ฒ•๋„ ๋งŽ์ด ๋‹ค๋ฅด๋‹ค.
์ผ๋‹จ 1.1 ๊ธฐ์ค€์œผ๋กœ ์ ์–ด๋ณด์ž๋ฉด, ์ด๋ ‡๋‹ค.

๋””ํŽœ๋˜์‹œ๋ฅผ ํ•จ์ˆ˜ ๋‚ด์— ๋•Œ๋ ค๋ฐ•์•„์•ผ ํ•œ๋‹ค.

CREATE OR REPLACE FUNCTION is_email(input text)
RETURNS bool
LANGUAGE plrust
AS $$
[dependencies]
regex = "1"

[code]
use regex::Regex;

let s = input.unwrap_or_default();

let re = Regex::new(
    r"^[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}$"
)?;

Ok(Some(re.is_match(&s)))
$$;

์ข€ ๋ชป์ƒ๊ธฐ๊ธด ํ–ˆ์ง€๋งŒ ๋™์ž‘์€ ํ•œ๋‹ค.



์ฐธ์กฐ
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL.Concepts.General.Using.PL_Rust.html
https://plrust.io/config-pg.html
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL.Concepts.General.Using.PL_Rust.html?utm_source=chatgpt.com