Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into i-am-tom/rename-deplo…
Browse files Browse the repository at this point in the history
…yment
  • Loading branch information
i-am-tom committed Jan 22, 2024
2 parents 027a95a + 2567175 commit 44e9054
Show file tree
Hide file tree
Showing 37 changed files with 9,129 additions and 47 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

### Added

- Support auto-generating insert procedures v1
([#261](https://github.com/hasura/ndc-postgres/pull/261))
- Return the generated SQL of an explain request with empty variables
([#241](https://github.com/hasura/ndc-postgres/pull/241))
- Emit invalid request, constraint not met, and unprocessable content errors at the relevant scenarios
Expand Down
14 changes: 8 additions & 6 deletions crates/connectors/ndc-postgres/src/configuration/version2.sql
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,14 @@ WITH
WHEN att.attidentity = 'a' THEN 'identityAlways'
ELSE 'notIdentity'
END
AS is_identity
-- skipped because yugabyte is based on pg11 that does not have this field.
-- CASE WHEN att.attgenerated = 's' THEN 'isGenerated' ELSE 'notGenerated' END
-- AS is_generated
AS is_identity,
CASE WHEN attgenerated_exists
THEN CASE WHEN attgenerated::text = 's' THEN 'stored' ELSE 'notGenerated' END
ELSE 'notGenerated'
END as is_generated
FROM
pg_catalog.pg_attribute AS att
CROSS JOIN (SELECT current_setting('server_version_num')::int >= 120000) AS attgenerated(attgenerated_exists)
WHERE
-- We only include columns that are actually part of the table currently.
NOT att.attisdropped -- This table also records historic columns.
Expand Down Expand Up @@ -854,8 +856,8 @@ FROM
c.has_default,
'isIdentity',
c.is_identity,
-- 'isGenerated',
-- c.is_generated,
'isGenerated',
c.is_generated,
'description',
comm.description
)
Expand Down
77 changes: 74 additions & 3 deletions crates/connectors/ndc-postgres/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::configuration;
use ndc_sdk::connector;
use ndc_sdk::models;
use query_engine_metadata::metadata;
use query_engine_translation::translation::mutation::{delete, generate};
use query_engine_translation::translation::mutation::{delete, generate, insert};

/// Get the connector's schema.
///
Expand Down Expand Up @@ -252,16 +252,18 @@ pub async fn get_schema(
})
.collect();

let mut more_object_types = BTreeMap::new();
let generated_procedures: Vec<models::ProcedureInfo> =
query_engine_translation::translation::mutation::generate::generate(
&metadata.tables,
&config.mutations_version,
)
.iter()
.map(|(name, mutation)| mutation_to_procedure(name, mutation))
.map(|(name, mutation)| mutation_to_procedure(name, mutation, &mut more_object_types))
.collect();

procedures.extend(generated_procedures);
object_types.extend(more_object_types);

Ok(models::SchemaResponse {
collections,
Expand Down Expand Up @@ -294,9 +296,16 @@ fn type_to_type(typ: &metadata::Type) -> models::Type {
}

/// Turn our different `Mutation` items into `ProcedureInfo`s to be output in the schema
fn mutation_to_procedure(name: &String, mutation: &generate::Mutation) -> models::ProcedureInfo {
fn mutation_to_procedure(
name: &String,
mutation: &generate::Mutation,
object_types: &mut BTreeMap<String, models::ObjectType>,
) -> models::ProcedureInfo {
match mutation {
generate::Mutation::DeleteMutation(delete) => delete_to_procedure(name, delete),
generate::Mutation::InsertMutation(insert) => {
insert_to_procedure(name, insert, object_types)
}
}
}

Expand Down Expand Up @@ -330,3 +339,65 @@ fn delete_to_procedure(name: &String, delete: &delete::DeleteMutation) -> models
}
}
}

/// Create an ObjectType out of columns metadata.
fn make_object_type(
columns: &BTreeMap<String, metadata::database::ColumnInfo>,
) -> models::ObjectType {
let mut fields = BTreeMap::new();
for (name, column) in columns {
match column {
// columns that are generated or are always identity should not be insertable.
metadata::database::ColumnInfo {
is_generated: metadata::database::IsGenerated::Stored,
..
}
| metadata::database::ColumnInfo {
is_identity: metadata::database::IsIdentity::IdentityAlways,
..
} => (),
_ => {
fields.insert(
name.clone(),
models::ObjectField {
r#type: column_to_type(column),
description: None,
},
);
}
}
}
models::ObjectType {
description: None,
fields,
}
}

/// Given an `InsertMutation`, turn it into a `ProcedureInfo` to be output in the schema.
fn insert_to_procedure(
name: &String,
insert: &insert::InsertMutation,
object_types: &mut BTreeMap<String, models::ObjectType>,
) -> models::ProcedureInfo {
let mut arguments = BTreeMap::new();
let object_type = make_object_type(&insert.columns);
let object_name = format!("{name}_object").to_string();
object_types.insert(object_name.clone(), object_type);

arguments.insert(
"_object".to_string(),
models::ArgumentInfo {
argument_type: models::Type::Named { name: object_name },
description: None,
},
);

models::ProcedureInfo {
name: name.to_string(),
description: Some(insert.description.to_string()),
arguments,
result_type: models::Type::Named {
name: insert.collection_name.to_string(),
},
}
}
2 changes: 1 addition & 1 deletion crates/query-engine/metadata/src/metadata/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ pub enum IsIdentity {
pub enum IsGenerated {
#[default]
NotGenerated,
IsGenerated,
Stored,
}

/// Information about a database column.
Expand Down
11 changes: 11 additions & 0 deletions crates/query-engine/sql/src/sql/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct CommonTableExpression {
pub enum CTExpr {
RawSql(Vec<RawSql>),
Delete(Delete),
Insert(Insert),
}

/// Raw SQL written by a user which is opaque to us
Expand All @@ -51,6 +52,16 @@ pub struct Select {
pub limit: Limit,
}

/// An INSERT clause
#[derive(Debug, Clone, PartialEq)]
pub struct Insert {
pub schema: SchemaName,
pub table: TableName,
pub columns: Vec<ColumnName>,
pub values: Vec<Expression>,
pub returning: Returning,
}

/// A DELETE clause
#[derive(Debug, Clone, PartialEq)]
pub struct Delete {
Expand Down
33 changes: 33 additions & 0 deletions crates/query-engine/sql/src/sql/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ impl CTExpr {
}
}
CTExpr::Delete(delete) => delete.to_sql(sql),
CTExpr::Insert(insert) => insert.to_sql(sql),
}
}
}
Expand Down Expand Up @@ -122,6 +123,38 @@ impl Select {
}
}

