@balena/abstract-sql-compiler
Advanced tools
Comparing version 9.0.1 to 9.0.2-build-184-operator-precedence-light-be84fe9af991e4b921673872f6dffd33f1427db8-1
@@ -7,2 +7,7 @@ # Change Log | ||
## 9.0.2 - 2023-05-15 | ||
* Wrap expressions with parenthesis based on their precedence [Thodoris Greasidis] | ||
* Wrap composite comparison operations with parenthesis when necessary [Thodoris Greasidis] | ||
## 9.0.1 - 2023-05-15 | ||
@@ -9,0 +14,0 @@ |
@@ -251,4 +251,4 @@ "use strict"; | ||
(0, exports.checkArgs)(comparison, args, 2); | ||
const a = AnyValue((0, exports.getAbstractSqlQuery)(args, 0), indent); | ||
const b = AnyValue((0, exports.getAbstractSqlQuery)(args, 1), indent); | ||
const a = precedenceSafeOpValue(comparison, AnyValue, args, 0, indent); | ||
const b = precedenceSafeOpValue(comparison, AnyValue, args, 1, indent); | ||
return a + exports.comparisons[comparison] + b; | ||
@@ -315,18 +315,46 @@ }; | ||
}; | ||
const mathOperatorNodeTypes = new Set([ | ||
...Object.keys(mathOps), | ||
'AddDateDuration', | ||
'AddDateNumber', | ||
'SubtractDateDate', | ||
'SubtractDateDuration', | ||
'SubtractDateNumber', | ||
]); | ||
const mathOpValue = (valueMatchFn, args, index, indent) => { | ||
const opsPrecedence = (() => { | ||
const operatorsByPrecedence = [ | ||
['Multiply', 'Divide'], | ||
[ | ||
'Add', | ||
'AddDateDuration', | ||
'AddDateNumber', | ||
'Subtract', | ||
'SubtractDateDate', | ||
'SubtractDateDuration', | ||
'SubtractDateNumber', | ||
], | ||
['Between', 'Like'], | ||
[ | ||
'Equals', | ||
'NotEquals', | ||
'GreaterThan', | ||
'GreaterThanOrEqual', | ||
'LessThan', | ||
'LessThanOrEqual', | ||
], | ||
]; | ||
const operatorPrecedence = {}; | ||
let precedence = 0; | ||
for (const samePrecedenceOps of operatorsByPrecedence) { | ||
for (const op of samePrecedenceOps) { | ||
operatorPrecedence[op] = precedence; | ||
} | ||
precedence++; | ||
} | ||
return operatorPrecedence; | ||
})(); | ||
const precedenceSafeOpValue = (parentNodeType, valueMatchFn, args, index, indent) => { | ||
const operandAbstractSql = (0, exports.getAbstractSqlQuery)(args, index); | ||
const numericValue = valueMatchFn(operandAbstractSql, indent); | ||
const valueExpr = valueMatchFn(operandAbstractSql, indent); | ||
const [childNodeType] = operandAbstractSql; | ||
if (mathOperatorNodeTypes.has(childNodeType)) { | ||
return `(${numericValue})`; | ||
const parentOperatorPrecedence = opsPrecedence[parentNodeType]; | ||
const childOperatorPrecedence = opsPrecedence[childNodeType]; | ||
if (childOperatorPrecedence != null && | ||
parentOperatorPrecedence != null && | ||
parentOperatorPrecedence <= childOperatorPrecedence) { | ||
return `(${valueExpr})`; | ||
} | ||
return numericValue; | ||
return valueExpr; | ||
}; | ||
@@ -336,4 +364,4 @@ const MathOp = (type) => { | ||
(0, exports.checkArgs)(type, args, 2); | ||
const a = mathOpValue(NumericValue, args, 0, indent); | ||
const b = mathOpValue(NumericValue, args, 1, indent); | ||
const a = precedenceSafeOpValue(type, NumericValue, args, 0, indent); | ||
const b = precedenceSafeOpValue(type, NumericValue, args, 1, indent); | ||
return `${a} ${mathOps[type]} ${b}`; | ||
@@ -406,4 +434,4 @@ }; | ||
(0, exports.checkArgs)('AddDateNumber', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(NumericValue, args, 1, indent); | ||
const a = precedenceSafeOpValue('AddDateNumber', DateValue, args, 0, indent); | ||
const b = precedenceSafeOpValue('AddDateNumber', NumericValue, args, 1, indent); | ||
if (engine === "postgres") { | ||
@@ -421,4 +449,4 @@ return `${a} + ${b}`; | ||
(0, exports.checkArgs)('AddDateDuration', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(DurationValue, args, 1, indent); | ||
const a = precedenceSafeOpValue('AddDateDuration', DateValue, args, 0, indent); | ||
const b = precedenceSafeOpValue('AddDateDuration', DurationValue, args, 1, indent); | ||
if (engine === "postgres") { | ||
@@ -436,4 +464,4 @@ return `${a} + ${b}`; | ||
(0, exports.checkArgs)('SubtractDateDuration', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(DurationValue, args, 1, indent); | ||
const a = precedenceSafeOpValue('SubtractDateDuration', DateValue, args, 0, indent); | ||
const b = precedenceSafeOpValue('SubtractDateDuration', DurationValue, args, 1, indent); | ||
if (engine === "postgres") { | ||
@@ -451,4 +479,4 @@ return `${a} - ${b}`; | ||
(0, exports.checkArgs)('SubtractDateNumber', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(NumericValue, args, 1, indent); | ||
const a = precedenceSafeOpValue('SubtractDateNumber', DateValue, args, 0, indent); | ||
const b = precedenceSafeOpValue('SubtractDateNumber', NumericValue, args, 1, indent); | ||
if (engine === "postgres") { | ||
@@ -466,4 +494,4 @@ return `${a} - ${b}`; | ||
(0, exports.checkArgs)('SubtractDateDate', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(DateValue, args, 1, indent); | ||
const a = precedenceSafeOpValue('SubtractDateDate', DateValue, args, 0, indent); | ||
const b = precedenceSafeOpValue('SubtractDateDate', DateValue, args, 1, indent); | ||
if (engine === "postgres") { | ||
@@ -840,5 +868,5 @@ return `${a} - ${b}`; | ||
(0, exports.checkArgs)('Between', args, 3); | ||
const v = AnyValue((0, exports.getAbstractSqlQuery)(args, 0), indent); | ||
const a = AnyValue((0, exports.getAbstractSqlQuery)(args, 1), indent); | ||
const b = AnyValue((0, exports.getAbstractSqlQuery)(args, 2), indent); | ||
const v = precedenceSafeOpValue('Between', AnyValue, args, 0, indent); | ||
const a = precedenceSafeOpValue('Between', AnyValue, args, 1, indent); | ||
const b = precedenceSafeOpValue('Between', AnyValue, args, 2, indent); | ||
return `${v} BETWEEN ${a} AND (${b})`; | ||
@@ -845,0 +873,0 @@ }, |
{ | ||
"name": "@balena/abstract-sql-compiler", | ||
"version": "9.0.1", | ||
"version": "9.0.2-build-184-operator-precedence-light-be84fe9af991e4b921673872f6dffd33f1427db8-1", | ||
"description": "A translator for abstract sql into sql.", | ||
@@ -64,4 +64,4 @@ "main": "out/AbstractSQLCompiler.js", | ||
"versionist": { | ||
"publishedAt": "2023-05-15T12:42:37.997Z" | ||
"publishedAt": "2023-05-15T21:42:55.457Z" | ||
} | ||
} |
@@ -318,4 +318,4 @@ import * as _ from 'lodash'; | ||
checkArgs(comparison, args, 2); | ||
const a = AnyValue(getAbstractSqlQuery(args, 0), indent); | ||
const b = AnyValue(getAbstractSqlQuery(args, 1), indent); | ||
const a = precedenceSafeOpValue(comparison, AnyValue, args, 0, indent); | ||
const b = precedenceSafeOpValue(comparison, AnyValue, args, 1, indent); | ||
return a + comparisons[comparison] + b; | ||
@@ -389,12 +389,40 @@ }; | ||
const mathOperatorNodeTypes = new Set([ | ||
...Object.keys(mathOps), | ||
'AddDateDuration', | ||
'AddDateNumber', | ||
'SubtractDateDate', | ||
'SubtractDateDuration', | ||
'SubtractDateNumber', | ||
]); | ||
const opsPrecedence = (() => { | ||
const operatorsByPrecedence = [ | ||
['Multiply', 'Divide'], | ||
[ | ||
'Add', | ||
'AddDateDuration', | ||
'AddDateNumber', | ||
'Subtract', | ||
'SubtractDateDate', | ||
'SubtractDateDuration', | ||
'SubtractDateNumber', | ||
], | ||
['Between', 'Like'], | ||
[ | ||
'Equals', | ||
'NotEquals', | ||
'GreaterThan', | ||
'GreaterThanOrEqual', | ||
'LessThan', | ||
'LessThanOrEqual', | ||
], | ||
// In, Exists, NotExists, 'IsDistinctFrom', 'IsNotDistinctFrom', Not, | ||
// And, Or are already adding parenthesis. | ||
] as const; | ||
const mathOpValue = ( | ||
const operatorPrecedence = {} as Record<string, number>; | ||
let precedence = 0; | ||
for (const samePrecedenceOps of operatorsByPrecedence) { | ||
for (const op of samePrecedenceOps) { | ||
operatorPrecedence[op] = precedence; | ||
} | ||
precedence++; | ||
} | ||
return operatorPrecedence; | ||
})(); | ||
const precedenceSafeOpValue = ( | ||
parentNodeType: string, | ||
valueMatchFn: MetaMatchFn, | ||
@@ -406,8 +434,14 @@ args: AbstractSqlType[], | ||
const operandAbstractSql = getAbstractSqlQuery(args, index); | ||
const numericValue = valueMatchFn(operandAbstractSql, indent); | ||
const valueExpr = valueMatchFn(operandAbstractSql, indent); | ||
const [childNodeType] = operandAbstractSql; | ||
if (mathOperatorNodeTypes.has(childNodeType)) { | ||
return `(${numericValue})`; | ||
const parentOperatorPrecedence = opsPrecedence[parentNodeType]; | ||
const childOperatorPrecedence = opsPrecedence[childNodeType]; | ||
if ( | ||
childOperatorPrecedence != null && | ||
parentOperatorPrecedence != null && | ||
parentOperatorPrecedence <= childOperatorPrecedence | ||
) { | ||
return `(${valueExpr})`; | ||
} | ||
return numericValue; | ||
return valueExpr; | ||
}; | ||
@@ -418,4 +452,4 @@ | ||
checkArgs(type, args, 2); | ||
const a = mathOpValue(NumericValue, args, 0, indent); | ||
const b = mathOpValue(NumericValue, args, 1, indent); | ||
const a = precedenceSafeOpValue(type, NumericValue, args, 0, indent); | ||
const b = precedenceSafeOpValue(type, NumericValue, args, 1, indent); | ||
return `${a} ${mathOps[type]} ${b}`; | ||
@@ -489,4 +523,10 @@ }; | ||
checkArgs('AddDateNumber', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(NumericValue, args, 1, indent); | ||
const a = precedenceSafeOpValue('AddDateNumber', DateValue, args, 0, indent); | ||
const b = precedenceSafeOpValue( | ||
'AddDateNumber', | ||
NumericValue, | ||
args, | ||
1, | ||
indent, | ||
); | ||
@@ -504,4 +544,16 @@ if (engine === Engines.postgres) { | ||
checkArgs('AddDateDuration', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(DurationValue, args, 1, indent); | ||
const a = precedenceSafeOpValue( | ||
'AddDateDuration', | ||
DateValue, | ||
args, | ||
0, | ||
indent, | ||
); | ||
const b = precedenceSafeOpValue( | ||
'AddDateDuration', | ||
DurationValue, | ||
args, | ||
1, | ||
indent, | ||
); | ||
@@ -519,4 +571,16 @@ if (engine === Engines.postgres) { | ||
checkArgs('SubtractDateDuration', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(DurationValue, args, 1, indent); | ||
const a = precedenceSafeOpValue( | ||
'SubtractDateDuration', | ||
DateValue, | ||
args, | ||
0, | ||
indent, | ||
); | ||
const b = precedenceSafeOpValue( | ||
'SubtractDateDuration', | ||
DurationValue, | ||
args, | ||
1, | ||
indent, | ||
); | ||
@@ -534,4 +598,16 @@ if (engine === Engines.postgres) { | ||
checkArgs('SubtractDateNumber', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(NumericValue, args, 1, indent); | ||
const a = precedenceSafeOpValue( | ||
'SubtractDateNumber', | ||
DateValue, | ||
args, | ||
0, | ||
indent, | ||
); | ||
const b = precedenceSafeOpValue( | ||
'SubtractDateNumber', | ||
NumericValue, | ||
args, | ||
1, | ||
indent, | ||
); | ||
@@ -549,4 +625,16 @@ if (engine === Engines.postgres) { | ||
checkArgs('SubtractDateDate', args, 2); | ||
const a = mathOpValue(DateValue, args, 0, indent); | ||
const b = mathOpValue(DateValue, args, 1, indent); | ||
const a = precedenceSafeOpValue( | ||
'SubtractDateDate', | ||
DateValue, | ||
args, | ||
0, | ||
indent, | ||
); | ||
const b = precedenceSafeOpValue( | ||
'SubtractDateDate', | ||
DateValue, | ||
args, | ||
1, | ||
indent, | ||
); | ||
if (engine === Engines.postgres) { | ||
@@ -956,5 +1044,5 @@ return `${a} - ${b}`; | ||
checkArgs('Between', args, 3); | ||
const v = AnyValue(getAbstractSqlQuery(args, 0), indent); | ||
const a = AnyValue(getAbstractSqlQuery(args, 1), indent); | ||
const b = AnyValue(getAbstractSqlQuery(args, 2), indent); | ||
const v = precedenceSafeOpValue('Between', AnyValue, args, 0, indent); | ||
const a = precedenceSafeOpValue('Between', AnyValue, args, 1, indent); | ||
const b = precedenceSafeOpValue('Between', AnyValue, args, 2, indent); | ||
return `${v} BETWEEN ${a} AND (${b})`; | ||
@@ -961,0 +1049,0 @@ }, |
@@ -0,1 +1,2 @@ | ||
import { stripIndent } from 'common-tags'; | ||
import { AnyTypeNodes } from '../../src/AbstractSQLCompiler'; | ||
@@ -27,1 +28,158 @@ | ||
}); | ||
describe('Comparison Operator Precedence', () => { | ||
// Different precedence | ||
test( | ||
[ | ||
'SelectQuery', | ||
[ | ||
'Select', | ||
[ | ||
[ | ||
'Equals', | ||
['Boolean', true], | ||
['Equals', ['Boolean', true], ['Boolean', true]], | ||
], | ||
], | ||
], | ||
], | ||
(result, sqlEquals) => { | ||
it('should produce a valid Equals statement when the second operand is also an Equals', () => { | ||
sqlEquals(result.query, 'SELECT TRUE = (TRUE = TRUE)'); | ||
}); | ||
}, | ||
); | ||
test( | ||
[ | ||
'SelectQuery', | ||
[ | ||
'Select', | ||
[ | ||
[ | ||
'NotEquals', | ||
['Equals', ['Boolean', false], ['Boolean', false]], | ||
['Equals', ['Boolean', true], ['Boolean', true]], | ||
], | ||
], | ||
], | ||
], | ||
(result, sqlEquals) => { | ||
it('should produce a valid NotEquals statement when both operands are Equals comparisons', () => { | ||
sqlEquals(result.query, 'SELECT (FALSE = FALSE) != (TRUE = TRUE)'); | ||
}); | ||
}, | ||
); | ||
test( | ||
[ | ||
'SelectQuery', | ||
[ | ||
'Select', | ||
[ | ||
[ | ||
'NotEquals', | ||
['Add', ['Integer', 1], ['Add', ['Integer', 2], ['Integer', 3]]], | ||
['Add', ['Integer', 1], ['Integer', 0]], | ||
], | ||
], | ||
], | ||
], | ||
(result, sqlEquals) => { | ||
it('should produce a valid NotEquals statement when the operands are math expressions', () => { | ||
sqlEquals( | ||
result.query, | ||
stripIndent` | ||
SELECT 1 + (2 + 3) != 1 + 0 | ||
`, | ||
); | ||
}); | ||
}, | ||
); | ||
test( | ||
[ | ||
'SelectQuery', | ||
[ | ||
'Select', | ||
[ | ||
[ | ||
'And', | ||
[ | ||
'Or', | ||
['GreaterThan', ['Integer', 1], ['Integer', 0]], | ||
['LessThan', ['Integer', 1], ['Integer', 0]], | ||
], | ||
['GreaterThan', ['Integer', 1], ['Integer', 0]], | ||
], | ||
], | ||
], | ||
], | ||
(result, sqlEquals) => { | ||
it('should produce a valid And statement when the operands are composite boolean expressions', () => { | ||
sqlEquals( | ||
result.query, | ||
stripIndent` | ||
SELECT (1 > 0 | ||
OR 1 < 0) | ||
AND 1 > 0 | ||
`, | ||
); | ||
}); | ||
}, | ||
); | ||
test( | ||
[ | ||
'SelectQuery', | ||
[ | ||
'Select', | ||
[ | ||
[ | ||
'Between', | ||
['Add', ['Integer', 1], ['Integer', 0]], | ||
['Add', ['Integer', 1], ['Integer', 0]], | ||
['Add', ['Integer', 1], ['Integer', 0]], | ||
], | ||
], | ||
], | ||
], | ||
(result, sqlEquals) => { | ||
it('should produce a valid Between statement when the operands are math expressions', () => { | ||
sqlEquals( | ||
result.query, | ||
stripIndent` | ||
SELECT 1 + 0 BETWEEN 1 + 0 AND (1 + 0) | ||
`, | ||
); | ||
}); | ||
}, | ||
); | ||
test( | ||
[ | ||
'SelectQuery', | ||
[ | ||
'Select', | ||
[ | ||
[ | ||
'Between', | ||
['Equals', ['Integer', 1], ['Integer', 0]], | ||
['LessThan', ['Integer', 1], ['Integer', 0]], | ||
['GreaterThan', ['Integer', 1], ['Integer', 0]], | ||
], | ||
], | ||
], | ||
], | ||
(result, sqlEquals) => { | ||
it('should produce a valid Between statement when the operands are comparison expressions', () => { | ||
sqlEquals( | ||
result.query, | ||
stripIndent` | ||
SELECT (1 = 0) BETWEEN (1 < 0) AND ((1 > 0)) | ||
`, | ||
); | ||
}); | ||
}, | ||
); | ||
}); |
@@ -131,3 +131,3 @@ import { AnyTypeNodes } from '../../src/AbstractSQLCompiler'; | ||
it('should produce a valid Add statement when the first operand is a Multiply', () => { | ||
sqlEquals(result.query, 'SELECT (2 * 3) + 4'); | ||
sqlEquals(result.query, 'SELECT 2 * 3 + 4'); | ||
}); | ||
@@ -147,3 +147,3 @@ }, | ||
it('should produce a valid Add statement when the second operand is a Multiply', () => { | ||
sqlEquals(result.query, 'SELECT 2 + (3 * 4)'); | ||
sqlEquals(result.query, 'SELECT 2 + 3 * 4'); | ||
}); | ||
@@ -169,3 +169,3 @@ }, | ||
it('should produce a valid Add statement of two Multiplications', () => { | ||
sqlEquals(result.query, 'SELECT (2 * 3) + (4 * 5)'); | ||
sqlEquals(result.query, 'SELECT 2 * 3 + 4 * 5'); | ||
}); | ||
@@ -242,3 +242,3 @@ }, | ||
it('should produce a valid Subtract statement of two Multiplications', () => { | ||
sqlEquals(result.query, 'SELECT (2 * 3) - (4 * 5)'); | ||
sqlEquals(result.query, 'SELECT 2 * 3 - 4 * 5'); | ||
}); | ||
@@ -264,3 +264,3 @@ }, | ||
it('should produce a valid Subtract statement of two Divisions', () => { | ||
sqlEquals(result.query, 'SELECT (2 / 3) - (4 / 5)'); | ||
sqlEquals(result.query, 'SELECT 2 / 3 - 4 / 5'); | ||
}); | ||
@@ -267,0 +267,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
833652
18007
2