sqlite-parser icon indicating copy to clipboard operation
sqlite-parser copied to clipboard

Incorrect AST when using "LIKE" expression

Open malolans-r7 opened this issue 7 years ago • 2 comments

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.

malolans-r7 avatar Jan 23 '18 21:01 malolans-r7

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"}}}]}]}

cisaacson avatar Apr 22 '18 10:04 cisaacson

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' } }

eatonphil avatar Jun 27 '18 15:06 eatonphil