> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Postgres chdb_hook 모듈에 대한 전체 참고 문서

# chdb_hook 모듈 참고 문서

## 개요

```psql theme={null}
# LOAD 'chdb_hook';
LOAD

# CREATE TABLE times (
    id     INT NOT NULL,
    months INT NOT NULL,
    days   INT NOT NULL
);
CREATE TABLE

# COPY times FROM 's3://datasets-documentation/my-test-bucket-768/{some,another}_prefix/some_file_{1..3}.csv';
COPY 18
```

## 설명

chdb\_hook 모듈은 PostgreSQL [COPY](#copy-overloading) 명령에 후크를 걸어, [chDB]를 사용해 로컬 파일, [AWS S3] 버킷, [Google Cloud Storage] 등에서 \[chDB가 지원하는 데이터 포맷]\[formats] 중 하나로 데이터를 `TO` 또는 `FROM` 복사할 수 있도록 합니다. 또한 [CREATE TABLE]에도 후크를 걸어, 동일한 대상들로부터 테이블의 컬럼을 도출하고 행을 로드할 수 있게 합니다.

## 로딩

수퍼유저(super user) 권한으로 다음 방법 중 하나를 사용해 chdb\_hook을 로드하십시오. 사용 사례에
가장 적합한 방법을 선택하십시오:

* [LOAD] 명령으로 명시적으로 로드하며, 해당 세션이 유지되는 동안에만 적용됩니다:

  ```sql theme={null}
  LOAD 'chdb_hook';
  ```

  > [!NOTE]
  >
  > ClickHouse Cloud SQL 콘솔은 아직 `LOAD 'chdb_hook'` 명령을 지원하지 않지만,
  > psql이나 다른 데이터베이스 연결을 통해 실행할 수 있습니다.
  > 그렇지 않은 경우 지원 담당자에게 문의하여 Postgres 서비스 구성에 추가하면,
  > 이후 SQL 콘솔에서도 사용할 수 있습니다.

* 모든 세션에 적용하려면 `postgresql.conf`에서 \[session\_preload\_libraries]
  설정을 사용하십시오:

  ```ini theme={null}
  session_preload_libraries = chdb_hook
  ```

  또는 [ALTER SYSTEM]으로 설정합니다:

  ```sql theme={null}
  ALTER SYSTEM SET session_preload_libraries = 'chdb_hook';
  ```

  이 설정은 [ALTER DATABASE]를 사용해 데이터베이스 단위로도 지정할 수 있습니다:

  ```sql theme={null}
  ALTER DATABASE name SET session_preload_libraries = 'chdb_hook';
  ```

  또는 [ALTER ROLE]을 사용해 특정 사용자 및 그룹에 지정할 수 있습니다:

  ```sql theme={null}
  ALTER ROLE name SET session_preload_libraries = 'chdb_hook';
  ```

* \[shared\_preload\_libraries] 설정으로 서버 시작 시 로드하면, 모든 세션과
  데이터베이스에서 항상 사용할 수 있습니다:

  ```ini theme={null}
  shared_preload_libraries = chdb_hook
  ```

> [!WARNING]
>
> chdb\_hook을 로드하면 `pg_read_server_files` 또는 `pg_write_server_files` 역할에
> 속한 사용자가 Postgres 서버의 파일은 물론 클라우드 스토리지를 대상으로 데이터를 `COPY`할
> 수 있게 되므로 유의하십시오.

## COPY 오버로딩

[loading](#loading) 시 chdb\_hook은 Postgres [COPY] 명령에 후크를 걸어, 로컬 파일, [AWS S3] 버킷, [Google Cloud Storage] 등에서 \[chDB가 제공하는 데이터 포맷]\[formats] 중 하나로 데이터를 `TO` 또는 `FROM` 방향으로 복사합니다. 예를 들어 S3에 있는 CSV 파일에서 테이블을 로드하려면, 먼저 테이블을 CREATE한 다음 `s3://` URL과 함께 `COPY`를 호출하십시오:

```sql theme={null}
CREATE TABLE times (
    id     INT PRIMARY KEY,
    months INT NOT NULL,
    days   INT NOT NULL
);

COPY times FROM 's3://datasets-documentation/my-test-bucket-768/some_prefix/some_file_1.csv';
```

### 권한

chdb\_hook `COPY`는 자신이 대체하는 [COPY]와 동일한 권한을 필요로 합니다.
즉, `COPY TO`에는 릴레이션 또는 복사되는 모든 컬럼에 대한 `SELECT` 권한이,
`COPY FROM`에는 `INSERT` 권한이 필요합니다. `file://` URL은 server의 파일을
읽거나 쓰기 때문에 `pg_read_server_files` 또는 `pg_write_server_files` 멤버십도 필요합니다.
또한 `COPY FROM`은 읽기-쓰기 transaction에서만 사용할 수 있습니다.

### URL 스킴

chdb\_hook은 다음 스킴 중 하나를 사용하는 URL `COPY` 대상에 대해서만 실행됩니다:

| 스킴 | 대상 | chDB 함수 |
| - | - | - |
| `file` | Postgres 서버의 절대 경로 | [`file()`] |
| `http`, `https` | HTTP URL | [`url()`] |
| `s3` | [AWS S3] | [`s3()`] |
| `gs`, `gcs`, `oss` | [Google Cloud Storage] | [`gcs()`] |
| `az`, `azure`, `abfss`, `abfs` | [Azure Blob Storage] 또는 [Azure ABFS] | [`azureBlobStorage()`] |
| `hdfs` | [Hadoop Distributed File System] | [`hdfs()`] |

### URL 포맷

URL 포맷은 대상(target)에 따라 다릅니다.

#### File

`file://` 스킴은 ClickHouse Cloud에서 지원되지 않습니다.

Postgres 서버의 절대 경로여야 합니다. 상대 경로를 지정하면 오류가 발생합니다. Postgres 사용자는 용도에 따라 `pg_read_server_files` 또는 `pg_write_server_files` 역할(Role)의 멤버여야 합니다. 또한 Postgres 시스템 사용자에게 해당 파일에 대한 읽기 또는 쓰기 액세스 권한이 있어야 합니다. `COPY TO`에서 경로가 존재하지 않으면 chdb\_hook이 누락된 상위 디렉터리를 생성하며, 이때 필요한 파일 시스템 권한을 갖추고 있어야 합니다. 예시:

```
file:///tmp/users.parquet
```

#### HTTP

퍼블릭 클라우드 스토리지를 포함한 일반적인 HTTP URL을 모두 지원합니다. `COPY TO`에서는
chdb\_hook이 해당 URL로 데이터를 `POST`하려고 시도합니다. 예시:

```
https://datasets-documentation.s3.eu-west-3.amazonaws.com/my-test-bucket-768/some_prefix/some_file_1.csv
```

#### S3

S3 URL은 S3 URI 형식일 수 있습니다

```
s3://{bucket}/{path}
```

또는 객체 URL 형식:

```
s3://{bucket}.s3.{region}.amazonaws.com/{path}
```

#### GCS

GCS URL은 퍼블릭 URL 형식을 따릅니다:

```
gs://storage.googleapis.com/{bucket}/{path}
```

또는 chdb\_hook이 공개 URL로 변환해 주는 Cloud Storage URI를 사용할 수도 있습니다:

```
gs://{bucket}/{path}
```

#### Azure Blob Storage

계정 이름을 하위 도메인으로 지정한 `blob.windows.net` URL을 사용하십시오:

```
az://{account}.blob.core.windows.net/{container}/{blob}
```

또는 다른 host name을 사용할 수도 있습니다:

```
az://{host}/{container}/{blob}
```

#### Azure ABFS

ABFS URL은 반드시 다음 포맷을 사용해야 합니다:

```
abfs://{container}@{account}.dfs.core.windows.net/{blob}
```

#### HDFS URL

HDFS URL은 일반적인 HTTP 스타일 URL 형식을 사용할 수 있으며, 포트는 선택 사항입니다:

```
hdfs://{host}/{path}
hdfs://{host}:{port}/{path}
```

### 경로 와일드카드

`COPY FROM` 명령어의 URL 경로에는 글롭 패턴을 사용할 수 있습니다. 파일은 접미사나 접두사만이 아니라 전체 경로 패턴과 일치해야 합니다. 단 한 가지 예외가 있는데, 경로가 기존 디렉터리를 가리키고 글롭 패턴을 사용하지 않는 경우에는 해당 디렉터리의 모든 파일을 선택하도록 경로 뒤에 `*`가 암시적으로 추가됩니다.

지원되는 와일드카드는 다음과 같습니다.

* `*`: `/`를 제외한 임의 개수의 문자와 일치하며, 빈 문자열도 포함합니다.
* `?`: 임의의 단일 문자와 일치합니다.
* `{groucho,harpo,chico}`: "groucho", "harpo", "chico" 문자열 중 하나로 대체합니다. 이 문자열에는 `/`가 포함될 수 있습니다.
* `{N..M}`: `>= N` 이고 `<= M` 인 임의의 숫자와 일치합니다.
* `**`: 디렉터리 내의 모든 파일을 재귀적으로 일치시킵니다.

예를 들어 다음 파일들의 데이터를 하나의 명령어로 로드하려는 경우를 살펴봅니다.

* [https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/some\&#95;prefix/some\&#95;file\&#95;1.csv](https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/some\&#95;prefix/some\&#95;file\&#95;1.csv)
* [https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/some\&#95;prefix/some\&#95;file\&#95;2.csv](https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/some\&#95;prefix/some\&#95;file\&#95;2.csv)
* [https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/some\&#95;prefix/some\&#95;file\&#95;3.csv](https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/some\&#95;prefix/some\&#95;file\&#95;3.csv)
* [https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/another\&#95;prefix/some\&#95;file\&#95;1.csv](https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/another\&#95;prefix/some\&#95;file\&#95;1.csv)
* [https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/another\&#95;prefix/some\&#95;file\&#95;2.csv](https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/another\&#95;prefix/some\&#95;file\&#95;2.csv)
* [https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/another\&#95;prefix/some\&#95;file\&#95;3.csv](https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/another\&#95;prefix/some\&#95;file\&#95;3.csv)

두 디렉터리 이름은 `{some,another}_prefix`로, 파일은 `some_file_{1..3}.csv'`로 일치시킵니다. 다음과 같이 작성하십시오:

```sql theme={null}
CREATE TABLE times (
    id     INT NOT NULL,
    months INT NOT NULL,
    days   INT NOT NULL
);

COPY times FROM 's3://datasets-documentation/my-test-bucket-768/{some,another}_prefix/some_file_{1..3}.csv';
```

### 옵션

chdb\_hook의 `COPY` 명령은 다음 옵션을 지원합니다:

#### `format`:

읽거나 쓸 포맷입니다. [chDB]에서 제공하는 [포맷] 중 하나여야 하며,
여기에는 TSV, CSV, Parquet, Iceberg, JSON 등이 포함됩니다. 생략하거나
`auto`로 설정하면 chDB가 URL 끝의 파일 이름 확장자를 보고 포맷을 결정합니다.

#### `structure`

행의 [chDB] 데이터 구조입니다. 컬럼명 목록과 [ClickHouse 데이터 타입], 수정자로
구성됩니다. 생략하면 chdb\_hook이 Postgres 데이터 타입을 대체로 적합한
ClickHouse 타입으로 매핑합니다. 자세한 내용은 [Postgres to
chDB](#postgres-to-chdb)를 참조하십시오. `auto`로 설정하면 chDB가 타입을 추론합니다.

예시:

```sql theme={null}
COPY users TO 'file:///tmp/users.parquet' (
    structure 'id Int64, name String, age Nullable(UInt8), attributes JSON'
);
```

#### `access_key`와 `access_secret`

요청을 인증하는 데 사용되는 AWS 계정 사용자의 장기 자격 증명입니다.

* **S3:** AWS \[액세스 키 ID 및 액세스 시크릿]으로, 보통 `AWS_ACCESS_KEY_ID` 및 `AWS_SECRET_ACCESS_KEY`
  환경 변수로 정의됩니다
* **GCS:** GCP \[HMAC 키 및 시크릿]
* **Azure:** Azure 스토리지 계정 이름 및 \[액세스 키]

#### `session_token`

`access_key` 및 `access_secret`과 함께 사용하는 AWS 세션 토큰으로, 보통 환경 변수 `AWS_SESSION_TOKEN`으로 정의됩니다. S3 URL에만 사용됩니다.

#### `compression`

파일 압축 포맷입니다. 파일 이름으로 압축 방식을 추론할 수 없을 때 사용합니다.
지원되는 값:

* `auto` (기본값)
* `none`
* `gzip` 또는 `gz`
* `brotli` 또는 `br`
* `xz` 또는 `LZMA`
* `zstd` 또는 `zst`
* `lz4`
* `bz2`
* `snappy`

#### `timeout`

요청 타임아웃(밀리초 단위)입니다. HTTP, S3, GCS, Azure URL에 적용됩니다.
기본값은 `30000`(30초)입니다.

#### `encoding_check`

chDB의 String 및 JSON 값을 변환할 때 [database encoding]에서 유효하지 않은 문자를 처리하는 방식을 지정합니다. 다음 값 중 하나를 사용합니다.

* `fail` (기본값): 오류를 발생시킵니다
* `remove`: 유효하지 않은 바이트를 제거합니다
* `replace`: UTF-8 인코딩에서는 유효하지 않은 바이트를
  유니코드 대체 문자(`�`)로 대체합니다. 다른 인코딩에서는
  `remove`와 동일하게 동작합니다
* `truncate`: 처음으로 나타나는 유효하지 않은 바이트 위치에서 텍스트를 잘라냅니다

### 디버깅

오류가 발생하면 chdb\_hook의 `COPY` 명령은 실행을 시도했던 [chDB] 쿼리를 오류 Context에 포함합니다:

```
ERROR:  chdb: error executing chDB query
DETAIL:  Code: 53. DB::Exception: Requested type of column p doesn't match parquet schema
CONTEXT:  query: SELECT * FROM file({path:String}, {format:String}, {structure:String})
STATEMENT:  COPY "users" FROM 'file:///tmp/users.data' (format 'Parquet');
```

chdb\_hook은 SQL injection 취약점을 방지하고 자격 증명과 같은 민감한 데이터가 로깅될 위험을 최소화하기 위해 쿼리 매개변수에 `{name:Type}` 형식의 placeholder를 사용합니다.

다만 문제를 디버깅하기 위해 해당 매개변수의 내용을 확인해야 한다면, Postgres의 \[log\_min\_messages] GUC를 일시적으로 `DEBUG1` 이상으로 설정하십시오. 그러면 chdb\_hook이 쿼리와 매개변수를 Postgres 로그로 전송하며(클라이언트로는 절대 전송하지 않습니다), 다음과 같이 표시됩니다:

```
2026-08-08 09:41:06.842 EDT [59940] LOG:  executing chDB query
2026-08-08 09:41:06.842 EDT [59940] DETAIL:  query: SELECT * FROM file({path:String}, {format:String}, {structure:String})
2026-08-08 09:41:06.842 EDT [59940] CONTEXT:  params: { path: "/tmp/users.data", format: "Parquet", structure: "user_id Nullable(Int64), username Nullable(String), password Nullable(String)" }
2026-08-08 09:41:06.842 EDT [59940] STATEMENT:  COPY "users" FROM 'file:///tmp/users.data' (format 'Parquet');
```

> [!WARNING]
>
> \[log\_min\_messages]를 디버깅 수준으로 설정한 상태는 단일 디버깅 세션을 넘어
> 유지하지 마십시오. 자격 증명과 같은 민감한 정보가 로그에 기록되는 것을 방지하기 위한 것이며,
> PostgreSQL 자체도 디버깅 정보를 기록하므로 로그가 빠르게 가득 찰 수 있습니다.

## CREATE TABLE 오버로딩

chdb\_hook은 [CREATE TABLE]에도 후크를 연결하므로, 테이블이 URL에서 컬럼을 도출하고 행을 로드할 수 있습니다.

URL에서 도출된 구조로 테이블을 생성하려면 `structure_from` 옵션에 URL을 전달하고 컬럼 목록은 비워 두십시오(주의: 이 공개 데이터 파일에는 4,100만 개의 행이 있습니다):

```sql theme={null}
CREATE TABLE reviews () WITH (
    encoding_check = 'replace',
    structure_from = 's3://datasets-documentation/amazon_reviews/amazon_reviews_2015.snappy.parquet'
);
```

컬럼뿐만 아니라 행까지 로드하려면 `copy_from`을 사용하십시오:

```sql theme={null}
CREATE TABLE reviews () WITH (
    encoding_check = 'replace',
    copy_from = 's3://datasets-documentation/amazon_reviews/amazon_reviews_2015.snappy.parquet'
);
```

`copy_from`은 statement가 자체 컬럼을 전혀 지정하지 않은 경우에만 컬럼을 추론합니다.
컬럼 목록, `INHERITS` 절, `OF` 유형, 파티션은 각각 컬럼을 정의하므로,
이 경우 `copy_from`은 다음 항목만 복사합니다:

```sql theme={null}
CREATE TABLE times (
    id     INT NOT NULL,
    months INT NOT NULL,
    days   INT NOT NULL
) WITH (copy_from = 's3://datasets-documentation/my-test-bucket-768/some_prefix/some_file_1.csv');
```

`copy_from`이 컬럼을 추론할 때는 `DESCRIBE`가 반환하는 소스 스키마를 사용합니다.
이로 인해 `Map`, `Tuple`, `Nested` 값은 텍스트 배열 컬럼으로 로드되며,
이 컬럼에는 원래의 ClickHouse 타입 정보가 남지 않습니다.

두 옵션 모두 `COPY`와 동일한 [URL scheme](#url-schemes)과
[옵션](#options)을 지원하며, 자격 증명, 포맷, 압축, timeout,
심지어 명시적인 [구조](#structure) 지정까지 모두 적용됩니다. Postgres는 남아 있는
스토리지 매개변수를 그대로 유지합니다:

```sql theme={null}
CREATE TABLE logs () WITH (
    copy_from  = 's3://chdb-lakedata-public/logs/logs-2026-08-26.csv',
    format     = 'CSVWithNames',
    timeout    = 60000,
    fillfactor = 90
);
```

`structure_from`과 `copy_from`은 모두 `IF NOT EXISTS`와 함께 사용할 수 없습니다. 기존 릴레이션을 로드하려면
[COPY]를 사용하십시오.

## 제한 사항

Postgres와 chDB 간 데이터 타입 동작의 차이 및 몇 가지 알려진 문제로 인해
chdb\_hook에는 다음과 같은 제한 사항이 있습니다:

* 복사를 수행하는 역할에 적용되는 [row-level security] 정책이 있는 릴레이션은
  `COPY`할 수 없습니다. Postgres는 `COPY TO`를 쿼리로 다시 작성하는 방식으로
  이러한 정책을 적용하는데, chdb\_hook은 이를 지원하지 않습니다.
* `WHERE` 절을 사용하여 URL에서 `COPY FROM`을 수행할 수 없습니다.
* chDB에는 NULL 배열이 없으므로, `COPY TO`는 NULL을 빈 배열(`[]`)로
  저장합니다.
* chDB는 `lseg`, `path`, `polygon`에 해당하는 값을 배열로 표현합니다.
  따라서 이러한 타입의 NULL 값도 `COPY TO` 시 빈 배열(`[]`)로 출력됩니다.
* ClickHouse에는 NULL 배열이 없으므로, `COPY TO`는 `NULL`을 빈 배열(`[]`)로
  저장합니다.
* ClickHouse는 `lseg`, `path`, `polygon`에 해당하는 값을 배열로 표현합니다.
  따라서 이러한 타입의 NULL 값도 `COPY TO` 시 빈 배열(`[]`)로 출력됩니다.
* 지정된 [structure](#structure)에서 해당 컬럼을 Nullable로 정의하지 않으면
  NULL 값은 기본값으로 출력됩니다. 이러한 변환을 방지하려면
  [structure](#structure)에 널 허용 컬럼을 항상 명시적으로 정의하십시오.
* 마지막 점이 첫 번째 점과 같은 열린 `path`는 닫힌 경로로 출력됩니다.
* Protobuf의 반복 필드에는 null이 없으므로 배열의 NULL 값은 생략됩니다.
* chDB [JSON type]은 JSON 객체만 지원합니다. 모든 값이 JSON 객체인 경우에만
  `json` 및 `jsonb`의 기본 `String` 매핑을 `JSON`으로 재정의하십시오.
  (ClickHouse/ClickHouse#68428)
* chDB [JSON type]은 `null`을 무시하므로, NULL 값을 가진 객체 키는 출력 시
  생략됩니다. 객체 값이 `null`이 아니거나 그 값의 손실을 허용할 수 있는 경우에만
  `json` 및 `jsonb`의 기본 `String` 매핑을 `JSON`으로 재정의하십시오.
  (ClickHouse/ClickHouse#68428)
* JSON, JSONCompact, JSONColumnsWithMetadata 포맷은 항상 UTF-8을 검사하므로,
  bytea 값을 대체 문자로 치환하여 출력합니다.
* `COPY FROM`은 빈 문자열이나 0을 포함하는 Protobuf `Nullable` 필드를 `NULL`로
  읽습니다. (chdb-io/chdb-core#152)
* Parquet으로의 `COPY TO`는 Nullable Tuple 자체의 null map에서 `NULL`을 누락시킵니다.
  (ClickHouse/ClickHouse#112427)
* Parquet, Arrow, ArrowStream, ORC, Avro, Protobuf, ProtobufList, MsgPack,
  BSONEachRow 포맷에는 Postgres `time` 또는 chDB `Time64`에 해당하는 타입이
  없습니다. 값을 보존하려면 명시적인 [structure](#structure)에서 `time` 컬럼을
  `String`으로 구성하십시오.
* Protobuf 출력은 timestamp 값을 초 단위로 절삭합니다.
* Protobuf 출력은 1970-01-01 이전 날짜를 지원하지 않습니다. 값을 보존하려면
  명시적인 [structure](#structure)에서 `time` 컬럼을 `String`으로
  구성하십시오. (ClickHouse/ClickHouse#111860)

## 데이터 타입

[COPY](#copy-overloading)는 릴레이션의 Postgres 타입을 chDB 타입으로 매핑하고,
[CREATE TABLE](#create-table-overloading)은 URL의 chDB 타입을 Postgres 타입으로
매핑합니다.

### Postgres에서 chDB로

명시적인 [structure](#structure) 옵션이 없으면 chdb\_hook은
Postgres 타입을 적절한 chDB 대응 타입으로 매핑합니다. 매핑된 타입이 사용 사례에 맞지 않으면
[structure](#structure)를 지정하여 생성된 타입을
원하는 타입으로 재정의하십시오.

| PostgreSQL | chDB | 참고 |
| - | - | - |
| boolean | Bool | |
| smallint | Int16 | |
| integer | Int32 | |
| bigint | Int64 | |
| oid | UInt32 | |
| xid8 | UInt64 | |
| oid8 | UInt64 | |
| real | Float32 | |
| double precision | Float64 | |
| numeric | Decimal256(38) | 정밀도가 76자리를 초과하는 경우에도 이 타입으로 매핑됩니다. |
| numeric(12,6) | Decimal(12,6) | 정밀도(precision)와 스케일(scale)이 그대로 유지됩니다. |
| text | String | |
| bytea | String | |
| date | Date32 | |
| time | Time64(6) | 시간 타입을 지원하지 않는 포맷에서는 `String`으로 재정의하십시오. |
| timestamp | DateTime64(6, 'UTC') | 세션 시간대를 기준으로 변환됩니다. |
| timestamptz | DateTime64(6, 'UTC') | |
| interval | String | `IntervalDay`와 같은 `Interval` 단위로 재정의하십시오. |
| uuid | UUID | |
| json | String | 데이터가 객체로만 이루어진 경우 `JSON`으로 재정의하십시오. |
| jsonb | String | 데이터가 객체로만 이루어진 경우 `JSON`으로 재정의하십시오. |
| inet | String | 데이터가 IPv4 또는 IPv6 중 한 종류로만 이루어진 경우 `IPv4` 또는 `IPv6`으로 재정의하십시오. |
| point | Point | Postgres와 동일하게 두 좌표로 구성됩니다. |
| lseg | LineString | 정확히 두 점으로 이루어진 선입니다. |
| path | LineString | 닫힌 경로는 첫 번째 점이 끝에 반복됩니다. |
| polygon | Ring | Ring은 polygon과 마찬가지로 암묵적으로 닫힙니다. |
| box | Tuple(high Point, low Point) | 두 모서리 점이며, Postgres와 동일한 방식으로 정렬됩니다. |
| circle | Tuple(center Point, radius Float64) | |
| line | Tuple(a Float64, b Float64, c Float64) | 방정식 `Ax + By + C = 0`을 나타냅니다. |

`name`, `varchar`, `char`, `bit`, `timetz`, `money`, enum 등
이 표에 없는 타입은 `String`으로 매핑됩니다.

배열 타입은 매핑된 원소 타입의 `Array`로 매핑됩니다. ClickHouse는
널 허용 여부를 컬럼 단위로 제한하는 반면 Postgres는 배열 단위로 제한하므로, 원소는
항상 `Nullable`입니다.

`Map`, `Tuple`, `Nested`로 직접 매핑되는 Postgres 타입은 없지만,
[structure](#structure)나 소스 파일에서 이러한 타입을 지정할 수 있습니다. `Tuple`은
튜플 필드마다 원소 하나씩을 담는 `text[]`로 읽거나, 필드 타입을 보존하려면 구조가 일치하는 [composite
type]으로 읽을 수 있습니다. `Map` 또는 `Nested`는
키-값 쌍 또는 중첩 튜플마다 내부 배열 하나씩을 담는 `text[][]`로 읽거나,
구조가 일치하는 복합 타입의 배열로 읽을 수 있습니다. 예시는 [수동 타입
매핑](#manual-type-mappings)을 참조하십시오.

### 타임스탬프 변환

일반 텍스트 형식(TSV, CSV 등)에서 `COPY` 후크는 현재 `datestyle` 설정과 관계없이
DateTime 및 DateTime64 값을 ISO-8601 형식인 `YYYY-MM-DDThh:mm:ssZ`로 출력합니다.
이를 통해 값을 가져오는 소스가 다른 시간대를 사용하더라도 timestamptz 값이
일관되게 유지됩니다. `structure` 출력에서 `Datetime64(3,
'America/Los_Angeles')`와 같은 다른 유형을 사용해도 출력 오프셋에는 영향이 없으며,
정밀도만 달라집니다.

Timestamp TZ 예시:

| timestamptz | `DateTime64(6, 'UTC')` | `DateTime64(3 'Japan')` |
| - | - | - |
| `2026-08-28T12:00:00Z` | `2026-08-28T12:00:00.000000Z` | `2026-08-28T12:00:00.000Z` |
| `2026-08-28T11:00:00 America/Los_Angeles` | `2026-08-28T18:00:00.000000Z` | `2026-08-28T18:00:00.000Z` |
| `2026-08-28T10:00:00.723923 Asia/Tokyo` | `2026-08-28T01:00:00.723923Z` | `2026-08-28T01:00:00.723Z` |

또한 `COPY` 후크는 timestamp 값을 세션 시간대에서 UTC로 변환하므로, 값이 항상 UTC를
기준으로 출력됩니다. 이 값을 새로운 시스템에 로드하면 해당 시스템의 로컬 시간대로
변환됩니다. 따라서 시간대가 다르면 표시되는 값도 달라지지만, 시간대 차이를 감안하면
동일한 시점을 가리킵니다.

`timezone` 설정이 timestamp `2026-08-28T12:00:00`에 미치는 영향의 예시:

| timezone 설정 | `DateTime64(6, 'UTC')` | `DateTime64(3 'Japan')` |
| - | - | - |
| `UTC` | `2026-08-28T12:00:00.000000Z` | `2026-08-28T12:00:00.000Z` |
| `America/Los_Angeles` | `2026-08-28T19:00:00.000000Z` | `2026-08-28T19:00:00.000Z` |
| `America/New_York` | `2026-08-28T16:00:00.000000Z` | `2026-08-28T16:00:00.000Z` |
| `Japan` | `2026-08-28T03:00:00.000000Z` | `2026-08-28T03:00:00.000Z` |

### chDB에서 Postgres로

기존 테이블에서는 값이 선언된 컬럼 타입으로 변환됩니다.
다른 스칼라 타입은 가능한 경우 PostgreSQL의 명시적 형변환을 사용하며,
배열 원소는 선언된 원소 타입으로 변환됩니다. 지원되지 않는
변환이나 범위를 벗어난 값은 오류를 발생시킵니다.

chDB `Interval` 타입을 `smallint`, `integer` 또는 `bigint`에 매핑하면 해당 단위의 개수를
읽고 쓸 수 있습니다. 예를 들어 `IntervalDay` 값 `3`은
정수 `3`에 매핑됩니다. `IntervalNanosecond`를 `bigint`에 매핑하면 나노초 정밀도가
유지되지만, `interval`에 매핑하면 마이크로초 단위로 잘립니다.

chdb\_hook은 [`DESCRIBE`]가 보고하는 ClickHouse 타입을 기반으로 테이블을 생성할 때
아래의 기본 Postgres 타입을 사용합니다. 추가 읽기 대상은
명시적으로 선언된 컬럼에 적용되며, PostgreSQL 명시적 형변환 외의 변환도
포함합니다. 셀이 비어 있더라도 해당 형변환은 허용됩니다. 이 대상은 읽기에 해당하며,
쓰기에는 별도의 변환 규칙이 적용됩니다.

| chDB | 기본 PostgreSQL | 추가 읽기 대상 | 참고 |
| - | - | - | - |
| Array(T) | T\[] | | 중첩 깊이마다 PG 배열 타입 하나씩 사용 |
| BFloat16 | real | | |
| Bool | boolean | | |
| Date | date | | |
| Date32 | date | | |
| DateTime | timestamp with time zone | time | |
| DateTime64(P) | timestamp(P) with time zone | time | P가 6을 초과하면 6으로 제한됨 |
| Decimal(P,S) | numeric(P,S) | xid8, oid8 | |
| Decimal32(S) | numeric(9,S) | xid8, oid8 | |
| Decimal64(S) | numeric(18,S) | xid8, oid8 | |
| Decimal128(S) | numeric(38,S) | xid8, oid8 | |
| Decimal256(S) | numeric(76,S) | xid8, oid8 | |
| Enum8 | text | bytea; 입력 호환 타입 | 레이블로 디코딩됨. 레이블이 일치하는 PG enum도 사용 가능 |
| Enum16 | text | bytea; 입력 호환 타입 | 레이블로 디코딩됨. 레이블이 일치하는 PG enum도 사용 가능 |
| FixedString(N) | text | bytea; 입력 호환 타입 | bytea만 끝부분의 NUL 문자를 유지함 |
| Float32 | real | | |
| Float64 | double precision | | |
| IPv4 | inet | | |
| IPv6 | inet | | |
| Int8 | smallint | boolean | 0은 false, 0이 아닌 값은 true |
| Int16 | smallint | boolean | 0은 false, 0이 아닌 값은 true |
| Int32 | integer | | |
| Int64 | bigint | | |
| Int128 | numeric(39,0) | xid8, oid8 | |
| Int256 | numeric(77,0) | xid8, oid8 | |
| IntervalDay | interval | smallint, integer, bigint | 정수 타입에는 단위 개수가 저장됨 |
| IntervalHour | interval | smallint, integer, bigint | 정수 타입에는 단위 개수가 저장됨 |
| IntervalMicrosecond | interval | smallint, integer, bigint | 정수 타입에는 단위 개수가 저장됨 |
| IntervalMillisecond | interval | smallint, integer, bigint | 정수 타입에는 단위 개수가 저장됨 |
| IntervalMinute | interval | smallint, integer, bigint | 정수 타입에는 단위 개수가 저장됨 |
| IntervalMonth | interval | smallint, integer, bigint | 정수는 단위 개수로 받습니다 |
| IntervalNanosecond | interval | smallint, integer, bigint | 정수는 ns 단위를 유지하며, interval은 us 단위로 잘립니다 |
| IntervalQuarter | interval | smallint, integer, bigint | 정수는 단위 개수로 받습니다 |
| IntervalSecond | interval | smallint, integer, bigint | 정수는 단위 개수로 받습니다 |
| IntervalWeek | interval | smallint, integer, bigint | 정수는 단위 개수로 받습니다 |
| IntervalYear | interval | smallint, integer, bigint | 정수는 단위 개수로 받습니다 |
| JSON | jsonb | json, text, bytea; 입력 호환 타입 | jsonb는 문서를 정규화합니다 |
| LineString | path | lseg | lseg는 점 2개가 필요합니다 |
| LowCardinality(T) | T | | |
| Map(K,V) | text\[]\[] | composite\[], T\[]\[], text | 쌍마다 텍스트 항목 1개 행으로 표현됩니다 |
| MultiLineString | path\[] | | |
| MultiPolygon | polygon\[]\[] | | |
| Nested(...) | text\[]\[] | composite\[], T\[]\[], text | 중첩 행마다 텍스트 항목 1개 행으로 표현됩니다 |
| Nullable(T) | T | | 컬럼에 nullable이 설정됩니다 |
| Point | point | | |
| Polygon | polygon\[] | | |
| Ring | polygon | lseg | lseg는 점 2개가 필요합니다 |
| SimpleAggregateFunction(f,T) | T | | 값을 T로 저장합니다 |
| String | text | bytea; 입력 호환 타입 | bytea는 원시 바이트를 그대로 유지합니다 |
| Time | time without time zone | | |
| Time64(P) | time(P) without time zone | | P가 6을 초과하면 6으로 제한됩니다 |
| Tuple(...) | text\[] | composite, T\[], text; box, circle, line | 각 필드가 텍스트 항목으로 변환됩니다 |
| UInt8 | smallint | boolean | 0은 false, 0이 아닌 값은 true입니다 |
| UInt16 | integer | | |
| UInt32 | bigint | | |
| UInt64 | numeric(20,0) | xid8, oid8 | |
| UInt128 | numeric(39,0) | xid8, oid8 | |
| UInt256 | numeric(78,0) | xid8, oid8 | |
| UUID | uuid | | |

입력 호환 타입은 해당 PostgreSQL 입력 함수를 사용하여 문자열을 읽을 수 있습니다.
복합 타입은 필드의 구성과 순서가 일치해야 합니다. 튜플을 배열로 읽으려면 각 필드가 배열의 원소 타입으로 변환될 수 있어야 하며, 배열인 필드가 있어서는 안 됩니다.

배열로 읽을 때 `Map`은 키-값 쌍마다 한 행을, `Nested`는 중첩된 행마다 한 행을 사용합니다. 튜플을 `box`로 읽으려면 두 개의 점을 제공하고, `circle`로 읽으려면 점과 반지름을, `line`으로 읽으려면 세 개의 계수를 제공하십시오.

이 표에 없는 chDB 타입은 모두 오류를 발생시키며, `Variant`, `Dynamic`, `AggregateFunction`이 여기에 해당합니다. 이러한 타입을 텍스트로 읽으려면 해당 타입을 `String`으로 매핑하는 [structure](#structure)를 사용하십시오.

Postgres는 이러한 타입 중 일부에서 chDB보다 좁은 범위만 지원합니다. 따라서 24시간을 초과하는 `Time` 또는 `Time64` 값, 그리고 Postgres의 날짜 범위를 벗어난 `Date32` 값을 복사하면 오류가 발생합니다.

### 수동 타입 매핑

컬럼 추론 시 enum은 `text`로, `Tuple`, `Map`, `Nested` 값은 텍스트 배열로
처리됩니다. 필드 타입을 유지하거나 enum 값을 제한하려면 PostgreSQL enum 타입과
복합 타입(composite type)을 생성한 다음, 해당 타입으로 컬럼을 선언하십시오.
chDB 타입은 [structure](#structure)로 지정하거나, `structure 'auto'`를 사용하여
Postgres 테이블 대신 소스 데이터에서 추론하도록 할 수 있습니다. 예를
들어, 다음 타입을 사용하면 이벤트 상태, 좌표, 레이블,
중첩 항목을 로드할 수 있습니다.

```sql theme={null}
CREATE TYPE event_status AS ENUM ('new', 'done');
CREATE TYPE event_point AS (x integer, y integer);
CREATE TYPE event_label AS (key text, value bigint);
CREATE TYPE event_item AS (id integer, name text);

CREATE TABLE events (
    status event_status,
    point  event_point,
    labels event_label[],
    items  event_item[]
);

COPY events FROM 's3://chdb-lakedata-public/examples/events.parquet' (
    structure $$
        status Enum8('new' = 1, 'done' = 2),
        point  Tuple(Int32, Int32),
        labels Map(String, Int64),
        items  Array(Tuple(id Int32, name String))
    $$
);
```

복합 필드(composite field)는 호환되는 PostgreSQL 타입을 사용하여 chDB 필드 순서대로 선언하십시오. `Map` 값은 키 필드를 먼저 선언한 다음 값 필드를 선언하십시오. 각 `Tuple`은 하나의 복합 값이 되며, 각 `Map` 쌍 또는 `Nested` 행은 복합 배열의 원소 하나가 됩니다.

chDB는 명시적 `structure`에 포함된 `Nested`를 필드마다 하나의 `Array` 컬럼으로 분할합니다. 위 예시처럼 `Array(Tuple(...))`를 사용하여 중첩된 행을 하나의 복합 배열 컬럼으로 읽거나, 타입 정보가 포함된 chDB 소스 포맷이라면 `structure 'auto'`를 사용하십시오.

### 텍스트 인코딩

chDB는 `String`, `FixedString`, `Enum`, `JSON`을 바이트로 읽으며, 인코딩은 보장하지 않습니다. 이러한 컬럼을 `text` 또는 그 외 비바이너리 타입으로 복사하면 데이터베이스 인코딩을 기준으로 바이트를 검증하고, 표현할 수 없는 데이터에 대해서는 오류를 발생시킵니다:

```
ERROR:  invalid byte sequence for encoding "UTF8": 0x00
```

모든 인코딩은 NUL을 거부하며, Postgres는 이를 `text`에 저장할 수 없습니다.

chDB가 기록한 바이트를 그대로 유지하려면 `bytea`로 복사하십시오. [CREATE TABLE](#create-table-overloading)은 이러한 타입에 대해 `text`를 도출하므로, 다음과 같이 이름을 지정하십시오:

```sql theme={null}
CREATE TABLE logs (req_id numeric(20,0), resource bytea) WITH (
    copy_from = 's3://chdb-lakedata-public/logs/logs-2026-08-26.parquet'
);
```

`FixedString(N)`은 길이가 짧은 값을 NUL 바이트로 채웁니다. `text`로 복사하면
뒤쪽의 NUL이 삭제되지만, `bytea`는 N 바이트 전체를 유지합니다.

## 설정

### `chdb_hook.max_memory`

```sql theme={null}
SET chdb_hook.max_memory = '1 GB';
```

chDB 쿼리에 사용할 최대 메모리 양을 정의하며, chDB의
[`max_memory_usage`] 설정을 지정하는 데 사용됩니다. 슈퍼유저 권한이 필요합니다. 메가바이트 단위의
정수 값을 지정하거나 다음 메모리 단위 중 하나를 사용하십시오:

* `B` (바이트)
* `kB` (킬로바이트)
* `MB` (메가바이트)
* `GB` (기가바이트)
* `TB` (테라바이트)

기본값은 `0`이며, 이 경우 메모리를 제한하지 않습니다.

### `chdb_hook.max_threads`

```sql theme={null}
SET chdb_hook.max_threads = 4;
```

chDB 쿼리의 최대 쿼리 처리 스레드 수로, chDB [`max_threads`] 설정을 지정하는 데 사용됩니다. 슈퍼유저 권한이 필요합니다. 기본값은 `0`이며, 이 경우 chDB가 값을 자동으로 결정합니다.

대규모 `COPY`를 실행하기 전에 `chdb_hook.max_threads`를 설정하여 chDB가 CPU 사용량을 독점해 PostgreSQL의 성능이 저하되는 것을 방지할 것을 강력히 권장합니다.

### `chdb_hook.max_parsing_threads`

```sql theme={null}
SET chdb_hook.max_parsing_threads = 2;
```

chDB가 병렬 파싱을 지원하는 입력 형식의 데이터를 파싱할 때 사용할 수 있는 최대 스레드 수이며, chDB [`max_parsing_threads`]
설정을 지정하는 데 사용됩니다. 슈퍼유저 권한이 필요합니다. 기본값은 `0`이며, 이 경우 chDB가
값을 직접 결정합니다.

대량의 데이터를 `COPY`하기 전에 `chdb_hook.max_parsing_threads`를 설정하여
chDB가 PostgreSQL을 희생시키면서 CPU 사용량을 최대치까지 점유하지 않도록 하는 것을 권장합니다.

## 버전 관리 정책

chdb\_hook는 공개 릴리스에 [Semantic Versioning]을 따릅니다.

* 메이저 버전은 API 변경 시 증가합니다
* 마이너 버전은 하위 호환되는 SQL 변경 시 증가합니다
* 패치 버전은 바이너리 전용 변경 시 증가합니다

설치 후에는 Postgres 18의
[`pg_get_loaded_modules()`] 함수를 통해 PostgreSQL에서 버전을 확인할 수 있습니다.

```sql theme={null}
SELECT version FROM pg_get_loaded_modules() WHERE module_name = 'chdb_hook';
```

## 작성자

* [David E. Wheeler](https://justatheory.com/)
* [serprex](https://github.com/serprex)

## 저작권

Copyright (c) 2026, ClickHouse

[chDB]: https://clickhouse.com/chdb "chDB - 빠르고 안정적이며 확장 가능한 인프로세스 데이터베이스"

[Semantic Versioning]: https://semver.org/spec/v2.0.0.html "Semantic Versioning 2.0.0"

[COPY]: https://www.postgresql.org/docs/current/sql-copy.html "Postgres Docs: COPY"

[CREATE TABLE]: https://www.postgresql.org/docs/current/sql-createtable.html "Postgres Docs: CREATE TABLE"

[`DESCRIBE`]: https://clickhouse.com/docs/sql-reference/statements/describe-table "ClickHouse Docs: DESCRIBE TABLE"

[포맷]: https://github.com/chdb-io/chdb/blob/main/refs/clickhouse-formats-settings.md#complete-format-names-table "chDB Docs: Complete Format Names Table"

[access key ID and access secret]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html "AWS Identity and Access Management: Manage access keys for IAM users"

[HMAC key and secret]: https://docs.cloud.google.com/storage/docs/authentication/hmackeys "Google Cloud Storage: HMAC keys"

[access key]: https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-cli "Azure: Manage storage account access keys"

[row-level security]: https://www.postgresql.org/docs/current/ddl-rowsecurity.html "Postgres Docs: Row Security Policies"

[JSON type]: https://clickhouse.com/docs/reference/data-types/newjson.md "ClickHouse Docs: JSON Data Type"

[LOAD]: https://www.postgresql.org/docs/current/sql-load.html "Postgres Docs: LOAD"

[session_preload_libraries]: https://www.postgresql.org/docs/18/runtime-config-client.html#GUC-SESSION-PRELOAD-LIBRARIES "Postgres Docs: `session_preload_libraries`"

[shared_preload_libraries]: https://www.postgresql.org/docs/18/runtime-config-client.html#GUC-SESSION-PRELOAD-LIBRARIES "Postgres Docs: `shared_preload_libraries`"

[ALTER SYSTEM]: https://www.postgresql.org/docs/18/sql-altersystem.html "Postgres Docs: ALTER SYSTEM"

[ALTER DATABASE]: https://www.postgresql.org/docs/current/sql-alterdatabase.html "Postgres Docs: ALTER DATABASE"

[ALTER ROLE]: https://www.postgresql.org/docs/18/sql-alterrole.html "Postgres Docs: ALTER ROLE"

[AWS S3]: https://aws.amazon.com/s3/ "Cloud Object Storage - Amazon S3 - Amazon Web Services"

[Google Cloud Storage]: https://cloud.google.com/storage "Cloud Storage - Google Cloud"

[`file()`]: https://clickhouse.com/docs/sql-reference/table-functions/file "ClickHouse Docs: file Table Function"

[`url()`]: https://clickhouse.com/docs/sql-reference/table-functions/url "ClickHouse Docs: url Table Function"

[`s3()`]: https://clickhouse.com/docs/sql-reference/table-functions/s3 "ClickHouse Docs: s3 Table Function"

[`gcs()`]: https://clickhouse.com/docs/sql-reference/table-functions/gcs "ClickHouse Docs: gcs Table Function"

[Azure Blob Storage]: https://azure.microsoft.com/en-us/products/storage/blobs/

[Azure ABFS]: https://learn.microsoft.com/en-us/azure/storage/blobs/data-lake-storage-introduction-abfs-uri "Use the Azure Data Lake Storage URI (ABFS) - Azure Storage"

[`azureBlobStorage()`]: https://clickhouse.com/docs/sql-reference/table-functions/azureBlobStorage "ClickHouse Docs: azureBlobStorage Table Function"

[Hadoop Distributed File System]: https://en.wikipedia.org/wiki/Apache_Hadoop#Overview "Wikipedia: Apache Hadoop Overview"

[`hdfs()`]: https://clickhouse.com/docs/sql-reference/table-functions/hdfs "ClickHouse Docs: hdfs Table Function"

[ClickHouse 데이터 타입]: https://clickhouse.com/docs/reference/data-types/index.md "ClickHouse Docs: Data Types in ClickHouse"

[composite type]: https://www.postgresql.org/docs/current/rowtypes.html#ROWTYPES-DECLARING "PostgreSQL Docs: Declaring Composite Types"

[log_min_messages]: https://www.postgresql.org/docs/current/runtime-config-logging.html#GUC-LOG-MIN-MESSAGES "PostgreSQL Docs: log_min_messages"

[`pg_get_loaded_modules()`]: https://pgpedia.info/g/pg_get_loaded_modules.html "pgPedia: pg_get_loaded_modules()"

[`max_memory_usage`]: https://clickhouse.com/docs/reference/settings/session-settings/max-memory-usage "ClickHouse Docs: max_memory_usage_* session settings"

[`max_threads`]: https://clickhouse.com/docs/reference/settings/session-settings/max-threads "ClickHouse Docs: max_threads_* 세션 설정"

[`max_parsing_threads`]: https://clickhouse.com/docs/reference/settings/session-settings/max#max_parsing_threads "ClickHouse Docs: max_parsing_threads 세션 설정"

[database encoding]: https://www.postgresql.org/docs/current/multibyte.html "PostgreSQL Docs: 문자 집합 지원"
