HTTP 인터페이스

HTTP 인터페이스 (HTTP interface)

HTTP 인터페이스는 ClickHouse를 REST API 형태로 어떤 플랫폼에서든 어떤 프로그래밍 언어로든 사용할 수 있게 해 줘요. 네이티브 인터페이스보다는 제한적이지만 언어 지원이 더 좋아요.

출처: 문서

본문

사전 준비 (Prerequisites)

이 문서의 예시를 위해서는 다음이 필요해요:

  • 실행 중인 ClickHouse 서버 인스턴스
  • curl 설치. Ubuntu 또는 Debian에서 sudo apt install curl을 실행하거나 이 문서를 참고해 설치 지침을 확인하세요.

개요 (Overview)

HTTP 인터페이스는 REST API 형태로 어떤 플랫폼에서든 어떤 프로그래밍 언어로든 ClickHouse를 사용할 수 있게 해 줘요. HTTP 인터페이스는 네이티브 인터페이스보다 제한적이지만 언어 지원이 더 좋아요.

기본적으로 clickhouse-server는 다음 포트를 듣습니다:

  • HTTP용 8123 포트
  • HTTPS용 8443 포트 활성화 가능

파라미터 없이 GET / 요청을 하면 200 응답 코드와 함께 문자열 "Ok."이 반환돼요:

$ curl 'http://localhost:8123/'
Ok.

"Ok."는 http_server_default_response에 정의된 기본값이며 원하면 변경할 수 있어요.

또한 참고: HTTP 응답 코드 주의 사항.

웹 사용자 인터페이스 (Web user interface)

ClickHouse는 다음 주소에서 접근할 수 있는 웹 사용자 인터페이스를 포함해요:

http://localhost:8123/play

웹 UI는 쿼리 실행 중 진행 표시, 쿼리 취소, 결과 스트리밍을 지원해요. 쿼리 파이프라인을 위한 차트와 그래프를 표시하는 비밀 기능도 있어요.

쿼리가 성공적으로 실행된 후 다운로드 버튼이 나타나며, CSV, TSV, JSON, JSONLines, Parquet, Markdown 또는 ClickHouse가 지원하는 어떤 사용자 정의 포맷으로든 쿼리 결과를 다운로드할 수 있어요. 다운로드 기능은 쿼리 캐시를 사용해 쿼리를 재실행하지 않고 효율적으로 결과를 가져와요. UI가 여러 페이지 중 한 페이지만 표시했더라도 전체 결과 세트를 다운로드해요.

웹 UI는 여러분 같은 전문가를 위해 설계됐어요.

헬스 체크 스크립트에서는 GET /ping 요청을 사용하세요. 이 핸들러는 항상 "Ok."을 반환해요(끝에 줄바꿈 포함). 버전 18.12.13부터 사용 가능. 복제본 지연을 확인하려면 /replicas_status도 참고하세요.

$ curl 'http://localhost:8123/ping'
Ok.
$ curl 'http://localhost:8123/replicas_status'
Ok.

HTTP/HTTPS로 쿼리하기 (Querying over HTTP/HTTPS)

HTTP/HTTPS로 쿼리하는 방법은 세 가지가 있어요:

  • URL 'query' 파라미터로 요청 보내기
  • POST 메서드 사용.
  • 'query' 파라미터에 쿼리 시작 부분을 보내고 나머지는 POST로 보내기

참고: URL의 크기는 기본적으로 1MiB로 제한되며, http_max_uri_size 설정으로 변경할 수 있어요.

성공하면 200 응답 코드와 함께 응답 본문에 결과를 받아요. 오류가 발생하면 500 응답 코드와 함께 응답 본문에 오류 설명 텍스트를 받아요.

GET을 사용한 요청은 'readonly'예요. 즉 데이터를 수정하는 쿼리에는 POST 메서드만 사용할 수 있어요. 쿼리 자체는 POST 본문이나 URL 파라미터에 보낼 수 있어요. 몇 가지 예를 살펴볼게요.

아래 예시에서 curl은 SELECT 1 쿼리를 보내는 데 사용돼요. 공백에 URL 인코딩 %20이 사용된 것에 주목하세요.

curl 'http://localhost:8123/?query=SELECT%201'
1

이 예시에서 wget은 결과를 터미널에 출력하기 위해 -nv(non-verbose)와 -O- 파라미터와 함께 사용돼요. 이 경우 공백에 URL 인코딩을 사용할 필요가 없어요:

wget -nv -O- 'http://localhost:8123/?query=SELECT 1'
1

이 예시에서는 raw HTTP 요청을 netcat에 파이프해요:

echo -ne 'GET /?query=SELECT%201 HTTP/1.0\r\n\r\n' | nc localhost 8123
HTTP/1.0 200 OK
X-ClickHouse-Summary: {"read_rows":"1","read_bytes":"1","written_rows":"0","written_bytes":"0","total_rows_to_read":"1","result_rows":"0","result_bytes":"0","elapsed_ns":"4505959","memory_usage":"1111711"}
Date: Tue, 11 Nov 2025 18:16:01 GMT
Connection: Close
Content-Type: text/tab-separated-values; charset=UTF-8
Access-Control-Expose-Headers: X-ClickHouse-Query-Id,X-ClickHouse-Summary,X-ClickHouse-Server-Display-Name,X-ClickHouse-Format,X-ClickHouse-Timezone,X-ClickHouse-Exception-Code,X-ClickHouse-Exception-Tag
X-ClickHouse-Server-Display-Name: MacBook-Pro.local
X-ClickHouse-Query-Id: ec0d8ec6-efc4-4e1d-a14f-b748e01f5294
X-ClickHouse-Format: TabSeparated
X-ClickHouse-Timezone: Europe/London
X-ClickHouse-Exception-Tag: dngjzjnxkvlwkeua

1

보시다시피 curl 명령은 공백을 URL 이스케이프해야 해서 다소 불편해요. wget은 스스로 모든 것을 이스케이프하지만, keep-alive와 Transfer-Encoding: chunked를 사용할 때 HTTP 1.1에서 잘 동작하지 않으므로 사용을 권장하지 않아요.

$ echo 'SELECT 1' | curl 'http://localhost:8123/' --data-binary @-
1

$ echo 'SELECT 1' | curl 'http://localhost:8123/?query=' --data-binary @-
1

$ echo '1' | curl 'http://localhost:8123/?query=SELECT' --data-binary @-
1

쿼리의 일부가 파라미터로, 일부가 POST로 보내지면 이 두 데이터 부분 사이에 줄바꿈이 삽입돼요. 예를 들어 이것은 동작하지 않아요:

$ echo 'ECT 1' | curl 'http://localhost:8123/?query=SEL' --data-binary @-
Code: 59, e.displayText() = DB::Exception: Syntax error: failed at position 0: SEL
ECT 1
, expected One of: SHOW TABLES, SHOW DATABASES, SELECT, INSERT, CREATE, ATTACH, RENAME, DROP, DETACH, USE, SET, OPTIMIZE., e.what() = DB::Exception

기본적으로 데이터는 TabSeparated 포맷으로 반환돼요.

다른 포맷을 요청하려면 쿼리에서 FORMAT 절을 사용해요. 예를 들어:

