pgrx_sql_entity_graph/to_sql/entity.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
//LICENSE Portions Copyright 2019-2021 ZomboDB, LLC.
//LICENSE
//LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc.
//LICENSE
//LICENSE Portions Copyright 2023-2023 PgCentral Foundation, Inc. <contact@pgcentral.org>
//LICENSE
//LICENSE All rights reserved.
//LICENSE
//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
/*!
`sql = ...` fragment related entities for Rust to SQL translation
> Like all of the [`sql_entity_graph`][crate] APIs, this is considered **internal**
> to the `pgrx` framework and very subject to change between versions. While you may use this, please do it with caution.
*/
use crate::pgrx_sql::PgrxSql;
use crate::to_sql::ToSqlFn;
use crate::SqlGraphEntity;
/// Represents configuration options for tuning the SQL generator.
///
/// When an item that can be rendered to SQL has these options at hand, they should be
/// respected. If an item does not have them, then it is not expected that the SQL generation
/// for those items can be modified.
///
/// The default configuration has `enabled` set to `true`, and `callback` to `None`, which indicates
/// that the default SQL generation behavior will be used. These are intended to be mutually exclusive
/// options, so `callback` should only be set if generation is enabled.
///
/// When `enabled` is false, no SQL is generated for the item being configured.
///
/// When `callback` has a value, the corresponding `ToSql` implementation should invoke the
/// callback instead of performing their default behavior.
#[derive(Default, Clone)]
pub struct ToSqlConfigEntity {
pub enabled: bool,
pub callback: Option<ToSqlFn>,
pub content: Option<&'static str>,
}
impl ToSqlConfigEntity {
/// Helper used to implement traits (`Eq`, `Ord`, etc) despite `ToSqlFn` not
/// having an implementation for them.
#[inline]
fn fields(&self) -> (bool, Option<&str>, Option<usize>) {
(self.enabled, self.content, self.callback.map(|f| f as usize))
}
/// Given a SqlGraphEntity, this function converts it to SQL based on the current configuration.
///
/// If the config overrides the default behavior (i.e. using the `ToSql` trait), then `Some(eyre::Result)`
/// is returned. If the config does not override the default behavior, then `None` is returned. This can
/// be used to dispatch SQL generation in a single line, e.g.:
///
/// ```rust,ignore
/// config.to_sql(entity, context).unwrap_or_else(|| entity.to_sql(context))?
/// ```
pub fn to_sql(
&self,
entity: &SqlGraphEntity,
context: &PgrxSql,
) -> Option<eyre::Result<String>> {
use eyre::{eyre, WrapErr};
if !self.enabled {
return Some(Ok(format!(
"\n\
{sql_anchor_comment}\n\
-- Skipped due to `#[pgrx(sql = false)]`\n",
sql_anchor_comment = entity.sql_anchor_comment(),
)));
}
if let Some(content) = self.content {
let module_pathname = context.get_module_pathname();
let content = content.replace("@MODULE_PATHNAME@", &module_pathname);
return Some(Ok(format!(
"\n\
{sql_anchor_comment}\n\
{content}\n\
",
content = content,
sql_anchor_comment = entity.sql_anchor_comment()
)));
}
if let Some(callback) = self.callback {
let content = callback(entity, context)
.map_err(|e| eyre!(e))
.wrap_err("Failed to run specified `#[pgrx(sql = path)] function`");
return match content {
Ok(content) => {
let module_pathname = &context.get_module_pathname();
let content = content.replace("@MODULE_PATHNAME@", module_pathname);
Some(Ok(format!(
"\n\
{sql_anchor_comment}\n\
{content}\
",
content = content,
sql_anchor_comment = entity.sql_anchor_comment(),
)))
}
Err(e) => Some(Err(e)),
};
}
None
}
}
impl std::cmp::PartialOrd for ToSqlConfigEntity {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for ToSqlConfigEntity {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.fields().cmp(&other.fields())
}
}
impl std::cmp::PartialEq for ToSqlConfigEntity {
fn eq(&self, other: &Self) -> bool {
self.fields() == other.fields()
}
}
impl std::cmp::Eq for ToSqlConfigEntity {}
impl std::hash::Hash for ToSqlConfigEntity {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.fields().hash(state);
}
}
impl std::fmt::Debug for ToSqlConfigEntity {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let (enabled, content, callback) = self.fields();
f.debug_struct("ToSqlConfigEntity")
.field("enabled", &enabled)
.field("callback", &callback)
.field("content", &content)
.finish()
}
}