FORMAT Native のトラフィックのみです。具体的には、query、query_np、query_df、およびそれらの block・row ストリーミング版、さらに insert_df を含む insert が対象です。Arrow の methods は FORMAT Arrow を使用するため影響を受けません。raw クエリ、raw insert、Native 以外のフォーマットも同様です。
この codec は experimental であり、オプトイン方式です。デフォルトは引き続き Python codec です。
インストール
コンパイル済みコーデックは、clickhouse-connect-core という独立した wheel として提供されており、_ch_core 拡張モジュールが含まれています。アプリケーションで使用するインテグレーションとあわせて、コーデックをインストールしてください。
query_np、query_df、それらのストリーミング用バリアント、および query(..., use_numpy=True) が含まれます。NumPy のクエリに Pandas は不要です。非同期クライアントを使用する場合は、async extra を追加してください。
Arrow のメソッドおよび Arrow バックエンドの Pandas ストレージを使用する場合は、引き続き PyArrow が必要です。これらの機能を利用するには、arrow extra をインストールしてください:
mode.string_storage オプションに従います。PyArrow がインストールされている場合、文字列はどちらのストレージバックエンドでも、完全な UTF-8 検証を伴う既存の Arrow 変換を使用します。PyArrow がない場合、ドライバーは Rust のオブジェクト変換を使用し、Pandas のデフォルトストレージでは Python の文字列が使用されます。PyArrow がない状態で Arrow 文字列ストレージを明示的に指定すると、依存関係エラーが送出されます。不正な UTF-8 は、どちらのストレージバックエンドでも従来どおり 16 進表記で出力されます。
このコーデック、そのパッケージング、および依存関係のセットは実験的なものです。直接バッファ変換は現在、非 Nullable の 8〜64 ビット整数、Float32/64、Boolean、BFloat16、Interval、Date、Date32、DateTime、DateTime64 のカラムに対応しています。スカラーの Time および Time64 カラム (Nullable 値を含む) も直接バッファを使用します。Nullable の BFloat16 や、拡張 Pandas の Nullable な整数、浮動小数点数、Interval の出力も同様です。BFloat16 は float32 出力と、既存の Pandas Float32 の欠損値ポリシーを維持します。Interval の値は、引き続き宣言された単位での符号付きカウントとなります。時間型の変換では、宣言された単位と既存のタイムゾーン規則が維持されます。Time64 の Nullable な SimpleAggregateFunction 別名は、正しい期間を返し、NULL 値に対しては NaT を返すようになりました。Nullable なスカラー DateTime64(9) カラムも、NumPy および Pandas の出力でナノ秒を保持するためにバッファを使用します。その他の Nullable な日付およびタイムスタンプは、従来のオブジェクト変換パスを引き続き使用します。
Time/Time64 の Array (ネストされた Array や Nullable なリーフを含む) 、および LowCardinality(Time) (Nullable 値を含む) も直接バッファを使用します。ネスト構造、期間の単位、NULL の表現は変わりません。LowCardinality(Time64) は、引き続き Rust コアではサポートされていません。
Nullable なスカラー DateTime64(9) については、query_df と query_df_stream は、SimpleAggregateFunction 別名を経由する場合も含め、小数点以下の桁数 9 桁をすべて保持します。Pandas 3 では、デフォルト出力の datetime カラムはマイクロ秒ではなくナノ秒単位を使用するようになりました。すべてが NULL の Block に対する既存の object dtype 推論は変わりません。名前付きタイムゾーンによって値が Pandas のナノ秒範囲外になる場合、そのカラムでは従来のオブジェクト変換と範囲の動作が維持されます。
これらのカラムについて、query_np と query_np_stream のオブジェクトフィールドには、Python の datetime セルではなく numpy.datetime64 セルが格納されるようになり、SQL の NULL には None が入ります。名前付きタイムゾーンの値は、Python codec と同様に、タイムゾーンのメタデータを持たない UTC ティックを使用します。これにより、Pandas がなくてもナノ秒が保持されます。タイムゾーン対応のタイムスタンプが必要な場合は query_df を使用してください。その他の Nullable な時間型の NumPy セルの型は変わりません。
スカラー DateTime64 の NumPy および Pandas クエリでは、Nullable カラムや SimpleAggregateFunction 別名を含め、0、3、6、9 以外の精度は ProgrammingError で拒否されます。
Rust codec が選択されているにもかかわらずコンパイル済みモジュールがインストールされていない場合、クライアントの作成時に、このインストールコマンドを示す NotSupportedError が送出されます。
codec の有効化
native_codec client オプションで codec を選択します:
デフォルト値は、
native_codec 共通設定または CLICKHOUSE_CONNECT_NATIVE_CODEC 環境変数であらかじめ指定することもできます。優先順位は、client の keyword argument、共通設定、環境変数の順です。
このオプションは interface="chdb" の clients では無視され、常に Python codec が使用されます。
Rust コーデック を使うべき場面
Rust コーデック は、String、LowCardinality、Map、Array、JSON、Decimal、UUID といったテキスト型・コンテナー型・複合型を含む大規模な DataFrame の結果で最も効果を発揮します。また、大規模な行BlockおよびカラムBlockのストリーム、同時実行のクエリ workload、bulk 挿入 でも効果があります。
小規模な結果やネットワーク律速のクエリでは、ほとんど変化が見られないことがあります。フラットな数値のみの結果は Python コーデック でもすでに効率的なバルク処理パスを利用しているため、同様に効果は小さくなる傾向があります。
直接バッファを使うと、サポート対象の型では中間的な Arrow 配列を経由せずに済みます。ただし、NumPy や Pandas の結果を最終的に組み立てる段階では、メモリ確保やコピーが発生する場合があります。そのため、すべてのクエリ結果がゼロコピーになるわけではありません。
PyArrow がない場合、Rust の文字列変換では、デコード済みの各Blockから Python の文字列オブジェクトを構築します。デフォルトのBlockサイズであれば、大規模な String の結果に対するピークメモリは Python コーデック とほぼ同等です。一方、1 つのBlockに 100 万行を保持するような非常に大きなBlockでは、文字列の構築中もデコード済みのBlock全体がメモリ上に残るため、Python コーデック よりもピークメモリが増えることがあります。大規模な Pandas の StringDtype の結果を扱う場合は、arrow extra をインストールし、Arrow の文字列ストレージを使用してください。Pandas 3 では、PyArrow がインストールされていればデフォルトで Arrow ストレージが選択されます。Pandas 2 では pd.options.mode.string_storage = "pyarrow" を指定する必要があります。
非常に横に広い結果や全カラムが数値の結果に対するバッファリングされた query() の呼び出しは、現時点では Rust コーデック の方が遅く、ピークメモリも多くなる場合があります。そうした workload では query_df、query_row_block_stream、query_column_block_stream の利用を推奨します。NumPy や Pandas の場合は、query_np_stream や query_df_stream を使ってピークメモリを抑えることもできます。ストリーム全体をまとめて収集するのではなく、Blockごとに処理して解放してください。ただし、メモリ使用量は各Blockのサイズに左右されるため、単一の大きなBlockをストリーミングしても同じ効果は得られません。
コーデック を採用する前に、ご自身の workload でベンチマークを実施してください。測定時には native_codec="rust_strict" を指定してください。そうすることで、未サポートのオプションや依存関係の欠落があった場合に、クエリが黙って Python へ回されるのではなく例外が送出されます。
フォールバックルール
フォールバックの判断はバイトの読み取りや送信が始まる前に行われるため、ストリームの途中でcodecが切り替わることはありません。クエリの場合、この選択はresponse bodyが消費される前に行われます。insertの場合、すべてのcolumn typeがサポートされているときにのみRustエンコーダーが選択され、そうでなければinsert全体がPython codecで実行されます。naive_datetime_insert="server" が有効な場合、rust は DateTime または DateTime64 のcolumnを含むinsertをPython codecへルーティングし、宣言されたcolumnのtimezoneまたはserver timezoneが適用されるようにします。rust_strict はこの組み合わせを拒否します。デフォルトの naive_datetime_insert="local" モードでは、引き続きRustエンコーダーが使用されます。
SQLAlchemyのdialect reflection文を含むドライバー内部のメタデータクエリは、どのモードでも常に、通知なくPython codecを使用します。
Rust codecが検出した不正な形式のNativeペイロードは DataError を発生させます。
Versioning
clickhouse-connect-core は clickhouse-connect とは独立してバージョン管理されます。rust extra には clickhouse-connect-core>=0.2.1,<0.3 が必要です。Rust コーデック が選択されると、ドライバーはバインディング API のバージョンとカラムバッファ機能の両方を検証します。公開済みの core 0.2.0 の wheel はカラムバッファ機能を備えていません。インストールされている wheel に互換性がない場合、rust と rust_strict のどちらでも Client の作成時に NotSupportedError が送出され、次のアップグレード用のコマンドが示されます:
clickhouse-connect-core のリリースとして提供されるため、互換性のある core wheel へのアップグレードで取り込めます。ドライバーの Rust インテグレーションに関する変更を取り込むには、clickhouse-connect のアップグレードが必要です。
既知の動作の相違点
Rust コーデック は、Python コーデック とのセル単位での完全な一致を目指しています。既知の相違点は以下のとおりです。Variantカラムに対するquery_npおよびquery_dfの結果には、numpy のスカラー値ではなくプレーンな Python オブジェクトが含まれます。値は同一ですが、セルの型が異なります。Time64を含むDynamicの値は、Rust のquery_npおよびquery_dfの結果ではdatetime.timedeltaとしてマテリアライズされます。スケール 0、3、6、9 では値は Python コーデック のnumpy.timedelta64セルと等しくなりますが、セルの型が異なります。それ以外のスケールでは、Rust コーデック はdatetime.timedeltaを返しますが、Python コーデック は NumPy に対応する単位が存在しないためProgrammingErrorを送出します。Rust によるデコード後は Dynamic のメンバーメタデータがドライバーに公開されないため、NumPy のセル型やサポート対象外スケールの検証が必要な場合はnative_codec="python"を使用してください。query_dfでは、Python コーデック が JSON の shared data に格納された複合値を文字列化する場合があります。Rust コーデック はデコード済みのオブジェクトを返し、両コーデックのquery_npの結果と一致します。Array(Variant(...))のようにセル単位でマテリアライズされるコンテナー内のLowCardinalityの候補型では、値としては等しいセルが生成されますが、Python コーデック で見られるような dictionary の slot ごとのオブジェクト同一性は共有されません。- 1 つ以上の elements を持つ
Nullable(Tuple(...))カラムは、Rust コーデック では正しくデコードされます。Python コーデック はこの layout を誤って読み取るため、Rust の結果が基準となる動作です。両コーデックともNullable(Tuple())をサポートします。 rust_strictは、クエリごとのカスタムなquery_formatsなど、Rust パスが実装していないクエリオプションについて、暗黙のうちに動作を変更するのではなくエラーとして拒否します。- Rust の 挿入 における変換エラーおよび検証エラーでは、Python コーデック が
ValueErrorを送出する場面でDataErrorが送出されることがあり、メッセージの文言も異なる場合があります。例としては、無効なTimeおよびTime64の値、IPv6 アドレス、QBit の dimension、FixedStringの length、Float64の文字列、Tuple()カラムへの elements の挿入の試行などが挙げられます。 - Rust エンコーダーは、
FixedString(N)に対するb""や、整数カラムに対する"2"のような数値文字列を拒否します。一方、Python コーデック は空のFixedStringの値をゼロ埋めし、数値文字列を強制変換します。 - Python コーデック が値を raw bytes のまま残す場合でも、Rust コーデック は一部の
Dynamicの shared variant の値を対応する Python の型にデコードします。例えば、格納されたDecimalは Rust ではdecimal.Decimalとして返され、Python ではバイナリ値として返されます。 Dynamicの shared storage 内のDateの値については、query_npおよびquery_dfは Rust コーデック ではdatetime.dateのセルを、Python コーデック ではnumpy.datetime64のセルを返します。queryでは両コーデックともdatetime.dateを返します。