Node.js 클라이언트
Node.js 클라이언트 (Neo)
@duckdb/node-api는 [DuckDB]({% link index.html %})를 Node.js에서 사용하기 위한 고수준 API예요. 이 문서는 설치, 플랫폼, 그리고 인스턴스 생성부터 쿼리 실행, 결과 읽기까지 기본적인 사용법을 예제와 함께 다뤄요. 함께 살펴볼까요?
출처: 문서
본문
설치: DuckDB Node.js 클라이언트를 사용하려면 [Node.js 설치 페이지]({% link install/index.html %}?environment=nodejs)를 방문하세요.
DuckDB Node.js (Neo) 클라이언트의 최신 안정 버전은 {% if site.current_duckdb_node_neo_version != "" %}{{ site.current_duckdb_node_neo_version }}{% else %}{{ site.lts_duckdb_node_neo_version }}{% endif %}이에요.
[DuckDB]({% link index.html %})를 Node.js에서 사용하기 위한 API예요.
주 패키지인 @duckdb/node-api는 애플리케이션을 위한 고수준 API예요. 이것은 [DuckDB의 C API]({% link docs/current/clients/c/overview.md %})를 밀접하게 따르는 저수준 바인딩에 의존하는데, 그 바인딩은 별도로 @duckdb/node-bindings로 제공돼요.
로드맵 (Roadmap)
아직 완성되지 않은 기능들이 있어요:
- MAP과 UNION 데이터 타입의 바인딩 및 추가
- 행 단위로 기본값 추가
- 사용자 정의 타입 및 함수
- 프로파일링 정보
- 테이블 설명
- Arrow용 API
가장 최신 로드맵은 GitHub의 issues 목록을 참고하세요.
플랫폼 (Platforms)
Node.js (Neo) 클라이언트는 다음 [플랫폼]({% link docs/current/dev/building/overview.md %}#supported-platforms)을 지원해요:
linux_amd64linux_arm64osx_amd64osx_arm64windows_amd64
현재 windows_arm64 플랫폼은 지원하지 않아요.
예시 (Examples)
기본 정보 얻기 (Get Basic Information)
import duckdb from '@duckdb/node-api';
console.log(duckdb.version());
console.log(duckdb.configurationOptionDescriptions());
연결 (Connect)
import { DuckDBConnection } from '@duckdb/node-api';
const connection = await DuckDBConnection.create();
이것은 기본 인스턴스를 사용해요. 고급 사용법을 위해 인스턴스를 명시적으로 만들 수도 있어요.
인스턴스 생성 (Create Instance)
import { DuckDBInstance } from '@duckdb/node-api';
인메모리 데이터베이스로 생성:
const instance = await DuckDBInstance.create(':memory:');
위와 동일:
const instance = await DuckDBInstance.create();
필요하면 생성되는 데이터베이스 파일에 읽고 쓰기:
const instance = await DuckDBInstance.create('my_duckdb.db');
[구성 옵션]({% link docs/current/configuration/overview.md %}) 설정:
const instance = await DuckDBInstance.create('my_duckdb.db', {
threads: '4'
});
인스턴스 캐시 (Instance Cache)
같은 프로세스의 여러 인스턴스는 같은 데이터베이스를 연결해서는 안 돼요.
이를 막기 위해 인스턴스 캐시를 사용할 수 있어요:
const instance = await DuckDBInstance.fromCache('my_duckdb.db');
이것은 기본 인스턴스 캐시를 사용해요. 고급 사용법을 위해 인스턴스 캐시를 명시적으로 만들 수 있어요:
import { DuckDBInstanceCache } from '@duckdb/node-api';
const cache = new DuckDBInstanceCache();
const instance = await cache.getOrCreateInstance('my_duckdb.db');
인스턴스에 연결 (Connect to Instance)
const connection = await instance.connect();
연결 해제 (Disconnect)
연결은 참조가 버려진 직후 자동으로 해제되지만, 원한다면 언제든 명시적으로 해제할 수도 있어요:
connection.disconnectSync();
또는 동일하게:
connection.closeSync();
SQL 실행 (Run SQL)
const result = await connection.run('from test_all_types()');
SQL 파라미터화 (Parameterize SQL)
const prepared = await connection.prepare('select $1, $2, $3');
prepared.bindVarchar(1, 'duck');
prepared.bindInteger(2, 42);
prepared.bindList(3, listValue([10, 11, 12]), LIST(INTEGER));
const result = await prepared.run();
또는:
const prepared = await connection.prepare('select $a, $b, $c');
prepared.bind({
'a': 'duck',
'b': 42,
'c': listValue([10, 11, 12]),
}, {
'a': VARCHAR,
'b': INTEGER,
'c': LIST(INTEGER),
});
const result = await prepared.run();
또는 심지어:
const result = await connection.run('select $a, $b, $c', {
'a': 'duck',
'b': 42,
'c': listValue([10, 11, 12]),
}, {
'a': VARCHAR,
'b': INTEGER,
'c': LIST(INTEGER),
});
지정되지 않은 타입은 추론돼요:
const result = await connection.run('select $a, $b, $c', {
'a': 'duck',
'b': 42,
'c': listValue([10, 11, 12]),
});
값 지정 (Specifying Values)
많은 데이터 타입의 값은 JS 원시 타입 boolean, number, bigint, string 중 하나로 표현돼요.
또한 어떤 타입이든 null 값을 가질 수 있어요.
일부 데이터 타입의 값은 특별한 함수를 사용해 구성해야 해요. 그것들은 다음과 같아요:
| Type | Function |
|---|---|
ARRAY |
arrayValue |
BIT |
bitValue |
BLOB |
blobValue |
DATE |
dateValue |
DECIMAL |
decimalValue |
INTERVAL |
intervalValue |
LIST |
listValue |
MAP |
mapValue |
STRUCT |
structValue |
TIME |
timeValue |
TIMETZ |
timeTZValue |
TIMESTAMP |
timestampValue |
TIMESTAMPTZ |
timestampTZValue |
TIMESTAMP_S |
timestampSecondsValue |
TIMESTAMP_MS |
timestampMillisValue |
TIMESTAMP_NS |
timestampNanosValue |
UNION |
unionValue |
UUID |
uuidValue |
결과 스트리밍 (Stream Results)
스트리밍 결과는 행을 읽을 때 지연 평가돼요.
const result = await connection.stream('from range(10_000)');
결과 메타데이터 검사 (Inspect Result Metadata)
컬럼 이름과 타입 얻기:
const columnNames = result.columnNames();
const columnTypes = result.columnTypes();
결과 데이터 읽기 (Read Result Data)
실행하고 모든 데이터 읽기:
const reader = await connection.runAndReadAll('from test_all_types()');
const rows = reader.getRows();
// OR: const columns = reader.getColumns();
스트리밍하고 (적어도) 어떤 수의 행까지 읽기:
const reader = await connection.streamAndReadUntil(
'from range(5000)',
1000
);
const rows = reader.getRows();
// rows.length === 2048. (Rows are read in chunks of 2048.)
행을 점진적으로 읽기:
const reader = await connection.streamAndRead('from range(5000)');
reader.readUntil(2000);
// reader.currentRowCount === 2048 (Rows are read in chunks of 2048.)
// reader.done === false
reader.readUntil(4000);
// reader.currentRowCount === 4096
// reader.done === false
reader.readUntil(6000);
// reader.currentRowCount === 5000
// reader.done === true
결과 데이터 얻기 (Get Result Data)
결과 데이터는 다양한 형태로 얻을 수 있어요:
const reader = await connection.runAndReadAll(
'from range(3) select range::int as i, 10 + i as n'
);
const rows = reader.getRows();
// [ [0, 10], [1, 11], [2, 12] ]
const rowObjects = reader.getRowObjects();
// [ { i: 0, n: 10 }, { i: 1, n: 11 }, { i: 2, n: 12 } ]
const columns = reader.getColumns();
// [ [0, 1, 2], [10, 11, 12] ]
const columnsObject = reader.getColumnsObject();
// { i: [0, 1, 2], n: [10, 11, 12] }
결과 데이터 변환 (Convert Result Data)
기본적으로 JS 내장값으로 표현할 수 없는 데이터 값은 특수한 JS 객체로 반환돼요. 아래 Inspect Data Values를 참고하세요.
데이터를 JS 내장값이나 JSON으로 손실 없이 직렬화할 수 있는 값처럼 다른 형태로 얻으려면, 위 결과 데이터 메서드의 JS 또는 Json 형태를 사용하세요.
커스텀 변환기를 제공할 수도 있어요. 방법은 JSDuckDBValueConverter와 JsonDuckDBValueConverters의 구현을 참고하세요.
예시 (Json 형태 사용):
const reader = await connection.runAndReadAll(
'from test_all_types() select bigint, date, interval limit 2'
);
const rows = reader.getRowsJson();
// [
// [
// "-9223372036854775808",
// "5877642-06-25 (BC)",
// { "months": 0, "days": 0, "micros": "0" }
// ],
// [
// "9223372036854775807",
// "5881580-07-10",
// { "months": 999, "days": 999, "micros": "999999999" }
// ]
// ]
const rowObjects = reader.getRowObjectsJson();
// [
// {
// "bigint": "-9223372036854775808",
// "date": "5877642-06-25 (BC)",
// "interval": { "months": 0, "days": 0, "micros": "0" }
// },
// {
// "bigint": "9223372036854775807",
// "date": "5881580-07-10",
// "interval": { "months": 999, "days": 999, "micros": "999999999" }
// }
// ]
const columns = reader.getColumnsJson();
// [
// [ "-9223372036854775808", "9223372036854775807" ],
// [ "5877642-06-25 (BC)", "5881580-07-10" ],
// [
// { "months": 0, "days": 0, "micros": "0" },
// { "months": 999, "days": 999, "micros": "999999999" }
// ]
// ]
const columnsObject = reader.getColumnsObjectJson();
// {
// "bigint": [ "-9223372036854775808", "9223372036854775807" ],
// "date": [ "5877642-06-25 (BC)", "5881580-07-10" ],
// "interval": [
// { "months": 0, "days": 0, "micros": "0" },
// { "months": 999, "days": 999, "micros": "999999999" }
// ]
// }
이 메서드들은 중첩 타입도 처리해요:
const reader = await connection.runAndReadAll(
'from test_all_types() select int_array, struct, map, "union" limit 2'
);
const rows = reader.getRowsJson();
// [
// [
// [],
// { "a": null, "b": null },
// [],
// { "tag": "name", "value": "Frank" }
// ],
// [
// [ 42, 999, null, null, -42],
// { "a": 42, "b": "🦆🦆🦆🦆🦆🦆" },
// [
// { "key": "key1", "value": "🦆🦆🦆🦆🦆🦆" },
// { "key": "key2", "value": "goose" }
// ],
// { "tag": "age", "value": 5 }
// ]
// ]
const rowObjects = reader.getRowObjectsJson();
// [
// {
// "int_array": [],
// "struct": { "a": null, "b": null },
// "map": [],
// "union": { "tag": "name", "value": "Frank" }
// },
// {
// "int_array": [ 42, 999, null, null, -42 ],
// "struct": { "a": 42, "b": "🦆🦆🦆🦆🦆🦆" },
// "map": [
// { "key": "key1", "value": "🦆🦆🦆🦆🦆🦆" },
// { "key": "key2", "value": "goose" }
// ],
// "union": { "tag": "age", "value": 5 }
// }
// ]
const columns = reader.getColumnsJson();
// [
// [
// [],
// [42, 999, null, null, -42]
// ],
// [
// { "a": null, "b": null },
// { "a": 42, "b": "🦆🦆🦆🦆🦆🦆" }
// ],
// [
// [],
// [
// { "key": "key1", "value": "🦆🦆🦆🦆🦆🦆" },
// { "key": "key2", "value": "goose"}
// ]
// ],
// [
// { "tag": "name", "value": "Frank" },
// { "tag": "age", "value": 5 }
// ]
// ]
const columnsObject = reader.getColumnsObjectJson();
// {
// "int_array": [
// [],
// [42, 999, null, null, -42]
// ],
// "struct": [
// { "a": null, "b": null },
// { "a": 42, "b": "🦆🦆🦆🦆🦆🦆" }
// ],
// "map": [
// [],
// [
// { "key": "key1", "value": "🦆🦆🦆🦆🦆🦆" },
// { "key": "key2", "value": "goose" }
// ]
// ],
// "union": [
// { "tag": "name", "value": "Frank" },
// { "tag": "age", "value": 5 }
// ]
// }
컬럼 이름과 타입도 JSON으로 직렬화할 수 있어요:
const columnNamesAndTypes = reader.columnNamesAndTypesJson();
// {
// "columnNames": [
// "int_array",
// "struct",
// "map",
// "union"
// ],
// "columnTypes": [
// {
// "typeId": 24,
// "valueType": {
// "typeId": 4
// }
// },
// {
// "typeId": 25,
// "entryNames": [
// "a",
// "b"
// ],
// "entryTypes": [
// {
// "typeId": 4
// },
// {
// "typeId": 17
// }
// ]
// },
// {
// "typeId": 26,
// "keyType": {
// "typeId": 17
// },
// "valueType": {
// "typeId": 17
// }
// },
// {
// "typeId": 28,
// "memberTags": [
// "name",
// "age"
// ],
// "memberTypes": [
// {
// "typeId": 17
// },
// {
// "typeId": 3
// }
// ]
// }
// ]
// }
const columnNameAndTypeObjects = reader.columnNameAndTypeObjectsJson();
// [
// {
// "columnName": "int_array",
// "columnType": {
// "typeId": 24,
// "valueType": {
// "typeId": 4
// }
// }
// },
// {
// "columnName": "struct",
// "columnType": {
// "typeId": 25,
// "entryNames": [
// "a",
// "b"
// ],
// "entryTypes": [
// {
// "typeId": 4
// },
// {
// "typeId": 17
// }
// ]
// }
// },
// {
// "columnName": "map",
// "columnType": {
// "typeId": 26,
// "keyType": {
// "typeId": 17
// },
// "valueType": {
// "typeId": 17
// }
// }
// },
// {
// "columnName": "union",
// "columnType": {
// "typeId": 28,
// "memberTags": [
// "name",
// "age"
// ],
// "memberTypes": [
// {
// "typeId": 17
// },
// {
// "typeId": 3
// }
// ]
// }
// }
// ]
청크 가져오기 (Fetch Chunks)
모든 청크 가져오기:
const chunks = await result.fetchAllChunks();
한 번에 하나씩 청크 가져오기:
const chunks = [];
while (true) {
const chunk = await result.fetchChunk();
// Last chunk will have zero rows.
if (chunk.rowCount === 0) {
break;
}
chunks.push(chunk);
}
구체화된(비스트리밍) 결과의 경우 청크는 인덱스로 읽을 수 있어요:
const rowCount = result.rowCount;
const chunkCount = result.chunkCount;
for (let i = 0; i < chunkCount; i++) {
const chunk = result.getChunk(i);
// ...
}
청크 데이터 얻기:
const rows = chunk.getRows();
const rowObjects = chunk.getRowObjects(result.deduplicatedColumnNames());
const columns = chunk.getColumns();
const columnsObject =
chunk.getColumnsObject(result.deduplicatedColumnNames());
청크 데이터 얻기 (한 번에 하나의 값)
const columns = [];
const columnCount = chunk.columnCount;
for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
const columnValues = [];
const columnVector = chunk.getColumnVector(columnIndex);
const itemCount = columnVector.itemCount;
for (let itemIndex = 0; itemIndex < itemCount; itemIndex++) {
const value = columnVector.getItem(itemIndex);
columnValues.push(value);
}
columns.push(columnValues);
}
데이터 타입 검사 (Inspect Data Types)
import { DuckDBTypeId } from '@duckdb/node-api';
if (columnType.typeId === DuckDBTypeId.ARRAY) {
const arrayValueType = columnType.valueType;
const arrayLength = columnType.length;
}
if (columnType.typeId === DuckDBTypeId.DECIMAL) {
const decimalWidth = columnType.width;
const decimalScale = columnType.scale;
}
if (columnType.typeId === DuckDBTypeId.ENUM) {
const enumValues = columnType.values;
}
if (columnType.typeId === DuckDBTypeId.LIST) {
const listValueType = columnType.valueType;
}
if (columnType.typeId === DuckDBTypeId.MAP) {
const mapKeyType = columnType.keyType;
const mapValueType = columnType.valueType;
}
if (columnType.typeId === DuckDBTypeId.STRUCT) {
const structEntryNames = columnType.names;
const structEntryTypes = columnType.valueTypes;
}
if (columnType.typeId === DuckDBTypeId.UNION) {
const unionMemberTags = columnType.memberTags;
const unionMemberTypes = columnType.memberTypes;
}
// For the JSON type (https://duckdb.org/docs/current/data/json/json_type)
if (columnType.alias === 'JSON') {
const json = JSON.parse(columnValue);
}
모든 타입은 toString을 구현해요. 결과는 사람이 읽기 좋으면서도 DuckDB가 적절한 표현식으로 읽을 수 있어요.
const typeString = columnType.toString();
데이터 값 검사 (Inspect Data Values)
import { DuckDBTypeId } from '@duckdb/node-api';
if (columnType.typeId === DuckDBTypeId.ARRAY) {
const arrayItems = columnValue.items; // array of values
const arrayString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.BIT) {
const bools = columnValue.toBools(); // array of booleans
const bits = columnValue.toBits(); // array of 0s and 1s
const bitString = columnValue.toString(); // string of '0's and '1's
}
if (columnType.typeId === DuckDBTypeId.BLOB) {
const blobBytes = columnValue.bytes; // Uint8Array
const blobString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.DATE) {
const dateDays = columnValue.days;
const dateString = columnValue.toString();
const { year, month, day } = columnValue.toParts();
}
if (columnType.typeId === DuckDBTypeId.DECIMAL) {
const decimalWidth = columnValue.width;
const decimalScale = columnValue.scale;
// Scaled-up value. Represented number is value/(10^scale).
const decimalValue = columnValue.value; // bigint
const decimalString = columnValue.toString();
const decimalDouble = columnValue.toDouble();
}
if (columnType.typeId === DuckDBTypeId.INTERVAL) {
const intervalMonths = columnValue.months;
const intervalDays = columnValue.days;
const intervalMicros = columnValue.micros; // bigint
const intervalString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.LIST) {
const listItems = columnValue.items; // array of values
const listString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.MAP) {
const mapEntries = columnValue.entries; // array of { key, value }
const mapString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.STRUCT) {
// { name1: value1, name2: value2, ... }
const structEntries = columnValue.entries;
const structString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.TIMESTAMP_MS) {
const timestampMillis = columnValue.milliseconds; // bigint
const timestampMillisString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.TIMESTAMP_NS) {
const timestampNanos = columnValue.nanoseconds; // bigint
const timestampNanosString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.TIMESTAMP_S) {
const timestampSecs = columnValue.seconds; // bigint
const timestampSecsString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.TIMESTAMP_TZ) {
const timestampTZMicros = columnValue.micros; // bigint
const timestampTZString = columnValue.toString();
const {
date: { year, month, day },
time: { hour, min, sec, micros },
} = columnValue.toParts();
}
if (columnType.typeId === DuckDBTypeId.TIMESTAMP) {
const timestampMicros = columnValue.micros; // bigint
const timestampString = columnValue.toString();
const {
date: { year, month, day },
time: { hour, min, sec, micros },
} = columnValue.toParts();
}
if (columnType.typeId === DuckDBTypeId.TIME_TZ) {
const timeTZMicros = columnValue.micros; // bigint
const timeTZOffset = columnValue.offset;
const timeTZString = columnValue.toString();
const {
time: { hour, min, sec, micros },
offset,
} = columnValue.toParts();
}
if (columnType.typeId === DuckDBTypeId.TIME) {
const timeMicros = columnValue.micros; // bigint
const timeString = columnValue.toString();
const { hour, min, sec, micros } = columnValue.toParts();
}
if (columnType.typeId === DuckDBTypeId.UNION) {
const unionTag = columnValue.tag;
const unionValue = columnValue.value;
const unionValueString = columnValue.toString();
}
if (columnType.typeId === DuckDBTypeId.UUID) {
const uuidHugeint = columnValue.hugeint; // bigint
const uuidString = columnValue.toString();
}
// other possible values are: null, boolean, number, bigint, or string
시간대 표시 (Displaying Timezones)
TIMESTAMP_TZ 값을 문자열로 변환하는 것은 시간대 오프셋에 따라 달라져요.
기본적으로 이것은 Node 프로세스가 시작될 때 로컬 시간대의 오프셋으로 설정돼요.
바꾸려면 DuckDBTimestampTZValue의 timezoneOffsetInMinutes 속성을 설정하세요:
DuckDBTimestampTZValue.timezoneOffsetInMinutes = -8 * 60;
const pst = DuckDBTimestampTZValue.Epoch.toString();
// 1969-12-31 16:00:00-08
DuckDBTimestampTZValue.timezoneOffsetInMinutes = +1 * 60;
const cet = DuckDBTimestampTZValue.Epoch.toString();
// 1970-01-01 01:00:00+01
이 문자열 변환에 사용되는 시간대 오프셋은 DuckDB의 TimeZone 설정과는 구별된다는 점을 주의하세요.
다음은 이 오프셋을 DuckDB의 TimeZone 설정과 일치시키는 방법이에요:
const reader = await connection.runAndReadAll(
`select (timezone(current_timestamp) / 60)::int`
);
DuckDBTimestampTZValue.timezoneOffsetInMinutes =
reader.getColumns()[0][0];
테이블에 추가 (Append To Table)
await connection.run(
`create or replace table target_table(i integer, v varchar)`
);
const appender = await connection.createAppender('target_table');
appender.appendInteger(42);
appender.appendVarchar('duck');
appender.endRow();
appender.appendInteger(123);
appender.appendVarchar('mallard');
appender.endRow();
appender.flushSync();
appender.appendInteger(17);
appender.appendVarchar('goose');
appender.endRow();
appender.closeSync(); // also flushes
데이터 청크 추가 (Append Data Chunk)
await connection.run(
`create or replace table target_table(i integer, v varchar)`
);
const appender = await connection.createAppender('target_table');
const chunk = DuckDBDataChunk.create([INTEGER, VARCHAR]);
chunk.setColumns([
[42, 123, 17],
['duck', 'mallard', 'goose'],
]);
// OR:
// chunk.setRows([
// [42, 'duck'],
// [123, 'mallard'],
// [17, 'goose'],
// ]);
appender.appendDataChunk(chunk);
appender.flushSync();
appender에 값을 제공하는 방법은 위 "값 지정 (Specifying Values)"을 참고하세요.
문장 추출 (Extract Statements)
const extractedStatements = await connection.extractStatements(`
create or replace table numbers as from range(?);
from numbers where range < ?;
drop table numbers;
`);
const parameterValues = [10, 7];
const statementCount = extractedStatements.count;
for (let stmtIndex = 0; stmtIndex < statementCount; stmtIndex++) {
const prepared = await extractedStatements.prepare(stmtIndex);
let parameterCount = prepared.parameterCount;
for (let paramIndex = 1; paramIndex <= parameterCount; paramIndex++) {
prepared.bindInteger(paramIndex, parameterValues.shift());
}
const result = await prepared.run();
// ...
}
작업 평가 제어 (Control Evaluation of Tasks)
import { DuckDBPendingResultState } from '@duckdb/node-api';
async function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
const prepared = await connection.prepare('from range(10_000_000)');
const pending = prepared.start();
while (pending.runTask() !== DuckDBPendingResultState.RESULT_READY) {
console.log('not ready');
await sleep(1);
}
console.log('ready');
const result = await pending.getResult();
// ...
SQL 실행 방법 (Ways to Run SQL)
// Run to completion but don't yet retrieve any rows.
// Optionally take values to bind to SQL parameters,
// and (optionally) types of those parameters,
// either as an array (for positional parameters),
// or an object keyed by parameter name.
const result = await connection.run(sql);
const result = await connection.run(sql, values);
const result = await connection.run(sql, values, types);
// Run to completion but don't yet retrieve any rows.
// Wrap in a DuckDBDataReader for convenient data retrieval.
const reader = await connection.runAndRead(sql);
const reader = await connection.runAndRead(sql, values);
const reader = await connection.runAndRead(sql, values, types);
// Run to completion, wrap in a reader, and read all rows.
const reader = await connection.runAndReadAll(sql);
const reader = await connection.runAndReadAll(sql, values);
const reader = await connection.runAndReadAll(sql, values, types);
// Run to completion, wrap in a reader, and read at least
// the given number of rows. (Rows are read in chunks, so more than
// the target may be read.)
const reader = await connection.runAndReadUntil(sql, targetRowCount);
const reader =
await connection.runAndReadAll(sql, targetRowCount, values);
const reader =
await connection.runAndReadAll(sql, targetRowCount, values, types);
// Create a streaming result and don't yet retrieve any rows.
const result = await connection.stream(sql);
const result = await connection.stream(sql, values);
const result = await connection.stream(sql, values, types);
// Create a streaming result and don't yet retrieve any rows.
// Wrap in a DuckDBDataReader for convenient data retrieval.
const reader = await connection.streamAndRead(sql);
const reader = await connection.streamAndRead(sql, values);
const reader = await connection.streamAndRead(sql, values, types);
// Create a streaming result, wrap in a reader, and read all rows.
const reader = await connection.streamAndReadAll(sql);
const reader = await connection.streamAndReadAll(sql, values);
const reader = await connection.streamAndReadAll(sql, values, types);
// Create a streaming result, wrap in a reader, and read at least
// the given number of rows.
const reader = await connection.streamAndReadUntil(sql, targetRowCount);
const reader =
await connection.streamAndReadUntil(sql, targetRowCount, values);
const reader =
await connection.streamAndReadUntil(sql, targetRowCount, values, types);
// Prepared Statements
// Prepare a possibly-parametered SQL statement to run later.
const prepared = await connection.prepare(sql);
// Bind values to the parameters.
prepared.bind(values);
prepared.bind(values, types);
// Run the prepared statement. These mirror the methods on the connection.
const result = prepared.run();
const reader = prepared.runAndRead();
const reader = prepared.runAndReadAll();
const reader = prepared.runAndReadUntil(targetRowCount);
const result = prepared.stream();
const reader = prepared.streamAndRead();
const reader = prepared.streamAndReadAll();
const reader = prepared.streamAndReadUntil(targetRowCount);
// Pending Results
// Create a pending result.
const pending = await connection.start(sql);
const pending = await connection.start(sql, values);
const pending = await connection.start(sql, values, types);
// Create a pending, streaming result.
const pending = await connection.startStream(sql);
const pending = await connection.startStream(sql, values);
const pending = await connection.startStream(sql, values, types);
// Create a pending result from a prepared statement.
const pending = await prepared.start();
const pending = await prepared.startStream();
while (pending.runTask() !== DuckDBPendingResultState.RESULT_READY) {
// optionally sleep or do other work between tasks
}
// Retrieve the result. If not yet READY, will run until it is.
const result = await pending.getResult();
const reader = await pending.read();
const reader = await pending.readAll();
const reader = await pending.readUntil(targetRowCount);
결과 데이터 얻기 방법 (Ways to Get Result Data)
// From a result
// Asynchronously retrieve data for all rows:
const columns = await result.getColumns();
const columnsJson = await result.getColumnsJson();
const columnsObject = await result.getColumnsObject();
const columnsObjectJson = await result.getColumnsObjectJson();
const rows = await result.getRows();
const rowsJson = await result.getRowsJson();
const rowObjects = await result.getRowObjects();
const rowObjectsJson = await result.getRowObjectsJson();
// From a reader
// First, (asynchronously) read some rows:
await reader.readAll();
// or:
await reader.readUntil(targetRowCount);
// Then, (synchronously) get result data for the rows read:
const columns = reader.getColumns();
const columnsJson = reader.getColumnsJson();
const columnsObject = reader.getColumnsObject();
const columnsObjectJson = reader.getColumnsObjectJson();
const rows = reader.getRows();
const rowsJson = reader.getRowsJson();
const rowObjects = reader.getRowObjects();
const rowObjectsJson = reader.getRowObjectsJson();
// Individual values can also be read directly:
const value = reader.value(columnIndex, rowIndex);
// Using chunks
// If desired, one or more chunks can be fetched from a result:
const chunk = await result.fetchChunk();
const chunks = await result.fetchAllChunks();
// And then data can be retrieved from each chunk:
const columnValues = chunk.getColumnValues(columnIndex);
const columns = chunk.getColumns();
const rowValues = chunk.getRowValues(rowIndex);
const rows = chunk.getRows();
// Or, values can be visited:
chunk.visitColumnValues(columnIndex,
(value, rowIndex, columnIndex, type) => { /* ... */ }
);
chunk.visitColumns((column, columnIndex, type) => { /* ... */ });
chunk.visitColumnMajor(
(value, rowIndex, columnIndex, type) => { /* ... */ }
);
chunk.visitRowValues(rowIndex,
(value, rowIndex, columnIndex, type) => { /* ... */ }
);
chunk.visitRows((row, rowIndex) => { /* ... */ });
chunk.visitRowMajor(
(value, rowIndex, columnIndex, type) => { /* ... */ }
);
// Or converted:
// The `converter` argument implements `DuckDBValueConverter`,
// which has the single method convertValue(value, type).
const columnValues = chunk.convertColumnValues(columnIndex, converter);
const columns = chunk.convertColumns(converter);
const rowValues = chunk.convertRowValues(rowIndex, converter);
const rows = chunk.convertRows(converter);
// The reader abstracts these low-level chunk manipulations
// and is recommended for most cases.