Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ test-python:
test: test-rust test-python

.PHONY: all ## Run the standard set of checks performed in CI
all: format lint build-dev test
all: format build-dev lint test

.PHONY: clean ## Clear local caches and build artifacts
clean:
Expand Down
4 changes: 2 additions & 2 deletions rapidquery-macros/cust_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
1 => Some(pyo3::Bound::from_owned_ptr(#bound_object.py(), attribute)),
0 => None,
_ => {
return Err(pyo3::PyErr::fetch(#bound_object.py()))
return Err(pyo3::PyErr::fetch(#bound_object.py()).into())
}
}
};
Expand All @@ -109,7 +109,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {

if attribute.is_null() {
if pyo3::ffi::PyErr_ExceptionMatches(pyo3::ffi::PyExc_AttributeError) != 1 {
return Err(pyo3::PyErr::fetch(#bound_object.py()));
return Err(pyo3::PyErr::fetch(#bound_object.py()).into());
} else {
pyo3::ffi::PyErr_Clear();
None
Expand Down
15 changes: 0 additions & 15 deletions rapidquery-macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use proc_macro::TokenStream;
mod cust_protocol;
mod define_options;
mod pyclass;
mod pyerr;
mod pyo3_constructor;

/// Defines a pyo3 class with a required module path.
Expand All @@ -25,20 +24,6 @@ pub fn pyclass(attr: TokenStream, item: TokenStream) -> TokenStream {
}
}

/// Builds a [`PyErr`], or `Err(PyErr)` when the name is wrapped in `Err`.
///
/// ```ignore
/// pyerr!(PyTypeError, "bad {}", x)
/// pyerr!(Err(PyValueError))
/// ```
#[proc_macro]
pub fn pyerr(input: TokenStream) -> TokenStream {
match pyerr::expand(input.into()) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}

/// Builds a customization protocol checking.
///
/// Note: you can access to attribute result using `protocol` variable.
Expand Down
85 changes: 0 additions & 85 deletions rapidquery-macros/pyerr.rs

This file was deleted.

62 changes: 46 additions & 16 deletions rapidquery-macros/pyo3_constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,14 @@ fn screaming_snake(s: &str) -> String {
out
}

/// Single-segment type name used for `OPT_*` constants.
fn enum_type_ident(ty: &Type) -> &Ident {
match ty {
Type::Path(type_name) => type_name.path.get_ident().expect("enum/bits type is a single ident"),
_ => panic!("enum/bits type is a single ident"),
}
}

/// Name of the exported module constant: `OPT_<TYPE_NAME>_<ITEM>`.
fn const_name(field: &Ident, item: &Ident) -> String {
format!(
Expand Down Expand Up @@ -362,31 +370,53 @@ impl Field {
/// May `return Err(..)` / use `?`, so it must be used inside `__new__`.
fn convert(&self, value: &TokenStream) -> TokenStream {
let name_str = self.name.unraw().to_string();
let value_error = quote!(::pyo3::exceptions::PyValueError);

match &self.kind {
Kind::Plain(_) | Kind::Class(_) => value.clone(),

Kind::Enum { ty, variants } => quote! {
match #value {
#( __x if __x == <#ty>::#variants as u8 => <#ty>::#variants, )*
__x => return ::core::result::Result::Err(
#value_error::new_err(format!("invalid value {} for `{}`", __x, #name_str)),
),
Kind::Enum { ty, variants } => {
let type_ident = enum_type_ident(ty);
let names: Vec<_> = variants.iter().map(|v| const_name(type_ident, v)).collect();
let values = 0..variants.len() as u8;
quote! {
match #value {
#( __x if __x == <#ty>::#variants as u8 => <#ty>::#variants, )*
__x => return ::core::result::Result::Err(
error::Error::InvalidEnumOptionField {
field: #name_str,
value: __x,
expected: error::format_options(&[#(#names),*], &[#(#values),*]),
},
),
}
}
},
}

Kind::Bits { ty, .. } => quote! {
<#ty>::from_bits(#value).ok_or_else(|| {
#value_error::new_err(format!("invalid bits {:#b} for `{}`", #value, #name_str))
})?
},
Kind::Bits { ty, flags } => {
let type_ident = enum_type_ident(ty);
let names: Vec<_> = flags.iter().map(|flag| const_name(type_ident, flag)).collect();
quote! {
<#ty>::from_bits(#value).ok_or_else(|| {
error::Error::InvalidBitOptionField {
field: #name_str,
value: #value,
expected: error::format_options(
&[#(#names),*],
&[#(<#ty>::#flags.bits()),*],
),
}
})?
}
}

Kind::Template { keys, min, max } => {
let keys = template_keys(keys);
quote! {
template::Template::parse(#value, #keys, #min, #max)
.map_err(|e| #value_error::new_err(e.to_string()))?
.map_err(|reason| error::Error::InvalidTemplate {
field: #name_str,
reason,
})?
}
}
}
Expand Down Expand Up @@ -605,7 +635,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
#[cold]
fn #export_fn(
module: &::pyo3::Bound<'_, ::pyo3::types::PyModule>,
) -> ::pyo3::PyResult<()> {
) -> pyo3::PyResult<()> {
use ::pyo3::types::PyModuleMethods;
#(#exports)*
Ok(())
Expand All @@ -620,7 +650,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
clippy::fn_params_excessive_bools,
clippy::cognitive_complexity
)]
fn __new__(#(#params),*) -> ::pyo3::PyResult<Self> {
fn __new__(#(#params),*) -> error::Result<Self> {
Ok(Self { #(#inits),* })
}

Expand Down
3 changes: 2 additions & 1 deletion src/data_type/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::error;
use crate::sql;

#[cfg(test)]
Expand Down Expand Up @@ -187,7 +188,7 @@ impl std::fmt::Display for DataTypeName {
}

impl sql::WriteSql for DataTypeName {
fn write_sql<W: sql::SqlWriter>(&self, _dialect: &crate::dialect::PyDialect, out: &mut W) -> pyo3::PyResult<()> {
fn write_sql<W: sql::SqlWriter>(&self, _dialect: &crate::dialect::PyDialect, out: &mut W) -> error::Result<()> {
out.write_str(self.as_str());
Ok(())
}
Expand Down
61 changes: 31 additions & 30 deletions src/ddl/schema.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::dialect;
use crate::error;
use crate::expr::enum_::Ident;
use crate::expr::extractors::IdentExtractor;
use crate::internal::alias;
Expand All @@ -24,12 +25,12 @@ pub struct PyCreateSchema {
}

impl sql::WriteSql for PyCreateSchema {
fn write_sql<W: sql::SqlWriter>(&self, dialect: &dialect::PyDialect, out: &mut W) -> pyo3::PyResult<()> {
fn write_sql<W: sql::SqlWriter>(&self, dialect: &dialect::PyDialect, out: &mut W) -> error::Result<()> {
if !dialect.schema.features.contains(dialect::SchemaFeatures::SUPPORTED) {
return Err(rapidquery_macros::pyerr!(
PyRuntimeError,
"CREATE SCHEMA statement is not supported by this dialect"
));
return Err(error::Error::Unsupported {
feature: "CREATE SCHEMA is not supported by this dialect",
fix: "set schema.features to include OPT_SCHEMA_FEATURES_SUPPORTED",
});
}

// CREATE {SCHEMA | DATABASE}
Expand All @@ -49,10 +50,10 @@ impl sql::WriteSql for PyCreateSchema {
match dialect.schema.authorization {
dialect::SchemaAuthorization::Unsupported => {
if self.authorization.is_some() {
return Err(rapidquery_macros::pyerr!(
PyRuntimeError,
"authorization is not supported in CREATE SCHEMA by this dialect"
));
return Err(error::Error::Unsupported {
feature: "authorization is not supported in CREATE SCHEMA by this dialect",
fix: "set schema.authorization to OPT_SCHEMA_AUTHORIZATION_OPTIONAL",
});
}

self.name.write_sql(dialect, out)?;
Expand All @@ -70,10 +71,10 @@ impl sql::WriteSql for PyCreateSchema {
if let Some(x) = &self.location {
// ... {LOCATION | MANAGED LOCATION | EXTERNAL_VOLUME =} <location>
let Some(tmpl) = &dialect.schema.location else {
return Err(rapidquery_macros::pyerr!(
PyRuntimeError,
"location is not supported in CREATE SCHEMA by this dialect"
));
return Err(error::Error::Unsupported {
feature: "location is not supported in CREATE SCHEMA by this dialect",
fix: "set schema.location to a template such as \" LOCATION {}\"",
});
};

out.write(' ');
Expand Down Expand Up @@ -124,7 +125,7 @@ impl PyCreateSchema {
}

/// Render this expression as raw SQL (values inlined as literals).
fn to_sql(&self, dialect: pyo3::Bound<'_, crate::dialect::PyDialect>) -> pyo3::PyResult<String> {
fn to_sql(&self, dialect: pyo3::Bound<'_, crate::dialect::PyDialect>) -> error::Result<String> {
sql::write_sql(dialect.py(), self, dialect.get())
}

Expand Down Expand Up @@ -166,12 +167,12 @@ pub struct PyDropSchema {
}

impl sql::WriteSql for PyDropSchema {
fn write_sql<W: sql::SqlWriter>(&self, dialect: &crate::dialect::PyDialect, out: &mut W) -> pyo3::PyResult<()> {
fn write_sql<W: sql::SqlWriter>(&self, dialect: &crate::dialect::PyDialect, out: &mut W) -> error::Result<()> {
if !dialect.schema.features.contains(dialect::SchemaFeatures::SUPPORTED) {
return Err(rapidquery_macros::pyerr!(
PyRuntimeError,
"DROP SCHEMA statement is not supported by this dialect"
));
return Err(error::Error::Unsupported {
feature: "DROP SCHEMA is not supported by this dialect",
fix: "set schema.features to include OPT_SCHEMA_FEATURES_SUPPORTED",
});
}

// DROP {SCHEMA | DATABASE}
Expand All @@ -195,20 +196,20 @@ impl sql::WriteSql for PyDropSchema {
None => {}
Some(DropSchemaBehavior::Cascade) => {
if !dialect.schema.features.contains(dialect::SchemaFeatures::DROP_CASCADE) {
return Err(rapidquery_macros::pyerr!(
PyRuntimeError,
"This dialect does not support CASCADE keyword in DROP SCHEMA statement"
));
return Err(error::Error::Unsupported {
feature: "CASCADE is not supported in DROP SCHEMA by this dialect",
fix: "set schema.features to include OPT_SCHEMA_FEATURES_DROP_CASCADE",
});
}

out.write_str(" CASCADE");
}
Some(DropSchemaBehavior::Restrict) => {
if !dialect.schema.features.contains(dialect::SchemaFeatures::DROP_RESTRICT) {
return Err(rapidquery_macros::pyerr!(
PyRuntimeError,
"This dialect does not support RESTRICT keyword in DROP SCHEMA statement"
));
return Err(error::Error::Unsupported {
feature: "RESTRICT is not supported in DROP SCHEMA by this dialect",
fix: "set schema.features to include OPT_SCHEMA_FEATURES_DROP_RESTRICT",
});
}

out.write_str(" RESTRICT");
Expand All @@ -227,7 +228,7 @@ impl PyDropSchema {
name: IdentExtractor,
if_exists: bool,
behavior: Option<&str>,
) -> pyo3::PyResult<pyo3::PyClassInitializer<Self>> {
) -> error::Result<pyo3::PyClassInitializer<Self>> {
use std::str::FromStr;

let result = Self {
Expand All @@ -237,7 +238,7 @@ impl PyDropSchema {
None => None,
Some(x) => Some(
DropSchemaBehavior::from_str(x)
.map_err(|_| rapidquery_macros::pyerr!(PyValueError, "invalid value for behavior: {}", x))?,
.map_err(|_| error::Error::InvalidSchemaBehavior { value: x.to_owned() })?,
),
},
};
Expand All @@ -260,7 +261,7 @@ impl PyDropSchema {
}

/// Render this expression as raw SQL (values inlined as literals).
fn to_sql(&self, dialect: pyo3::Bound<'_, crate::dialect::PyDialect>) -> pyo3::PyResult<String> {
fn to_sql(&self, dialect: pyo3::Bound<'_, crate::dialect::PyDialect>) -> error::Result<String> {
sql::write_sql(dialect.py(), self, dialect.get())
}

Expand Down
Loading
Loading