pgdog_plugin/
lib.rs

1//! PgDog plugins library.
2//!
3//! Implements data types and methods plugins can use to interact with PgDog at runtime.
4//!
5//! # Getting started
6//!
7//! Create a Rust library package with Cargo:
8//!
9//! ```bash
10//! cargo init --lib my_plugin
11//! ```
12//!
13//! The plugin needs to be built as a C ABI-compatible shared library. Add the following to Cargo.toml in the new plugin directory:
14//!
15//! ```toml
16//! [lib]
17//! crate-type = ["rlib", "cdylib"]
18//! ```
19//!
20//! ## Dependencies
21//!
22//! PgDog is using [`pg_query`] to parse SQL. It produces an Abstract Syntax Tree (AST) which plugins can use to inspect queries
23//! and make statement routing decisions.
24//!
25//! The AST is computed by PgDog at runtime. It then passes it down to plugins, using a FFI interface. To make this safe, plugins must follow the
26//! following 2 requirements:
27//!
28//! 1. Plugins must be compiled with the **same version of the Rust compiler** as PgDog. This is automatically checked at runtime and plugins that don't do this are not loaded.
29//! 2. Plugins must use the **same version of [`pg_query`] crate** as PgDog. This happens automatically when using `pg_query` structs re-exported by this crate.
30//!
31//!
32//! #### Configure dependencies
33//!
34//! Add the following to your plugin's `Cargo.toml`:
35//!
36//! ```toml
37//! [dependencies]
38//! pgdog-plugin = "0.1.6"
39//! ```
40//!
41//! # Required methods
42//!
43//! All plugins need to implement a set of functions that PgDog calls at runtime to load the plugin. You can implement them automatically
44//! using a macro. Inside the plugin's `src/lib.rs` file, add the following code:
45//!
46//! ```
47//! // src/lib.rs
48//! use pgdog_plugin::macros;
49//!
50//! macros::plugin!();
51//! ```
52//!
53//! # Routing queries
54//!
55//! Plugins are most commonly used to route queries. To do this, they need to implement a function that reads
56//! the [`Context`] passed in by PgDog, and returns a [`Route`] that indicates which database the query should be sent to.
57//!
58//! ### Example
59//!
60//! ```no_run
61//! use pgdog_plugin::prelude::*;
62//! use pg_query::{protobuf::{Node, RawStmt}, NodeEnum};
63//!
64//! #[route]
65//! fn route(context: Context) -> Route {
66//!     let proto = context
67//!         .statement()
68//!         .protobuf();
69//!     let root = proto.stmts.first();
70//!     if let Some(root) = root {
71//!         if let Some(ref stmt) = root.stmt {
72//!             if let Some(ref node) = stmt.node {
73//!                 if let NodeEnum::SelectStmt(_) = node {
74//!                     return Route::new(Shard::Unknown, ReadWrite::Read);
75//!                 }
76//!             }
77//!         }
78//!     }
79//!
80//!     Route::new(Shard::Unknown, ReadWrite::Write)
81//! }
82//! ```
83//!
84//! The [`macros::route`] macro wraps the function into a safe FFI interface which PgDog calls at runtime.
85//!
86//! ### Parsing parameters
87//!
88//! If your clients are using prepared statements (or the extended protocol), query parameters will be sent separately
89//! from query text. They are stored in the [`crate::parameters::Parameters`] struct, passed down from PgDog's query parser:
90//!
91//! ```
92//! # use pgdog_plugin::prelude::*;
93//! # let context = unsafe { Context::doc_test() };
94//! let params = context.parameters();
95//! if let Some(param) = params
96//!     .get(0)
97//!     .map(|p| p.decode(params.parameter_format(0)))
98//!     .flatten() {
99//!         println!("param $1 = {:?}", param);
100//! }
101//! ```
102//!
103//! ### Errors
104//!
105//! Plugin functions cannot return errors or panic. To handle errors, you can log them to `stderr` and return a default route,
106//! which PgDog will ignore.
107//!
108//! ### Blocking queries
109//!
110//! Plugins can block queries from executing. This is useful if you'd like to enforce specific requirements,
111//! like a mandatory `tenant_id` column, for example, or want to block your apps from saving sensitive information,
112//! like credit card numbers or plain text passwords.
113//!
114//! #### Example
115//!
116//! ```
117//! use pgdog_plugin::prelude::*;
118//!
119//! #[route]
120//! fn route(context: Context) -> Route {
121//!     let params = context.parameters();
122//!     let password = params
123//!         .get(3)
124//!         .map(|param| param.decode(ParameterFormat::Text))
125//!         .flatten();
126//!     if let Some(ParameterValue::Text(password)) = password {
127//!         if !password.starts_with("$bcrypt") {
128//!             return Route::block();
129//!         }
130//!     }
131//!
132//!     Route::unknown()
133//! }
134//! ```
135//!
136//! # Enabling plugins
137//!
138//! Plugins are shared libraries, loaded by PgDog at runtime using `dlopen(3)`. If specifying only its name, make sure to place the plugin's shared library
139//! into one of the following locations:
140//!
141//! - Any of the system default paths, e.g.: `/lib`, `/usr/lib`, `/lib64`, `/usr/lib64`, etc.
142//! - Path specified by the `LD_LIBRARY_PATH` (on Linux) or `DYLD_LIBRARY_PATH` (Mac OS) environment variables.
143//!
144//! Alternatively, specify the relative or absolute path to the shared library as the plugin name. Plugins aren't loaded automatically. For each plugin you want to enable, add it to `pgdog.toml`:
145//!
146//! ```toml
147//! [[plugins]]
148//! # Plugin should be in /usr/lib or in LD_LIBRARY_PATH.
149//! name = "my_plugin"
150//!
151//! [[plugins]]
152//! # Plugin should be in $PWD/libmy_plugin.so
153//! name = "libmy_plugin.so"
154//!
155//! [[plugins]]
156//! # Absolute path to the plugin.
157//! name = "/usr/local/lib/libmy_plugin.so"
158//! ```
159//!
160
161/// Bindgen-generated FFI bindings.
162#[allow(non_upper_case_globals)]
163#[allow(non_camel_case_types)]
164#[allow(non_snake_case)]
165pub mod bindings;
166
167pub mod ast;
168pub mod comp;
169pub mod context;
170pub mod parameters;
171pub mod plugin;
172pub mod prelude;
173pub mod string;
174
175pub use bindings::*;
176pub use context::*;
177pub use plugin::*;
178
179pub use libloading;
180
181pub use pg_query;
182pub use pgdog_macros as macros;