Skip to main content
미리보기 기능은 API가 최종화되기 전에 확장 프로그램에 노출되는 서버 제공 기능입니다. 미리보기 기능을 선언하는 확장 프로그램은 설치 시 vsql_allow_preview_extensions = ON이 필요합니다 (참조: 미리보기 계층 활성화) — 미리보기 기능을 사용하지 않는 확장 프로그램은 이 설정과 무관하게 일반적으로 설치됩니다.
미리보기 기능 API는 안정적이지 않습니다. 미리보기 기능을 기반으로 빌드된 확장 프로그램은 서버 업데이트 후 로드에 실패할 수 있습니다. 기능이 안정화되면 헤더가 버전화된 안정적 C++ SDK 경로로 이동합니다.

미리보기 계층 활성화

미리보기 기능을 사용하는 확장 프로그램을 설치하기 전에 vsql_allow_preview_extensions = ONSET PERSIST로 설정합니다:
SET GLOBAL은 이 변수에 대해 거부됩니다 — 서버는 설정이 재시작 후에도 유지되도록 하기 위해 SET PERSIST를 요구합니다. 미리보기 기능을 사용하는 확장 프로그램은 시작 시 로드되므로, 서버가 시작될 때 이 변수가 ON이어야 합니다. mysqld를 직접 실행하는 경우(예: 서버를 처음 시작하는 설치 스크립트에서), mysqld-auto.cnf가 아직 존재하지 않아 지속된 값을 저장할 수 없으므로 명령줄 플래그를 전달합니다:
비활성화하려면:
미리보기 기능을 사용하는 확장 프로그램이 현재 설치되어 있으면 이 설정을 비활성화하는 작업이 실패합니다. 먼저 해당 확장 프로그램을 제거한 후 설정을 끄세요.

기능 인덱스

등록 패턴

미리보기 기능을 사용하려면 파일 범위에서 값으로 기능 객체를 선언하고, make_extension() 내부의 .with()에 참조로 전달합니다. 서버는 등록 중에 객체의 abi 포인터를 채웁니다:
.with(capability)은 서버에 확장 프로그램이 필요한 기능을 알립니다. vsql_allow_preview_extensions가 OFF 상태에서 확장 프로그램이 설치되면, 서버는 확장 프로그램 이름을 명시한 오류와 함께 설치를 거부합니다: ERROR 3219 (HY000): Failed to load VEF extension 'name': extension requires preview capabilities but vsql_allow_preview_extensions is OFF. 이 메시지는 어떤 기능이 원인이었는지는 알려주지 않습니다.
확장 프로그램 내에서 선언된 모든 기능 객체는 정확히 한 번 .with()에 전달되어야 합니다. 로드 시 서버는 선언된 모든 기능 인스턴스를 .with()가 받은 내용과 비교하고 규칙을 위반하면 INSTALL EXTENSION을 실패시킵니다:
  • 선언되었으나 .with()에 전달되지 않은 경우: capability '<Type>' was declared but never passed to .with(); every CapabilityBase-derived static must be registered via .with(cap) in the extension builder
  • 동일한 인스턴스가 .with()에 두 번 이상 전달된 경우: capability '<Type>' passed to .with() more than once
  • .with()에 전달된 객체가 기능이 아닌 경우: .with() received an object that does not inherit vsql::detail::CapabilityBase; not a registered capability
전체 오류는 다음과 같이 표시됩니다: Failed to load VEF extension '<name>': vef_register returned an error: <message above>.

Keyring 접근

keyring 기능(vsql::preview::keyring)은 MySQL keyring 구성 요소에 저장된 비밀을 읽고 쓸 수 있게 해줍니다. 확장 프로그램은 API 키, 암호화 키 또는 SQL 테이블에 저장되지 않아야 하는 기타 비밀을 처리하는 데 사용합니다. 기능 이름 VEF_PREVIEW_KEYRING_NAME"vsql::preview::keyring"입니다. 읽기와 쓰기 작업이 성공하려면 MySQL 서버에 keyring 구성 요소가 설치되어야 합니다. 없으면 작업은 KeyringCapability::Status::UNAVAILABLE을 반환합니다.

상태 값

KeyringCapability::Statusread()(내부 ReadResult에서) 및 write()에서 반환되는 범위화된 열거형입니다:

기능 선언

헤더를 포함하고 파일 범위에서 기능 객체를 선언한 후 .with()에 전달합니다:
g_keyring 객체는 서버가 로드 시 채웁니다. keyring 구성 요소가 설치되지 않은 경우 read()write() 메서드는 런타임에 Status::UNAVAILABLE을 반환합니다 — 별도의 가용성 검사 대신 각 호출에서 상태를 확인하세요.

읽기 및 쓰기

data_id는 키 식별자입니다. auth_id는 소유 사용자입니다 — 내부 키(특정 사용자와 연결되지 않음)를 읽거나 쓰려면 빈 문자열(또는 read에서 생략, 기본값 {})을 전달합니다. read는 값으로 ReadResult를 반환합니다. 구조화된 바인딩으로 바인딩합니다:
Status::OK가 아닌 상태에서는 value가 비어 있습니다. writeStatus를 직접 반환하고 datadata_id/auth_id 아래에 저장합니다.

완전한 예제

이것은 서버의 villagesql/test-extensions/ 트리에 있는 vsql_keyring_reader 테스트 확장 프로그램의 간단한 버전입니다. 이 확장 프로그램은 2개의 VDF를 등록합니다: keyring_readkeyring_store.

MySQL 서비스