wget -nv -O- 'http://localhost:8123/?query=SELECT 1, 2, 3 FORMAT JSON'
{
    "meta":
    [
        {
            "name": "1",
            "type": "UInt8"
        },
        {
            "name": "2",
            "type": "UInt8"
        },
        {
            "name": "3",
            "type": "UInt8"
        }
    ],

    "data":
    [
        {
            "1": 1,
            "2": 2,
            "3": 3
        }
    ],

    "rows": 1,

    "statistics":
    {
        "elapsed": 0.000515,
        "rows_read": 1,
        "bytes_read": 1
    }
}

TabSeparated 외의 기본 포맷을 지정하려면 default_format URL 파라미터를 사용할 수 있어요. X-ClickHouse-Format 헤더는 응답의 포맷을 명시적으로 선택해요. 이는 output_format 설정의 별칭이므로 쿼리의 FORMAT 절도 재정의해요. INSERT의 요청 본문이 어떻게 파싱되는지는 절대 바꾸지 않아요. 그 용도는 input_format 또는 format을 사용하세요.

$ echo 'SELECT 1 FORMAT Pretty' | curl 'http://localhost:8123/?' --data-binary @-
┏━━━┓
┃ 1 ┃
┡━━━┩
│ 1 │
└───┘

POST 메서드를 파라미터화된 쿼리와 함께 사용할 수 있어요. 파라미터는 이름과 타입과 함께 {name:Type}처럼 중괄호로 지정돼요. 파라미터 값은 param_name으로 전달돼요:

$ curl -X POST -F 'query=select {p1:UInt8} + {p2:UInt8}' -F "param_p1=3" -F "param_p2=4" 'http://localhost:8123/'

7

URL 경로로 테이블 접근 및 쿼리 구성 (Access tables through URL paths and construct queries)

ClickHouse 26.8 이상에서 사용 가능.

HTTP 인터페이스는 URL 경로를 데이터베이스, 테이블, 출력 포맷, 압축 방법으로 해석할 수 있어요. URL 파라미터가 결과를 만들 수 있으므로, 간단한 읽기 요청은 SQL이 필요 없어요. 이 기능들은 기본적으로 비활성화되어 있으며 명시적으로 활성화해야 해요.

경로 라우팅 활성화 (Enable path routing)

경로 해석은 두 수준에서 게이팅돼요:

  1. HTTP 인터페이스가 경로 스타일 요청을 쿼리 핸들러로 라우팅할 수 있게 하는 서버 레벨 http_allow_path_requests 구성 설정을 활성화:
<clickhouse>
    <http_allow_path_requests>1</http_allow_path_requests>
</clickhouse>
  1. 필요한 사용자별 설정을 활성화:
설정 효과
http_allow_table_as_file 마지막 경로 구성 요소를 table, table.format 또는 table.format.compression으로 해석.
http_allow_database_as_path 선행 /database/ 경로 구성 요소를 현재 데이터베이스로 해석.
http_allow_filters_as_path Hive 스타일 /name=value/ 경로 구성 요소를 WHERE 필터로 해석.
http_allow_filters_as_unrecognized_url_parameters 인식되지 않은 URL 파라미터를 WHERE 필터로 해석.

참고: 라우팅은 인증 전에 발생하므로 사용자별 설정에 의존할 수 없어요. http_allow_path_requests가 비활성화되면 알 수 없는 경로는 인증 전 404를 반환해요. 라우팅과 인증 후에는 사용자별 설정이 경로가 어떻게 해석되는지 결정하므로, 사용자, 역할 또는 프로필에 대해 선택적으로 기능을 활성화할 수 있어요.

테이블을 파일로 접근 (Access tables as files)

http_allow_table_as_file이 활성화되면 /table.format.compression에 대한 요청이 SELECT * FROM table로 처리돼요. /database/table.format.compression을 사용하려면 http_allow_database_as_path를 활성화하세요.

# SELECT * FROM my_db.hits, formatted as CSV
curl 'http://localhost:8123/my_db/hits.csv'

# The same result, compressed with gzip
curl 'http://localhost:8123/my_db/hits.csv.gz' | gzip -d

포맷과 압축 확장자는 대소문자를 구분하지 않고 인식되므로 hits.csvhits.CSV는 동등해요.

명시적 format 또는 output_format URL 파라미터는 경로의 포맷을 재정의해요. default_format는 그렇지 않아요. 다른 어떤 것도 포맷을 선택하지 않을 때 사용할 포맷만 제공하며, 경로 확장자가 더 구체적이에요. 명시적 compression 파라미터는 경로의 압축과 일치해야 해요. 충돌하는 값은 예외가 발생해요.

쿼리 구성 (Construct a query)

다음 설정은 기본 쿼리를 파생 테이블로 감싸요. 그것들은 서로 및 기존 쿼리와 구성돼요. HTTP URL 파라미터로, 쿼리 내 SETTINGS 절로, 또는 사용자 프로필을 통해 제공할 수 있어요.

설정 효과
select SELECT <expression_list> FROM (…)
filter WHERE <expression> 추가. 여러 filter URL 파라미터는 AND로 결합.
order ORDER BY <expression_list> 추가.
sort 식별자나 열 위치의 쉼표 구분 목록에서 ORDER BY 추가. 각각 선택적 +(오름차순) 또는 -(내림차순) 접두사 사용, 예: sort=a,-b. order와 결합할 수 없음.
limitoffset LIMIT <n> OFFSET <m> 추가. 둘 다 꼬리 선택을 위한 음수 값과 행의 비율을 나타내는 분수 값을 받아들임.
page offset = limit * (page - 1) 설정. limit이 필요하며 offset와 결합할 수 없음.
# Filter my_db.hits, sort by a descending, and return the first 10 rows
curl 'http://localhost:8123/my_db/hits?filter=a%3E0&sort=-a&limit=10'

# Return the second page of 100 rows
curl 'http://localhost:8123/my_db/hits?limit=100&page=2'
기존 쿼리 수정 (Modify an existing query)

구성 설정은 경로 요청에만 국한되지 않아요. 직접 작성한 쿼리도 만들 수 있어요. 쿼리는 파생 테이블이 되므로 설정은 그 안에 병합되는 것이 아니라 그 결과에 적용돼요. 예를 들어 ?query=SELECT a, b FROM hits&filter=a > 0&sort=-b&limit=10은 이렇게 실행돼요:

SELECT * FROM (SELECT a, b FROM hits) WHERE a > 0 ORDER BY b DESC LIMIT 10

이것은 다른 곳에 고정된 쿼리 — 저장된 대시보드 쿼리, predefined_query_handler, URL 파라미터만 추가할 수 있는 클라이언트 — 의 결과를 페이지로 나누거나, 정렬하거나, 좁히는 데 유용해요:

# Page through the result of an arbitrary query without editing the query text
curl 'http://localhost:8123/?query=SELECT+a,+b+FROM+hits+GROUP+BY+a,+b&limit=100&page=3'

# Keep only some of the resulting columns and rows
curl -G 'http://localhost:8123/' --data-urlencode 'query=SELECT * FROM hits' --data-urlencode 'select=a, b' --data-urlencode 'filter=b != 0'

