⌘+k ctrl+k
1.1.3 (stable)
Search Shortcut cmd + k | ctrl + k
Vectors

向量表示列的横向切片。它们保存特定类型的多个值,类似于数组。向量是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_t10500。为了获得正确的小数值,该值应除以适当的十的幂。

枚举

枚举在内部存储为无符号整数值。确切的本地类型取决于枚举字典的大小,如下表所示:

字典大小 原生类型
<= 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_validityduckdb_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