mysql_services 기능(vsql::preview::mysql_services)은 확장 프로그램이 MySQL 레지스트리 서비스를 사용할 수 있게 해줍니다 — MySQL 구성 요소가 사용하는 것과 동일한 서비스로, 설치된 구성 요소 또는 서버 코어가 제공합니다. 확장 프로그램은 필요한 모든 서비스를 한곳에서 선언하고, 서버는 확장 프로그램이 로드될 때 각 서비스를 획득하고 확장 프로그램이 언로드될 때 해제합니다. 기능 이름 VEF_PREVIEW_MYSQL_SERVICES_NAME"vsql::preview::mysql_services"입니다. 서버 설비에 자체 VEF 기능이 없을 때 이것을 사용하세요. 세션 속성과 keyring 자체의 구성 요소 서비스는 모두 이 방식으로 접근할 수 있습니다. 사용만 지원됩니다: 확장 프로그램 자체의 구현을 레지스트리에 등록하는 것은 향후 계획된 작업이며 이 기능에 포함되지 않습니다.

기능 선언

파일 범위에서 하나의 MysqlServices 객체를 선언하고, 사용하는 각 서비스를 VSQL_REQUIRE_SERVICE로 지정한 후, 그 객체를 .with()에 전달합니다. 각 서비스에 대해 MySQL 자체 헤더를 포함하세요 — 해당 헤더가 서비스의 유형과 메서드를 선언하는 곳입니다:
VSQL_REQUIRE_SERVICE(services, name, var)은 서버가 획득한 서비스를 기록하는 참조인 var를 선언하고, servicesname을 등록합니다. 이 매크로는 varstatic으로 선언해 줍니다. MysqlServices 객체도 static이어야 하며, 직접 선언하는 모든 참조도 마찬가지입니다: 서버가 로드 시 이들을 통해 값을 기록하므로, 확장 프로그램보다 오래 유지되어야 합니다.

특정 구현 고정

VSQL_REQUIRE_SERVICEname을 두 번 사용합니다 — C++ SERVICE_TYPE(name)으로, 그리고 서버가 레지스트리에서 조회하는 문자열로. 이 단순 이름 아래에서 서버는 해당 서비스의 기본 구현을 획득합니다. 대신 하나의 구현을 지정하려면, MySQL의 PROVIDES_SERVICE(component, service)가 생성하는 형식인 정규화된 레지스트리 이름 service.component를 사용하세요. 다음은 기본 구현이 아니라 component_keyring_file 구성 요소의 keyring 리더를 요청합니다:
정규화된 이름은 단순 이름과 동일한 방식으로 획득되므로, 일반적인 규칙이 그대로 적용됩니다: 해당 구현이 정확히 등록되어 있지 않으면 확장 프로그램은 다른 구현으로 대체되지 않고 설치에 실패합니다.

MySQL 헤더에 대해 빌드

