向量代表列的水平切片。它们存储特定类型的值,类似于数组。向量是DuckDB中使用的核心数据表示形式。向量通常存储在数据块中。
向量和数据块接口是与DuckDB交互的最有效方式,可实现最高性能。然而,这些接口也难以使用,在使用时必须谨慎。
向量格式
向量是特定数据类型的数组。向量的逻辑类型可以使用duckdb_vector_get_column_type
获取。然后可以使用duckdb_get_type_id
获取逻辑类型的类型ID。
向量本身没有大小。相反,父数据块有一个大小(可以通过duckdb_data_chunk_get_size
获取)。属于同一数据块的所有向量都具有相同的大小。
基本类型
对于基本类型,可以使用duckdb_vector_get_data
方法获取底层数组。然后可以使用正确的原生类型访问该数组。下表包含duckdb_type
到数组原生类型的映射。
duckdb_type | NativeType |
---|---|
DUCKDB_TYPE_BOOLEAN | bool |
DUCKDB_TYPE_TINYINT | int8_t |
DUCKDB_TYPE_SMALLINT | int16_t |
DUCKDB_TYPE_INTEGER | int32_t |
DUCKDB_TYPE_BIGINT | int64_t |
DUCKDB_TYPE_UTINYINT | uint8_t |
DUCKDB_TYPE_USMALLINT | uint16_t |
DUCKDB_TYPE_UINTEGER | uint32_t |
DUCKDB_TYPE_UBIGINT | uint64_t |
DUCKDB_TYPE_FLOAT | float |
DUCKDB_TYPE_DOUBLE | double |
DUCKDB_TYPE_TIMESTAMP | duckdb_timestamp |
DUCKDB_TYPE_DATE | duckdb_date |
DUCKDB_TYPE_TIME | duckdb_time |
DUCKDB_TYPE_INTERVAL | duckdb_interval |
DUCKDB_TYPE_HUGEINT | duckdb_hugeint |
DUCKDB_TYPE_UHUGEINT | duckdb_uhugeint |
DUCKDB_TYPE_VARCHAR | duckdb_string_t |
DUCKDB_TYPE_BLOB | duckdb_string_t |
DUCKDB_TYPE_TIMESTAMP_S | duckdb_timestamp |
DUCKDB_TYPE_TIMESTAMP_MS | duckdb_timestamp |
DUCKDB_TYPE_TIMESTAMP_NS | duckdb_timestamp |
DUCKDB_TYPE_UUID | duckdb_hugeint |
DUCKDB_TYPE_TIME_TZ | duckdb_time_tz |
DUCKDB_TYPE_TIMESTAMP_TZ | duckdb_timestamp |
NULL
值
向量中的任何值都可以是NULL
。当值为NULL
时,主数组中该索引处包含的值是未定义的(并且可能未初始化)。有效性掩码是由uint64_t
元素组成的位掩码。对于向量中的每64
个值,存在一个uint64_t
元素(向上取整)。如果值有效,则有效性掩码的位设置为1;如果值无效(即NULL
),则设置为0。
可以直接读取位掩码的位,也可以使用较慢的辅助方法duckdb_validity_row_is_valid
来检查值是否为NULL
。
duckdb_vector_get_validity
返回指向有效性掩码的指针。请注意,如果向量中的所有值都有效,此函数可能返回nullptr
,在这种情况下无需检查有效性掩码。
字符串
字符串值存储为duckdb_string_t
。这是一个特殊的结构体,如果字符串较短(即<= 12 字节
),则内联存储字符串;如果字符串长度超过12
字节,则存储指向字符串数据的指针。
typedef struct {
union {
struct {
uint32_t length;
char prefix[4];
char *ptr;
} pointer;
struct {
uint32_t length;
char inlined[12];
} inlined;
} value;
} duckdb_string_t;
可以直接访问长度,或者使用duckdb_string_is_inlined
来检查字符串是否内联。
小数
小数在内部存储为整数值。具体的原生类型取决于小数类型的宽度
,如下表所示
宽度 | NativeType |
---|---|
<= 4 | int16_t |
<= 9 | int32_t |
<= 18 | int64_t |
<= 38 | duckdb_hugeint |
可以使用duckdb_decimal_internal_type
获取小数的内部类型。
小数以乘以10^scale
的整数值存储。小数的比例尺可以使用duckdb_decimal_scale
获取。例如,类型为DECIMAL(8, 3)
的小数值10.5
在内部存储为int32_t
值10500
。为了获得正确的小数值,该值应除以相应的10的幂。
枚举
枚举在内部存储为无符号整数值。具体的原生类型取决于枚举字典的大小,如下表所示
字典大小 | NativeType |
---|---|
<= 255 | uint8_t |
<= 65535 | uint16_t |
<= 4294967295 | uint32_t |
可以使用duckdb_enum_internal_type
获取枚举的内部类型。
为了获取枚举的实际字符串值,必须使用duckdb_enum_dictionary_value
函数来获取与给定字典条目对应的枚举值。请注意,枚举字典对于整个列是相同的,因此只需构建一次。
结构体
结构体是嵌套类型,可以包含任意数量的子类型。可以将其视为C语言中的struct
。使用向量访问结构体数据的方法是使用duckdb_struct_vector_get_child
方法递归访问子向量。
结构体向量本身不包含任何数据(即,不应在结构体上使用duckdb_vector_get_data
方法)。然而,结构体向量本身确实具有有效性掩码。这是因为结构体的子元素可以是NULL
,但结构体本身也可以是NULL
。
列表
列表是嵌套类型,包含一个单一的子类型,每行重复x
次。可以将其视为C语言中的变长数组。使用向量访问列表数据的方法是使用duckdb_list_vector_get_child
方法访问子向量。
必须使用duckdb_vector_get_data
获取存储为duckdb_list_entry
的列表的偏移量和长度,然后将其应用于子向量。
typedef struct {
uint64_t offset;
uint64_t length;
} duckdb_list_entry;
请注意,列表条目本身以及存储在列表中的任何子项也可能是NULL
。这必须再次使用有效性掩码进行检查。
数组
数组是嵌套类型,包含一个单一的子类型,每行精确重复array_size
次。可以将其视为C语言中的固定大小数组。数组的工作方式与列表完全相同,除了每个条目的长度和偏移量是固定的。固定数组大小可以通过使用duckdb_array_type_array_size
获取。条目n
的数据位于offset = n * array_size
处,并且总是具有length = array_size
。
请注意,与列表非常相似,数组仍然可以是NULL
,这必须使用有效性掩码进行检查。
示例
以下是关于如何与向量交互的几个完整的端到端示例。
示例:读取带有NULL
值的int64向量
duckdb_database db;
duckdb_connection con;
duckdb_open(nullptr, &db);
duckdb_connect(db, &con);
duckdb_result res;
duckdb_query(con, "SELECT CASE WHEN i%2=0 THEN NULL ELSE i END res_col FROM range(10) t(i)", &res);
// iterate until result is exhausted
while (true) {
duckdb_data_chunk result = duckdb_fetch_chunk(res);
if (!result) {
// result is exhausted
break;
}
// get the number of rows from the data chunk
idx_t row_count = duckdb_data_chunk_get_size(result);
// get the first column
duckdb_vector res_col = duckdb_data_chunk_get_vector(result, 0);
// get the native array and the validity mask of the vector
int64_t *vector_data = (int64_t *) duckdb_vector_get_data(res_col);
uint64_t *vector_validity = duckdb_vector_get_validity(res_col);
// iterate over the rows
for (idx_t row = 0; row < row_count; row++) {
if (duckdb_validity_row_is_valid(vector_validity, row)) {
printf("%lld\n", vector_data[row]);
} else {
printf("NULL\n");
}
}
duckdb_destroy_data_chunk(&result);
}
// clean-up
duckdb_destroy_result(&res);
duckdb_disconnect(&con);
duckdb_close(&db);
示例:读取字符串向量
duckdb_database db;
duckdb_connection con;
duckdb_open(nullptr, &db);
duckdb_connect(db, &con);
duckdb_result res;
duckdb_query(con, "SELECT CASE WHEN i%2=0 THEN CONCAT('short_', i) ELSE CONCAT('longstringprefix', i) END FROM range(10) t(i)", &res);
// iterate until result is exhausted
while (true) {
duckdb_data_chunk result = duckdb_fetch_chunk(res);
if (!result) {
// result is exhausted
break;
}
// get the number of rows from the data chunk
idx_t row_count = duckdb_data_chunk_get_size(result);
// get the first column
duckdb_vector res_col = duckdb_data_chunk_get_vector(result, 0);
// get the native array and the validity mask of the vector
duckdb_string_t *vector_data = (duckdb_string_t *) duckdb_vector_get_data(res_col);
uint64_t *vector_validity = duckdb_vector_get_validity(res_col);
// iterate over the rows
for (idx_t row = 0; row < row_count; row++) {
if (duckdb_validity_row_is_valid(vector_validity, row)) {
duckdb_string_t str = vector_data[row];
if (duckdb_string_is_inlined(str)) {
// use inlined string
printf("%.*s\n", str.value.inlined.length, str.value.inlined.inlined);
} else {
// follow string pointer
printf("%.*s\n", str.value.pointer.length, str.value.pointer.ptr);
}
} else {
printf("NULL\n");
}
}
duckdb_destroy_data_chunk(&result);
}
// clean-up
duckdb_destroy_result(&res);
duckdb_disconnect(&con);
duckdb_close(&db);
示例:读取结构体向量
duckdb_database db;
duckdb_connection con;
duckdb_open(nullptr, &db);
duckdb_connect(db, &con);
duckdb_result res;
duckdb_query(con, "SELECT CASE WHEN i%5=0 THEN NULL ELSE {'col1': i, 'col2': CASE WHEN i%2=0 THEN NULL ELSE 100 + i * 42 END} END FROM range(10) t(i)", &res);
// iterate until result is exhausted
while (true) {
duckdb_data_chunk result = duckdb_fetch_chunk(res);
if (!result) {
// result is exhausted
break;
}
// get the number of rows from the data chunk
idx_t row_count = duckdb_data_chunk_get_size(result);
// get the struct column
duckdb_vector struct_col = duckdb_data_chunk_get_vector(result, 0);
uint64_t *struct_validity = duckdb_vector_get_validity(struct_col);
// get the child columns of the struct
duckdb_vector col1_vector = duckdb_struct_vector_get_child(struct_col, 0);
int64_t *col1_data = (int64_t *) duckdb_vector_get_data(col1_vector);
uint64_t *col1_validity = duckdb_vector_get_validity(col1_vector);
duckdb_vector col2_vector = duckdb_struct_vector_get_child(struct_col, 1);
int64_t *col2_data = (int64_t *) duckdb_vector_get_data(col2_vector);
uint64_t *col2_validity = duckdb_vector_get_validity(col2_vector);
// iterate over the rows
for (idx_t row = 0; row < row_count; row++) {
if (!duckdb_validity_row_is_valid(struct_validity, row)) {
// entire struct is NULL
printf("NULL\n");
continue;
}
// read col1
printf("{'col1': ");
if (!duckdb_validity_row_is_valid(col1_validity, row)) {
// col1 is NULL
printf("NULL");
} else {
printf("%lld", col1_data[row]);
}
printf(", 'col2': ");
if (!duckdb_validity_row_is_valid(col2_validity, row)) {
// col2 is NULL
printf("NULL");
} else {
printf("%lld", col2_data[row]);
}
printf("}\n");
}
duckdb_destroy_data_chunk(&result);
}
// clean-up
duckdb_destroy_result(&res);
duckdb_disconnect(&con);
duckdb_close(&db);
示例:读取列表向量
duckdb_database db;
duckdb_connection con;
duckdb_open(nullptr, &db);
duckdb_connect(db, &con);
duckdb_result res;
duckdb_query(con, "SELECT CASE WHEN i % 5 = 0 THEN NULL WHEN i % 2 = 0 THEN [i, i + 1] ELSE [i * 42, NULL, i * 84] END FROM range(10) t(i)", &res);
// iterate until result is exhausted
while (true) {
duckdb_data_chunk result = duckdb_fetch_chunk(res);
if (!result) {
// result is exhausted
break;
}
// get the number of rows from the data chunk
idx_t row_count = duckdb_data_chunk_get_size(result);
// get the list column
duckdb_vector list_col = duckdb_data_chunk_get_vector(result, 0);
duckdb_list_entry *list_data = (duckdb_list_entry *) duckdb_vector_get_data(list_col);
uint64_t *list_validity = duckdb_vector_get_validity(list_col);
// get the child column of the list
duckdb_vector list_child = duckdb_list_vector_get_child(list_col);
int64_t *child_data = (int64_t *) duckdb_vector_get_data(list_child);
uint64_t *child_validity = duckdb_vector_get_validity(list_child);
// iterate over the rows
for (idx_t row = 0; row < row_count; row++) {
if (!duckdb_validity_row_is_valid(list_validity, row)) {
// entire list is NULL
printf("NULL\n");
continue;
}
// read the list offsets for this row
duckdb_list_entry list = list_data[row];
printf("[");
for (idx_t child_idx = list.offset; child_idx < list.offset + list.length; child_idx++) {
if (child_idx > list.offset) {
printf(", ");
}
if (!duckdb_validity_row_is_valid(child_validity, child_idx)) {
// col1 is NULL
printf("NULL");
} else {
printf("%lld", child_data[child_idx]);
}
}
printf("]\n");
}
duckdb_destroy_data_chunk(&result);
}
// clean-up
duckdb_destroy_result(&res);
duckdb_disconnect(&con);
duckdb_close(&db);
API参考概览
duckdb_vector duckdb_create_vector(duckdb_logical_type type, idx_t capacity);
void duckdb_destroy_vector(duckdb_vector *vector);
duckdb_logical_type duckdb_vector_get_column_type(duckdb_vector vector);
void *duckdb_vector_get_data(duckdb_vector vector);
uint64_t *duckdb_vector_get_validity(duckdb_vector vector);
void duckdb_vector_ensure_validity_writable(duckdb_vector vector);
void duckdb_vector_assign_string_element(duckdb_vector vector, idx_t index, const char *str);
void duckdb_vector_assign_string_element_len(duckdb_vector vector, idx_t index, const char *str, idx_t str_len);
duckdb_vector duckdb_list_vector_get_child(duckdb_vector vector);
idx_t duckdb_list_vector_get_size(duckdb_vector vector);
duckdb_state duckdb_list_vector_set_size(duckdb_vector vector, idx_t size);
duckdb_state duckdb_list_vector_reserve(duckdb_vector vector, idx_t required_capacity);
duckdb_vector duckdb_struct_vector_get_child(duckdb_vector vector, idx_t index);
duckdb_vector duckdb_array_vector_get_child(duckdb_vector vector);
void duckdb_slice_vector(duckdb_vector vector, duckdb_selection_vector selection, idx_t len);
void duckdb_vector_reference_value(duckdb_vector vector, duckdb_value value);
void duckdb_vector_reference_vector(duckdb_vector to_vector, duckdb_vector from_vector);
有效性掩码函数
bool duckdb_validity_row_is_valid(uint64_t *validity, idx_t row);
void duckdb_validity_set_row_validity(uint64_t *validity, idx_t row, bool valid);
void duckdb_validity_set_row_invalid(uint64_t *validity, idx_t row);
void duckdb_validity_set_row_valid(uint64_t *validity, idx_t row);
duckdb_create_vector
创建扁平向量。
语法
duckdb_vector duckdb_create_vector(
duckdb_logical_type type,
idx_t capacity
);
duckdb_destroy_vector
销毁向量并释放为该向量分配的所有内存(如果未在其他地方使用)。
语法
void duckdb_destroy_vector(
duckdb_vector *vector
);
duckdb_vector_get_column_type
检索指定向量的列类型。
结果必须使用duckdb_destroy_logical_type
销毁。
语法
duckdb_logical_type duckdb_vector_get_column_type(
duckdb_vector vector
);
参数
vector
:要获取数据的向量
返回值
向量的类型
duckdb_vector_get_data
检索向量的数据指针。
数据指针可用于从向量中读取或写入值。如何读取或写入值取决于向量的类型。
语法
void *duckdb_vector_get_data(
duckdb_vector vector
);
参数
vector
:要获取数据的向量
返回值
数据指针
duckdb_vector_get_validity
检索指定向量的有效性掩码指针。
如果所有值都有效,此函数可能返回NULL!
有效性掩码是一个位集,表示数据块内的空值。它是一系列uint64_t
值,其中每个uint64_t
值包含64个元组的有效性。如果值有效(即非NULL),则位设置为1;如果值无效(即NULL),则位设置为0。
特定值的有效性可以通过以下方式获取
idx_t entry_idx = row_idx / 64; idx_t idx_in_entry = row_idx % 64; bool is_valid = validity_mask[entry_idx] & (1 « idx_in_entry);
或者,可以使用(较慢的)duckdb_validity_row_is_valid
函数。
语法
uint64_t *duckdb_vector_get_validity(
duckdb_vector vector
);
参数
vector
:要获取数据的向量
返回值
指向有效性掩码的指针,如果不存在有效性掩码则为NULL
duckdb_vector_ensure_validity_writable
通过分配有效性掩码来确保其可写。
调用此函数后,duckdb_vector_get_validity
将始终返回非NULL。这允许将NULL值写入向量,无论之前是否存在有效性掩码。
语法
void duckdb_vector_ensure_validity_writable(
duckdb_vector vector
);
参数
vector
:要修改的向量
duckdb_vector_assign_string_element
在向量中指定位置分配字符串元素。
语法
void duckdb_vector_assign_string_element(
duckdb_vector vector,
idx_t index,
const char *str
);
参数
vector
:要修改的向量index
:在向量中分配字符串的行位置str
:以空字符结尾的字符串
duckdb_vector_assign_string_element_len
在向量中指定位置分配字符串元素。您也可以使用此函数分配BLOB。
语法
void duckdb_vector_assign_string_element_len(
duckdb_vector vector,
idx_t index,
const char *str,
idx_t str_len
);
参数
vector
:要修改的向量index
:在向量中分配字符串的行位置str
:字符串str_len
:字符串的长度(以字节为单位)
duckdb_list_vector_get_child
检索列表向量的子向量。
只要父向量有效,结果向量就有效。
语法
duckdb_vector duckdb_list_vector_get_child(
duckdb_vector vector
);
参数
vector
:向量
返回值
子向量
duckdb_list_vector_get_size
返回列表子向量的大小。
语法
idx_t duckdb_list_vector_get_size(
duckdb_vector vector
);
参数
vector
:向量
返回值
子列表的大小
duckdb_list_vector_set_size
设置列表向量底层子向量的总大小。
语法
duckdb_state duckdb_list_vector_set_size(
duckdb_vector vector,
idx_t size
);
参数
vector
:列表向量。size
:子列表的大小。
返回值
DuckDB状态。如果向量为nullptr
则返回DuckDBError
。
duckdb_list_vector_reserve
设置列表底层子向量的总容量。
调用此方法后,您必须调用duckdb_vector_get_validity
和duckdb_vector_get_data
以获取当前数据和有效性指针。
语法
duckdb_state duckdb_list_vector_reserve(
duckdb_vector vector,
idx_t required_capacity
);
参数
vector
:列表向量。required_capacity
:要保留的总容量。
返回值
DuckDB状态。如果向量为nullptr
则返回DuckDBError
。
duckdb_struct_vector_get_child
检索结构体向量的子向量。
只要父向量有效,结果向量就有效。
语法
duckdb_vector duckdb_struct_vector_get_child(
duckdb_vector vector,
idx_t index
);
参数
vector
:向量index
:子索引
返回值
子向量
duckdb_array_vector_get_child
检索数组向量的子向量。
只要父向量有效,结果向量就有效。结果向量的大小是父向量大小乘以数组大小。
语法
duckdb_vector duckdb_array_vector_get_child(
duckdb_vector vector
);
参数
vector
:向量
返回值
子向量
duckdb_slice_vector
使用选择向量切片向量。
选择向量中的最大值必须小于向量的长度
结果向量恰好是字典向量。
语法
void duckdb_slice_vector(
duckdb_vector vector,
duckdb_selection_vector selection,
idx_t len
);
参数
vector
:将成为字典的向量selection
:选择向量len
:选择向量的长度
duckdb_vector_reference_value
将值从value
复制到vector
。
语法
void duckdb_vector_reference_value(
duckdb_vector vector,
duckdb_value value
);
duckdb_vector_reference_vector
在to
向量中引用from
向量,这使得共享值缓冲区的拥有权。
语法
void duckdb_vector_reference_vector(
duckdb_vector to_vector,
duckdb_vector from_vector
);
duckdb_validity_row_is_valid
返回给定有效性掩码中行是否有效(即非NULL)。
语法
bool duckdb_validity_row_is_valid(
uint64_t *validity,
idx_t row
);
参数
validity
:通过duckdb_vector_get_validity
获取的有效性掩码row
:行索引
返回值
如果行有效则为true,否则为false
duckdb_validity_set_row_validity
在有效性掩码中,将特定行设置为有效或无效。
请注意,在调用duckdb_vector_get_validity
之前,应调用duckdb_vector_ensure_validity_writable
,以确保存在可写入的有效性掩码。
语法
void duckdb_validity_set_row_validity(
uint64_t *validity,
idx_t row,
bool valid
);
参数
validity
:通过duckdb_vector_get_validity
获取的有效性掩码。row
:行索引valid
:是否将行设置为有效或无效
duckdb_validity_set_row_invalid
在有效性掩码中,将特定行设置为无效。
等同于将valid
设置为false的duckdb_validity_set_row_validity
。
语法
void duckdb_validity_set_row_invalid(
uint64_t *validity,
idx_t row
);
参数
validity
:有效性掩码row
:行索引
duckdb_validity_set_row_valid
在有效性掩码中,将特定行设置为有效。
等同于将valid
设置为true的duckdb_validity_set_row_validity
。
语法
void duckdb_validity_set_row_valid(
uint64_t *validity,
idx_t row
);
参数
validity
:有效性掩码row
:行索引