-
-
Notifications
You must be signed in to change notification settings - Fork 255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove the pg_sql_graph_magic!()
macro.
#1591
Changes from all commits
5685b01
8dd9469
90098e7
0317831
7216d02
e845056
f720ef2
ee63bf9
5581c40
e4a5e1d
6a258b6
0611431
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -183,7 +183,14 @@ pub(crate) fn generate_schema( | |
out_dot = Some(x.to_string()); | ||
}; | ||
|
||
let codegen = compute_codegen(package_manifest_path, &symbols, &lib_name, out_path, out_dot)?; | ||
let codegen = compute_codegen( | ||
control_file, | ||
package_manifest_path, | ||
&symbols, | ||
&lib_name, | ||
out_path, | ||
out_dot, | ||
)?; | ||
|
||
let embed = { | ||
let mut embed = tempfile::NamedTempFile::new()?; | ||
|
@@ -214,7 +221,7 @@ pub(crate) fn generate_schema( | |
&package_name, | ||
)?; | ||
|
||
compute_sql(profile, &package_name)?; | ||
compute_sql(profile, &package_name, &manifest)?; | ||
|
||
Ok(()) | ||
} | ||
|
@@ -381,6 +388,7 @@ fn first_build( | |
} | ||
|
||
fn compute_codegen( | ||
control_file_path: impl AsRef<Path>, | ||
package_manifest_path: impl AsRef<Path>, | ||
symbols: &[String], | ||
lib_name: &str, | ||
|
@@ -391,9 +399,20 @@ fn compute_codegen( | |
let lib_name_ident = Ident::new(&lib_name, Span::call_site()); | ||
|
||
let inputs = { | ||
let control_file_path = control_file_path | ||
.as_ref() | ||
.to_str() | ||
.expect(".control file filename should be valid UTF8"); | ||
let mut out = quote::quote! { | ||
// call the marker. Primarily this ensures that rustc will actually link to the library | ||
// during the "pgrx_embed" build initiated by `cargo-pgrx schema` generation | ||
#lib_name_ident::__pgrx_marker(); | ||
|
||
let mut entities = Vec::new(); | ||
let control_file_entity = ::pgrx::pgrx_sql_entity_graph::SqlGraphEntity::ExtensionRoot(::#lib_name_ident::__pgrx_marker()); | ||
let control_file_path = std::path::PathBuf::from(#control_file_path); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if there's some cursed set of cases that results in this being a malformed UTF-8 string? ...but if someone seriously asks for supporting non-UTF-8 paths in our greenfield extension build system, I'm probably going to tell them exactly where they can shove that request. Maybe we should be simplifying our code by using camino. |
||
let control_file = ::pgrx::pgrx_sql_entity_graph::ControlFile::try_from(control_file_path).expect(".control file should properly formatted"); | ||
let control_file_entity = ::pgrx::pgrx_sql_entity_graph::SqlGraphEntity::ExtensionRoot(control_file); | ||
|
||
entities.push(control_file_entity); | ||
}; | ||
for name in symbols.iter() { | ||
|
@@ -535,7 +554,11 @@ fn second_build( | |
Ok(()) | ||
} | ||
|
||
fn compute_sql(profile: &CargoProfile, package_name: &str) -> eyre::Result<()> { | ||
fn compute_sql( | ||
profile: &CargoProfile, | ||
package_name: &str, | ||
manifest: &Manifest, | ||
) -> eyre::Result<()> { | ||
let mut bin = get_target_dir()?; | ||
bin.push(profile.target_subdir()); | ||
bin.push(format!("pgrx_embed_{package_name}")); | ||
|
@@ -545,6 +568,12 @@ fn compute_sql(profile: &CargoProfile, package_name: &str) -> eyre::Result<()> { | |
command.stdout(Stdio::inherit()); | ||
command.stderr(Stdio::inherit()); | ||
|
||
// pass the package version through as an environment variable | ||
let cargo_pkg_version = std::env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| { | ||
manifest.package_version().expect("`CARGO_PKG_VERSION` should be set, and barring that, `Cargo.toml` should have a package version property") | ||
}); | ||
command.env("CARGO_PKG_VERSION", cargo_pkg_version); | ||
|
||
let command_str = format!("{:?}", command); | ||
tracing::debug!(command = %command_str, "Running"); | ||
let embed_output = command | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ to the `pgrx` framework and very subject to change between versions. While you m | |
use super::{SqlGraphEntity, SqlGraphIdentifier, ToSql}; | ||
use core::convert::TryFrom; | ||
use std::collections::HashMap; | ||
use std::path::PathBuf; | ||
use thiserror::Error; | ||
|
||
/// The parsed contents of a `.control` file. | ||
|
@@ -26,7 +27,8 @@ use thiserror::Error; | |
/// use pgrx_sql_entity_graph::ControlFile; | ||
/// use std::convert::TryFrom; | ||
/// # fn main() -> eyre::Result<()> { | ||
/// let context = include_str!("../../pgrx-examples/custom_types/custom_types.control"); | ||
/// # // arrays.control chosen because it does **NOT** use the @CARGO_VERSION@ variable | ||
/// let context = include_str!("../../pgrx-examples/arrays/arrays.control"); | ||
/// let _control_file = ControlFile::try_from(context)?; | ||
/// # Ok(()) | ||
/// # } | ||
|
@@ -43,18 +45,46 @@ pub struct ControlFile { | |
} | ||
|
||
impl ControlFile { | ||
/// Parse a `.control` file. | ||
/// Parse a `.control` file, performing all known pgrx dynamic variable substitutions. | ||
/// | ||
/// # Supported Dynamic Variable Substitutions | ||
/// | ||
/// `@CARGO_VERSION@`: Replaced with the value of the environment variable `CARGO_PKG_VERSION`, | ||
/// which is set by cargo, or failing that, `cargo-pgrx` using the package | ||
/// version from the extension's `Cargo.toml` file | ||
/// | ||
/// # Errors | ||
/// | ||
/// Returns a `ControlFileError` if any of the required fields are missing from the input string | ||
/// or if any required environment variables (for dynamic variable substitution) are missing | ||
/// | ||
/// ```rust | ||
/// use pgrx_sql_entity_graph::ControlFile; | ||
/// # fn main() -> eyre::Result<()> { | ||
/// let context = include_str!("../../pgrx-examples/custom_types/custom_types.control"); | ||
/// # // arrays.control chosen because it does **NOT** use the @CARGO_VERSION@ variable | ||
/// let context = include_str!("../../pgrx-examples/arrays/arrays.control"); | ||
Comment on lines
+64
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah this seems fair. We have other tests to guarantee this works. |
||
/// let _control_file = ControlFile::from_str(context)?; | ||
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
#[allow(clippy::should_implement_trait)] | ||
pub fn from_str(input: &str) -> Result<Self, ControlFileError> { | ||
fn do_var_replacements(mut input: String) -> Result<String, ControlFileError> { | ||
const CARGO_VERSION: &str = "@CARGO_VERSION@"; | ||
|
||
// endeavor to not require external values if they're not used by the input | ||
if input.contains(CARGO_VERSION) { | ||
input = input.replace( | ||
CARGO_VERSION, | ||
&std::env::var("CARGO_PKG_VERSION").map_err(|_| { | ||
ControlFileError::MissingEnvvar("CARGO_PKG_VERSION".to_string()) | ||
})?, | ||
); | ||
} | ||
Comment on lines
+75
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does introduce a slight behavior divergence, but that seems fine, since the main validation effort is done via cargo-pgrx. |
||
|
||
Ok(input) | ||
} | ||
|
||
let mut temp = HashMap::new(); | ||
for line in input.lines() { | ||
let parts: Vec<&str> = line.split('=').collect(); | ||
|
@@ -68,7 +98,7 @@ impl ControlFile { | |
let v = v.trim_start_matches('\''); | ||
let v = v.trim_end_matches('\''); | ||
|
||
temp.insert(k, v); | ||
temp.insert(k, do_var_replacements(v.to_string())?); | ||
} | ||
let control_file = ControlFile { | ||
comment: temp | ||
|
@@ -83,13 +113,13 @@ impl ControlFile { | |
relocatable: temp | ||
.get("relocatable") | ||
.ok_or(ControlFileError::MissingField { field: "relocatable" })? | ||
== &"true", | ||
== "true", | ||
eeeebbbbrrrr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
superuser: temp | ||
.get("superuser") | ||
.ok_or(ControlFileError::MissingField { field: "superuser" })? | ||
== &"true", | ||
== "true", | ||
schema: temp.get("schema").map(|v| v.to_string()), | ||
trusted: if let Some(v) = temp.get("trusted") { v == &"true" } else { false }, | ||
trusted: if let Some(v) = temp.get("trusted") { v == "true" } else { false }, | ||
}; | ||
|
||
if !control_file.superuser && control_file.trusted { | ||
|
@@ -108,12 +138,28 @@ impl From<ControlFile> for SqlGraphEntity { | |
} | ||
|
||
/// An error met while parsing a `.control` file. | ||
#[derive(Debug, Clone, Error)] | ||
#[derive(Debug, Error)] | ||
pub enum ControlFileError { | ||
#[error("Filesystem error reading control file")] | ||
IOError { | ||
#[from] | ||
error: std::io::Error, | ||
}, | ||
#[error("Missing field in control file! Please add `{field}`.")] | ||
MissingField { field: &'static str }, | ||
#[error("Redundant field in control file! Please remove `{field}`.")] | ||
RedundantField { field: &'static str }, | ||
#[error("Missing environment variable: {0}")] | ||
MissingEnvvar(String), | ||
} | ||
|
||
impl TryFrom<PathBuf> for ControlFile { | ||
type Error = ControlFileError; | ||
|
||
fn try_from(value: PathBuf) -> Result<Self, Self::Error> { | ||
let contents = std::fs::read_to_string(value)?; | ||
ControlFile::try_from(contents.as_str()) | ||
} | ||
} | ||
|
||
impl TryFrom<&str> for ControlFile { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -177,12 +177,19 @@ const _: () = { | |
/// | ||
/// </pre></div> | ||
/// | ||
/// This calls both [`pg_magic_func!()`](pg_magic_func) and [`pg_sql_graph_magic!()`](pg_sql_graph_magic). | ||
/// This calls [`pg_magic_func!()`](pg_magic_func). | ||
#[macro_export] | ||
macro_rules! pg_module_magic { | ||
() => { | ||
$crate::pg_magic_func!(); | ||
$crate::pg_sql_graph_magic!(); | ||
|
||
// A marker function which must exist in the root of the extension for proper linking by the | ||
// "pgrx_embed" binary during `cargo-pgrx schema` generation. | ||
#[inline(never)] /* we don't want DCE to remove this as it *could* cause the compiler to decide to not link to us */ | ||
#[doc(hidden)] | ||
pub fn __pgrx_marker() { | ||
eeeebbbbrrrr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// noop | ||
} | ||
}; | ||
} | ||
|
||
|
@@ -259,39 +266,6 @@ macro_rules! pg_magic_func { | |
}; | ||
} | ||
|
||
/// Create necessary extension-local internal marker for use with SQL generation. | ||
/// | ||
/// <div class="example-wrap" style="display:inline-block"> | ||
/// <pre class="ignore" style="white-space:normal;font:inherit;"> | ||
/// | ||
/// **Note**: Generally [`pg_module_magic`] is preferred, and results in this macro being called. | ||
/// This macro should only be directly called in advanced use cases. | ||
/// | ||
/// </pre></div> | ||
#[macro_export] | ||
macro_rules! pg_sql_graph_magic { | ||
() => { | ||
// A marker which must exist in the root of the extension. | ||
#[doc(hidden)] | ||
pub fn __pgrx_marker() -> $crate::pgrx_sql_entity_graph::ControlFile { | ||
use ::core::convert::TryFrom; | ||
let package_version = env!("CARGO_PKG_VERSION"); | ||
let context = include_str!(concat!( | ||
env!("CARGO_MANIFEST_DIR"), | ||
"/", | ||
env!("CARGO_CRATE_NAME"), | ||
".control" | ||
)) | ||
.replace("@CARGO_VERSION@", package_version); | ||
|
||
let control_file = | ||
$crate::pgrx_sql_entity_graph::ControlFile::try_from(context.as_str()) | ||
.expect("Could not parse control file, is it valid?"); | ||
control_file | ||
} | ||
}; | ||
} | ||
Comment on lines
-271
to
-293
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Excellent. |
||
|
||
pub(crate) static UTF8DATABASE: Lazy<Utf8Compat> = Lazy::new(|| { | ||
let encoding_int = unsafe { pgrx_pg_sys::GetDatabaseEncoding() }; | ||
match encoding_int as core::ffi::c_uint { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should use
pub extern "C" fn
on this marker? rustc is more reluctant to play around with obviously-for-external-consumption interfaces. It's not something we need to fix now, however, just musing aloud.