Skip to content

remove transactions in queries #421

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

Merged
merged 4 commits into from
Apr 15, 2024
Merged

remove transactions in queries #421

merged 4 commits into from
Apr 15, 2024

Conversation

soupi
Copy link
Contributor

@soupi soupi commented Apr 15, 2024

What

Running queries in transactions require that we run 3 statements: begin, the query, commit.
We use transactions to enforce a READ ONLY setting so that native queries which run mutations will be rejected.
However, each roundtrip takes time. If we can avoid it, it would improve the performance of queries.

In the PR we use the fact the mutating sql statements can only occur in a CTE at the top-level, and wrap native queries in another layer of CTE, thereby enforce the read only requirement without using transactions.

How

  1. We remove the begin and commit statements before/after a query.
  2. We wrap each query in another CTE of the form WITH <native_query_name> as (WITH <alias> as (<native_query>) SELECT * FROM CTE as <alias>) SELECT ...
  3. We extract the global table alias index from the state and use it in wrapping the CTEs.
  4. We wrap ctes in the caller side because this code is used for mutations as well, for which we don't want to apply this hack.

isolation_level,
sql::ast::transaction::TransactionMode::ReadOnly,
),
pre: vec![],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No more begin and commit for queries.

TableAliasIndex(index)
/// Fetch the tracked native queries used in the query plan and their table alias,
/// and the global table index.
pub fn get_native_queries_and_global_index(self) -> (Vec<NativeQueryInfo>, TableAliasIndex) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We break down the state so we can consume the native queries when processing native queries, and use the global table alias index outside of it when inventing names for the wrapping ctes.

@@ -257,7 +257,8 @@ fn translate_native_query(

// add the procedure native query definition is a with clause.
select.with = sql::ast::With {
common_table_expressions: crate::translation::query::native_queries::translate(env, state)?,
common_table_expressions: crate::translation::query::native_queries::translate(env, state)?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mutations do need to run native queries at the top-level, because they are mutations. So we don't wrap the ctes here.

Comment on lines +68 to +74
common_table_expressions: {
let (ctes, mut global_table_index) = native_queries::translate(&env, state)?;
// wrap ctes in another cte to guard against mutations in queries
ctes.into_iter()
.map(|cte| native_queries::wrap_cte_in_cte(&mut global_table_index, cte))
.collect()
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we wrap.

@@ -72,5 +75,38 @@ pub fn translate(env: &Env, state: State) -> Result<Vec<sql::ast::CommonTableExp
});
}

Ok(ctes)
Ok((ctes, global_table_index))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We return the global table index here as well so it can be used when wrapping ctes, but the native queries with the state is consumed.

Comment on lines +81 to +111
/// Wrap a CTE in another CTE so we can guard against mutations in queries.
pub fn wrap_cte_in_cte(
table_alias_index: &mut TableAliasIndex,
mut cte: sql::ast::CommonTableExpression,
) -> sql::ast::CommonTableExpression {
// This is the name the rest of the query knows, so we keep it on the outer parts.
let outer_cte_alias = cte.alias;

// this is going to be internal, so we replace the CTE.
cte.alias = table_alias_index.make_table_alias(outer_cte_alias.name.clone());

// build the internal `WITH <CTE> SELECT * FROM CTE as <nested_cte_alias>`.
let nested_cte_alias = table_alias_index.make_table_alias(outer_cte_alias.name.clone());

let nested_cte_select = sql::ast::CTExpr::Select({
let mut select = sql::helpers::star_select(sql::ast::From::Table {
reference: sql::ast::TableReference::AliasedTable(cte.alias.clone()),
alias: nested_cte_alias.clone(),
});
select.with = sql::ast::With {
common_table_expressions: vec![cte],
};
select
});

// wrap in another CTE.
sql::ast::CommonTableExpression {
alias: outer_cte_alias,
column_names: None,
select: nested_cte_select,
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to wrap ctes.

cast(
(
("%0_%variables_table"."%variables" -> $2) #>> cast(ARRAY [] as text[])) as int4),cast((("%0_%variables_table"."%variables" -> $3) #>> cast(ARRAY [] as text[])) as int4)) AS series) AS arr
WITH "%9_NATIVE_QUERY_array_series" AS (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an example of a wrapping.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

begin and commit no more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test that this works.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I wanted to see. Thank you!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I wanted to see. Thank you!

@soupi soupi added this pull request to the merge queue Apr 15, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Apr 15, 2024
@soupi soupi added this pull request to the merge queue Apr 15, 2024
Merged via the queue into main with commit c12f569 Apr 15, 2024
35 checks passed
@soupi soupi deleted the gil/remove-select-transaction branch April 15, 2024 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants