sqlite-parser
sqlite-parser copied to clipboard
Incorrect AST when using "LIKE" expression
When parsing a query like:
SELECT *
FROM Bees b
WHERE wings LIKE '1' AND limbs = 'blah'
The AST generated is incorrect.
{
"type": "statement",
"variant": "list",
"statement": [
{
"type": "statement",
"variant": "select",
"result": [
{
"type": "identifier",
"variant": "star",
"name": "*"
}
],
"from": {
"type": "identifier",
"variant": "table",
"name": "bees",
"alias": "b"
},
"where": [
{
"type": "expression",
"format": "binary",
"variant": "operation",
"operation": "like",
"right": {
"type": "expression",
"format": "binary",
"variant": "operation",
"operation": "and",
"left": {
"type": "literal",
"variant": "text",
"value": "1"
},
"right": {
"type": "expression",
"format": "binary",
"variant": "operation",
"operation": "=",
"left": {
"type": "identifier",
"variant": "column",
"name": "limbs"
},
"right": {
"type": "literal",
"variant": "text",
"value": "blah"
}
}
},
"left": {
"type": "identifier",
"variant": "column",
"name": "wings"
}
}
]
}
]
}
In the where clause, the LIKE
node is the root node. However AND
node should be the root node.
I have confirmed this bug as well. Here is another more complex example with several operators, LIKE is the only one that sometimes puts a 'binary' 'and' between the 'left' and 'right'. It seems to be dependent on where the LIKE is in the order of statements too, if the LIKE expression is last it works fine. If you view the AST in a JSON viewer it is very easy to see the issue.
SELECT a, b
FROM x, y
WHERE d = 'X'
AND e BETWEEN '1/1/2018' AND '1/31/2018'
AND f != 'NEW'
AND g >= 1000000
AND (h IS NULL
OR j IS NOT NULL)
AND k LIKE 'Jones%'
AND l IN ('CA', 'OR', 'NY')
{"type":"statement","variant":"list","statement":[{"type":"statement","variant":"select","result":[{"type":"identifier","variant":"column","name":"a"},{"type":"identifier","variant":"column","name":"b"}],"from":{"type":"map","variant":"join","source":{"type":"identifier","variant":"table","name":"x"},"map":[{"type":"join","variant":"cross join","source":{"type":"identifier","variant":"table","name":"y"}}]},"where":[{"type":"expression","format":"binary","variant":"operation","operation":"and","left":{"type":"expression","format":"binary","variant":"operation","operation":"and","left":{"type":"expression","format":"binary","variant":"operation","operation":"and","left":{"type":"expression","format":"binary","variant":"operation","operation":"and","left":{"type":"expression","format":"binary","variant":"operation","operation":"and","left":{"type":"expression","format":"binary","variant":"operation","operation":"=","left":{"type":"identifier","variant":"column","name":"d"},"right":{"type":"literal","variant":"text","value":"X"}},"right":{"type":"expression","format":"binary","variant":"operation","operation":"between","right":{"type":"expression","format":"binary","variant":"operation","operation":"and","left":{"type":"literal","variant":"text","value":"1/1/2018"},"right":{"type":"literal","variant":"text","value":"1/31/2018"}},"left":{"type":"identifier","variant":"column","name":"e"}}},"right":{"type":"expression","format":"binary","variant":"operation","operation":"!=","left":{"type":"identifier","variant":"column","name":"f"},"right":{"type":"literal","variant":"text","value":"NEW"}}},"right":{"type":"expression","format":"binary","variant":"operation","operation":">=","left":{"type":"identifier","variant":"column","name":"g"},"right":{"type":"literal","variant":"decimal","value":"1000000"}}},"right":{"type":"expression","format":"binary","variant":"operation","operation":"or","left":{"type":"expression","format":"binary","variant":"operation","operation":"is","left":{"type":"identifier","variant":"column","name":"h"},"right":{"type":"literal","variant":"null","value":"null"}},"right":{"type":"expression","format":"binary","variant":"operation","operation":"is not","left":{"type":"identifier","variant":"column","name":"j"},"right":{"type":"literal","variant":"null","value":"null"}}}},"right":{"type":"expression","format":"binary","variant":"operation","operation":"like","right":{"type":"expression","format":"binary","variant":"operation","operation":"and","left":{"type":"literal","variant":"text","value":"Jones%"},"right":{"type":"expression","format":"binary","variant":"operation","operation":"in","right":{"type":"expression","variant":"list","expression":[{"type":"literal","variant":"text","value":"CA"},{"type":"literal","variant":"text","value":"OR"},{"type":"literal","variant":"text","value":"NY"}]},"left":{"type":"identifier","variant":"column","name":"l"}}},"left":{"type":"identifier","variant":"column","name":"k"}}}]}]}
Here is another minimal example (just two like
expressions joined with an and
), first with parens (correct), second (wrong one) without parens:
> const { statement: [{ where: [where7] }] } = parser("select 1 where (description like 'foo') and (description like 'bar')");
undefined
> where7
{ type: 'expression',
format: 'binary',
variant: 'operation',
operation: 'and',
left:
{ type: 'expression',
format: 'binary',
variant: 'operation',
operation: 'like',
right: { type: 'literal', variant: 'text', value: 'foo' },
left: { type: 'identifier', variant: 'column', name: 'description' } },
right:
{ type: 'expression',
format: 'binary',
variant: 'operation',
operation: 'like',
right: { type: 'literal', variant: 'text', value: 'bar' },
left: { type: 'identifier', variant: 'column', name: 'description' } } }
> const { statement: [{ where: [where9] }] } = parser("select 1 where description like 'foo' and description like 'bar'");
undefined
> where9
{ type: 'expression',
format: 'binary',
variant: 'operation',
operation: 'like',
right:
{ type: 'expression',
format: 'binary',
variant: 'operation',
operation: 'and',
left: { type: 'literal', variant: 'text', value: 'foo' },
right:
{ type: 'expression',
format: 'binary',
variant: 'operation',
operation: 'like',
right: [Object],
left: [Object] } },
left: { type: 'identifier', variant: 'column', name: 'description' } }