impl Insert {
pub fn to_sql(&self, sql: &mut SQL) {
sql.append_syntax("INSERT INTO ");

sql.append_identifier(&self.schema.0);
sql.append_syntax(".");
sql.append_identifier(&self.table.0);
sql.append_syntax("(");
for (index, column_name) in self.columns.iter().enumerate() {
sql.append_identifier(&column_name.0.to_string());
if index < (self.columns.len() - 1) {
sql.append_syntax(", ")
}
}
sql.append_syntax(")");

sql.append_syntax(" VALUES ");
sql.append_syntax("(");
for (index, value) in self.values.iter().enumerate() {
value.to_sql(sql);
if index < (self.values.len() - 1) {
sql.append_syntax(", ")
}
}
sql.append_syntax(")");

sql.append_syntax(" ");

self.returning.to_sql(sql);
}
}

impl Delete {
pub fn to_sql(&self, sql: &mut SQL) {
let Delete {
Expand Down
7 changes: 7 additions & 0 deletions crates/query-engine/sql/src/sql/rewrites/constant_folding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub fn normalize_cte(mut cte: CommonTableExpression) -> CommonTableExpression {
.collect(),
),
CTExpr::Delete(delete) => CTExpr::Delete(normalize_delete(delete)),
CTExpr::Insert(insert) => CTExpr::Insert(normalize_insert(insert)),
};
cte
}
Expand All @@ -111,6 +112,12 @@ fn normalize_delete(mut delete: Delete) -> Delete {
delete
}

/// Normalize everything in an Insert
fn normalize_insert(mut insert: Insert) -> Insert {
insert.values = insert.values.into_iter().map(normalize_expr).collect();
insert
}

/// Constant expressions folding. Remove redundant expressions.
/// This is the main work. The other parts are just trying to apply
/// this rewrite to their Expressions.
Expand Down
20 changes: 20 additions & 0 deletions crates/query-engine/translation/src/translation/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub enum Error {
UnexpectedVariable,
CapabilityNotSupported(UnsupportedCapabilities),
UnableToDeserializeNumberAsF64(serde_json::Number),
ColumnIsGenerated(String),
ColumnIsIdentityAlways(String),
MissingColumnInInsert(String, String),
NotImplementedYet(String),
InternalError(String),
}
Expand Down Expand Up @@ -93,6 +96,23 @@ impl std::fmt::Display for Error {
Error::UnableToDeserializeNumberAsF64(num) => {
write!(f, "Unable to deserialize the number '{}' as f64.", num)
}
Error::ColumnIsGenerated(column) => {
write!(
f,
"Unable to insert into the generated column '{}'.",
column
)
}
Error::ColumnIsIdentityAlways(column) => {
write!(f, "Unable to insert into the identity column '{}'.", column)
}
Error::MissingColumnInInsert(column, collection) => {
write!(
f,
"Unable to insert into '{}'. Column '{}' is missing.",
collection, column
)
}
Error::CapabilityNotSupported(thing) => {
write!(f, "Queries containing {} are not supported.", thing)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
//! Given introspection data, generate a set of standard mutation procedures
use super::delete::{generate_delete_by_unique, DeleteMutation};
use super::insert;
use super::insert::InsertMutation;
use query_engine_metadata::metadata::{database, mutations};
use std::collections::BTreeMap;

#[derive(Debug, Clone)]
pub enum Mutation {
DeleteMutation(DeleteMutation),
InsertMutation(InsertMutation),
}

/// Given our introspection data, work out all the mutations we can generate
Expand All @@ -24,6 +27,8 @@ pub fn generate(
for (name, delete_mutation) in delete_mutations {
mutations.insert(name, Mutation::DeleteMutation(delete_mutation));
}
let (name, insert_mutation) = insert::generate(collection_name, table_info);
mutations.insert(name, Mutation::InsertMutation(insert_mutation));
}
}
None => {}
Expand Down
Loading

0 comments on commit 44e9054

Please sign in to comment.