Inspired by a little chat with Frits Hoogland about the future of programming languages I challenged myself to learn a bit of Rust and show how to create a shared library to send emails and attach it to Oracle as an external procedure (just as I did with GoLang here: https://blog.ora-600.pl/2021/05/04/golang-as-oracle-external-procedure/)
The steps are actually pretty easy after installing the Rust (which is also trivial):
First we have to create a new project with cargo:
1 | [oracle@b3124c94c1c7 ~]$ cargo new rustmail |
2 | Created binary (application) `rustmail` package |
This is the default output of Cargo.toml file, which we have to modify a bit:
1 | [oracle@b3124c94c1c7 ~]$ cd rustmail/ |
2 | [oracle@b3124c94c1c7 rustmail]$ ls |
4 | [oracle@b3124c94c1c7 rustmail]$ cat Cargo.toml |
We will add the following sections to Cargo.toml with dependencies and mark the project to be compiled as shared dynamic library:
2 | lettre = "0.10.0-beta.4" |
With those changes in place we can create a file in directory src, called lib.rs – this is the source code of the library with comments to clarify the syntax:
1 | use lettre::transport::smtp::authentication::Credentials; |
2 | use lettre::{Message, SmtpTransport, Transport}; |
11 | pub extern "C" fn sendemail(c_to: * const c_char, c_subj: * const c_char, c_what: * const c_char ) -> *mut c_char{ |
14 | assert !(!c_to.is_null()); |
19 | assert !(!c_subj.is_null()); |
20 | CStr::from_ptr(c_subj) |
24 | assert !(!c_what.is_null()); |
25 | CStr::from_ptr(c_what) |
28 | let email = Message::builder() |
29 | .from( "someone <someone@ora-600.pl>" .parse().unwrap()) |
30 | .reply_to( "someone < someone@ora-600.pl>" .parse().unwrap()) |
31 | .to(s_to.to_str().unwrap().parse().unwrap()) |
32 | .subject(String::from(s_subj.to_str().unwrap())) |
33 | .body(String::from(s_what.to_str().unwrap())) |
36 | let creds = Credentials:: new ( "someone@ora-600.pl" .to_string(), "SuperSecretPassword" .to_string()); |
39 | let mailer = SmtpTransport::relay( "mailserver.smtp.com.pl" ) |
45 | let mut s_result = String:: new (); |
47 | match mailer.send(&email) { |
48 | Ok(_) => s_result.push_str( "Email sent successfully!" ), |
49 | Err(e) => s_result = format!( "Something went wrong: {}" , e.to_string()) |
52 | let c_result = CString:: new (s_result).unwrap(); |
Great, let’s save it and compile our project!
1 | [oracle@b3124c94c1c7 rustmail]$ vim src/lib.rs |
2 | [oracle@b3124c94c1c7 rustmail]$ cargo build --release |
3 | Updating crates.io index |
5 | Compiling cfg- if v1.0.0 |
6 | Compiling autocfg v1.0.1 |
7 | Compiling pkg-config v0.3.19 |
9 | Compiling smallvec v1.6.1 |
10 | Compiling bitflags v1.2.1 |
12 | Compiling radium v0.5.3 |
13 | Compiling scopeguard v1.1.0 |
14 | Compiling tinyvec_macros v0.1.0 |
15 | Compiling version_check v0.9.3 |
16 | Compiling foreign-types-shared v0.1.1 |
17 | Compiling openssl v0.10.34 |
18 | Compiling memchr v2.4.0 |
19 | Compiling native-tls v0.2.7 |
20 | Compiling once_cell v1.7.2 |
21 | Compiling funty v1.1.0 |
23 | Compiling matches v0.1.8 |
25 | Compiling openssl-probe v0.1.2 |
26 | Compiling match_cfg v0.1.0 |
27 | Compiling regex-syntax v0.6.25 |
28 | Compiling mime v0.3.16 |
29 | Compiling fastrand v1.4.1 |
30 | Compiling httpdate v1.0.0 |
31 | Compiling base64 v0.13.0 |
32 | Compiling quoted_printable v0.4.3 |
33 | Compiling instant v0.1.9 |
34 | Compiling lock_api v0.4.4 |
35 | Compiling tinyvec v1.2.0 |
36 | Compiling foreign-types v0.3.2 |
37 | Compiling unicode-bidi v0.3.5 |
39 | Compiling openssl-sys v0.9.63 |
40 | Compiling regex v1.5.4 |
41 | Compiling unicode-normalization v0.1.17 |
42 | Compiling parking_lot_core v0.8.3 |
43 | Compiling hostname v0.3.1 |
44 | Compiling bitvec v0.19.5 |
45 | Compiling parking_lot v0.11.1 |
47 | Compiling scheduled-thread-pool v0.2.5 |
49 | Compiling lettre v0.10.0-beta.4 |
50 | Compiling rustmail v0.1.0 (/home/oracle/rustmail) |
51 | Finished release [optimized] target(s) in 21.39s |
After this you should have a library ready to use:
1 | [oracle@b3124c94c1c7 rustmail]$ file target/release/librustmail.so |
2 | target/release/librustmail.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=e54868dcf696e60a89222aab4e5ead32899edc88, not stripped |
Now we can move it to appropriate location, and modify extproc.ora to look like this:
1 | [oracle@b3124c94c1c7 rustmail]$ cp target/release/librustmail.so ~/extproc/ |
2 | [oracle@b3124c94c1c7 rustmail]$ vim $ORACLE_HOME/hs/admin/extproc.ora |
3 | [oracle@b3124c94c1c7 rustmail]$ cat !$ | tail -1 |
4 | cat $ORACLE_HOME/hs/admin/extproc.ora | tail -1 |
5 | SET EXTPROC_DLLS=ONLY:/home/oracle/extproc/sendemail.so,ONLY:/home/oracle/extproc/librustmail.so |
The rest is the same:
1 | SQL> create or replace library lib_rustmail as '/home/oracle/extproc/librustmail.so' ; |
6 | SQL> create or replace function f_rust_sendmail(p_to varchar2, p_subcject varchar2, p_body varchar2) return varchar2 as |
15 | SQL> select f_rust_sendmail( 'kamil@ora-600.pl' , 'jebacpis' , 'Beer' ) from dual; |
17 | F_RUST_SENDMAIL( 'KAMIL@ORA-600.PL' , 'JEBACPIS' , 'DUPA' ) |
19 | Email sent successfully! |
That’s it 🙂 I personally believe, that this is extremely powerful feature of Oracle Database. You don’t have to wait for implementing Graal inside a database to use different languages – you can use the full power of modern languages right now and in a very easy way!