# The last 5 rows of an aggregate (a negative `limit` selects from the tail)
curl 'http://localhost:8123/?query=SELECT+a,+count()+FROM+hits+GROUP+BY+a+ORDER+BY+a&limit=-5'

설정이 쿼리를 다시 쓰는 대신 감싸므로, FORMAT 절과 쿼리 내 ORDER BY와 구성되며 INSERT가 쓰는 행을 절대 바꾸지 않아요. INSERT ... SELECT 문에 놓인 구성 설정은 문 자신의 (빈) 결과를 만들지, 그 소스 SELECT를 만들지 않아요. 소스를 만들려면 설정을 SELECT 자신의 SETTINGS 절에 넣으세요:

-- Inserts all 10 rows: `limit` applies to the INSERT statement, not to its SELECT
INSERT INTO t SETTINGS limit = 2 SELECT number FROM numbers(10);

-- Inserts 2 rows: `limit` applies to the SELECT itself
INSERT INTO t SELECT number FROM numbers(10) SETTINGS limit = 2;

경고: filter는 접근 제어 메커니즘이 아니에요. 그것은 래핑 하위 쿼리 위에 WHERE 절을 추가하므로, 필터가 적용되기 전에 기본 데이터를 읽고 처리할 수 있어요. 사용자가 접근할 수 있는 행을 제한하려면 행 정책(row policies) 또는 additional_table_filters를 사용하세요.

포맷 및 압축 재정의 (Override formats and compression)

설정 효과
output_format 출력 포맷을 재정의. 쿼리의 FORMAT 절, 경로 확장자, format, default_format보다 우선. X-ClickHouse-Format 헤더로도 설정 가능.
input_format INSERT의 입력 포맷을 재정의. 쿼리의 FORMAT 절과 format보다 우선.
format 방향별 설정이 제공되지 않는 한 양방향으로 포맷 재정의.
default_format 쿼리에 FORMAT 절, 경로 확장자, 다른 포맷 재정의가 없을 때 출력 포맷을 설정.
compression 응답 본문을 압축, 예: compression=gz. HTTP Content-Encoding 및 ClickHouse 네이티브 compress 파라미터와 독립적.

포맷 설정은 네이티브 클라이언트와 clickhouse-local을 포함한 다른 프로토콜에서도 작동해요. input_formatoutput_format--input-format--output-format 옵션에 해당해요. --format 옵션은 clickhouse-local에서는 양방향 format 설정에 매핑되고, clickhouse-client에서는 역사적 출력 전용 의미를 유지하며 output_format에 매핑돼요.

compression은 HTTP 응답 형성에 한정되며 쿼리 실행 전에 소비돼요. HTTP URL 파라미터, 경로 확장자 또는 사용자 프로필로 제공하세요. 쿼리 내 SETTINGS 절에서는 거부돼요.

바이너리 및 압축된 HTTP 응답에는 Content-Disposition: attachment; filename=… 헤더가 포함돼요. 파일 이름은 URL 경로에서 파생되거나, 경로가 제공하지 않을 때 result.<format>.<compression>을 사용해요.

경로 테이블을 쿼리와 함께 사용 (Use a path table with a query)

경로가 테이블을 식별하고 query 파라미터도 제공하면, 경로 테이블은 implicit_table_at_top_level을 통해 노출돼요. FROM 절이 없는 SELECT는 경로 테이블에서 읽어요:

# Read columns a and b from my_db.hits without a FROM clause
curl 'http://localhost:8123/my_db/hits.CSV?query=SELECT+a,+b'

쿼리에 이미 FROM 절이 있으면 경로 구성 요소는 다운로드 파일 이름만 제공해요.

HTTP/HTTPS를 통한 INSERT 쿼리 (Insert queries over HTTP/HTTPS)

INSERT 쿼리에는 데이터를 전송하는 POST 메서드가 필요해요. 이 경우 URL 파라미터에 쿼리 시작 부분을 쓰고 POST로 삽입할 데이터를 전달할 수 있어요. 삽입할 데이터는 예를 들어 MySQL의 탭으로 구분된 덤프일 수 있어요. 이렇게 INSERT 쿼리는 MySQL의 LOAD DATA LOCAL INFILE을 대체해요.

예시 (Examples)

테이블을 만들려면:

$ echo 'CREATE TABLE t (a UInt8) ENGINE = Memory' | curl 'http://localhost:8123/' --data-binary @-

익숙한 INSERT 쿼리로 데이터 삽입에 사용하려면:

$ echo 'INSERT INTO t VALUES (1),(2),(3)' | curl 'http://localhost:8123/' --data-binary @-

쿼리와 별도로 데이터를 보내려면:

$ echo '(4),(5),(6)' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20VALUES' --data-binary @-

어떤 데이터 포맷이든 지정할 수 있어요. 예를 들어 INSERT INTO t VALUES를 쓸 때 사용되는 것과 같은 'Values' 포맷을 지정할 수 있어요:

$ echo '(7),(8),(9)' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20FORMAT%20Values' --data-binary @-

탭으로 구분된 덤프에서 데이터를 삽입하려면 해당 포맷을 지정해요:

$ echo -ne '10\n11\n12\n' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20FORMAT%20TabSeparated' --data-binary @-

테이블 내용을 읽으려면:

$ curl 'http://localhost:8123/?query=SELECT%20a%20FROM%20t'
7
8
9
10
11
12
1
2
3
4
5
6

참고: 병렬 쿼리 처리로 인해 데이터는 임의의 순서로 출력돼요.

테이블을 삭제하려면:

$ echo 'DROP TABLE t' | curl 'http://localhost:8123/' --data-binary @-

데이터 테이블을 반환하지 않는 성공적인 요청에는 빈 응답 본문이 반환돼요.

압축 (Compression)

압축은 많은 양의 데이터를 전송할 때 네트워크 트래픽을 줄이거나, 즉시 압축된 덤프를 만드는 데 사용할 수 있어요.

데이터 전송 시 ClickHouse 내부 압축 포맷을 사용할 수 있어요. 압축된 데이터는 비표준 포맷이며, 그것을 다루려면 clickhouse-compressor 프로그램이 필요해요. clickhouse-client 패키지와 함께 기본 설치돼요.

데이터 삽입의 효율을 높이려면 http_native_compression_disable_checksumming_on_decompress 설정을 사용해 서버 측 체크섬 검증을 비활성화하세요.

URL에 compress=1을 지정하면 서버가 보내는 데이터를 압축해요. URL에 decompress=1을 지정하면 서버가 POST 메서드로 전달하는 데이터를 압축 해제해요.

HTTP 압축을 선택할 수도 있어요. ClickHouse는 다음 압축 방법을 지원해요:

  • gzip
  • br
  • deflate
  • xz
  • zstd
  • lz4
  • bz2
  • snappy

압축된 POST 요청을 보내려면 요청 헤더 Content-Encoding: compression_method를 추가하세요.

ClickHouse가 응답을 압축하게 하려면 요청에 Accept-Encoding: compression_method 헤더를 추가하세요.

모든 압축 방법에 대해 http_zlib_compression_level 설정으로 데이터 압축 수준을 구성할 수 있어요.

