Specification
$flags: [ 'CASE_INSENSITIVE', 'WORD_BOUNDARY', 'FLEXIBLE_WHITESPACE' ]
Keyword:
Select: 'select'
Distinct: 'distinct'
From: 'from'
JoinType: { Inner: 'inner', Left: 'left( outer)?', Right: 'right( outer)?', Full: 'full( outer)?' }
Join: 'join'
On: 'on'
Where: 'where'
As: 'as'
And: 'and'
Or: 'or'
In: 'in'
GroupBy: 'group by'
Having: 'having'
OrderBy: 'order by'
Limit: 'limit'
Direction: { Asc: 'asc', Desc: 'desc' }
Token:
String: [ '''.*?''', '".*?"' ]
Number: '\d+(\.\d+)?'
Identifier: '${!Keyword}[A-Za-z_][A-Za-z0-9_]*'
Wildcard: '\*'
ColumnRef:
$fields: { table: Identifier, column: Identifier }
$patterns: '#{table}\.#{column}'
FunctionCall:
$flags: OPTIONAL_WHITESPACE
$fields: { name: Identifier, arguments: 'Token[]' }
$patterns: '#{name}\(#{arguments}?\)'
AliasedExpression:
$fields: { value: Token, alias: Identifier }
$patterns: '#{value} ${Keyword::As} #{alias}'
Operator: '>=|<=|<>|!=|=|>|<'
Subquery: '\(${Query}\)'
Condition:
Comparison:
$fields: { left: Token, operator: Operator, right: Token }
$patterns: '#{left} #{operator} #{right}'
InPredicate:
$flags: OPTIONAL_WHITESPACE
$fields: { field: Token, values: 'Token[]' }
$patterns: '#{field} ${Keyword::In} \((#{values}(, #{values})*)\)'
And:
$fields: { conditions: 'Condition[]' }
$patterns: '#{conditions}( ${Keyword::And} #{conditions})+'
Or:
$fields: { conditions: 'Condition[]' }
$patterns: '#{conditions}( ${Keyword::Or} #{conditions})+'
Group:
$flags: OPTIONAL_WHITESPACE
$patterns: '\( ${Condition} \)'
Clause:
$types:
TableRef:
$fields: { name: Identifier, alias: Identifier }
$patterns: '#{name}( #{alias})?'
Select:
$flags: OPTIONAL_WHITESPACE
$fields: { selectItems: 'Token[]' }
$patterns: '${Keyword::Select} #{selectItems}(, #{selectItems})*'
From:
$fields: { table: TableRef }
$patterns: '${Keyword::From} #{table}'
Join:
$fields: { joinType: Keyword::JoinType, table: TableRef, condition: Condition }
$patterns: '(#{joinType} )?${Keyword::Join} #{table} ${Keyword::On} #{condition}'
Where:
$fields: { condition: Condition }
$patterns: '${Keyword::Where} #{condition}'
GroupBy:
$flags: OPTIONAL_WHITESPACE
$fields: { columns: 'Token[]' }
$patterns: '${Keyword::GroupBy} #{columns}(, #{columns})*'
Having:
$fields: { condition: Condition }
$patterns: '${Keyword::Having} #{condition}'
OrderBy:
$flags: OPTIONAL_WHITESPACE
$types:
SortItem:
$fields: { column: Token, direction: Keyword::Direction }
$patterns: '#{column} #{direction}'
$fields: { sortItems: 'SortItem[]' }
$patterns: '${Keyword::OrderBy} #{sortItems}(, #{sortItems})*'
Limit:
$fields: { count: Number }
$patterns: '${Keyword::Limit} #{count}'
Query:
$fields:
select: Clause::Select
from: Clause::From
join: Clause::Join
where: Clause::Where
groupBy: Clause::GroupBy
having: Clause::Having
orderBy: Clause::OrderBy
limit: Clause::Limit
$patterns:
- '#{select} #{from}( #{join})?( #{where})?( #{groupBy})?( #{having})?( #{orderBy})?( #{limit})?'Sample input
select * from projects;
SELECT customers.name, orders.order_date, orders.total_amount
FROM customers
INNER JOIN orders ON customers.id = orders.customer_id
WHERE orders.total_amount > 100
AND orders.status = 'Completed'
ORDER BY orders.order_date ASC
LIMIT 20;
select name, age, count(*) as total
from users
where age >= 18
group by name, age
having count(*) > 1
order by age desc
limit 10;
Select p.product_name, c.category_name, p.price
From products p
Join categories c On p.category_id = c.id
Where p.price > (Select avg(price) From products Where category_id = p.category_id)
And c.category_name In ('Books', 'Games')
Order By p.price Desc
Limit 15;
Result
[
{
"type": "Query",
"text": "select * from projects",
"start": 0,
"end": 22,
"fields": [
{
"type": "Clause::Select",
"field": "select",
"text": "select *",
"start": 0,
"end": 8,
"fields": [
{
"type": "Token[]",
"field": "selectItems",
"text": "*",
"start": 7,
"end": 8,
"items": [
{
"type": "Token::Wildcard",
"text": "*",
"start": 7,
"end": 8
}
]
}
]
},
{
"type": "Clause::From",
"field": "from",
"text": "from projects",
"start": 9,
"end": 22,
"fields": [
{
"type": "TableRef",
"field": "table",
"text": "projects",
"start": 14,
"end": 22,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "projects",
"start": 14,
"end": 22
}
]
}
]
}
]
},
{
"type": "Query",
"text": "SELECT customers.name, orders.order_date, orders.total_amount\nFROM customers\n INNER JOIN orders ON customers.id = orders.customer_id\nWHERE orders.total_amount > 100\n AND orders.status = 'Completed'\nORDER BY orders.order_date ASC\nLIMIT 20",
"start": 25,
"end": 264,
"fields": [
{
"type": "Clause::Select",
"field": "select",
"text": "SELECT customers.name, orders.order_date, orders.total_amount",
"start": 25,
"end": 86,
"fields": [
{
"type": "Token[]",
"field": "selectItems",
"text": "customers.name, orders.order_date, orders.total_amount",
"start": 32,
"end": 86,
"items": [
{
"type": "Token::ColumnRef",
"text": "customers.name",
"start": 32,
"end": 46,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "customers",
"start": 32,
"end": 41
},
{
"type": "Token::Identifier",
"field": "column",
"text": "name",
"start": 42,
"end": 46
}
]
},
{
"type": "Token::ColumnRef",
"text": "orders.order_date",
"start": 48,
"end": 65,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "orders",
"start": 48,
"end": 54
},
{
"type": "Token::Identifier",
"field": "column",
"text": "order_date",
"start": 55,
"end": 65
}
]
},
{
"type": "Token::ColumnRef",
"text": "orders.total_amount",
"start": 67,
"end": 86,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "orders",
"start": 67,
"end": 73
},
{
"type": "Token::Identifier",
"field": "column",
"text": "total_amount",
"start": 74,
"end": 86
}
]
}
]
}
]
},
{
"type": "Clause::From",
"field": "from",
"text": "FROM customers",
"start": 87,
"end": 101,
"fields": [
{
"type": "TableRef",
"field": "table",
"text": "customers",
"start": 92,
"end": 101,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "customers",
"start": 92,
"end": 101
}
]
}
]
},
{
"type": "Clause::Join",
"field": "join",
"text": "INNER JOIN orders ON customers.id = orders.customer_id",
"start": 104,
"end": 158,
"fields": [
{
"type": "Keyword::JoinType::Inner",
"field": "joinType",
"text": "INNER",
"start": 104,
"end": 109
},
{
"type": "TableRef",
"field": "table",
"text": "orders",
"start": 115,
"end": 121,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "orders",
"start": 115,
"end": 121
}
]
},
{
"type": "Condition::Comparison",
"field": "condition",
"text": "customers.id = orders.customer_id",
"start": 125,
"end": 158,
"fields": [
{
"type": "Token::ColumnRef",
"field": "left",
"text": "customers.id",
"start": 125,
"end": 137,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "customers",
"start": 125,
"end": 134
},
{
"type": "Token::Identifier",
"field": "column",
"text": "id",
"start": 135,
"end": 137
}
]
},
{
"type": "Token::Operator",
"field": "operator",
"text": "=",
"start": 138,
"end": 139
},
{
"type": "Token::ColumnRef",
"field": "right",
"text": "orders.customer_id",
"start": 140,
"end": 158,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "orders",
"start": 140,
"end": 146
},
{
"type": "Token::Identifier",
"field": "column",
"text": "customer_id",
"start": 147,
"end": 158
}
]
}
]
}
]
},
{
"type": "Clause::Where",
"field": "where",
"text": "WHERE orders.total_amount > 100\n AND orders.status = 'Completed'",
"start": 159,
"end": 224,
"fields": [
{
"type": "Condition::And",
"field": "condition",
"text": "orders.total_amount > 100\n AND orders.status = 'Completed'",
"start": 165,
"end": 224,
"fields": [
{
"type": "Condition[]",
"field": "conditions",
"text": "orders.total_amount > 100\n AND orders.status = 'Completed'",
"start": 165,
"end": 224,
"items": [
{
"type": "Condition::Comparison",
"text": "orders.total_amount > 100",
"start": 165,
"end": 190,
"fields": [
{
"type": "Token::ColumnRef",
"field": "left",
"text": "orders.total_amount",
"start": 165,
"end": 184,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "orders",
"start": 165,
"end": 171
},
{
"type": "Token::Identifier",
"field": "column",
"text": "total_amount",
"start": 172,
"end": 184
}
]
},
{
"type": "Token::Operator",
"field": "operator",
"text": ">",
"start": 185,
"end": 186
},
{
"type": "Token::Number",
"field": "right",
"text": "100",
"start": 187,
"end": 190
}
]
},
{
"type": "Condition::Comparison",
"text": "orders.status = 'Completed'",
"start": 197,
"end": 224,
"fields": [
{
"type": "Token::ColumnRef",
"field": "left",
"text": "orders.status",
"start": 197,
"end": 210,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "orders",
"start": 197,
"end": 203
},
{
"type": "Token::Identifier",
"field": "column",
"text": "status",
"start": 204,
"end": 210
}
]
},
{
"type": "Token::Operator",
"field": "operator",
"text": "=",
"start": 211,
"end": 212
},
{
"type": "Token::String",
"field": "right",
"text": "'Completed'",
"start": 213,
"end": 224
}
]
}
]
}
]
}
]
},
{
"type": "Clause::OrderBy",
"field": "orderBy",
"text": "ORDER BY orders.order_date ASC",
"start": 225,
"end": 255,
"fields": [
{
"type": "SortItem[]",
"field": "sortItems",
"text": "orders.order_date ASC",
"start": 234,
"end": 255,
"items": [
{
"type": "SortItem",
"text": "orders.order_date ASC",
"start": 234,
"end": 255,
"fields": [
{
"type": "Token::ColumnRef",
"field": "column",
"text": "orders.order_date",
"start": 234,
"end": 251,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "orders",
"start": 234,
"end": 240
},
{
"type": "Token::Identifier",
"field": "column",
"text": "order_date",
"start": 241,
"end": 251
}
]
},
{
"type": "Keyword::Direction::Asc",
"field": "direction",
"text": "ASC",
"start": 252,
"end": 255
}
]
}
]
}
]
},
{
"type": "Clause::Limit",
"field": "limit",
"text": "LIMIT 20",
"start": 256,
"end": 264,
"fields": [
{
"type": "Token::Number",
"field": "count",
"text": "20",
"start": 262,
"end": 264
}
]
}
]
},
{
"type": "Query",
"text": "select name, age, count(*) as total\nfrom users\nwhere age >= 18\ngroup by name, age\nhaving count(*) > 1\norder by age desc\nlimit 10",
"start": 267,
"end": 395,
"fields": [
{
"type": "Clause::Select",
"field": "select",
"text": "select name, age, count(*) as total",
"start": 267,
"end": 302,
"fields": [
{
"type": "Token[]",
"field": "selectItems",
"text": "name, age, count(*) as total",
"start": 274,
"end": 302,
"items": [
{
"type": "Token::Identifier",
"text": "name",
"start": 274,
"end": 278
},
{
"type": "Token::Identifier",
"text": "age",
"start": 280,
"end": 283
},
{
"type": "Token::AliasedExpression",
"text": "count(*) as total",
"start": 285,
"end": 302,
"fields": [
{
"type": "Token::FunctionCall",
"field": "value",
"text": "count(*)",
"start": 285,
"end": 293,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "count",
"start": 285,
"end": 290
},
{
"type": "Token[]",
"field": "arguments",
"text": "*",
"start": 291,
"end": 292,
"items": [
{
"type": "Token::Wildcard",
"text": "*",
"start": 291,
"end": 292
}
]
}
]
},
{
"type": "Token::Identifier",
"field": "alias",
"text": "total",
"start": 297,
"end": 302
}
]
}
]
}
]
},
{
"type": "Clause::From",
"field": "from",
"text": "from users",
"start": 303,
"end": 313,
"fields": [
{
"type": "TableRef",
"field": "table",
"text": "users",
"start": 308,
"end": 313,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "users",
"start": 308,
"end": 313
}
]
}
]
},
{
"type": "Clause::Where",
"field": "where",
"text": "where age >= 18",
"start": 314,
"end": 329,
"fields": [
{
"type": "Condition::Comparison",
"field": "condition",
"text": "age >= 18",
"start": 320,
"end": 329,
"fields": [
{
"type": "Token::Identifier",
"field": "left",
"text": "age",
"start": 320,
"end": 323
},
{
"type": "Token::Operator",
"field": "operator",
"text": ">=",
"start": 324,
"end": 326
},
{
"type": "Token::Number",
"field": "right",
"text": "18",
"start": 327,
"end": 329
}
]
}
]
},
{
"type": "Clause::GroupBy",
"field": "groupBy",
"text": "group by name, age",
"start": 330,
"end": 348,
"fields": [
{
"type": "Token[]",
"field": "columns",
"text": "name, age",
"start": 339,
"end": 348,
"items": [
{
"type": "Token::Identifier",
"text": "name",
"start": 339,
"end": 343
},
{
"type": "Token::Identifier",
"text": "age",
"start": 345,
"end": 348
}
]
}
]
},
{
"type": "Clause::Having",
"field": "having",
"text": "having count(*) > 1",
"start": 349,
"end": 368,
"fields": [
{
"type": "Condition::Comparison",
"field": "condition",
"text": "count(*) > 1",
"start": 356,
"end": 368,
"fields": [
{
"type": "Token::FunctionCall",
"field": "left",
"text": "count(*)",
"start": 356,
"end": 364,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "count",
"start": 356,
"end": 361
},
{
"type": "Token[]",
"field": "arguments",
"text": "*",
"start": 362,
"end": 363,
"items": [
{
"type": "Token::Wildcard",
"text": "*",
"start": 362,
"end": 363
}
]
}
]
},
{
"type": "Token::Operator",
"field": "operator",
"text": ">",
"start": 365,
"end": 366
},
{
"type": "Token::Number",
"field": "right",
"text": "1",
"start": 367,
"end": 368
}
]
}
]
},
{
"type": "Clause::OrderBy",
"field": "orderBy",
"text": "order by age desc",
"start": 369,
"end": 386,
"fields": [
{
"type": "SortItem[]",
"field": "sortItems",
"text": "age desc",
"start": 378,
"end": 386,
"items": [
{
"type": "SortItem",
"text": "age desc",
"start": 378,
"end": 386,
"fields": [
{
"type": "Token::Identifier",
"field": "column",
"text": "age",
"start": 378,
"end": 381
},
{
"type": "Keyword::Direction::Desc",
"field": "direction",
"text": "desc",
"start": 382,
"end": 386
}
]
}
]
}
]
},
{
"type": "Clause::Limit",
"field": "limit",
"text": "limit 10",
"start": 387,
"end": 395,
"fields": [
{
"type": "Token::Number",
"field": "count",
"text": "10",
"start": 393,
"end": 395
}
]
}
]
},
{
"type": "Query",
"text": "Select p.product_name, c.category_name, p.price\nFrom products p\n Join categories c On p.category_id = c.id\nWhere p.price > (Select avg(price) From products Where category_id = p.category_id)\n And c.category_name In ('Books', 'Games')\nOrder By p.price Desc\nLimit 15",
"start": 398,
"end": 664,
"fields": [
{
"type": "Clause::Select",
"field": "select",
"text": "Select p.product_name, c.category_name, p.price",
"start": 398,
"end": 445,
"fields": [
{
"type": "Token[]",
"field": "selectItems",
"text": "p.product_name, c.category_name, p.price",
"start": 405,
"end": 445,
"items": [
{
"type": "Token::ColumnRef",
"text": "p.product_name",
"start": 405,
"end": 419,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "p",
"start": 405,
"end": 406
},
{
"type": "Token::Identifier",
"field": "column",
"text": "product_name",
"start": 407,
"end": 419
}
]
},
{
"type": "Token::ColumnRef",
"text": "c.category_name",
"start": 421,
"end": 436,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "c",
"start": 421,
"end": 422
},
{
"type": "Token::Identifier",
"field": "column",
"text": "category_name",
"start": 423,
"end": 436
}
]
},
{
"type": "Token::ColumnRef",
"text": "p.price",
"start": 438,
"end": 445,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "p",
"start": 438,
"end": 439
},
{
"type": "Token::Identifier",
"field": "column",
"text": "price",
"start": 440,
"end": 445
}
]
}
]
}
]
},
{
"type": "Clause::From",
"field": "from",
"text": "From products p",
"start": 446,
"end": 461,
"fields": [
{
"type": "TableRef",
"field": "table",
"text": "products p",
"start": 451,
"end": 461,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "products",
"start": 451,
"end": 459
},
{
"type": "Token::Identifier",
"field": "alias",
"text": "p",
"start": 460,
"end": 461
}
]
}
]
},
{
"type": "Clause::Join",
"field": "join",
"text": "Join categories c On p.category_id = c.id",
"start": 464,
"end": 505,
"fields": [
{
"type": "TableRef",
"field": "table",
"text": "categories c",
"start": 469,
"end": 481,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "categories",
"start": 469,
"end": 479
},
{
"type": "Token::Identifier",
"field": "alias",
"text": "c",
"start": 480,
"end": 481
}
]
},
{
"type": "Condition::Comparison",
"field": "condition",
"text": "p.category_id = c.id",
"start": 485,
"end": 505,
"fields": [
{
"type": "Token::ColumnRef",
"field": "left",
"text": "p.category_id",
"start": 485,
"end": 498,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "p",
"start": 485,
"end": 486
},
{
"type": "Token::Identifier",
"field": "column",
"text": "category_id",
"start": 487,
"end": 498
}
]
},
{
"type": "Token::Operator",
"field": "operator",
"text": "=",
"start": 499,
"end": 500
},
{
"type": "Token::ColumnRef",
"field": "right",
"text": "c.id",
"start": 501,
"end": 505,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "c",
"start": 501,
"end": 502
},
{
"type": "Token::Identifier",
"field": "column",
"text": "id",
"start": 503,
"end": 505
}
]
}
]
}
]
},
{
"type": "Clause::Where",
"field": "where",
"text": "Where p.price > (Select avg(price) From products Where category_id = p.category_id)\n And c.category_name In ('Books', 'Games')",
"start": 506,
"end": 633,
"fields": [
{
"type": "Condition::And",
"field": "condition",
"text": "p.price > (Select avg(price) From products Where category_id = p.category_id)\n And c.category_name In ('Books', 'Games')",
"start": 512,
"end": 633,
"fields": [
{
"type": "Condition[]",
"field": "conditions",
"text": "p.price > (Select avg(price) From products Where category_id = p.category_id)\n And c.category_name In ('Books', 'Games')",
"start": 512,
"end": 633,
"items": [
{
"type": "Condition::Comparison",
"text": "p.price > (Select avg(price) From products Where category_id = p.category_id)",
"start": 512,
"end": 589,
"fields": [
{
"type": "Token::ColumnRef",
"field": "left",
"text": "p.price",
"start": 512,
"end": 519,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "p",
"start": 512,
"end": 513
},
{
"type": "Token::Identifier",
"field": "column",
"text": "price",
"start": 514,
"end": 519
}
]
},
{
"type": "Token::Operator",
"field": "operator",
"text": ">",
"start": 520,
"end": 521
},
{
"type": "Token::Subquery",
"field": "right",
"text": "(Select avg(price) From products Where category_id = p.category_id)",
"start": 522,
"end": 589,
"fields": [
{
"type": "Clause::Select",
"field": "select",
"text": "Select avg(price)",
"start": 523,
"end": 540,
"fields": [
{
"type": "Token[]",
"field": "selectItems",
"text": "avg(price)",
"start": 530,
"end": 540,
"items": [
{
"type": "Token::FunctionCall",
"text": "avg(price)",
"start": 530,
"end": 540,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "avg",
"start": 530,
"end": 533
},
{
"type": "Token[]",
"field": "arguments",
"text": "price",
"start": 534,
"end": 539,
"items": [
{
"type": "Token::Identifier",
"text": "price",
"start": 534,
"end": 539
}
]
}
]
}
]
}
]
},
{
"type": "Clause::From",
"field": "from",
"text": "From products",
"start": 541,
"end": 554,
"fields": [
{
"type": "TableRef",
"field": "table",
"text": "products",
"start": 546,
"end": 554,
"fields": [
{
"type": "Token::Identifier",
"field": "name",
"text": "products",
"start": 546,
"end": 554
}
]
}
]
},
{
"type": "Clause::Where",
"field": "where",
"text": "Where category_id = p.category_id",
"start": 555,
"end": 588,
"fields": [
{
"type": "Condition::Comparison",
"field": "condition",
"text": "category_id = p.category_id",
"start": 561,
"end": 588,
"fields": [
{
"type": "Token::Identifier",
"field": "left",
"text": "category_id",
"start": 561,
"end": 572
},
{
"type": "Token::Operator",
"field": "operator",
"text": "=",
"start": 573,
"end": 574
},
{
"type": "Token::ColumnRef",
"field": "right",
"text": "p.category_id",
"start": 575,
"end": 588,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "p",
"start": 575,
"end": 576
},
{
"type": "Token::Identifier",
"field": "column",
"text": "category_id",
"start": 577,
"end": 588
}
]
}
]
}
]
}
]
}
]
},
{
"type": "Condition::InPredicate",
"text": "c.category_name In ('Books', 'Games')",
"start": 596,
"end": 633,
"fields": [
{
"type": "Token::ColumnRef",
"field": "field",
"text": "c.category_name",
"start": 596,
"end": 611,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "c",
"start": 596,
"end": 597
},
{
"type": "Token::Identifier",
"field": "column",
"text": "category_name",
"start": 598,
"end": 611
}
]
},
{
"type": "Token[]",
"field": "values",
"text": "'Books', 'Games'",
"start": 616,
"end": 632,
"items": [
{
"type": "Token::String",
"text": "'Books'",
"start": 616,
"end": 623
},
{
"type": "Token::String",
"text": "'Games'",
"start": 625,
"end": 632
}
]
}
]
}
]
}
]
}
]
},
{
"type": "Clause::OrderBy",
"field": "orderBy",
"text": "Order By p.price Desc",
"start": 634,
"end": 655,
"fields": [
{
"type": "SortItem[]",
"field": "sortItems",
"text": "p.price Desc",
"start": 643,
"end": 655,
"items": [
{
"type": "SortItem",
"text": "p.price Desc",
"start": 643,
"end": 655,
"fields": [
{
"type": "Token::ColumnRef",
"field": "column",
"text": "p.price",
"start": 643,
"end": 650,
"fields": [
{
"type": "Token::Identifier",
"field": "table",
"text": "p",
"start": 643,
"end": 644
},
{
"type": "Token::Identifier",
"field": "column",
"text": "price",
"start": 645,
"end": 650
}
]
},
{
"type": "Keyword::Direction::Desc",
"field": "direction",
"text": "Desc",
"start": 651,
"end": 655
}
]
}
]
}
]
},
{
"type": "Clause::Limit",
"field": "limit",
"text": "Limit 15",
"start": 656,
"end": 664,
"fields": [
{
"type": "Token::Number",
"field": "count",
"text": "15",
"start": 662,
"end": 664
}
]
}
]
}
]Notes
# SQL Query
Decompose a small SQL-like query subset into clauses, expressions, tables, and conditions.
## Notice
- SQL keywords are semantic keyword types.
- `Token` covers columns, literals, function calls, aliases, lists, and subqueries.
- `Condition` composes comparisons with `AND` and `OR`.
- `Clause` models select/from/join/where/group/having/order/limit parts.
- `OrderBy` supports multiple sort items.
- `Query` combines clauses into a full statement.
Try changing a column to `sum(orders.total)`.