Open11

DuckDBのパーザ

ktz_aliasktz_alias

duckdb::Parser::ToString()メソッド

パース結果の構文木をSQL文字列に変換する。

変換の際に、入力したSQLとは改変されて出力されることに注意。

  • キーワード
    • selectとかfromとかのキーワードは大文字にされるっぽい
  • where句の条件
    • x = 1 -> (x = 1)のように各条件がカッコで括られる
  • 型キャスト
    • xxx::yyyのような型キャストは、CAST(xxx AS yyy)に変換される
  • 型名
  • case式
    • (CASE WHEN ... THEN ... ELSE ... END)と書いた場合、外側の括弧は取り除かれる
      • CASEWHENの間の空白が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 fSELECT * FROM foo AS f
  • ture/falseはENUM型't'と'f'に型キャスト付きで変換された
    • true -> `CAST('t' AS BOOLEAN)
    • false -> `CAST('f' AS BOOLEAN)
  • JOININNERが補われて出力された。
    • JOIN bar ON ...INNER JOIN bar ON ...
  • LATERAL JOINは、ここに書くには余白が狭すぎるので別コメントとして記載した。
ktz_aliasktz_alias

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が子要素となる
  • 型キャストは、duckdb::CastExpressionの子要素に、対象の値のParsedExpressionが来る構造。わかりやすい。

DESCRIBE / SUMMARIZE文

公式ドキュメントにはステートメントとして記載されているが、その実体はSELECT文の亜種。

  • DESCRIBE / SUMMARIZESELECT
  • 後に続くテーブル名やクエリが、列リスト

な扱い。

duckdb::TableRefType::ShowRefduckdb::TableRef
実体は、duckdb::ShowRef

  • DESCRIBE <TABLE>の場合
    • table_nameフィールドに名前が入る
    • queryフィールドはリソース所有なしのunique_ptr
  • DESCRIBE <QUERY>の場合
    • table_nameフィールドは空文字列
    • queryフィールドは有効なduckdb::QueryNodeunique_ptr
      • duckdb::ShowRefを保持するduckdb::SelectNodeselect_listフィールドの列数は1を返す

DESCRIBEの場合、duckdb::ShowRefshow_typeフィールドに、duckdb::ShowType::DESCRIBEが入る。
SUMMARIZEの場合はduckdb::ShowType::SUMMARIZE

ktz_aliasktz_alias

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_aliasktz_alias

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_aliasktz_alias

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_aliasktz_alias

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で相関サブクエリをそのままジョインしてる!?

試しに変換後のSQLduckdb_cliに食わせたら問題なく実行された。
まじかよ・・・。

ドキュメントにも単なる目印だって記載されてた

追記:

Note
DuckDB detects when LATERAL joins should be used, making the use of the LATERAL keyword optional.

https://duckdb.org/docs/sql/expressions/subqueries

ktz_aliasktz_alias

intervalのプレースホルダ

intervalの期間をプレースホルダにする場合、カッコでくくる必要がある。

// 以下はSYNTAX ERROE
select current_timestamp + interval $days days as d
// 以下のようにすると通る
select current_timestamp + interval ($days) days as d