정보: 일부 HTTP 클라이언트는 기본적으로 서버의 데이터를 압축 해제할 수 있으므로(gzip 및 deflate) 압축 설정을 올바르게 사용해도 압축 해제된 데이터를 받을 수 있어요.

예시 (Examples)

서버에 압축된 데이터를 보내려면:

echo "SELECT 1" | gzip -c | \
curl -sS --data-binary @- -H 'Content-Encoding: gzip' 'http://localhost:8123/'

서버에서 압축된 데이터 아카이브를 받으려면:

curl -vsS "http://localhost:8123/?enable_http_compression=1" \
-H 'Accept-Encoding: gzip' --output result.gz -d 'SELECT number FROM system.numbers LIMIT 3'

zcat result.gz
0
1
2

gunzip으로 압축 해제된 데이터를 받으려면:

curl -sS "http://localhost:8123/?enable_http_compression=1" \
-H 'Accept-Encoding: gzip' -d 'SELECT number FROM system.numbers LIMIT 3' | gunzip -
0
1
2

기본 데이터베이스 (Default database)

database URL 파라미터나 X-ClickHouse-Database 헤더로 기본 데이터베이스를 지정할 수 있어요.

echo 'SELECT number FROM numbers LIMIT 10' | curl 'http://localhost:8123/?database=system' --data-binary @-
0
1
2
3
4
5
6
7
8
9

기본적으로 서버 설정에 등록된 데이터베이스가 기본 데이터베이스로 사용돼요. 즉시 사용 가능한 상태에서는 default라는 데이터베이스예요. 또는 테이블 이름 앞에 점을 사용해 항상 데이터베이스를 지정할 수 있어요.

인증 (Authentication)

사용자 이름과 비밀번호는 세 가지 방법 중 하나로 표시할 수 있어요:

  1. HTTP 기본 인증(Basic Authentication) 사용.

예를 들어:

echo 'SELECT 1' | curl 'http://user:password@localhost:8123/' -d @-
  1. userpassword URL 파라미터에

경고: 파라미터가 웹 프록시에 기록되고 브라우저에 캐시될 수 있으므로 이 방법을 권장하지 않아요.

예를 들어:

echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @-
  1. 'X-ClickHouse-User'와 'X-ClickHouse-Key' 헤더 사용

예를 들어:

echo 'SELECT 1' | curl -H 'X-ClickHouse-User: user' -H 'X-ClickHouse-Key: password' 'http://localhost:8123/' -d @-

사용자 이름이 지정되지 않으면 default 이름이 사용돼요. 비밀번호가 지정되지 않으면 빈 비밀번호가 사용돼요. URL 파라미터로 단일 쿼리 또는 전체 설정 프로필을 처리하기 위한 설정을 지정할 수도 있어요.

예를 들어:

http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1
$ echo 'SELECT number FROM system.numbers LIMIT 10' | curl 'http://localhost:8123/?' --data-binary @-
0
1
2
3
4
5
6
7
8
9

자세한 내용은 다음을 참고해요:

HTTP 프로토콜에서 ClickHouse 세션 사용 (Using ClickHouse sessions in the HTTP protocol)

HTTP 프로토콜에서도 ClickHouse 세션을 사용할 수 있어요. 그러려면 요청에 session_id GET 파라미터를 추가해야 해요. 세션 ID로 어떤 문자열이든 사용할 수 있어요.

기본적으로 세션은 60초의 비활성 후 종료돼요. 이 타임아웃(초)을 바꾸려면 서버 설정의 default_session_timeout 설정을 수정하거나 요청에 session_timeout GET 파라미터를 추가하세요.

세션 상태를 확인하려면 session_check=1 파라미터를 사용하세요. 단일 세션 안에서는 한 번에 하나의 쿼리만 실행할 수 있어요.

X-ClickHouse-Progress 응답 헤더에서 쿼리 진행 정보를 받을 수 있어요. 그러려면 send_progress_in_http_headers를 활성화하세요.

헤더 시퀀스의 예시:

X-ClickHouse-Progress: {"read_rows":"261636","read_bytes":"2093088","total_rows_to_read":"1000000","elapsed_ns":"14050417","memory_usage":"22205975"}
X-ClickHouse-Progress: {"read_rows":"654090","read_bytes":"5232720","total_rows_to_read":"1000000","elapsed_ns":"27948667","memory_usage":"83400279"}
X-ClickHouse-Progress: {"read_rows":"1000000","read_bytes":"8000000","total_rows_to_read":"1000000","elapsed_ns":"38002417","memory_usage":"80715679"}

가능한 헤더 필드:

헤더 필드 설명
read_rows 읽은 행 수.
read_bytes 읽은 데이터 양(바이트).
total_rows_to_read 읽을 총 행 수.
written_rows 쓴 행 수.
written_bytes 쓴 데이터 양(바이트).
elapsed_ns 쿼리 실행 시간(나노초).
memory_usage 쿼리가 사용한 메모리(바이트). (v25.11부터 사용 가능)

HTTP 연결이 끊겨도 실행 중인 요청은 자동으로 멈추지 않아요. 파싱과 데이터 포맷은 서버 측에서 수행되며, 네트워크 사용이 비효율적일 수 있어요.

다음 선택적 파라미터가 존재해요:

파라미터 설명
query_id (선택) 쿼리 ID(어떤 문자열)로 전달할 수 있음. replace_running_query
quota_key (선택) 할당량 키(어떤 문자열)로 전달할 수 있음. "할당량"

HTTP 인터페이스는 쿼리용 외부 데이터(외부 임시 테이블) 전달을 허용해요. 자세한 내용은 "쿼리 처리를 위한 외부 데이터"를 참고해요.

응답 버퍼링 (Response buffering)

응답 버퍼링은 서버 측에서 활성화할 수 있어요. 이를 위해 다음 URL 파라미터가 제공돼요:

  • buffer_size
  • wait_end_of_query

다음 설정을 사용할 수 있어요:

buffer_size는 서버 메모리에 버퍼링할 결과의 바이트 수를 결정해요. 결과 본문이 이 임계값보다 크면 버퍼가 HTTP 채널에 쓰여지고 나머지 데이터는 HTTP 채널로 직접 전송돼요.

전체 응답이 버퍼링되도록 하려면 wait_end_of_query=1을 설정하세요. 이 경우 메모리에 저장되지 않은 데이터는 임시 서버 파일에 버퍼링돼요.

예를 들어:

curl -sS 'http://localhost:8123/?max_result_bytes=4000000&buffer_size=3000000&wait_end_of_query=1' -d 'SELECT toUInt8(number) FROM system.numbers LIMIT 9000000 FORMAT RowBinary'

팁: 응답 코드와 HTTP 헤더가 클라이언트에 전송된 후 쿼리 처리 오류가 발생하는 상황을 피하려면 버퍼링을 사용하세요. 이 상황에서 오류 메시지가 응답 본문 끝에 쓰여지고, 클라이언트 측에서는 파싱 단계에서만 오류를 감지할 수 있어요.

쿼리 파라미터로 역할 설정 (Setting a role with query parameters)

이 기능은 ClickHouse 24.4에서 추가됐어요.