서비스 정의는 VEF가 아니라 MySQL의 구성 요소 프레임워크에 속하며, 서버는 이를 설치하지 않습니다. 따라서 mysql/components/services/*.h는 확장 프로그램 SDK에도, make install이 빌드하는 어떤 것에도 없으며, 여기에는 릴리스 tarball과 Docker 이미지가 포함됩니다. 서비스를 사용하는 확장 프로그램은 VillageSQL 서버 소스 트리에 대해 빌드합니다: 인트리 테스트 확장 프로그램은 vsql_add_test_extension()MYSQL_HEADERS 플래그에서 둘 다 얻으며, 이 플래그는 이들을 MYSQL_INCLUDE_DIRMYSQL_GENERATED_INCLUDE_DIR로 전달합니다. 아웃오브트리 빌드는 자체 포함 경로를 설정합니다. 두 가지 빌드 실패는 원인이 된 줄이 아닌 다른 곳에서 나타납니다. 서비스의 MySQL 헤더를 생략하면 VSQL_REQUIRE_SERVICE에 아무것도 해석되지 않는 이름이 남으므로, 오류는 누락된 include가 아니라 매크로에서 나타납니다(clang 17):
일부 서비스 정의는 <cstddef>를 포함하지 않고 size_t를 사용하므로, 그러한 헤더 중 하나를 모든 villagesql 헤더보다 앞에 두면 MySQL 자체 헤더 내부에서 실패합니다:
이 페이지의 예제처럼 <cstddef>를 먼저 포함하세요.

서비스 호출

서비스 참조는 자체적으로 valid()를 노출하며, ->는 서비스로 전달됩니다. 참조에는 .를, 서비스에는 ->를 사용하세요:
모든 -> 호출 전에 valid()를 확인하세요. ->는 획득된 포인터를 반환하며, 서비스가 획득되지 않았을 때는 null입니다. 획득에 실패한 서비스는 설치를 실패시키므로, 실행 중인 함수 내부에서는 필요한 서비스가 유효합니다. 그럼에도 이 검사는 여전히 중요합니다. 직접 선언하고 require()에 전달하지 않은 ServiceRef에는 아무것도 기록되지 않기 때문입니다: 컴파일도 되고 확장 프로그램도 설치되지만, 확장 프로그램이 살아 있는 동안 valid()는 계속 false입니다. 서비스가 무엇인지 — 그 메서드, 매개변수, 반환 값 — 는 여기가 아니라 MySQL이 문서화합니다. NAME이라는 서비스의 경우, 서버 트리의 include/mysql/components/services/NAME.h를 읽으세요: 그 안의 BEGIN_SERVICE_DEFINITION(NAME) 블록이 모든 메서드를 자체 문서와 함께 선언합니다. bool 반환에서 false가 성공을 의미하고 true가 실패를 의미한다는 MySQL의 관례를 포함하여, 해당 헤더가 명시하는 그대로 호출하세요.

획득 실패

선언된 모든 서비스는 확장 프로그램이 로드될 때, 즉 어떤 함수도 호출되기 전에 획득되므로, 등록되지 않은 서비스는 나중에 드러나지 않고 로드를 실패시킵니다. INSTALL EXTENSION이 실패하며 해당 서비스 이름을 명시합니다. 아래의 vsql_mysql_services_missing_test는 레지스트리에 없는 서비스를 요구하는 인트리 테스트 확장 프로그램입니다. 설치할 수 있는 것이 아니라 — 이 실패를 포착한 방법이며, 이 서버가 제공하지 않는 서비스를 요구할 때 여러분의 확장 프로그램이 생성하는 결과입니다:
다른 두 가지 설치 실패는 이 기능 밖에서 동일한 기능에 도달합니다: MysqlServices 객체를 .with()에서 빠뜨리는 경우, 그리고 vsql_allow_preview_extensions가 OFF인 서버에 설치하는 경우입니다. 둘 다 등록 패턴에서 다룹니다.

완전한 예제

서버의 villagesql/test-extensions/ 트리에 있는 vsql_mysql_services_session_test의 간단한 버전입니다. 이는 두 서비스를 조합하여 호출 세션에서 실행 중인 SQL 명령을 읽습니다: 하나는 현재 THD를 반환하고, 다른 하나는 그 위에서 이름이 지정된 속성을 읽습니다. 둘 다 모든 서버에 등록되는 서버 코어 서비스이므로, 먼저 설치할 것이 없습니다:
설치한 후 함수를 호출합니다:

상태 변수

status_var 기능(vsql::status_var)은 확장 프로그램이 MySQL 상태 변수로 long longdouble 카운터를 노출할 수 있게 해줍니다. 확장 프로그램은 저장소를 소유하고 쓰며, 서버는 상태 변수가 쿼리될 때마다 포인터를 통해 읽습니다. vsql::preview_status_var::make_capability()으로 기능을 구축하고, make_int(name, value_ptr) 또는 make_double(name, value_ptr)에서 제공하는 디스크립터 목록을 중괄호로 감싸 전달합니다. 템플릿은 중괄호 목록에서 개수를 추론하므로 명시적 크기는 필요하지 않습니다.

완전한 예제

make_intlong long *를 요구하며, make_doubledouble *를 요구합니다. 이 두 유형만 지원됩니다.

SQL에서 접근

INSTALL EXTENSION my_ext 후, 변수는 확장 프로그램 이름을 접두사로 사용하여 사용 가능합니다:
다중 쿼리 스레드에서 비원자적 ++를 사용해 동시 증가가 때로 손실될 수 있지만, 이는 SHOW STATUS를 통해 노출된 근사 호출 카운터에 대해 허용됩니다.

시스템 변수

sys_var 기능(vsql::sys_var)은 확장 프로그램이 소유한 저장소를 기반으로 하는 MySQL 시스템 변수를 등록할 수 있게 해줍니다. 네 가지 유형이 지원됩니다: BOOL(bool *), INT(long long *), DOUBLE(double *), STR(char **). INTDOUBLE 디스크립터는 min_valmax_val 경계도 포함하며, 모든 디스크립터는 기본값과 주석을 포함합니다. vsql::preview_sys_var::make_capability()으로 기능을 구축하고, 대응하는 팩토리 함수인 make_bool, make_int, make_double, make_str을 사용합니다. 기능 객체는 확장 코드에서의 프로그래밍 접근을 위해 get()set()을 노출합니다. 둘 다 성공 시 false를 반환합니다. 값 변경에 반응하려면 디스크립터에 .on_change<&fn>()을 연결합니다. 콜백은 var_name() 및 타입 접근자(as_int(), as_real(), as_str())를 포함하는 sv::SysVarChange를 수신합니다. 서버는 전역 시스템 변수 잠금을 보유한 상태에서 그 콜백을 호출합니다. 그곳에서 이 확장 프로그램의 다른 변수를 저장소 포인터를 통해 읽거나 쓰는 것은 안전하며, 서버가 동일한 잠금 아래에서 그 변수들을 읽기 때문에 다른 세션은 새 값을 즉시 봅니다.
기능의 get() 또는 set()을 호출하거나, SQL을 실행하거나, 둘 중 하나를 수행하는 스레드를 기다리면 그 잠금에서 교착 상태가 발생합니다. 콜백을 짧고 논블로킹으로 유지하고, SQL이 필요한 작업은 스레드 워커에 넘기거나, sql/sys_vars.ccevent_scheduler_update()가 하는 것처럼 블로킹 부분 주위에서 LOCK_global_system_variables를 해제했다가 반환하기 전에 다시 획득하세요.
기능 객체는 정적 저장 기간을 가져야 합니다. MySQL은 사용자가 변수를 설정할 때 저장소 포인터에 직접 씁니다.

완전한 예제

SQL에서 접근

INSTALL EXTENSION my_ext 후, 변수는 확장 프로그램 이름을 구성 접두사로 사용하여 접근 가능합니다:

확장 코드에서 읽기 및 쓰기

INT 및 BOOL 변수의 경우, 전역 저장소 포인터를 직접 읽습니다 — MySQL은 이들을 원자적으로 업데이트합니다. MySQL을 통해 변수를 업데이트하려면(잠금, 범위 검증, 지속성이 서버에서 처리되도록), SYS_VARS.set(extension_name, var_name, scope, value)를 호출합니다. setget 둘 다 성공 시 false를 반환합니다. 둘 중 어느 것도 on_change 콜백에서 호출할 수 없습니다: 둘 다 시스템 변수 잠금에서 교착 상태가 발생합니다.
scope 인수는 다음과 같이 지속성을 제어합니다:

스레드 워커

thread_worker 기능(vsql::preview::thread_worker)은 확장 프로그램이 서버가 제어하는 백그라운드 스레드를 실행할 수 있게 해줍니다. 스레드는 서버가 확장 프로그램 로드 시 등록하는 제어 시스템 변수를 통해 시작 및 중지되며, 서버는 주기적 타이머, 파일 디스크립터 준비, 또는 활성/비활성 이벤트에 반응하여 확장 프로그램의 작업 함수를 호출합니다. 기능 이름 VEF_PREVIEW_THREAD_WORKER_NAME"vsql::preview::thread_worker"입니다.

기능 선언

헤더를 포함하고, 파일 범위에서 작업 함수로 인스턴스화된 ThreadWorkerCapability을 선언하고, .with()에 전달합니다:
작업 함수는 비유형 템플릿 인수(ThreadWorkerCapability<&my_work>)로 제공되므로, 다음 서명을 가진 함수여야 합니다. 첫 번째 생성자 인수는 스레드 이름 접미사이며, 선택적 두 번째 인수는 제어 시스템 변수 이름을 오버라이드합니다.

작업 함수 서명

reason은 서버가 함수를 호출한 이유를 나타냅니다. thread는 이 워커에 대한 서버 소유 핸들입니다(초기 VEF_WAKEUP_ENABLE 호출 시 NULL — 아래 참조). arg는 디스크립터에 등록된 불투명(opaque) 포인터이며, 변경 없이 전달됩니다.

웨이크업 라이프사이클

서버는 다음 네 가지 이유 중 하나로 작업 함수를 호출합니다: reasonVEF_WAKEUP_ENABLE인 경우 thread 매개변수는 NULL입니다. 이 시점에는 스레드 핸들이 아직 존재하지 않기 때문입니다. 다른 세 가지 이유에 대해서는 thread가 NULL이 아닙니다.

웨이크업 반환 값

작업 함수는 vef_next_wakeup_t를 반환하여 다음 웨이크업 구성 설정을 업데이트합니다. 각 필드의 0 값은 “현재 설정 유지”를 의미합니다 — 변경하지 않으려면 구조체를 기본값으로 초기화하여 반환(return {};)하세요. 새로운 poll 파일 디스크립터를 설정하려면 해당 값을 반환합니다(0보다 커야 함). 기존 poll 파일 디스크립터를 제거하려면 poll_fd-1을 반환합니다. reasonVEF_WAKEUP_DISABLE인 경우 반환 값은 무시됩니다.

스레드 이름 및 제어 변수

디스크립터의 두 필드가 이름을 제어합니다:
  • suffix — 스레드 이름 접미사. 서버는 확장 프로그램 이름을 접두사로 추가하여 my_ext/monitor와 같은 스레드 이름을 생성합니다.
  • var_name — 선택적. NULL이 아닌 경우 서버는 이 정확한 이름을 제어 시스템 변수로 등록합니다. NULL인 경우 서버는 기본 패턴 {suffix}_enabled을 사용합니다.
제어 변수는 서버가 등록한 시스템 변수이므로, 확장 프로그램 이름을 구성 접두사로 사용합니다. 접미사가 monitor인 확장 프로그램 my_ext의 경우, 변수는 my_ext.monitor_enabled입니다. 이를 ON으로 설정하면 워커가 시작됩니다: 서버가 VEF_WAKEUP_ENABLE로 작업 함수를 호출한 다음 스레드를 생성하므로, 그 첫 호출이 끝날 때까지 문이 반환되지 않습니다. 워커가 이미 실행 중일 때 다시 ON으로 설정하면 아무 일도 일어나지 않습니다. OFF로 설정하면 스레드가 종료된 후에 반환됩니다. 서버는 두 경우 모두에서 전역 시스템 변수 잠금을 해제하므로, 작업 함수는 시스템 변수를 읽고 SQL을 실행할 수 있습니다.

완전한 예제

주기적 워커가 하나 있는 최소 확장 프로그램으로, 각 타이머 틱마다 심장 박동 카운터를 증가시킵니다.
이 확장 프로그램을 설치한 후(vsql_allow_preview_extensions = ON), 서버는 확장 프로그램 이름 아래에 heartbeat_enabled 시스템 변수를 등록합니다. my_ext라는 이름의 확장 프로그램의 경우, 워커를 활성화하려면:

SQL 쿼리

sql_query 기능(vsql::preview::sql_query)은 확장 프로그램이 백그라운드 스레드에서 SQL 문을 실행할 수 있게 해줍니다. 쿼리는 확장 프로그램이 MySQL 클라이언트 라이브러리에 링크하지 않고 기능 vtable을 통해 서버 내부에서 실행됩니다. 기능 이름 VEF_PREVIEW_SQL_QUERY_NAME"vsql::preview::sql_query"입니다.
SQL 세션은 스레드 워커 콜백에서 해당 콜백의 vef_thread_handle_t *를 사용하여 열어야 합니다. open()은 VDF 또는 임의의 확장 프로그램 생성 스레드에서 유효하지 않습니다 — 워커 세션 컨텍스트가 필요합니다.

기능 선언

헤더를 포함하고, 파일 범위에서 SqlQueryCapability을 선언한 후 .with()에 전달합니다. 세션은 워커 콜백에서 열리므로, 일반적으로 ThreadWorkerCapability과 함께 등록됩니다:
g_sql.open(handle)Session을 반환합니다. 사용 전에 operator bool로 확인하세요. 유효하지 않은 Session은 기능 vtable이 바인딩되지 않았거나 서버가 세션을 할당하지 못했음을 나타냅니다. Session은 이동 전용이며 소멸 시 자동으로 닫힙니다.

쿼리 실행

Sessionsession.sql(sv)를 통해 SqlQuery를 생성합니다. 쿼리는 두 가지 모드로 실행할 수 있습니다:
  • execute() — 문을 실행하고 전체 결과 세트를 Result에 버퍼링합니다. 호출자의 속도에 맞춰 next()를 호출하여 행을 반복합니다.
  • for_each(fn) — 문을 실행하고 행이 생성될 때마다 fn을 한 번 호출합니다(버퍼링 없음). 반환된 Result는 진단 정보만 포함합니다(행 없음).
둘 다 Result를 반환합니다. Result가 null이 아닌 경우 문이 성공했다는 의미가 아닙니다 — has_error()를 호출하여 확인하세요. 버퍼링(execute):
column_str()next() 호출 또는 Result 소멸 전까지 유효한 string_view를 반환합니다. 더 긴 수명이 필요한 경우 복사하세요. data() == nullptrstring_view는 SQL NULL을 나타냅니다. 스트리밍(for_each):
콜백에 전달된 Row는 호출 기간 동안만 유효합니다 — 행 간에 참조를 저장하지 마세요. for_each가 반환한 Result는 버퍼링된 행을 보유하지 않습니다. next()는 데이터를 생성하지 않습니다. has_error(), error(), warning_count(), warning(i)에만 사용하세요.

진단

execute()for_each()는 반환된 Result를 통해 진단을 표시합니다. 진단은 하나의 Diag입니다:
Result는 다음과 같이 노출합니다:
error()는 문이 성공했을 때 기본 생성된 Diag(errno_ == 0)을 반환합니다. warning(i)i >= warning_count()일 때 기본 생성된 Diag를 반환합니다. sqlstatemessage 뷰는 Result가 소유하는 저장소를 가리키며 Result가 소멸될 때 무효화됩니다 — 이들이 Result보다 오래 유지되어야 할 경우 복사하세요.

완전한 예제

각 틱마다 버퍼링 쿼리와 스트리밍 쿼리를 실행하고, 두 쿼리에서 진단을 로깅하는 워커:

컬럼 저장

컬럼 저장은 확장 프로그램이 InnoDB에 직접 커스텀 유형의 이진 디스크 레이아웃을 등록할 수 있게 해줍니다. 이는 VARBINARY 페이로드를 통해 유형 바이트를 라우팅하는 대신, 커스텀 유형의 디스크 모양을 VARBINARY가 표현할 수 없는 경우(예: 전용 페이지에 저장되어야 하는 밀집된 부동 소수점 배열)에 사용됩니다. 이는 미리보기 기능(Capability)입니다: 기존 레이아웃에 대한 튜닝 장치가 아닌 새로운 저장 레이아웃을 활성화합니다.
컬럼 저장은 미리보기 ABI입니다 — 개발 중이며 릴리스 간에 변경될 수 있습니다. 현재는 행 수준 지속성만 지원하며, 커스텀 저장 컬럼에 대한 인덱싱은 아직 사용할 수 없습니다.

기능 선언

두 개의 미리보기 기능이 함께 작동합니다:
  • vsql::preview::storage — InnoDB 저장소 인프라구조(미니 트랜잭션, 세그먼트, 페이지)에 접근합니다. 파일 범위에서 StorageCapability을 선언합니다.
  • vsql::preview::column_store — 확장 프로그램의 커스텀 유형 중 하나에 대한 저장 구현을 바인딩합니다. make_column_store<Ctx>(TYPE).…build()를 사용하여 파일 범위에서 ColumnStoreCapability을 선언합니다.
둘 다 make_extension().with()에 전달되어야 합니다:
make_column_store<MyCtx>(MY_TYPE)은 구현을 동일한 확장 프로그램에서 등록된 하나의 커스텀 유형과 연결합니다. 모든 7개의 슬롯은 build() 시점에 필수적입니다. 각 슬롯은 InnoDB가 정상 운영 중에 도달하는 고유한 컬럼 라이프사이클 단계를 나타내기 때문입니다.

일곱 개의 저장 함수

모든 함수는 storage::Column::StorageCtx<MyCtx>*를 받습니다. 이 user() 접근자는 확장 프로그램의 컬럼별 상태를 반환하고, arena()는 보조 개체에 대한 서버 관리 할당을 제공합니다. 모든 함수는 성공 시 false를 반환하고, 오류 시 true를 반환하며, error_msg(용량 error_msg_len)에 메시지를 작성하여 SQL 클라이언트에 표시합니다.
mark_deletepurge는 InnoDB MVCC가 삭제된 행을 오래된 스냅샷에서 읽을 수 있도록 유지해야 하기 때문에 구분됩니다.

컬럼별 컨텍스트 및 아레나

C++ SDK는 create 또는 load를 호출하기 전에 MyCtx를 기본 생성합니다 — ctx->user()는 함수가 실행될 때 이미 채워져 있습니다. MyCtx는 기본 생성 가능해야 하며, C++ SDK는 인수 없이 T()를 호출합니다. ctx->user()를 직접 사용하여 상태를 초기화하세요. ctx->arena().construct<MyCtx>()를 호출하지 마세요 — 이는 두 번째 사용되지 않는 인스턴스를 할당하고 ctx->user()가 이 인스턴스를 가리키지 않습니다.
load는 동일한 패턴을 따릅니다 — ctx->user()는 미리 채워져 있고, storage_refcreate에서 ctx->set_ref()로 저장된 패킹된 값을 담고 있습니다:
ctx->arena()MyCtx에 직접 포함할 수 없는 크기나 동적 객체를 할당하는 데만 사용하세요. C++ SDK는 drop이 반환된 후 자동으로 아레나를 파괴하고 ~MyCtx()를 호출합니다(성공 여부와 무관).

InnoDB 접근 유틸리티

InnoDB 원시 기능을 위해 <villagesql/preview/storage_api.h>를 포함합니다. 모든 페이지 읽기 및 쓰기는 미니 트랜잭션 내에서 발생해야 합니다:
미니 트랜잭션 커밋은 페이지 락을 해제하고 변경 사항을 영구화하는 redo 로그 기록을 작성합니다. 세그먼트create 시 예약됩니다 — 완전한 설정 패턴은 위 컬럼별 컨텍스트의 createload 예제를 참조하세요. DML 작업 중에는 루트 페이지에서 세그먼트 참조를 얻어 새 페이지를 할당합니다:
페이지는 공유 락으로 읽고 배타 락으로 쓰여야 합니다. InnoDB가 변경 사항을 로깅하도록 mtr_ref를 쓰기 호출에 전달합니다:
페이지 레이아웃 상수: 헤더 또는 트레일러 영역 내부에서 읽기 또는 쓰기 작업을 수행하면 페이지가 손상됩니다 — InnoDB는 해당 바이트 범위를 자체적인 관리 및 체크섬에 사용합니다.

문 이벤트

문 이벤트 기능(vsql::preview::statement_event)은 각 쿼리 실행이 완료된 후 확장 프로그램이 제공한 핸들러를 실행합니다. 서버는 쿼리 자체의 스레드에서 핸들러를 동기적으로 호출하고 실행 메타데이터 — 쿼리 텍스트, 타이밍, 행 개수, 연결 식별 정보, 그리고 옵티마이저 품질 지표를 전달합니다. 느린 쿼리 로깅, 감사 또는 메트릭 수집에 사용하세요. 기능 이름 VEF_PREVIEW_STATEMENT_EVENT_NAME"vsql::preview::statement_event"입니다.

기능 선언

파일 범위에서 발동 단계와 핸들러 함수로 인스턴스화된 StatementEventCapability을 선언하고 .with()에 전달합니다:
첫 번째 템플릿 인수는 발동 단계이며, vef_statement_event_phase_t 값입니다. VEF_STATEMENT_EVENT_POSTEXECUTE는 쿼리 실행이 완료된 후 성공 또는 실패와 관계없이 발동하며, 이 버전에서 구현된 유일한 단계입니다. 다른 vef_statement_event_phase_t 값은 예약되어 있으며, 그중 하나에 대한 핸들러를 선언하면 서버가 INSTALL EXTENSION을 거부합니다.

핸들러 인수

StatementEventArgs는 완료된 쿼리의 읽기 전용 뷰입니다. POSTEXECUTE 단계에서는 모든 필드가 채워져 있습니다. 주요 접근자: query()는 재작성된 형태가 존재하는 경우 그것을 반환하므로, 자격 증명을 담은 문은 일반 로그, 슬로우 쿼리 로그, 바이너리 로그가 이미 민감 정보를 가리는 방식과 일치하게 비밀이 평문이 아닌 난독화된 상태로 도착합니다: SET PASSWORD, CREATE/ALTER USER ... IDENTIFIED BY, CHANGE REPLICATION SOURCE ... SOURCE_PASSWORD, 그리고 CREATE SERVER ... OPTIONS(PASSWORD ...). 재작성 규칙이 없는 문은 그대로 전달됩니다. query(), sqlstate(), error_message()와 같은 문자열 접근자는 핸들러 호출 기간 동안만 유효한 저장소를 가리킵니다 — 핸들러가 반환된 후에도 필요하면 바이트를 복사하세요. StatementEventResult::error_msg(fmt, ...)은 printf 형식의 메시지를 작성합니다. POSTEXECUTE 단계에서는 이 메시지가 권고용입니다: 서버가 로깅하지만 클라이언트에 전파하지 않습니다.

완전한 예제

vsql_slow_query_log 테스트 확장 프로그램의 축약된 형태입니다. 이는 실행 시간이 임계값을 초과하는 각 쿼리를 로깅하며, 문 이벤트 기능을 런타임 구성을 위한 시스템 변수와 결합합니다:

SQL에서 활성화

미리보기 계층이 활성화된 상태에서(참조: 미리보기 계층 활성화), 확장 프로그램을 설치하고 시스템 변수를 통해 구성합니다:
임계값보다 느린 각 쿼리는 구성된 로그 파일에 추가됩니다:

인증 방식

auth 기능(vsql::preview::auth)은 확장 프로그램이 서버 인증 방식을 제공할 수 있게 해줍니다. 계정은 CREATE USER ... IDENTIFIED WITH <method-name>으로 이를 선택합니다. 연결 시점에 그 이름이 로드된 MySQL 인증 플러그인이 아니면, 서버는 VEF 인증 레지스트리를 조회하고 핸드셰이크 전반에 걸쳐 확장 프로그램의 핸들러를 호출합니다. MySQL 인증 플러그인을 작성하지 않고도 서버가 알지 못하는 자격 증명 소스 — 베어러 토큰, 외부 신원 공급자, 또는 사용자 정의 챌린지 — 를 기준으로 계정을 인증하는 데 사용하세요. 기능 이름 VEF_PREVIEW_AUTH_NAME"vsql::preview::auth"입니다. 핸들러는 AuthContext를 받는 타입이 지정된 함수입니다: 서버가 소유한 이 컨텍스트를 통해 핸드셰이크 패킷을 읽고 쓰면서 클라이언트와 통신하며, MySQL의 내부 인증 구조체는 전혀 보지 않습니다.
인증 결과는 실패 시 차단(fail-closed)됩니다. 서버는 AuthResult::kOk이 아닌 모든 것을 거부된 연결로 처리합니다 — “아마도”나 실패 시 허용(fail-open) 결과는 의도적으로 존재하지 않습니다. AuthResult::kReject를 반환하거나, AuthResult::kError를 반환하거나, 유효 계정을 설정하지 않는 핸들러는 로그인을 거부합니다.

기능 선언

헤더를 포함하고, 타입이 지정된 핸들러를 작성하고, 플루언트 make_auth<> 빌더로 디스크립터를 구축한 후, 그 디스크립터를 .with()에 전달할 AuthCapability 토큰에 넘깁니다. 미리보기 기능 헤더는 <villagesql/vsql.h> 우산 헤더에 포함되지 않으므로, <villagesql/preview/auth.h>를 명시적으로 포함하세요:
빌더는 여섯 부분으로 구성됩니다: AuthCapability g_auth{descriptor}.with()가 사용하는 자체 등록 토큰입니다. 등록보다 오래 유지되도록 static으로 선언하세요. client_plugin은 선택적입니다. make_auth는 광고되는 플러그인을 "mysql_clear_password" — 모든 MySQL 클라이언트가 제공하는 최소 공통 분모 — 로 기본 설정하므로, .client_plugin()을 전혀 호출하지 않는 방식도 설치되고 단순한 클라이언트도 연결됩니다. 다른 플러그인을 요청하려면 .client_plugin(name)을 호출하세요. mysql_clear_password는 베어러 토큰을 비밀번호 슬롯에 그대로 받습니다. 방식이 요청한 것과 다른 플러그인을 제안하는 클라이언트는 요청된 플러그인으로 전환되어 자격 증명을 그대로 다시 보내며, 이는 왕복 한 번의 비용이 들고 그 전환을 받아들이는 클라이언트가 필요합니다. .accepts_client_plugin(&callback)은 방식이 제안된 플러그인을 그대로 유지할 수 있게 합니다: 서버는 제안된 각 이름을 콜백에 전달하며, 요청된 플러그인도 포함되지만 이는 콜백의 반환 값과 무관하게 허용됩니다. 콜백을 설정하지 않은 방식은 다른 어떤 제안도 허용하지 않으므로, 다른 모든 제안은 요청된 플러그인으로 전환됩니다. 허용은 최종적입니다 — 서버는 그 후에 요청된 플러그인으로 되돌리지 않습니다 — 따라서 핸들러가 실제로 그 프레이밍을 해석하는 플러그인만 허용하세요. 서버는 핸들러의 첫 읽기 이전인 핸드셰이크 협상 중에 콜백을 조회하므로, 콜백은 순수 술어여야 합니다: 패킷 I/O 없음, 블로킹 없음, 부작용 없음.

핸들러 계약

핸들러는 AuthHandler 유형과 일치합니다 — AuthContext &를 받고 AuthResult를 반환합니다:
핸들러는 핸드셰이크 중에 연결하는 스레드에서 동기적으로 호출됩니다. AuthContext는 서버가 소유한 시도별 컨텍스트를 감쌉니다. 호출 기간 동안만 보유하고 보관하지 마세요. 함수 테이블을 통해 컨텍스트 포인터를 전달하는 대신 그 메서드를 호출하세요. 토큰 기반 핸들러가 사용하는 메서드: 핸들러는 세 가지 결과 중 하나를 반환합니다: AuthResult::kRejectAuthResult::kError 둘 다 연결을 거부합니다. AuthResult::kOk만 성공합니다. 핸들러가 연결하는 계정을 다른 유효 계정으로 매핑할 때 — 아래 예제가 연결하는 계정을 vsql_auth_test_user로 매핑하는 것처럼 — 그것은 프록시이며, MySQL 플러그인 인증 경로에서와 똑같이 GRANT PROXY가 필요합니다.

활성 역할 스테이징

c.set_active_roles(roles, n_roles)는 세션에서 활성화되어야 하는 역할을 스테이징하며, 이 로그인에 대해 계정의 기본 역할 활성화를 대체합니다. roles는 NUL로 끝나는 이름 n_roles개의 배열입니다. 문자열은 복사되므로 호출자가 유지할 필요가 없습니다. 서버는 계정 확인 후에 SET ROLE과 동일한 부여 검사 활성화를 사용하여 이를 적용합니다: 인증된 계정에 실제로 부여된 역할만 활성화되고, 부여되지 않은 이름은 조용히 건너뜁니다 — 따라서 토큰은 DBA가 프로비저닝한 범위를 넘어 권한을 부여하거나 상승시킬 수 없습니다. n_roles == 0을 전달하면 어떤 역할도 활성화되지 않습니다(SET ROLE NONE과 동등).

완전한 예제

어떤 릴리스에도 포함되지 않는, 서버 소스 트리의 villagesql/test-extensions/vsql-auth-test/에 있는 vsql_auth_test 확장 프로그램에서 축약한 최소 인증기입니다. 고정된 토큰 하나를 허용하고, 연결을 vsql_auth_test_user로 매핑하며, 토큰이 비밀번호 슬롯에 그대로 도착하도록 mysql_clear_password를 요청합니다. (인트리 확장 프로그램은 테스트 스위트를 구동하기 위해 추가 토큰 경로, .accepts_client_plugin() 콜백, 그리고 아래에 설명된 두 옵트인을 모두 추가합니다.)

계정 바인딩 및 연결

미리보기 계층이 활성화된 상태에서(참조: 미리보기 계층 활성화), 확장 프로그램을 설치하고 계정을 방식에 바인딩합니다. 핸들러가 두 번째 계정으로 매핑하므로, 그 계정도 생성하고 연결하는 계정이 그 신원을 취할 수 있도록 PROXY 권한을 부여합니다:
vsql_auth_test가 등록된 VEF 인증 방식이기 때문에 CREATE USER ... IDENTIFIED WITH vsql_auth_test가 허용됩니다 — 설치된 플러그인 이름이 허용되는 것과 동일한 방식입니다. IDENTIFIED WITH <method> 형식만 허용되며, 선택적으로 AS '...'를 붙일 수 있습니다. BY '...'를 추가하는 것은 방식에게 비밀번호를 저장된 자격 증명으로 변환하라고 요청하는 것인데 — MySQL 플러그인이 generate_authentication_string()을 통해 수행하는 작업입니다 — 오늘날 어떤 VEF 인증 방식도 그 훅을 선언하지 않으므로 서버가 이를 거부합니다:
바인딩된 방식 이름은 테이블 기본값이 아니라 계정의 plugin 컬럼에 기록되며, 이것이 그 계정의 다음 로그인이 읽는 값입니다:
방식이 mysql_clear_password를 요청하므로, 클라이언트는 토큰을 평문으로 보내기 위해 --enable-cleartext-plugin을 전달해야 합니다. 올바른 토큰이면 세션은 매핑된 계정으로 실행되고 @@external_user를 통해 연결하는 계정을 노출합니다:
확장 프로그램을 제거하면 방식이 사라지며, 여기에 바인딩된 계정은 더 이상 인증할 수 없습니다:

계정 자동 생성

방식은 아직 존재하지 않는 계정의 로그인도 처리하고, 로그인이 성공하는 과정의 일부로 서버가 계정을 생성하도록 할 수 있습니다. 이것이 없으면 알 수 없는 계정은 어떤 방식이 실행되기도 전에 거부됩니다. .auto_create(&callback)으로 옵트인합니다. 콜백은 인수를 받지 않고 bool을 반환합니다. 서버는 등록 시점에 한 번 읽는 것이 아니라 알 수 없는 계정의 로그인마다 이를 호출하므로, 방식은 확장 프로그램이 로드될 때 선택을 고정하는 대신 자체 런타임 설정을 따를 수 있습니다:
.auto_create()를 생략하거나 콜백에서 false를 반환하면 표준 동작이 유지됩니다: 알 수 없는 계정은 거부됩니다. 한 번에 설치된 방식 중 하나만 옵트인할 수 있습니다 — 둘이 true를 반환하면 서버는 추측하기를 거부하고, 오류 로그에 경고를 기록하며, 아무도 옵트인하지 않은 것처럼 알 수 없는 계정을 거부합니다. 핸들러에서는 c.account_unknown()이 두 경우를 구분합니다. 먼저 자격 증명을 검증한 다음, 무엇을 생성할지 기술하고 그 계정으로 인증합니다:
request_provision(account, roles, n_roles)는 의도를 기록하고 아무것도 반환하지 않습니다. 서버는 핸들러가 AuthResult::kOk을 반환한 후에 DDL을 직접 실행하며, 알 수 없는 계정으로 라우팅된 로그인에 대해서만 실행합니다 — 따라서 핸들러가 이어서 거부하는 로그인은 아무것도 생성하지 않고, 이미 존재하는 계정을 지정한 요청은 무시됩니다. 서버가 실행하는 것은 CREATE USER IF NOT EXISTS <account>@'%' IDENTIFIED WITH <method>이며, 그 뒤에 지정된 역할마다 하나씩 GRANT가 이어집니다: 계정은 항상 호스트 %에 대해 생성되고 자신을 인증한 방식에 바인딩되며, account가 연결하는 사용자 이름일 필요는 없습니다. 생성을 수행할 수 없으면 — 예를 들어 super_read_only 서버에서 — 계정 없이 진행하는 대신 로그인이 실패합니다. 역할은 활성 역할 스테이징에서와 동일하게 동작합니다: DBA가 이를 소유합니다. 각 이름은 부여 가능한 역할로 이미 존재해야 하며, 부여할 수 없는 이름은 로그인을 실패시키지 않고 로깅된 후 건너뜁니다. 따라서 토큰은 역할을 지정할 수는 있어도 결코 생성하거나 상승시킬 수 없습니다. 계정 이름은 클라이언트에서 오므로 서버는 이를 식별자로 인용합니다 — 조작된 이름은 이상한 이름의 계정 하나가 될 뿐, 결코 두 번째 문이 되지 않습니다. vsql_auth_test 확장 프로그램은 연결하는 사용자에게 vsql_role_granted 역할을 프로비저닝하며, 옵트인을 OFF로 시작하는 vsql_auth_test.auto_create 뒤에 둡니다. 이를 켜고 역할을 먼저 생성한 다음, 존재하지 않는 계정으로 연결하세요:
이제 계정이 존재하며, 방식에 바인딩되어 있고, 부여된 역할을 보유합니다:
잘못된 토큰은 여전히 실패 시 차단되며, 아무것도 프로비저닝하지 않습니다:
옵트인하면 알 수 없는 계정과 기존 계정의 차이가 유효한 자격 증명을 가진 누구에게나 관찰 가능해지는데, 이는 표준 알 수 없는 계정 거부가 의도적으로 숨기는 것입니다. 이것이 이 기능이 감수하는 대가입니다. 자격 증명이 널리 보유된 방식에서 옵트인을 활성화하기 전에 이를 저울질하세요.

역할 자동 부여

기본적으로 토큰이 지정한 역할은 계정이 이미 그것을 보유한 경우에만 적용되며, 보유하지 않은 역할은 로깅된 후 건너뜁니다. .auto_grant(&callback)은 이를 바꿉니다: 서버가 스테이징된 역할을 계정에 부여하므로, 계정의 기존 역할 중 어느 것을 켤지가 아니라 세션이 어떤 역할을 받을지를 토큰이 결정합니다. 콜백은 .auto_create()와 형태가 같습니다 — 인수가 없고 bool을 반환하며, 서버가 로그인마다 호출하므로 런타임 설정을 따를 수 있습니다:
두 옵트인은 서로 독립적입니다. .auto_create()는 존재하지 않는 계정의 로그인을 관장하고, .auto_grant()는 로그인이 확인한 계정에 대한 부여를 관장하며, 그 계정이 방금 생성되었는지 여부와 무관합니다. .auto_grant()를 생략하거나 false를 반환하면 활성화 전용 기본 동작이 유지됩니다. 부여는 지속됩니다 — 세션 한정 활성화가 아니라 일반적인 GRANT입니다 — 그리고 추가적입니다: 서버는 토큰이 더 이상 지정하지 않게 된 역할을 결코 취소하지 않습니다. vsql_auth_test는 이를 vsql_auth_test.auto_grant로 노출하며, 이 또한 OFF로 시작합니다. 그 -token-roles 토큰은 vsql_role_grantedvsql_role_denied를 스테이징하며, 아래 계정은 둘 다 보유하지 않습니다. 설정이 꺼져 있으면 로그인은 계정의 역할을 그대로 둡니다:
위와 동일한 방식으로 그 토큰을 사용해 auth_user로 연결하고 무엇이 활성 상태인지 물으면:
설정을 켜고 동일한 로그인을 반복합니다:
이제 두 역할이 모두 활성 상태이며, SHOW GRANTS는 서버가 추가한 부여를 보여줍니다:
.auto_grant()가 켜져 있으면 유효한 토큰만으로 그것이 지정하는 어떤 역할이든 얻을 수 있습니다. 역할은 이미 존재해야 하므로 토큰이 권한을 만들어낼 수는 없지만, 계정이 어떤 기존 역할에 도달할 수 있는지를 더 이상 DBA가 결정하지 않고 방식이 결정합니다.