向量表示列的横向切片。它们保存特定类型的多个值,类似于数组。向量是DuckDB中使用的核心数据表示形式。向量通常存储在数据块中。
向量和数据块接口是与DuckDB交互的最有效方式,能够实现最高性能。然而,这些接口也较难使用,使用时必须小心。
Vector Format
向量是特定数据类型的数组。可以使用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
来检查字符串是否内联。
Decimals
小数在内部存储为整数值。确切的本地类型取决于小数类型的width
,如下表所示:
宽度 | 原生类型 |
---|---|
<= 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
。为了获得正确的小数值,该值应除以适当的十的幂。
枚举
枚举在内部存储为无符号整数值。确切的本地类型取决于枚举字典的大小,如下表所示:
字典大小 | 原生类型 |
---|---|
<= 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
,必须使用有效性掩码进行检查。
Examples
以下是几个完整的端到端示例,展示了如何与向量进行交互。
示例:读取带有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 Reference Overview
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);
有效性掩码函数
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_vector_get_column_type
检索指定向量的列类型。
结果必须使用duckdb_destroy_logical_type
销毁。
Syntax
duckdb_logical_type duckdb_vector_get_column_type(
duckdb_vector vector
);
Parameters
vector
: 获取数据的向量
Return Value
向量的类型
duckdb_vector_get_data
检索向量的数据指针。
数据指针可用于从向量中读取或写入值。如何读取或写入值取决于向量的类型。
Syntax
void *duckdb_vector_get_data(
duckdb_vector vector
);
Parameters
vector
: 从中获取数据的向量
Return Value
数据指针
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函数。
Syntax
uint64_t *duckdb_vector_get_validity(
duckdb_vector vector
);
Parameters
vector
: 从中获取数据的向量
Return Value
指向有效性掩码的指针,如果没有有效性掩码则为NULL
duckdb_vector_ensure_validity_writable
通过分配确保有效性掩码是可写的。
调用此函数后,duckdb_vector_get_validity
将始终返回非NULL值。
这允许将NULL
值写入向量,无论之前是否存在有效性掩码。
Syntax
void duckdb_vector_ensure_validity_writable(
duckdb_vector vector
);
Parameters
vector
: 要修改的向量
duckdb_vector_assign_string_element
在向量的指定位置分配一个字符串元素。
Syntax
void duckdb_vector_assign_string_element(
duckdb_vector vector,
idx_t index,
const char *str
);
Parameters
vector
: 要修改的向量index
: 向量中要分配字符串的行位置str
: 以空字符结尾的字符串
duckdb_vector_assign_string_element_len
在向量的指定位置分配一个字符串元素。你也可以使用此函数来分配BLOBs。
Syntax
void duckdb_vector_assign_string_element_len(
duckdb_vector vector,
idx_t index,
const char *str,
idx_t str_len
);
Parameters
vector
: 要修改的向量index
: 向量中要分配字符串的行位置str
: 字符串str_len
: 字符串的长度(以字节为单位)
duckdb_list_vector_get_child
检索列表向量的子向量。
只要父向量有效,生成的向量就有效。
Syntax
duckdb_vector duckdb_list_vector_get_child(
duckdb_vector vector
);
Parameters
vector
: 向量
Return Value
子向量
duckdb_list_vector_get_size
返回列表子向量的大小。
Syntax
idx_t duckdb_list_vector_get_size(
duckdb_vector vector
);
Parameters
vector
: 向量
Return Value
子列表的大小
duckdb_list_vector_set_size
设置列表向量的基础子向量的总大小。
Syntax
duckdb_state duckdb_list_vector_set_size(
duckdb_vector vector,
idx_t size
);
Parameters
vector
: 列表向量。size
: 子列表的大小。
Return Value
duckdb 状态。如果向量为 nullptr,则返回 DuckDBError。
duckdb_list_vector_reserve
设置列表底层子向量的总容量。
调用此方法后,您必须调用duckdb_vector_get_validity
和duckdb_vector_get_data
以获取当前数据和有效性指针
Syntax
duckdb_state duckdb_list_vector_reserve(
duckdb_vector vector,
idx_t required_capacity
);
Parameters
vector
: 列表向量。required_capacity
: 需要保留的总容量。
Return Value
duckdb 状态。如果向量为 nullptr,则返回 DuckDBError。
duckdb_struct_vector_get_child
检索结构向量的子向量。
只要父向量有效,生成的向量就有效。
Syntax
duckdb_vector duckdb_struct_vector_get_child(
duckdb_vector vector,
idx_t index
);
Parameters
vector
: 向量index
: 子索引
Return Value
子向量
duckdb_array_vector_get_child
检索数组向量的子向量。
只要父向量有效,结果向量就有效。 结果向量的大小是父向量的大小乘以数组大小。
Syntax
duckdb_vector duckdb_array_vector_get_child(
duckdb_vector vector
);
Parameters
vector
: 向量
Return Value
子向量
duckdb_validity_row_is_valid
返回给定有效性掩码中的一行是否有效(即不为NULL)。
Syntax
bool duckdb_validity_row_is_valid(
uint64_t *validity,
idx_t row
);
Parameters
validity
: 有效性掩码,通过duckdb_vector_get_validity
获取row
: 行索引
Return Value
如果行有效则为true,否则为false
duckdb_validity_set_row_validity
在有效性掩码中,将特定行设置为有效或无效。
请注意,在调用duckdb_vector_get_validity
之前,应该先调用duckdb_vector_ensure_validity_writable
,以确保有一个有效性掩码可以写入。
Syntax
void duckdb_validity_set_row_validity(
uint64_t *validity,
idx_t row,
bool valid
);
Parameters
validity
: 有效性掩码,通过duckdb_vector_get_validity
获取。row
: 行索引valid
: 是否将行设置为有效或无效
duckdb_validity_set_row_invalid
在有效性掩码中,将特定行设置为无效。
等同于将 duckdb_validity_set_row_validity
的 valid 参数设置为 false。
Syntax
void duckdb_validity_set_row_invalid(
uint64_t *validity,
idx_t row
);
Parameters
validity
: 有效性掩码row
: 行索引
duckdb_validity_set_row_valid
在有效性掩码中,将特定行设置为有效。
等同于将 duckdb_validity_set_row_validity
的 valid 参数设置为 true。
Syntax
void duckdb_validity_set_row_valid(
uint64_t *validity,
idx_t row
);
Parameters
validity
: 有效性掩码row
: The row index