DuckDBのパーザ
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
DuckDBのメインレポジトリ
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
パーザクラス
duckdb::Parser::parse()メソッド呼ぶとSQLのパースが実行され、duckdb::Parser::statementsに格納される。
- 複数のクエリを一度にパースできるため、コレクションとなっているように見える。
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
duckdb::Parser::ToString()メソッド
パース結果の構文木をSQL文字列に変換する。
変換の際に、入力したSQLとは改変されて出力されることに注意。
- キーワード
-
select
とかfrom
とかのキーワードは大文字にされるっぽい
-
-
where
句の条件-
x = 1
->(x = 1)
のように各条件がカッコで括られる
-
- 型キャスト
-
xxx::yyy
のような型キャストは、CAST(xxx AS yyy)
に変換される
-
- 型名
- 別名(alias)はnameに変換される (加えて小文字は大文字に)
- e.g.
int
->INTRGER
- e.g.
- https://duckdb.org/docs/sql/data_types/overview#general-purpose-data-types
- 別名(alias)はnameに変換される (加えて小文字は大文字に)
-
case式
-
(CASE WHEN ... THEN ... ELSE ... END)
と書いた場合、外側の括弧は取り除かれる-
CASE
とWHEN
の間の空白が2になった。- これは、
CASE <条件> WHEN
の可能性も考慮してのことと思われる。
- これは、
-
-
WHEN
パートにx = 1
と書いた場合、((x = 1))
となった。-
WHEN
パートで1つ、条件式で1つ -
AND
で繋ぐとさらにカッコが一つ増える-
f = 1 and g = 2 and h = 3
が`(((f = 1) AND (g = 2) AND (h = 3)))
-
-
-
THEN
パートもTHEN x
と書いた場合に、THEN (x)
とされた。 - 一方、`ELSE`パートはそのまま出力された。
-
ELSE
パートを省略した場合、ELSE NULL
で補われて出力された。 -
CASE f WHEN 1 THEN ... 2 THEN ... ELSE ... END
と書いた場合、CASE WHEN ((f = 1))
のスタイルに変更された。驚いた。
-
-
selectless EXISTS
(EXISTS (FROM foo ...)
なやつ)の場合、
SELECT *`が補われて出力された。 - 表参照のエイリアスは
AS
が補われて出力された。-
SELECT * FROM foo f
がSELECT * FROM foo AS f
に
-
- ture/falseはENUM型't'と'f'に型キャスト付きで変換された
-
true
-> `CAST('t' AS BOOLEAN) -
false
-> `CAST('f' AS BOOLEAN)
-
-
JOIN
はINNER
が補われて出力された。-
JOIN bar ON ...
がINNER JOIN bar ON ...
に
-
-
LATERAL JOIN
は、ここに書くには余白が狭すぎるので別コメントとして記載した。
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
Statementの構造
- トップレベルは
duckdb::SQLStatement
- SQLStatementのtypeフィールドを見ることで種別 (duckdb::StatementType)がわかる
- Cast()メソッドを使って種別に該当する型に変換する
- 例えば、SELECT文なら、
duckdb::SelectStatement
auto& sel_stmt = stmt->Cast<duckdb::SelectStatement>()
- 例えば、SELECT文なら、
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
SELECT文
- 種別 duckdb::StatementType == SELECT_STATEMENT
- 型 duckdb::SelectStatement
SelectStatementはduckdb::QueryNode型のnodeフィールドを持つ
SELECTノード
QueryNodeのtypeフィールド(duckdb::QueryNodeType)を見ると、純粋なSELECT文なのかCTEなのかとかがわかる
同様にCast()メソッドで、QueryNodeTypeに該当する型にキャストする
例えば
純粋なSELECT文は、duckdb::SelectNode
SelectNodeはフィールドとして、select_list
, from_table
, where_clause
などを持つ
詳細は、https://github.com/duckdb/duckdb/tree/main/src/include/duckdb/parser/query_node/select_node.hpp
- select_listはParsedExpressionのvector (std::vectorを継承したduckdb::vector)
- where_clauseもParsedExpressionのvector
ParsedExpression
SELECT文の大半の構成要素は、このParsedExpressionとなる
SELECT句しかり、関数名、関数の引数、WHERE区の比較演算、比較演算の左辺値・右辺値
具象型は同様にCast()メソ(ry
- IN句 (NOT IN句)の子要素は少し特殊で、IN句の右辺値が展開されて格納される
- v in (1, 2, 3)の場合、
v
,1
,2
,3
が子要素となる
- v in (1, 2, 3)の場合、
- 型キャストは、duckdb::CastExpressionの子要素に、対象の値のParsedExpressionが来る構造。わかりやすい。
DESCRIBE / SUMMARIZE文
公式ドキュメントにはステートメントとして記載されているが、その実体はSELECT文の亜種。
-
DESCRIBE / SUMMARIZE
がSELECT
句 - 後に続くテーブル名やクエリが、列リスト
な扱い。
duckdb::TableRefType::ShowRef
なduckdb::TableRef
。
実体は、duckdb::ShowRef
。
-
DESCRIBE <TABLE>
の場合-
table_name
フィールドに名前が入る -
query
フィールドはリソース所有なしのunique_ptr
-
-
DESCRIBE <QUERY>
の場合-
table_name
フィールドは空文字列 -
query
フィールドは有効なduckdb::QueryNode
のunique_ptr
-
duckdb::ShowRef
を保持するduckdb::SelectNode
のselect_list
フィールドの列数は1を返す
-
-
DESCRIBE
の場合、duckdb::ShowRef
のshow_type
フィールドに、duckdb::ShowType::DESCRIBE
が入る。
SUMMARIZE
の場合はduckdb::ShowType::SUMMARIZE
。
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
Statement一覧
- 各型は、
duckdb/parser/statement/list.hpp
で一括インクルード可能
duckdb::StatementType | type (duckdb::xxxx) | path |
---|---|---|
SELECT_STATEMENT | SelectStatement | duckdb/parser/statement/select_statement.hpp |
INSERT_STATEMENT | InsertStatement | iduckdb/parser/statement/nsert_statement.hpp |
UPDATE_STATEMENT | UpdateStatement | duckdb/parser/statement/update_statement.hpp |
CREATE_STATEMENT | CreateStatement | duckdb/parser/statement/create_statement.hpp |
DELETE_STATEMENT | DeleteStatement | duckdb/parser/statement/delete_statement.hpp |
PREPARE_STATEMENT | PrepareStatement | duckdb/parser/statement/prepare_statement.hpp |
EXECUTE_STATEMENT | ExecuteStatement | duckdb/parser/statement/execute_statement.hpp |
ALTER_STATEMENT | AlterStatement | duckdb/parser/statement/alter_statement.hpp |
TRANSACTION_STATEMENT | TransactionStatement | duckdb/parser/statement/transaction_statement.hpp |
COPY_STATEMENT | CopyStatement | duckdb/parser/statement/copy_statement.hpp |
ANALYZE_STATEMENT | ||
VARIABLE_SET_STATEMENT | ||
CREATE_FUNC_STATEMENT | ||
EXPLAIN_STATEMENT | ExplainStatement | duckdb/parser/statement/ |
DROP_STATEMENT | DropStatement | duckdb/parser/statement/drop_statement.hpp |
EXPORT_STATEMENT | ExportStatement | duckdb/parser/statement/export_statement.hpp |
PRAGMA_STATEMENT | PragmaStatement | duckdb/parser/statement/pragma_statement.hpp |
VACUUM_STATEMENT | VacuumStatement | duckdb/parser/statement/vacuum_statement.hpp |
CALL_STATEMENT | CallStatement | duckdb/parser/statement/call_statement.hpp |
SET_STATEMENT | SetStatement | duckdb/parser/statement/set_statement.hpp |
LOAD_STATEMENT | LoadStatement | duckdb/parser/statement/load_statement.hpp |
RELATION_STATEMENT | RelationStatement | duckdb/parser/statement/relation_statement.hpp |
EXTENSION_STATEMENT | ExtensionStatement | duckdb/parser/statement/extension_statement.hpp |
LOGICAL_PLAN_STATEMENT | LogicalPlanStatement | duckdb/parser/statement/logical_plan_statement.hpp |
ATTACH_STATEMENT | AttachStatement | duckdb/parser/statement/attach_statement.hpp |
DETACH_STATEMENT | DetachStatement | duckdb/parser/statement/detach_statement.hpp |
MULTI_STATEMENT | MultiStatement | duckdb/parser/statement/multi_statement.hpp |
COPY_DATABASE_STATEMENT | CopyDatabaseStatement | duckdb/parser/statement/copy_database_statement.hpp |
UPDATE_EXTENSIONS_STATEMENT | UpdateExtensionsStatement | duckdb/parser/statement/update_extensions_statement.hpp |
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
SelectStatementのルートノード一覧
-
duckdb/parser/query_node/list.hpp
で一括インクルードできる
duckdb::QueryNodeType | type | path |
---|---|---|
SELECT_NODE | SelectNode | duckdb/parser/query_node/select_node.hpp |
SET_OPERATION_NODE | SetOperationNode | duckdb/parser/query_node/set_operation_node.hpp |
BOUND_SUBQUERY_NODE | ||
RECURSIVE_CTE_NODE | RecursiveCTENode | duckdb/parser/query_node/recursive_cte_node.hpp |
CTE_NODE | CTENode | duckdb/parser/query_node/cte_node.hpp |
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
Expression一覧
-
duckdb/parser/expression/list.hpp
で一括インクルードできる
duckdb::ExpressionClass | type | path | Remarks |
---|---|---|---|
AGGREGATE | |||
CASE | CaseExpression | duckdb/parser/expression/case_expression.hpp | |
CAST | CastExpression | duckdb/parser/expression/cast_expression.hpp | 型キャスト |
COLUMN_REF | ColumnRefExpression | duckdb/parser/expression/columnref_expression.hpp | リレーションの列名 |
COMPARISON | ComparisonExpression | duckdb/parser/expression/comparison_expression.hpp | 比較演算 |
CONJUNCTION | ConjunctionExpression | duckdb/parser/expression/conjunction_expression.hpp | AND・ORの論理演算 |
CONSTANT | ConstantExpression | duckdb/parser/expression/constant_expression.hpp | |
DEFAULT | DefaultExpression | duckdb/parser/expression/default_expression.hpp | |
FUNCTION | FunctionExpression | duckdb/parser/expression/function_expression.hpp | 関数呼び出し |
OPERATOR | OperatorExpression | duckdb/parser/expression/operator_expression.hpp | INとか |
STAR | StarExpression | duckdb/parser/expression/star_expression.hpp | |
SUBQUERY | SubqueryExpression | duckdb/parser/expression/subquery_expression.hpp | |
WINDOW | WindowBoundary | duckdb/parser/expression/window_expression.hpp | |
PARAMETER | PreparedParamType | duckdb/parser/expression/parameter_expression.hpp | プレースホルダ |
COLLATE | CollateExpression | duckdb/parser/expression/collate_expression.hpp | |
LAMBDA | LambdaExpression | duckdb/parser/expression/lambda_expression.hpp | |
POSITIONAL_REFERENCE | PositionalReferenceExpression | duckdb/parser/expression/positional_reference_expression.hpp | |
BETWEEN | BetweenExpression | duckdb/parser/expression/between_expression.hpp | |
LAMBDA_REF | LambdaRefExpression | duckdb/parser/expression/lambdaref_expression.hpp |
- サブクエリの式表現である、
Sduckdb::ubqueryExpression
は少し注意が必要- ソースのコメントにも書かれている通り、childフィールドは、比較演算の右辺としてサブクエリがくる場合にそのExpressionがくる。
-
EXISTS
やスカラサブクエリの場合は、unique_ptr
が無効(null
を指す)。 - どのようなサブクエリかは、
subquery_type
フィールドで判断できる。
BOUND Expression
-
BOUNDED_XXXX
は、中間的なダミー型とのこと- より正確にはクエリプラン内で保持される状態の型
- duckdb/parser/expression/bound_expression.hpp
duckdb::ExpressionClass |
---|
BOUND_AGGREGATE |
BOUND_CASE |
BOUND_CAST |
BOUND_COLUMN_REF |
BOUND_COMPARISON |
BOUND_CONJUNCTION |
BOUND_CONSTANT |
BOUND_DEFAULT |
BOUND_FUNCTION |
BOUND_OPERATOR |
BOUND_PARAMETER |
BOUND_REF |
BOUND_SUBQUERY |
BOUND_WINDOW |
BOUND_BETWEEN |
BOUND_UNNEST |
BOUND_LAMBDA |
BOUND_LAMBDA_REF |
BOUND_EXPRESSION |
BOUND_EXPANDED |
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
LATERAL JOINの真実
LATERAL JOIN
を含むSQL
をパース後、ToString()で文字列に戻したら以下のようになってた。
- パース前
select foo.*, b.a
from foo
join lateral (
select bar.a
from bar
where
bar.f = foo.f
and bar.g = $v::int
) b on true
パース後のToString()
SELECT foo.*, b.a
FROM foo
INNER JOIN (
SELECT bar.a
FROM bar
WHERE (
(bar.f = foo.f)
AND (bar.g = CAST($1 AS INTEGER))
)
) b ON (CAST('t' AS BOOLEAN))
INNER JOIN
で相関サブクエリをそのままジョインしてる!?
試しに変換後のSQL
をduckdb_cli
に食わせたら問題なく実行された。
まじかよ・・・。
ドキュメントにも単なる目印だって記載されてた
追記:
Note
DuckDB detects when LATERAL joins should be used, making the use of the LATERAL keyword optional.
![ktz_alias](https://zenn-dev.github.io/default-avatars/dark/k.png)
Syntax errorで送出される例外
duckdb::ParserException
- https://github.com/duckdb/duckdb/blob/main/src/common/exception/parser_exception.hpp
- エラーモデルは、ランタイムエラー (std::runtime_error を継承)