Skip to content

Commit

Permalink
Add support for non-null ReferencedField optimization using model info
Browse files Browse the repository at this point in the history
We automatically use the model info to optimize rules/check constraints
when compiling the schema.

Change-type: minor
  • Loading branch information
Page- committed Jan 4, 2021
1 parent e6269db commit d61d299
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 61 deletions.
28 changes: 21 additions & 7 deletions src/AbstractSQLCompiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,16 +571,19 @@ export function compileRule(
abstractSQL: UpsertQueryNode,
engine: Engines,
noBinds: true,
abstractSqlModel?: AbstractSqlModel,
): [string, string];
export function compileRule(
abstractSQL: AbstractSqlQuery,
engine: Engines,
noBinds: true,
abstractSqlModel?: AbstractSqlModel,
): string;
export function compileRule(
abstractSQL: UpsertQueryNode,
engine: Engines,
noBinds?: false,
abstractSqlModel?: AbstractSqlModel,
): [SqlResult, SqlResult];
export function compileRule(
abstractSQL:
Expand All @@ -591,23 +594,27 @@ export function compileRule(
| DeleteQueryNode,
engine: Engines,
noBinds?: false,
abstractSqlModel?: AbstractSqlModel,
): SqlResult;
export function compileRule(
abstractSQL: AbstractSqlQuery,
engine: Engines,
noBinds?: false,
abstractSqlModel?: AbstractSqlModel,
): SqlResult | [SqlResult, SqlResult];
export function compileRule(
abstractSQL: AbstractSqlQuery,
engine: Engines,
noBinds?: boolean,
abstractSqlModel?: AbstractSqlModel,
): SqlResult | [SqlResult, SqlResult] | string;
export function compileRule(
abstractSQL: AbstractSqlQuery,
engine: Engines,
noBinds = false,
abstractSqlModel?: AbstractSqlModel,
): SqlResult | [SqlResult, SqlResult] | string | [string, string] {
abstractSQL = AbstractSQLOptimiser(abstractSQL, noBinds);
abstractSQL = AbstractSQLOptimiser(abstractSQL, noBinds, abstractSqlModel);
return AbstractSQLRules2SQL(abstractSQL, engine, noBinds);
}

Expand Down Expand Up @@ -702,10 +709,12 @@ $$;`);
createSQL: [
`\
CREATE ${orReplaceStr}VIEW "${table.name}" AS (
${compileRule(definitionAbstractSql as AbstractSqlQuery, engine, true).replace(
/^/gm,
' ',
)}
${compileRule(
definitionAbstractSql as AbstractSqlQuery,
engine,
true,
abstractSqlModel,
).replace(/^/gm, ' ')}
);`,
],
dropSQL: [`DROP VIEW "${table.name}";`],
Expand Down Expand Up @@ -787,6 +796,7 @@ $$;`);
check.abstractSql as AbstractSqlQuery,
engine,
true,
abstractSqlModel,
);
createSqlElements.push(`\
${comment}${constraintName}CHECK (${sql})`);
Expand Down Expand Up @@ -912,6 +922,8 @@ CREATE TABLE ${ifNotExistsStr}"${table.name}" (
const { query: ruleSQL, bindings: ruleBindings } = compileRule(
ruleBody,
engine,
undefined,
abstractSqlModel,
) as SqlResult;
let referencedFields: ReferencedFields | undefined;
try {
Expand Down Expand Up @@ -944,8 +956,10 @@ const generateExport = (engine: Engines, ifNotExists: boolean) => {
optimizeSchema,
compileSchema: (abstractSqlModel: AbstractSqlModel) =>
compileSchema(abstractSqlModel, engine, ifNotExists),
compileRule: (abstractSQL: AbstractSqlQuery) =>
compileRule(abstractSQL, engine, false),
compileRule: (
abstractSQL: AbstractSqlQuery,
abstractSqlModel?: AbstractSqlModel,
) => compileRule(abstractSQL, engine, false, abstractSqlModel),
dataTypeValidate,
getReferencedFields,
getModifiedFields,
Expand Down
30 changes: 29 additions & 1 deletion src/AbstractSQLOptimiser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import * as _ from 'lodash';

import { Dictionary } from 'lodash';
import {
AbstractSqlModel,
AbstractSqlQuery,
AbstractSqlType,
AliasNode,
DurationNode,
ReferencedFieldNode,
ReplaceNode,
TableNode,
} from './AbstractSQLCompiler';
import * as AbstractSQLRules2SQL from './AbstractSQLRules2SQL';

Expand All @@ -14,7 +18,6 @@ const {
getAbstractSqlQuery,
checkArgs,
checkMinArgs,
isNotNullable,
} = AbstractSQLRules2SQL;

type OptimisationMatchFn = (
Expand All @@ -36,7 +39,9 @@ const escapeForLike = (str: AbstractSqlType): ReplaceNode => [
];

let helped = false;
let aliases: { [alias: string]: string } = {};
let noBinds = false;
let abstractSqlModel: AbstractSqlModel | undefined;
const Helper = <F extends (...args: any[]) => any>(fn: F) => {
return (...args: Parameters<F>): ReturnType<F> => {
const result = fn(...args);
Expand Down Expand Up @@ -65,6 +70,22 @@ const isEmptySelectQuery = (query: AbstractSqlQuery): boolean => {
return false;
};

const isNotNullable = (node: AbstractSqlQuery): boolean => {
switch (node[0]) {
case 'ReferencedField':
if (abstractSqlModel != null) {
const [, aliasName, fieldName] = node as ReferencedFieldNode;
const tableName = aliases[aliasName] ?? aliasName;
const table = abstractSqlModel.tables[tableName];
const field = table?.fields.find((f) => f.fieldName === fieldName);
if (field?.required === true) {
return true;
}
}
}
return AbstractSQLRules2SQL.isNotNullable(node);
};

const rewriteMatch = (
name: string,
matchers: Array<(args: AbstractSqlType) => AbstractSqlType>,
Expand Down Expand Up @@ -455,6 +476,10 @@ const typeRules: Dictionary<MatchFn> = {
},
From: (args) => {
checkArgs('From', args, 1);
const maybeAlias = args[0] as AliasNode<TableNode>;
if (maybeAlias[0] === 'Alias' && maybeAlias[1][0] === 'Table') {
aliases[maybeAlias[2]] = maybeAlias[1][1];
}
return ['From', MaybeAlias(args[0] as AbstractSqlQuery, FromMatch)];
},
Join: JoinMatch('Join'),
Expand Down Expand Up @@ -1311,10 +1336,13 @@ const typeRules: Dictionary<MatchFn> = {
export const AbstractSQLOptimiser = (
abstractSQL: AbstractSqlQuery,
$noBinds = false,
$abstractSqlModel?: AbstractSqlModel,
): AbstractSqlQuery => {
noBinds = $noBinds;
abstractSqlModel = $abstractSqlModel;
do {
helped = false;
aliases = {};
const [type, ...rest] = abstractSQL;
switch (type) {
case 'SelectQuery':
Expand Down
2 changes: 1 addition & 1 deletion src/AbstractSQLSchemaOptimiser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const optimizeSchema = (
}

// Optimize the rule body, this also normalizes it making the check constraint check easier
ruleBody = AbstractSQLOptimiser(ruleBody, true);
ruleBody = AbstractSQLOptimiser(ruleBody, true, abstractSqlModel);

const count = countFroms(ruleBody);
if (
Expand Down
Loading

0 comments on commit d61d299

Please sign in to comment.