특정 시나리오에서는 문 자체를 실행하기 전에 먼저 부여된 역할을 설정해야 할 수 있어요. 하지만 다중 문이 허용되지 않으므로 SET ROLE과 문을 함께 보낼 수 없어요:

curl -sS "http://localhost:8123" --data-binary "SET ROLE my_role;SELECT * FROM my_table;"

위 명령은 오류를 발생시켜요:

Code: 62. DB::Exception: Syntax error (Multi-statements are not allowed)

이 제한을 극복하려면 대신 role 쿼리 파라미터를 사용하세요:

curl -sS "http://localhost:8123?role=my_role" --data-binary "SELECT * FROM my_table;"

이것은 문 전에 SET ROLE my_role을 실행하는 것과 동일해요.

또한 여러 role 쿼리 파라미터를 지정할 수 있어요:

curl -sS "http://localhost:8123?role=my_role&role=my_other_role" --data-binary "SELECT * FROM my_table;"

이 경우 ?role=my_role&role=my_other_role은 문 전에 SET ROLE my_role, my_other_role을 실행하는 것과 비슷하게 동작해요.

HTTP 응답 코드 주의 사항 (HTTP response codes caveats)

HTTP 프로토콜의 제한 때문에 HTTP 200 응답 코드가 쿼리가 성공했음을 보장하지 않아요.

예시:

curl -v -Ss "http://localhost:8123/?max_block_size=1&query=select+sleepEachRow(0.001),throwIf(number=2)from+numbers(5)"
*   Trying 127.0.0.1:8123...
...
< HTTP/1.1 200 OK
...
Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(equals(number, 2) :: 1) -> throwIf(equals(number, 2))

이 동작의 이유는 HTTP 프로토콜의 특성 때문이에요. HTTP 헤더가 HTTP 코드 200으로 먼저 전송되고, 그 다음 HTTP 본문이 오고, 그 다음 오류가 평문 텍스트로 본문에 주입돼요.

이 동작은 사용된 포맷이 Native, TSV, JSON 중 무엇이든 무관해요. 오류 메시지는 항상 응답 스트림 중간에 있어요.

wait_end_of_query=1(응답 버퍼링)을 활성화하면 이 문제를 완화할 수 있어요. 이 경우 HTTP 헤더 전송이 전체 쿼리가 해결될 때까지 지연돼요. 하지만 이는 결과가 여전히 http_response_buffer_size에 들어맞아야 하고, send_progress_in_http_headers 같은 다른 설정이 헤더 지연을 방해할 수 있으므로 문제를 완전히 해결하지는 못해요.

팁: 모든 오류를 잡는 유일한 방법은 필요 포맷으로 파싱하기 전에 HTTP 본문을 분석하는 것이에요.

ClickHouse의 이러한 예외는 http_write_exception_in_output_format=0(기본값)일 때 사용된 포맷(예: Native, TSV, JSON 등)과 무관하게 아래와 같이 일관된 예외 포맷을 가져요. 이렇게 하면 클라이언트 측에서 오류 메시지를 쉽게 파싱하고 추출할 수 있어요.

\r\n
__exception__\r\n
<TAG>\r\n
<error message>\r\n
<message_length> <TAG>\r\n
__exception__\r\n

여기서 <TAG>는 16바이트 임의 태그이며, X-ClickHouse-Exception-Tag 응답 헤더로 보내지는 것과 같은 태그예요. <error message>는 실제 예외 메시지(정확한 길이는 <message_length>에서 찾을 수 있음). 위에 설명된 전체 예외 블록은 최대 16KiB까지 될 수 있어요.

JSON 포맷의 예시:

