Skip to content
Draft
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
4 changes: 2 additions & 2 deletions .github/workflows/flowzone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: Flowzone
on:
pull_request:
types: [opened, synchronize, closed]
branches: [main, master]
# branches: [main, master]
# allow external contributions to use secrets within trusted code
pull_request_target:
types: [opened, synchronize, closed]
branches: [main, master]
# branches: [main, master]

jobs:
flowzone:
Expand Down
47 changes: 47 additions & 0 deletions .versionbot/CHANGELOG.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
- commits:
- subject: Add binds for affected IDs in compiled rules
hash: 7d4bfac3b0dbcc6f916c90837398aff1e84588bc
body: |
After every write, pine has to rerun all rules from the model to ensure
consistency. These rules run over the entire database, sometimes causing
some queries to run for too long against what should be a simple write.

This commit adds a mechanism to help with this issue by narrowing the
set of rows that each rule should touch to those rows that were actually
changed.

Implementing this mechanism safely is doable and not necessarily complex
code-wise, but requires a deep modifications from the current
architecture. This commit adds a restricted form instead where we only
narrow the rows of the root table that were changed. If any other table
was changed then narrowing is a no op.

It can be proved that this is always safe as long as the root table is
selected from only once and the rule is positive ("It is necessary that
each ...").

The implementation here adds a single binding into the rule's SQL query
which can be bound by pine for each rule where an opportunity to use
this optimization arises.

The implementation itself is simple: count how many times the root table
is selected from and if it is selected from exactly one, then add a
narrowing constraint in the form of:

$1 = '{}' OR
<root table>.id = ANY(CAST($1 AS INTEGER[]))

Where $1 will be bound to either '{}', which disables narrowing, or to a
list of IDs that were affected by the write.

This initial implementation can be extended in the future.
footer:
Change-type: major
change-type: major
Signed-off-by: Carol Schulze <carol@balena.io>
signed-off-by: Carol Schulze <carol@balena.io>
author: Carol Schulze
nested: []
version: 8.0.0
title: ""
date: 2023-02-13T14:04:25.744Z
- commits:
- subject: Optimize schema during compilation
hash: e0b6b48c1429077d3c3742c9ee6224233a532bf7
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
This project adheres to [Semantic Versioning](http://semver.org/).

## 8.0.0 - 2023-02-13

* Add binds for affected IDs in compiled rules [Carol Schulze]

## 7.26.0 - 2023-02-07

* Optimize schema during compilation [Carol Schulze]
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Convert abstract SQL into dialect-specific SQL.
# abstract-sql-compiler

Convert abstract SQL into dialect-specific SQL.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@balena/abstract-sql-compiler",
"version": "7.26.0",
"version": "8.0.0",
"description": "A translator for abstract sql into sql.",
"main": "out/AbstractSQLCompiler.js",
"types": "out/AbstractSQLCompiler.d.ts",
Expand All @@ -16,11 +16,11 @@
"repository": "https://github.com/balena-io-modules/abstract-sql-compiler.git",
"author": "",
"dependencies": {
"@balena/sbvr-types": "^3.4.18",
"@balena/sbvr-types": "3.5.0-build-web-resource-2-cee49a331a438e988e109722d82dad2a04e88c0b-1",
"lodash": "^4.17.21"
},
"devDependencies": {
"@balena/lf-to-abstract-sql": "^4.6.0",
"@balena/lf-to-abstract-sql": "^5.0.0",
"@balena/lint": "^6.2.1",
"@balena/odata-parser": "^2.4.2",
"@balena/odata-to-abstract-sql": "^5.8.0",
Expand Down Expand Up @@ -58,6 +58,6 @@
]
},
"versionist": {
"publishedAt": "2023-02-07T12:42:59.778Z"
"publishedAt": "2023-02-13T14:04:26.240Z"
}
}
50 changes: 31 additions & 19 deletions src/AbstractSQLCompiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ReferencedFields,
RuleReferencedFields,
ModifiedFields,
insertAffectedIdsBinds,
} from './referenced-fields';

export type { ReferencedFields, RuleReferencedFields, ModifiedFields };
Expand Down Expand Up @@ -187,24 +188,21 @@ export type TextTypeNodes =
| ExtractJSONPathAsTextNode
| UnknownTypeNodes;

export type SelectQueryNode = [
'SelectQuery',
...Array<
| SelectNode
| FromNode
| InnerJoinNode
| LeftJoinNode
| RightJoinNode
| FullJoinNode
| CrossJoinNode
| WhereNode
| GroupByNode
| HavingNode
| OrderByNode
| LimitNode
| OffsetNode
>,
];
export type SelectQueryStatementNode =
| SelectNode
| FromNode
| InnerJoinNode
| LeftJoinNode
| RightJoinNode
| FullJoinNode
| CrossJoinNode
| WhereNode
| GroupByNode
| HavingNode
| OrderByNode
| LimitNode
| OffsetNode;
export type SelectQueryNode = ['SelectQuery', ...SelectQueryStatementNode[]];
export type UnionQueryNode = [
'UnionQuery',
// tslint:disable-next-line:array-type typescript fails on a circular reference when `Array<T>` form
Expand Down Expand Up @@ -238,7 +236,7 @@ export type FromTypeNodes =
| FromTypeNode[keyof FromTypeNode]
| AliasNode<FromTypeNode[keyof FromTypeNode]>;

type AliasableFromTypeNodes = FromTypeNodes | AliasNode<FromTypeNodes>;
export type AliasableFromTypeNodes = FromTypeNodes | AliasNode<FromTypeNodes>;

export type SelectNode = ['Select', AbstractSqlType[]];
export type FromNode = ['From', AliasableFromTypeNodes];
Expand Down Expand Up @@ -396,6 +394,17 @@ export interface AbstractSqlModel {
body: string;
language: 'plpgsql';
}>;
lfInfo: {
rules: {
[key: string]: LfRuleInfo;
};
};
}
export interface LfRuleInfo {
root: {
table: string;
alias: string;
};
}
export interface SqlModel {
synonyms: {
Expand Down Expand Up @@ -503,6 +512,8 @@ export const isSelectQueryNode = (n: AbstractSqlType): n is SelectQueryNode =>
n[0] === 'SelectQuery';
export const isSelectNode = (n: AbstractSqlType): n is SelectNode =>
n[0] === 'Select';
export const isWhereNode = (n: AbstractSqlType): n is WhereNode =>
n[0] === 'Where';

/**
*
Expand Down Expand Up @@ -873,6 +884,7 @@ CREATE TABLE ${ifNotExistsStr}"${table.name}" (
if (typeof ruleSE !== 'string') {
throw new Error('Invalid structured English');
}
insertAffectedIdsBinds(ruleBody, abstractSqlModel.lfInfo.rules[ruleSE]);
const { query: ruleSQL, bindings: ruleBindings } = compileRule(
ruleBody,
engine,
Expand Down
Loading