説明
はじめに
使い方
バージョニングポリシー
- API の変更時にはメジャーバージョンが増加します
- 後方互換性のある SQL の変更時にはマイナーバージョンが増加します
- binary のみの変更時にはパッチバージョンが増加します
- ライブラリバージョン (PostgreSQL 18 以降では
PG_MODULE_MAGICによって定義) には完全なセマンティックバージョンが含まれており、pgch_version()関数の出力または Postgres のpg_get_loaded_modules()関数で確認できます。 - 拡張機能バージョン (control file で定義) にはメジャー
バージョンとマイナーバージョンのみが含まれており、
pg_catalog.pg_extensionテーブル、pg_available_extension_versions()関数の出力、および\dx pg_clickhouseで確認できます。
v0.1.0 から v0.1.1 へのようにパッチバージョンが増加する release では、v0.1 を読み込んでいるすべての
database がその恩恵を受けられ、アップグレードを反映するために
ALTER EXTENSION を実行する必要がないことを意味します。
一方、マイナーバージョンまたはメジャーバージョンが増加する release には
SQL アップグレードスクリプトが付属し、拡張機能を含む既存のすべての database では、
アップグレードを反映するために ALTER EXTENSION pg_clickhouse UPDATE を実行する必要があります。
DDL SQL リファレンス
CREATE EXTENSION
WITH SCHEMA を使用します (推奨) :
ALTER EXTENSION
-
pg_clickhouse の新しい release をインストールした後は、
UPDATE句を使用します。 -
拡張機能を新しいスキーマに移動するには、
SET SCHEMAを使用します。
DROP EXTENSION
CASCADE 句を使用してください:
CREATE SERVER
driver: 使用する ClickHouse 接続ドライバーです。“binary” または “http” のいずれかを指定します。必須です。compression: バイナリドライバー用のネイティブプロトコル圧縮です。“none”、 “lz4”、“zstd” のいずれかです。デフォルトは “lz4” です。“http” ドライバーでは 無視されます。dbname: 接続時に使用する ClickHouse データベースです。デフォルトは “default” です。host: ClickHouse サーバーのホスト名です。デフォルトは “localhost” です。port: 接続先の ClickHouse サーバーのポートです。デフォルトは 次のとおりです。driverが “binary” で、hostが ClickHouse Cloud ホストの場合は 9440driverが “binary” で、hostが ClickHouse Cloud ホストではない場合は 9004driverが “http” で、hostが ClickHouse Cloud ホストの場合は 8443driverが “http” で、hostが ClickHouse Cloud ホストではない場合は 8123
min_tls_version: TLS を使用する接続でネゴシエートする最小 TLS プロトコルバージョンです。TLSv1、TLSv1.1、TLSv1.2、TLSv1.3のいずれかです。デフォルトは TLS ライブラリ自体の最小バージョンです。両方のドライバーに適用されます。secure: 接続で使用する TLS を制御します。次のいずれかです。auto(デフォルト):hostが ClickHouse Cloud ホストであるか、portがセキュアポートの場合は TLS を使用し、それ以外では平文を使用します。on(またはtrue/yes/1): 常に TLS を使用します。デフォルトのportは 8443 (“http”) または 9440 (“binary”) です。off(またはfalse/no/0): TLS を使用しません。デフォルトのportは 8123 (“http”) または 9000 (“binary”) です.
ALTER SERVER
DROP SERVER
CASCADE を使用すると、
それらの依存オブジェクトも削除できます:
CREATE USER MAPPING
taxi_srv 外部サーバー を使って接続する際に、現在の PostgreSQL ユーザーをリモートの ClickHouse ユーザーにマッピングするには、次のようにします。
user: ClickHouseユーザー名です。デフォルトは “default” です。password: ClickHouseユーザーのパスワードです。
ALTER USER MAPPING
DROP USER MAPPING
IMPORT FOREIGN SCHEMA
LIMIT TO を使用して、インポート対象を特定のテーブルのみに限定します:
EXCEPT を使用してテーブルを除外します:
CREATE FOREIGN TABLE
database: リモートデータベースの名前です。デフォルトでは、外部サーバーに 定義されているデータベースが使用されます。table_name: リモートテーブルの名前です。デフォルトでは、外部テーブルに 指定された名前が使用されます。engine: ClickHouse テーブルで使用される [テーブルエンジン] です。CollapsingMergeTree()およびAggregatingMergeTree()では、pg_clickhouse が テーブル上で実行される関数式に対して自動的にパラメーターを適用します。
-
column_name: ClickHouse 側のカラム名です。クエリや INSERT のデパース時には、PostgreSQL の属性名よりもこちらが優先して使用されます。 これは、引用符なしの小文字の PostgreSQL カラム名を 大文字と小文字を区別する ClickHouse カラムにマッピングする場合に便利です。例: -
AggregateFunction: [AggregateFunction 型] カラムに適用される 集約関数の名前です。データ型を、その関数に渡される ClickHouse の型にマッピングし、適切なカラムオプションで 集約関数名を指定すると、pg_clickhouse が自動的に カラムを評価する集約関数にMergeを付加します。 -
SimpleAggregateFunction: [SimpleAggregateFunction 型] カラムに適用される 集約関数の名前です。データ型を、その関数に渡される ClickHouse の型にマッピングし、適切なカラムオプションで 集約関数名を指定してください。
ALTER FOREIGN TABLE
DROP FOREIGN TABLE
CASCADE 句を使用します:
DML SQL リファレンス
EXPLAIN
VERBOSE オプションを指定すると
ClickHouse の “Remote SQL” クエリが出力されます:
SELECT
nodes テーブルのローカルコピーを作成し、リモートテーブルの代わりに
それと JOIN します。
node_id でグループ化することで、
集約処理のより多くを ClickHouse 側に任せられ、その後で
ルックアップテーブルと join できます。
node_id ごとの集約がプッシュダウンされるようになり、
Postgres に引き戻す必要のある行数が 1000 行 (全件) から
各ノード 1 行ずつのわずか 8 行まで削減されます。
パーティション化テーブル
enable_partitionwise_aggregate を有効にすると、PostgreSQL は Append の下で部分集約を実行し、その後、上位の最終集約でそれらを結合して結果を生成します。pg_clickhouse は外部パーティションの部分集約を ClickHouse にプッシュダウンします。
部分集約がプッシュダウンされる条件
- 遷移状態がすでに最終値となっている分解可能な集約関数は、直接プッシュダウンされます。対象は
count、sum、min、max、bool_and/every、bool_or、bit_and、bit_or、bit_xorです。 - **整数に対する
avg**は、{count, sum}の状態を配列としてプッシュダウンします。 - **浮動小数点に対する
avg、var_pop、var_samp、stddev_pop、stddev_samp**は、{N, sum, sum of squared deviations}の状態を配列としてプッシュダウンします。
FILTER (WHERE …) も、これらの集約関数とともにプッシュダウンされます。
フォールバックする場合
internal 型である集約関数には、
ポータブルな表現がないため、外部パーティションは行を取得してローカルで集約します。
これには numeric に対するすべての集約関数のほか、avg(bigint) と avg(interval) が含まれます。
DISTINCT、順序付き集合集約関数、可変長引数集約関数もフォールバックします。
PREPARE, EXECUTE, DEALLOCATE
{param:type} 形式の[クエリパラメータ]が送信されます。
パラメータ:
INSERT
COPY
⚠️ Batch API の制限 pg_clickhouse は、PostgreSQL FDW のバッチ insert API のサポートをまだ実装していません。そのため、現在 COPY はレコードを 挿入するために INSERT ステートメントを使用しています。これは今後のリリースで改善される予定です。
LOAD
SET
pg_clickhouse.session_settings
pg_clickhouse.session_settings パラメーターは、後続のクエリに適用する [ClickHouse
設定] を指定します。例:
join_use_nulls、IN ファミリーには transform_null_in
(IN と NULL のセマンティクスを参照) 。
date_time_output_format: http ドライバーではこれが “iso” である必要がありますformat_tsv_null_representation: http ドライバーではデフォルト値が必要ですoutput_format_tsv_crlf_end_of_linehttp ドライバーではデフォルト値が必要です
pg_clickhouse.session_settings を設定する前に、
pg_clickhouse を読み込んでおく必要がある点に注意してください。共有ライブラリのプリロード を使用するか、
または拡張機能内のいずれかのオブジェクトを使って、確実に読み込まれるようにしてください。
pg_clickhouse.pushdown_regex
pg_clickhouse.pushdown_regex パラメータは、pg_clickhouse が
正規表現関数および演算子をプッシュダウンするかどうかを制御します。既定では
プッシュダウンが有効です。プッシュダウンを無効にするには、このパラメータを false に設定します:
ALTER ROLE
SET コマンドを使用すると、pg_clickhouse をプリロードしたり、
特定のロールに対してそのパラメータをSETしたりできます。
RESET コマンドを使用して、pg_clickhouse のプリロード設定
やパラメータをリセットします:
プリロード
session_preload_libraries
データ型
すべてのカラムは、
text、varchar、またはその他の文字列型としても読み取れます。値は上記の PostgreSQL 型に変換された後、その型の出力関数を通じてレンダリングされます。bigint の最大値を超える UInt64 値は引き続きエラーになるため、これらは ClickHouse の toString() 関数でレンダリングしてください。
追加の注記と詳細を以下に示します。
BYTEA
SELECTクエリの出力は以下のとおりです:
関数と演算子のリファレンス
関数
clickhouse_raw_query
host=localhost port=8123 です。サポートされる connection
parameter は次のとおりです。
driver: 使用する connection driver。“http” または “binary” のいずれかです。デフォルトは “http” ですhost: 接続先の host。必須です。port: 接続先の port。“http” driver の場合はデフォルトで8123、“binary” driver の場合は9000です。hostが ClickHouse Cloud host の場合は、それぞれ8443または9440に なりますdbname: 接続先の database 名。username: 接続に使用する username。デフォルトはdefaultですpassword: 認証に使用する password。デフォルトでは password はありません
\N) を返しますが、値ごとの
表現は異なります。“http” driver は ClickHouse 独自の TSV
フォーマットをそのまま返すのに対し、“binary” driver は各値を
PostgreSQL の出力関数に渡します。
デフォルトでは、どのロールにもこの関数に対する EXECUTE 権限はありません。GRANTで、
アドホックな ClickHouse クエリを正当に実行する必要があるロールにのみ
アクセスを付与することを検討してください。たとえば、専用の ClickHouse 管理者ロールです。
結果を返さないクエリに便利ですが、値を返すクエリの場合は
単一のテキスト値として返されます。
clickhouse_server_version
major.minor.patch形式で返します。必要に応じて、server’s optionsおよびcurrent user’s user mappingを使用して接続します。
SELECT version() クエリからバージョンを取得し、接続の存続期間中キャッシュします。
clickhouse_query
driver、
credentials、database、およびconnection cacheが再利用されます。
最初のargumentには、CREATE SERVERで作成したserverの名前を指定します。
カラム定義リスト (AS name(col type, ...)) が必要です。PostgreSQLでは行を取得する前に
結果のshapeを把握する必要があり、クエリが返すカラムと一致している必要があります。値は、
外部テーブルのカラムと同様に、ClickHouseから宣言された型に変換されます。DDLなど、
結果を返さないステートメントでは宣言するものがありません。そのようなステートメントは
代わりにclickhouse_performで実行してください。
デフォルトでは、どのロールにもEXECUTEアクセス権はありません。関数の使用を許可するには、
ロールにGRANTしてください。
clickhouse_perform
clickhouse_query で
結果の shape を宣言できないステートメントに使用します。server は
clickhouse_query と同じ方法で解決され、その driver、credentials、database、
および connection cache が再利用されます。
プロシージャとして、SELECT ではなく CALL で呼び出す必要があり、行は返しません。
デフォルトでは、どのロールにも EXECUTE アクセスは付与されていません。プロシージャの使用を許可するには、ロールに GRANT します。
プッシュダウン関数
HAVING 句および WHERE 句) で使用される PostgreSQL の組み込み関数の一部をプッシュダウンします。対応する関数は、ClickHouse では以下のとおりです。
abs: absfactorial: factorialmod(int2/int4/int8/numeric): 剰余pow&power(float8/numeric): powround: roundsin,cos,tan,atan,atan2,sinh,cosh,tanh,asinh,degrees,radians,pi: ClickHouse の数学関数 と同名です。asin,acos,atanh,acoshはプッシュダウンされません。PG では範囲外の入力でエラーになりますが、CH ではNaNが返されます。date_part:date_part('day'): toDayOfMonthdate_part('doy'): toDayOfYeardate_part('dow'): toDayOfWeekdate_part('year'): toYeardate_part('month'): toMonthdate_part('hour'): toHourdate_part('minute'): toMinutedate_part('second'): toSeconddate_part('quarter'): toQuarterdate_part('isoyear'): toISOYeardate_part('week'): toISOYeardate_part('epoch'): toISOYear
date_trunc:date_trunc('week'): toMondaydate_trunc('second'): toStartOfSeconddate_trunc('minute'): toStartOfMinutedate_trunc('hour'): toStartOfHourdate_trunc('day'): toStartOfDaydate_trunc('month'): toStartOfMonthdate_trunc('quarter'): toStartOfQuarterdate_trunc('year'): toStartOfYear
extract(field FROM source):date_partと同じ対応date(timestamp)&date(timestamptz): toDate (CH aliasdateとしてデパースされます)array_position: indexOf に nullIf を使用して0をNULLに変換し、検索開始インデックスを指定する第 3 引数がある場合は arraySlice を使用します。なお、現在nanは一致しませんarray_cat: arrayConcatarray_append: arrayPushBackarray_prepend: arrayPushFrontarray_remove: arrayRemovecardinality: lengtharray_length(array, 1):nullIf(length(array), 0)array_length&cardinality: lengtharray_to_string: arrayStringConcatstring_to_array: splitByStringsplit_part: splitByString + 配列の添字指定trim_array: arrayResizearray_fill: arrayWithConstantarray_reverse: arrayReversearray_shuffle: arrayShufflearray_sample: arrayRandomSamplearray_sort: arraySort / arrayReverseSortbtrim: trimBothltrim: trimLeftrtrim: trimRightconcat_ws: concatWithSeparatorlower(text): lowerUTF8upper(text): upperUTF8substring(text, ...)&substr(text, ...): substringUTF8substring(bytea, ...)&substr(bytea, ...): substringlength(text): lengthUTF8length(bytea)&octet_length: lengthreverse(text): reverseUTF8reverse(bytea): reversestrpos: positionUTF8regexp_like: matchregexp_match: 正規表現に丸括弧で囲まれた部分式が含まれている場合は extractGroups、 そうでない場合は extractAll を arraySlice でスライスしたものです。regexp_replace:gフラグがある場合は replaceRegexpOne、または replaceRegexpOneregexp_split_to_array: splitByRegexpmd5: MD5fmtが文字列定数 (大文字と小文字を区別しない) の場合のencode(bytea, fmt):encode(bytea, 'hex'): hex を lower でラップします。 PostgreSQL は小文字の hex を出力するためです。encode(bytea, 'base64'): base64Encode を replaceRegexpAll でラップし、 PostgreSQL の MIME (RFC 2045) 形式の 76 文字ごとの改行を再現します。encode(bytea, 'base64url')(PostgreSQL 19 以降) : base64URLEncode。 これは、padding なしの PostgreSQL の RFC 4648 URL アルファベットに一致します。
json_extract_path_text: サブカラムの構文json_extract_path: toJSONString + サブカラムの構文jsonb_extract_path_text: サブカラム構文jsonb_extract_path: toJSONString + サブカラムの構文bit_count(bytea): bitCountto_timestamp(float8): toDateTime64to_char(timestamp[tz], fmt):fmtが、含まれるすべてのキーワードに ClickHouse で忠実に対応するものがある文字列定数である場合は、formatDateTime です。対応しているキーワードについては、Compatibility Notes の to_char() を参照してください。それ以外の場合、この関数は PostgreSQL 側でローカルに評価されます。statement_timestamp,transaction_timestamp, &clock_timestamp: nowInBlock64 (nowInBlock64(9, $session_timezone))CURRENT_DATE: now および toDate (toDate(now($session_timezone)))now,CURRENT_TIMESTAMP, &LOCALTIMESTAMP: now64 (now64(9, $session_timezone))CURRENT_TIMESTAMP(n)&LOCALTIMESTAMP(n): now64 (now64(n, $session_timezone))CURRENT_DATABASE: PostgreSQL 関数から渡される値です。CURRENT_SCHEMA: PostgreSQL関数から渡される値。CURRENT_CATALOG: PostgreSQL 関数から渡される値です。CURRENT_USER: PostgreSQL 関数の値として渡されます。USER: PostgreSQL の関数から値として渡されます。CURRENT_ROLE: PostgreSQL の関数から値として渡されます。SESSION_USER: PostgreSQL 関数から値として渡されるものです。
プッシュダウン演算子
- Array スライス (
arr[L:U]): arraySlice @>(配列が含む) : hasAll<@(配列に含まれる) : hasAll&&(配列の重複) : hasAny~(正規表現に一致) : match!~(正規表現に一致しない) : match~*(大文字と小文字を区別しない正規表現に一致しない) : match!~*(大文字と小文字を区別しない正規表現に一致しない) : match->>(JSON/JSONB の要素をテキストとして抽出) : sub-column syntax->(JSON/JSONB を抽出) : toJSONString + sub-column syntax
IN と NULL のセマンティクス
IN を二値論理で評価します。プローブで一致が見つからない場合、NULL が含まれていても 0 を返しますが、PostgreSQL では NULL になります。PostgreSQL のセマンティクスを維持するため、pg_clickhouse は定数リストまたは配列に対する IN ファミリー (IN、NOT IN、= ANY、= ALL、<> ANY、<> ALL) を無条件にプッシュダウンします。プローブと配列要素のいずれも NULL になり得ないことを証明できる場合はネイティブ形式または低コストな形式を使用し、それ以外の場合は実行時に NULL 値を確認するガード付き CASE 形式を使用します。これにより、SELECT リストや GROUP BY などの値の位置を含むあらゆるコンテキストで、PostgreSQL と完全に同じ三値の結果 (TRUE、FALSE、NULL) を計算します。
NULL 許容カラムに対する NOT IN (SELECT ...) フィルタもプッシュダウンされ、PostgreSQL の動作を維持するための補償ガードを付加して逆変換されます。NULL を含む集合はすべての行を除外し、NULL のプローブが通過するのは集合が空の場合のみです。NOT NULL 宣言によって不要であることが証明できる場合は、各ガードを省略します。上記の配列形式とは異なり、このガードは通常のフィルタ条件内 (または NOT 配下) でのみ適用されます。値の位置にある IN (SELECT ...) や、グループ化または集計されたサブクエリ本体は、引き続きプッシュダウンされません。カラムを NOT NULL と宣言すると、より低コストなガードなし形式を送信できるため、プッシュダウンを最大化できます。IMPORT FOREIGN SCHEMA は、Nullable ではない ClickHouse カラムに対してこれを自動的に行います。この証明では、非 NULL 定数、NOT NULL カラム、およびそれらに対する基本的な算術演算 (+、-、*、単項 -) を追跡します。
これらの規則は、ClickHouse のデフォルト設定である transform_null_in = 0 を前提としています。pg_clickhouse は pg_clickhouse.session_settings パラメータのデフォルト値を通じて、すべてのクエリでこれを設定します。これにより、ClickHouse サーバープロファイルがこの設定を暗黙的に変更することはありません。transform_null_in = 1 を設定すると、プッシュダウンされたすべての IN のセマンティクスが破綻します。
カスタム関数
拡張機能のプッシュダウン
re2
@~→ matchre2match→ matchre2extract→ extractre2extractall→ extractAllre2regexpextract→ regexpExtractre2extractgroups→ extractGroupsre2replaceregexpone→ replaceRegexpOnere2replaceregexpall→ replaceRegexpAllre2countmatches→ countMatchesre2countmatchescaseinsensitive→ countMatchesCaseInsensitivere2multimatchany→ multiMatchAnyre2multimatchanyindex→ multiMatchAnyIndexre2multimatchallindices→ multiMatchAllIndices
intarray
idx→ indexOf
fuzzystrmatch
soundex: soundexlevenshtein(2引数) : editDistanceUTF8
キャストのプッシュダウン
CAST(x AS bigint) のようなキャストをプッシュダウンします。互換性のない型ではプッシュダウンに失敗します。たとえば、この例で x が ClickHouse の UInt64 である場合、ClickHouse はその値をキャストしません。
互換性のないデータ型へのキャストをプッシュダウンするために、pg_clickhouse は次の関数を提供しています。これらの関数は、プッシュダウンされなかった場合に PostgreSQL で例外を発生させます。
集計のプッシュダウン
- any_value
- array_agg
- avg
- bit_and
- bit_or
- bit_xor
- bool_and / every
- bool_or
- count
- corr
- covarpop
- covarsamp
- min
- max
- stddev_pop
- stddev_samp / stddev
- string_agg
- sum
- var_op
- var_samp /variance
カスタム集約関数
ordered-set 集約関数 の プッシュダウン
ORDER BY 式を引数として渡すことで、ClickHouse のparametric
集約関数に対応します。たとえば、次の PostgreSQL クエリです。
ORDER BY 接尾辞である DESC と NULLS FIRST は
サポートされておらず、error が発生します。
percentile_cont(double): quantilepercentile_cont(double[]): quantilespercentile_disc(double): quantileExactLowpercentile_disc(double[]): quantilesExactLow
カスタム ordered-set 集約関数
quantile(double): quantilequantileExact(double): quantileExact
カスタム ordered-set 集約関数
プッシュダウンされるウィンドウ関数
OVER (PARTITION BY ... ORDER BY ...) 句とともに ClickHouse にプッシュダウンされます。
- row_number
- rank
- dense_rank
- ntile
- cume_dist
- percent_rank
- lead
- lag
- first_value
- last_value
- nth_value
min/max(OVER句あり)
row_number、rank、dense_rank、ntile、cume_dist、
percent_rank) では、ClickHouse がこれらの関数のフレーム指定を受け付けないため、プッシュダウン時にフレーム句が省略されます。
互換性に関する注意
正規表現
-
PostgreSQL は [POSIX 正規表現] をサポートし、ClickHouse は
RE2 正規表現 をサポートしています。動作の違いに注意してください。正規表現が ClickHouse によって評価される場合 (例:
WHERE句内) は RE2 を、Postgres によって評価される場合 (例:SELECT句内) は POSIX を使用してください。 -
pg_clickhouse は、[Postgres フラグ]を ClickHouse の正規表現の
(?)内の先頭に追加することで、プッシュダウンします。たとえば:になります -
両方でサポートされており、そのため
ClickHouse で評価される際に使用できるフラグは、次のとおりです:
RE2 がサポートしているのはこれらのフラグだけです。それ以外の [Postgres のフラグ] は使用しないでください。
-
この表は、改行および行末のマッチングにおける各種フラグ (およびフラグなし。これは
sと同じです) の効果を要約したものです。Postgres では、mとpを指定すると否定文字クラス ([^xyz]) が 改行にマッチしなくなる点に注意してください。一方、ClickHouse の同等の機能ではそうなりません。それ以外の 動作は、ClickHouse でも Postgres と同じです: - 正規表現関数に渡すその他のフラグがあると、その関数は プッシュダウンされません。
-
例外は
regexp_replace()で、これもgフラグをサポートします。gが設定されている場合、pg_clickhouse はreplaceRegexpOne()ではなくreplaceRegexpAll()を使用し、 他のフラグを先頭に追加する前にgフラグを削除します。 -
Postgres の
regexp_replace()の置換引数では、一致全体を参照するために\&を使用できますが、ClickHouse では一致全体に\0を使用します。 関数が ClickHouse にプッシュダウンされる場合は、必ず\0を使用してください。 -
Postgres の
regexp_matchは一致がない場合にNULLを返しますが、 push down される式は空の配列を返します。戻り値を 互換性のある形で比較するには、COALESCE()を使ってNULLではなく空の配列を返すようにします。例えば:
to_char()
timestamp および timestamp with time zone に対する PostgreSQL の to_char() は、フォーマット引数が非 NULL の文字列定数であり、なおかつ含まれる PostgreSQL のキーワードがすべて ClickHouse にバイト単位で完全一致する対応を持つ場合にのみ、ClickHouse の formatDateTime にプッシュダウンされます。フォーマットが動的な場合 (Const ではない場合) 、または未サポートのキーワードや modifier を含む場合、この呼び出しは PostgreSQL でローカルに評価されます。部分的な変換でプッシュダウンを試みることはないため、出力は PG 互換のまま保たれます。
numeric、interval、およびその他の timestamp 以外の型に対する 2 引数の to_char() 形式は、プッシュダウンされません。ClickHouse の formatDateTime は日付時刻値のみをフォーマットします。
変換されるキーワード
引用符付きテキストとリテラル
"..." で囲まれたテキストは、そのまま渡されます。リテラルの % は、
ClickHouse の指定子プレフィックスをエスケープするため、%% に
二重化されます。引用符の外側にある \" も、リテラルの " としてそのまま
渡されます。"..." の内側では、バックスラッシュでエスケープされるのは " のみで、
それ以外のバックスラッシュシーケンスはリテラルテキストとして扱われます。
David E. Wheeler