$ curl -v -Ss "http://localhost:8123/?max_block_size=1&query=select+sleepEachRow(0.001),throwIf(number=2)from+numbers(5)+FORMAT+JSON"
...
{
    "meta":
    [
        {
            "name": "sleepEachRow(0.001)",
            "type": "UInt8"
        },
        {
            "name": "throwIf(equals(number, 2))",
            "type": "UInt8"
        }
    ],

    "data":
    [
        {
            "sleepEachRow(0.001)": 0,
            "throwIf(equals(number, 2))": 0
        },
        {
            "sleepEachRow(0.001)": 0,
            "throwIf(equals(number, 2))": 0
        }
__exception__
dmrdfnujjqvszhav
Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(equals(__table1.number, 2_UInt8) :: 1) -> throwIf(equals(__table1.number, 2_UInt8)) UInt8 : 0'. (FUNCTION_THROW_IF_VALUE_IS_NON_ZERO) (version 25.11.1.1)
262 dmrdfnujjqvszhav
__exception__

CSV 포맷의 유사한 예시:

$ curl -v -Ss "http://localhost:8123/?max_block_size=1&query=select+sleepEachRow(0.001),throwIf(number=2)from+numbers(5)+FORMAT+CSV"
...
<
0,0
0,0

__exception__
rumfyutuqkncbgau
Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(equals(__table1.number, 2_UInt8) :: 1) -> throwIf(equals(__table1.number, 2_UInt8)) UInt8 : 0'. (FUNCTION_THROW_IF_VALUE_IS_NON_ZERO) (version 25.11.1.1)
262 rumfyutuqkncbgau
__exception__

파라미터가 있는 쿼리 (Queries with parameters)

파라미터가 있는 쿼리를 만들고 해당 HTTP 요청 파라미터에서 값을 전달할 수 있어요. 자세한 내용은 CLI용 파라미터 쿼리를 참고해요.

예시 (Example)

$ curl -sS "<address>?param_id=2&param_phrase=test" -d "SELECT * FROM table WHERE int_column = {id:UInt8} and string_column = {phrase:String}"

URL 파라미터의 탭 (Tabs in URL Parameters)

쿼리 파라미터는 "escaped" 포맷에서 파싱돼요. 이는 null을 \N으로 명확하게 파싱할 수 있는 것 같은 이점이 있어요. 즉 탭 문자는 \t(또는 \와 탭)로 인코딩해야 해요. 예를 들어 다음은 abc123 사이에 실제 탭을 포함하며 입력 문자열이 두 값으로 분리돼요:

curl -sS "http://localhost:8123" -d "SELECT splitByChar('\t', 'abc      123')"
['abc','123']

그러나 URL 파라미터에서 %09를 사용해 실제 탭을 인코딩하려 하면 제대로 파싱되지 않아요:

curl -sS "http://localhost:8123?param_arg1=abc%09123" -d "SELECT splitByChar('\t', {arg1:String})"
Code: 457. DB::Exception: Value abc    123 cannot be parsed as String for query parameter 'arg1' because it isn't parsed completely: only 3 of 7 bytes was parsed: abc. (BAD_QUERY_PARAMETER) (version 23.4.1.869 (official build))

URL 파라미터를 사용한다면 \t%5C%09로 인코딩해야 해요. 예를 들어:

curl -sS "http://localhost:8123?param_arg1=abc%5C%09123" -d "SELECT splitByChar('\t', {arg1:String})"
['abc','123']

사전 정의된 HTTP 인터페이스 (Predefined HTTP Interface)

ClickHouse는 HTTP 인터페이스를 통해 특정 쿼리를 지원해요. 예를 들어 다음과 같이 테이블에 데이터를 쓸 수 있어요:

$ echo '(4),(5),(6)' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20VALUES' --data-binary @-

ClickHouse는 Prometheus exporter 같은 서드파티 도구와 더 쉽게 통합하는 데 도움이 되는 사전 정의된 HTTP 인터페이스도 지원해요. 예를 살펴볼게요.

먼저 서버 설정 파일에 이 섹션을 추가하세요.

http_handlers는 여러 rule을 포함하도록 구성돼요. ClickHouse는 수신한 HTTP 요청을 rule의 사전 정의된 타입과 일치시키고, 첫 번째로 일치하는 규칙이 핸들러를 실행해요. 그런 다음 ClickHouse는 일치하는 경우 해당 사전 정의된 쿼리를 실행해요.

<http_handlers>
    <rule>
        <url>/predefined_query</url>
        <methods>POST,GET</methods>
        <handler>
            <type>predefined_query_handler</type>
            <query>SELECT * FROM system.metrics LIMIT 5 FORMAT Template SETTINGS format_template_resultset = 'prometheus_template_output_format_resultset', format_template_row = 'prometheus_template_output_format_row', format_template_rows_between_delimiter = '\n'</query>
        </handler>
    </rule>
    <rule>...</rule>
    <rule>...</rule>
</http_handlers>

이제 Prometheus 포맷의 데이터를 위해 URL을 직접 요청할 수 있어요:

$ curl -v 'http://localhost:8123/predefined_query'
*   Trying ::1...
* Connected to localhost (::1) port 8123 (#0)
> GET /predefined_query HTTP/1.1
> Host: localhost:8123
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 28 Apr 2020 08:52:56 GMT
< Connection: Keep-Alive
< Content-Type: text/plain; charset=UTF-8
< X-ClickHouse-Server-Display-Name: i-mloy5trc
< Transfer-Encoding: chunked
< X-ClickHouse-Query-Id: 96fe0052-01e6-43ce-b12a-6b7370de6e8a
< X-ClickHouse-Format: Template
< X-ClickHouse-Timezone: Asia/Shanghai
< Keep-Alive: timeout=10
< X-ClickHouse-Summary: {"read_rows":"0","read_bytes":"0","written_rows":"0","written_bytes":"0","total_rows_to_read":"0","elapsed_ns":"662334","memory_usage":"8451671"}
<
# HELP "Query" "Number of executing queries"
# TYPE "Query" counter
"Query" 1

# HELP "Merge" "Number of executing background merges"
# TYPE "Merge" counter
"Merge" 0

# HELP "PartMutation" "Number of mutations (ALTER DELETE/UPDATE)"
# TYPE "PartMutation" counter
"PartMutation" 0

# HELP "ReplicatedFetch" "Number of data parts being fetched from replica"
# TYPE "ReplicatedFetch" counter
"ReplicatedFetch" 0

# HELP "ReplicatedSend" "Number of data parts being sent to replicas"
# TYPE "ReplicatedSend" counter
"ReplicatedSend" 0

* Connection #0 to host localhost left intact

* Connection #0 to host localhost left intact

http_handlers의 구성 옵션은 다음과 같이 작동해요.

rule은 다음 파라미터를 구성할 수 있어요:

  • method
  • headers
  • url
  • full_url
  • handler

각각 아래에서 설명할게요:

  • method는 HTTP 요청의 메서드 부분 일치를 담당해요. method는 HTTP 프로토콜의 method 정의를 완전히 따릅니다. 선택적 구성이에요. 설정 파일에 정의되지 않으면 HTTP 요청의 메서드 부분을 일치시키지 않아요.
  • url은 HTTP 요청의 URL 부분(경로와 쿼리 문자열) 일치를 담당해요. urlregex:로 접두사 붙으면 RE2의 정규 표현식을 기대해요. 선택적 구성이에요. 설정 파일에 정의되지 않으면 HTTP 요청의 URL 부분을 일치시키지 않아요.
  • full_urlurl과 같지만 완전한 URL을 포함해요, 즉 schema://host:port/path?query_string. 참고로 ClickHouse는 "가상 호스트"를 지원하지 않으므로 host는 IP 주소예요(Host 헤더의 값이 아님).
  • empty_query_string - 요청에 쿼리 문자열(?query_string)이 없도록 보장.
  • headers는 HTTP 요청의 헤더 부분 일치를 담당해요. RE2의 정규 표현식과 호환돼요. 선택적 구성. 설정 파일에 정의되지 않으면 HTTP 요청의 헤더 부분을 일치시키지 않아요.
  • handler는 주요 처리 부분을 담고 있어요. 다음 type을 가질 수 있어요:

다음 파라미터도:

  • predefined_query_handler
  • dynamic_query_handler
  • static
  • redirect
  • querypredefined_query_handler 타입과 함께 사용. 핸들러가 호출될 때 쿼리를 실행.
  • query_param_namedynamic_query_handler 타입과 함께 사용. HTTP 요청 파라미터에서 query_param_name 값에 해당하는 값을 추출해 실행.
  • statusstatic 타입과 함께 사용. 응답 상태 코드.
  • content_type — 어떤 타입과도 함께 사용. 응답 content-type.
  • http_response_headers — 어떤 타입과도 함께 사용. 응답 헤더 맵. 콘텐츠 타입을 설정하는 데도 사용할 수 있음.
  • response_contentstatic 타입과 함께 사용. 클라이언트로 보내지는 응답 콘텐츠. 'file://' 또는 'config://' 접두사를 사용하면 파일이나 설정에서 콘텐츠를 찾아 클라이언트로 보냄.
  • user - 쿼리를 실행할 사용자(기본 사용자는 default). 참고, 이 사용자에 대한 비밀번호를 지정할 필요는 없어요.

서로 다른 type들에 대한 구성 방법은 다음에서 논의해요.

predefined_query_handler

predefined_query_handlerSettingsquery_params 값 설정을 지원해요. predefined_query_handler 타입에서 query를 구성할 수 있어요.

query 값은 predefined_query_handler의 사전 정의된 쿼리이며, HTTP 요청이 일치할 때 ClickHouse가 실행하고 쿼리 결과를 반환해요. 필수 구성이에요.

다음 예시는 max_threadsmax_final_threads 설정의 값을 정의한 다음 시스템 테이블을 조회해 이 설정들이 성공적으로 설정됐는지 확인해요.

참고: query, play, ping 같은 기본 핸들러를 유지하려면 <defaults/> 규칙을 추가하세요.

예를 들어:

<http_handlers>
    <rule>
        <url><![CDATA[regex:/query_param_with_url/(?P<name_1>[^/]+)]]></url>
        <methods>GET</methods>
        <headers>
            <XXX>TEST_HEADER_VALUE</XXX>
            <PARAMS_XXX><![CDATA[regex:(?P<name_2>[^/]+)]]></PARAMS_XXX>
        </headers>
        <handler>
            <type>predefined_query_handler</type>
            <query>
                SELECT name, value FROM system.settings
                WHERE name IN ({name_1:String}, {name_2:String})
            </query>
        </handler>
    </rule>
    <defaults/>
</http_handlers>
curl -H 'XXX:TEST_HEADER_VALUE' -H 'PARAMS_XXX:max_final_threads' 'http://localhost:8123/query_param_with_url/max_threads?max_threads=1&max_final_threads=2'
max_final_threads    2
max_threads    1
가상 파라미터 _request_body

URL 파라미터, 헤더, 쿼리 파라미터 외에도 predefined_query_handler는 특별한 가상 파라미터 _request_body를 지원해요. 이것은 원시 HTTP 요청 본문을 문자열로 담고 있어요. 이를 통해 임의의 데이터 포맷을 받아들이고 쿼리 안에서 처리할 수 있는 유연한 REST API를 만들 수 있어요.

예를 들어 _request_body를 사용해 POST 요청에서 JSON 데이터를 받아 테이블에 삽입하는 REST 엔드포인트를 구현할 수 있어요:

<http_handlers>
    <rule>
        <methods>POST</methods>
        <url>/api/events</url>
        <handler>
            <type>predefined_query_handler</type>
            <query>
                INSERT INTO events (id, data)
                SELECT {id:UInt32}, {_request_body:String}
            </query>
        </handler>
    </rule>
    <defaults/>
</http_handlers>

그런 다음 이 엔드포인트에 데이터를 보낼 수 있어요:

curl -X POST 'http://localhost:8123/api/events?id=123' \
  -H 'Content-Type: application/json' \
  -d '{"user": "john", "action": "login", "timestamp": "2024-01-01T10:00:00Z"}'

참고: 하나의 predefined_query_handler에서는 하나의 쿼리만 지원돼요.

dynamic_query_handler

dynamic_query_handler에서 쿼리는 HTTP 요청의 파라미터 형태로 작성돼요. 차이점은 predefined_query_handler에서는 쿼리가 설정 파일에 작성된다는 점이에요. query_param_namedynamic_query_handler에서 구성할 수 있어요.

ClickHouse는 HTTP 요청의 URL에서 query_param_name 값에 해당하는 값을 추출해 실행해요. query_param_name의 기본값은 /query예요. 선택적 구성이며 설정 파일에 정의가 없으면 파라미터가 전달되지 않아요.

이 기능을 실험하려면 다음 예시가 max_threadsmax_final_threads의 값을 정의하고 설정이 성공적으로 설정됐는지 조회해요.

예시:

<http_handlers>
    <rule>
    <headers>
        <XXX>TEST_HEADER_VALUE_DYNAMIC</XXX>    </headers>
    <handler>
        <type>dynamic_query_handler</type>
        <query_param_name>query_param</query_param_name>
    </handler>
    </rule>
    <defaults/>
</http_handlers>
curl  -H 'XXX:TEST_HEADER_VALUE_DYNAMIC'  'http://localhost:8123/own?max_threads=1&max_final_threads=2&param_name_1=max_threads&param_name_2=max_final_threads&query_param=SELECT%20name,value%20FROM%20system.settings%20where%20name%20=%20%7Bname_1:String%7D%20OR%20name%20=%20%7Bname_2:String%7D'
max_threads 1
max_final_threads   2

static

staticcontent_type, status, response_content를 반환할 수 있어요. response_content는 지정된 콘텐츠를 반환할 수 있어요.

예를 들어 "Say Hi!" 메시지를 반환하려면:

<http_handlers>
        <rule>
            <methods>GET</methods>
            <headers><XXX>xxx</XXX></headers>
            <url>/hi</url>
            <handler>
                <type>static</type>
                <status>402</status>
                <content_type>text/html; charset=UTF-8</content_type>
                <http_response_headers>
                    <Content-Language>en</Content-Language>
                    <X-My-Custom-Header>43</X-My-Custom-Header>
                </http_response_headers>
                <response_content>Say Hi!</response_content>
            </handler>
        </rule>
        <defaults/>
</http_handlers>

http_response_headerscontent_type 대신 콘텐츠 타입을 설정하는 데 사용할 수 있어요.

<http_handlers>
        <rule>
            <methods>GET</methods>
            <headers><XXX>xxx</XXX></headers>
            <url>/hi</url>
            <handler>
                <type>static</type>
                <status>402</status>
                <http_response_headers>
                    <Content-Type>text/html; charset=UTF-8</Content-Type>
                    <Content-Language>en</Content-Language>
                    <X-My-Custom-Header>43</X-My-Custom-Header>
                </http_response_headers>
                <response_content>Say Hi!</response_content>
            </handler>
        </rule>
        <defaults/>
</http_handlers>
curl -vv  -H 'XXX:xxx' 'http://localhost:8123/hi'
*   Trying ::1...
* Connected to localhost (::1) port 8123 (#0)
> GET /hi HTTP/1.1
> Host: localhost:8123
> User-Agent: curl/7.47.0
> Accept: */*
> XXX:xxx
>
< HTTP/1.1 402 Payment Required
< Date: Wed, 29 Apr 2020 03:51:26 GMT
< Connection: Keep-Alive
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Keep-Alive: timeout=10
< X-ClickHouse-Summary: {"read_rows":"0","read_bytes":"0","written_rows":"0","written_bytes":"0","total_rows_to_read":"0","elapsed_ns":"662334","memory_usage":"8451671"}
<
* Connection #0 to host localhost left intact
Say Hi!%

설정에서 콘텐츠를 찾아 클라이언트로 보내기.

<get_config_static_handler><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></get_config_static_handler>

<http_handlers>
        <rule>
            <methods>GET</methods>
            <headers><XXX>xxx</XXX></headers>
            <url>/get_config_static_handler</url>
            <handler>
                <type>static</type>
                <response_content>config://get_config_static_handler</response_content>
            </handler>
        </rule>
</http_handlers>
$ curl -v  -H 'XXX:xxx' 'http://localhost:8123/get_config_static_handler'
*   Trying ::1...
* Connected to localhost (::1) port 8123 (#0)
> GET /get_config_static_handler HTTP/1.1
> Host: localhost:8123
> User-Agent: curl/7.47.0
> Accept: */*
> XXX:xxx
>
< HTTP/1.1 200 OK
< Date: Wed, 29 Apr 2020 04:01:24 GMT
< Connection: Keep-Alive
< Content-Type: text/plain; charset=UTF-8
< Transfer-Encoding: chunked
< Keep-Alive: timeout=10
< X-ClickHouse-Summary: {"read_rows":"0","read_bytes":"0","written_rows":"0","written_bytes":"0","total_rows_to_read":"0","elapsed_ns":"662334","memory_usage":"8451671"}
<
* Connection #0 to host localhost left intact
<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>%

파일에서 콘텐츠를 찾아 클라이언트로 보내기:

<http_handlers>
        <rule>
            <methods>GET</methods>
            <headers><XXX>xxx</XXX></headers>
            <url>/get_absolute_path_static_handler</url>
            <handler>
                <type>static</type>
                <content_type>text/html; charset=UTF-8</content_type>
                <http_response_headers>
                    <ETag>737060cd8c284d8af7ad3082f209582d</ETag>
                </http_response_headers>
                <response_content>file:///absolute_path_file.html</response_content>
            </handler>
        </rule>
        <rule>
            <methods>GET</methods>
            <headers><XXX>xxx</XXX></headers>
            <url>/get_relative_path_static_handler</url>
            <handler>
                <type>static</type>
                <content_type>text/html; charset=UTF-8</content_type>
                <http_response_headers>
                    <ETag>737060cd8c284d8af7ad3082f209582d</ETag>
                </http_response_headers>
                <response_content>file://./relative_path_file.html</response_content>
            </handler>
        </rule>
</http_handlers>
$ user_files_path='/var/lib/clickhouse/user_files'
$ sudo echo "<html><body>Relative Path File</body></html>" > $user_files_path/relative_path_file.html
$ sudo echo "<html><body>Absolute Path File</body></html>" > $user_files_path/absolute_path_file.html
$ curl -vv -H 'XXX:xxx' 'http://localhost:8123/get_absolute_path_static_handler'
*   Trying ::1...
* Connected to localhost (::1) port 8123 (#0)
> GET /get_absolute_path_static_handler HTTP/1.1
> Host: localhost:8123
> User-Agent: curl/7.47.0
> Accept: */*
> XXX:xxx
>
< HTTP/1.1 200 OK
< Date: Wed, 29 Apr 2020 04:18:16 GMT
< Connection: Keep-Alive
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Keep-Alive: timeout=10
< X-ClickHouse-Summary: {"read_rows":"0","read_bytes":"0","written_rows":"0","written_bytes":"0","total_rows_to_read":"0","elapsed_ns":"662334","memory_usage":"8451671"}
<
<html><body>Absolute Path File</body></html>
* Connection #0 to host localhost left intact
$ curl -vv -H 'XXX:xxx' 'http://localhost:8123/get_relative_path_static_handler'
*   Trying ::1...
* Connected to localhost (::1) port 8123 (#0)
> GET /get_relative_path_static_handler HTTP/1.1
> Host: localhost:8123
> User-Agent: curl/7.47.0
> Accept: */*
> XXX:xxx
>
< HTTP/1.1 200 OK
< Date: Wed, 29 Apr 2020 04:18:31 GMT
< Connection: Keep-Alive
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Keep-Alive: timeout=10
< X-ClickHouse-Summary: {"read_rows":"0","read_bytes":"0","written_rows":"0","written_bytes":"0","total_rows_to_read":"0","elapsed_ns":"662334","memory_usage":"8451671"}
<
<html><body>Relative Path File</body></html>
* Connection #0 to host localhost left intact

redirect

redirectlocation으로 302 리다이렉트를 해요.

예를 들어 ClickHouse play에 대해 set user를 자동으로 play로 추가하는 방법이에요:

<clickhouse>
    <http_handlers>
        <rule>
            <methods>GET</methods>
            <url>/play</url>
            <handler>
                <type>redirect</type>
                <location>/play?user=play</location>
            </handler>
        </rule>
    </http_handlers>
</clickhouse>

HTTP 응답 헤더 (HTTP response headers)

ClickHouse는 구성할 수 있는 어떤 종류의 핸들러에든 적용할 수 있는 사용자 정의 HTTP 응답 헤더를 구성할 수 있게 해 줘요. 이 헤더들은 http_response_headers 설정으로 설정할 수 있으며, 헤더 이름과 값을 나타내는 키-값 쌍을 받아들여요. 이 기능은 ClickHouse HTTP 인터페이스 전반에 걸쳐 사용자 정의 보안 헤더, CORS 정책 또는 다른 HTTP 헤더 요구 사항을 구현하는 데 특히 유용해요.

예를 들어 다음에 대한 헤더를 구성할 수 있어요:

  • 일반 쿼리 엔드포인트
  • 웹 UI
  • 헬스 체크.

common_http_response_headers를 지정할 수도 있어요. 이것들은 구성에 정의된 모든 http 핸들러에 적용돼요.

헤더는 구성된 모든 핸들러의 HTTP 응답에 포함돼요.

아래 예시에서 모든 서버 응답은 X-My-Common-HeaderX-My-Custom-Header 두 개의 사용자 정의 헤더를 포함해요.

<clickhouse>
    <http_handlers>
        <common_http_response_headers>
            <X-My-Common-Header>Common header</X-My-Common-Header>
        </common_http_response_headers>
        <rule>
            <methods>GET</methods>
            <url>/ping</url>
            <handler>
                <type>ping</type>
                <http_response_headers>
                    <X-My-Custom-Header>Custom indeed</X-My-Custom-Header>
                </http_response_headers>
            </handler>
        </rule>
    </http_handlers>
</clickhouse>

HTTP 스트리밍 중 예외 시 유효한 JSON/XML 응답 (Valid JSON/XML response on exception during HTTP streaming)

HTTP를 통한 쿼리 실행 중 데이터의 일부가 이미 전송된 후 예외가 발생할 수 있어요. 보통 예외가 평문 텍스트로 클라이언트에 전송돼요. 데이터 출력에 특정 데이터 포맷이 사용되었고 그 출력이 지정된 데이터 포맷에 관해 유효하지 않게 될 수 있더라도 말이죠. 이를 방지하려면 http_write_exception_in_output_format 설정(기본적으로 비활성화)을 사용할 수 있는데, 이는 ClickHouse가 지정된 포맷으로 예외를 쓰도록 지시해요(현재 XML과 JSON* 포맷 지원).

예시:

$ curl 'http://localhost:8123/?query=SELECT+number,+throwIf(number>3)+from+system.numbers+format+JSON+settings+max_block_size=1&http_write_exception_in_output_format=1'
{
    "meta":
    [
        {
            "name": "number",
            "type": "UInt64"
        },
        {
            "name": "throwIf(greater(number, 2))",
            "type": "UInt8"
        }
    ],

    "data":
    [
        {
            "number": "0",
            "throwIf(greater(number, 2))": 0
        },
        {
            "number": "1",
            "throwIf(greater(number, 2))": 0
        },
        {
            "number": "2",
            "throwIf(greater(number, 2))": 0
        }
    ],

    "rows": 3,

    "exception": "Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(greater(number, 2) :: 2) -> throwIf(greater(number, 2)) UInt8 : 1'. (FUNCTION_THROW_IF_VALUE_IS_NON_ZERO) (version 23.8.1.1)"
}
$ curl 'http://localhost:8123/?query=SELECT+number,+throwIf(number>2)+from+system.numbers+format+XML+settings+max_block_size=1&http_write_exception_in_output_format=1'
<?xml version='1.0' encoding='UTF-8' ?>
<result>
    <meta>
        <columns>
            <column>
                <name>number</name>
                <type>UInt64</type>
            </column>
            <column>
                <name>throwIf(greater(number, 2))</name>
                <type>UInt8</type>
            </column>
        </columns>
    </meta>
    <data>
        <row>
            <number>0</number>
            <field>0</field>
        </row>
        <row>
            <number>1</number>
            <field>0</field>
        </row>
        <row>
            <number>2</number>
            <field>0</field>
        </row>
    </data>
    <rows>3</rows>
    <exception>Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(greater(number, 2) :: 2) -> throwIf(greater(number, 2)) UInt8 : 1'. (FUNCTION_THROW_IF_VALUE_IS_NON_ZERO) (version 23.8.1.1)</exception>
</result>

더 알아보기 (Learn more)