⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
向量

向量代表列的水平切片。它们存储特定类型的值,类似于数组。向量是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_t10500。为了获得正确的小数值,该值应除以相应的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_validityduckdb_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:行索引