ddl-manager
Version:
store postgres procedures and triggers in files
95 lines (81 loc) • 2.79 kB
text/typescript
import {
Expression,
Operator,
ColumnReference,
IExpressionElement,
UnknownExpressionElement
} from "../../../ast";
import { TableReference } from "../../../database/schema/TableReference";
// try using gin-index scan
// input (cannot use gin-index):
// from companies where orders.id = any(companies.order_ids)
// output (can use gin-index):
// from companies where array[ orders.id_client ] && companies.order_ids
export function replaceOperatorAnyToIndexedOperator(
cacheFor: TableReference,
input: Expression
): Expression {
if ( !input.isBinary("=") ) {
return input;
}
const [leftOperand, rightOperand] = input.getOperands();
const columnOperand = detectColumnOperand(leftOperand, rightOperand);
const anyOperand = detectAnyOperand(leftOperand, rightOperand);
if ( !columnOperand || !anyOperand ) {
return input;
}
if ( columnOperand.tableReference.equal(cacheFor) ) {
return input;
}
const arrOperand = executeAnyContent( anyOperand as UnknownExpressionElement );
let arrayLiteral = `ARRAY[${columnOperand}]`;
const arrColumns = arrOperand.getColumnReferences();
if ( arrColumns.length === 1 ) {
const table = arrColumns[0].tableReference.table;
const columnName = arrColumns[0].name;
arrayLiteral = `cm_build_array_for((null::${table.schema}.${table.name}).${columnName}, ${columnOperand})`;
}
const output = new Expression([
arrOperand,
new Operator("&&"),
// TODO: cast bigint to same type with array
// array[]::bigint[] && some_bigint_ids
UnknownExpressionElement.fromSql(
arrayLiteral,
{[columnOperand.toString()]: columnOperand}
)
]);
return output;
}
function detectColumnOperand(leftOperand: IExpressionElement, rightOperand: IExpressionElement) {
if ( leftOperand instanceof ColumnReference ) {
return leftOperand;
}
if ( rightOperand instanceof ColumnReference ) {
return rightOperand;
}
}
function detectAnyOperand(leftOperand: IExpressionElement, rightOperand: IExpressionElement) {
if ( isAny(leftOperand) ) {
return leftOperand;
}
if ( isAny(rightOperand) ) {
return rightOperand;
}
}
function isAny(operand: IExpressionElement) {
return (
/^any\s*\(.*\)$/.test( operand.toString().trim().toLowerCase() )
);
}
function executeAnyContent(anyOperand: UnknownExpressionElement): IExpressionElement {
const sql = anyOperand.toString()
.trim()
.replace(/^any\s*\(/, "")
.replace(/\)$/, "");
const arrOperand = UnknownExpressionElement.fromSql(
sql,
anyOperand.columnsMap
);
return arrOperand;
}