1
- import { type DuckDBConnection , DuckDBInstance } from "@ duckdb/node-api "
1
+ import { type Connection , Database } from "duckdb-async "
2
2
3
- let instance : DuckDBInstance | null = null
4
- let connection : DuckDBConnection | null = null
3
+ let db : Database | null = null
4
+ let connection : Connection | null = null
5
5
6
6
/**
7
7
* Initializes an in-memory DuckDB database
8
8
* @returns A reference to the initialized database
9
9
*/
10
- export async function initializeDuckDB ( databaseFile ?: string ) {
11
- if ( instance !== null ) {
12
- return { success : true , databaseInstance : instance }
13
- }
14
-
10
+ export async function initializeDuckDB (
11
+ file ?: string ,
12
+ ) : Promise < { success : boolean ; databaseInstance ?: Database ; error ?: string } > {
15
13
try {
16
- // Create a new DuckDB instance with a persistent database file
17
- instance = await DuckDBInstance . create ( databaseFile || ":memory:" )
18
- connection = await instance . connect ( )
14
+ // Create a new DuckDB instance with a persistent database file or in-memory
15
+ db = await Database . create ( file || ":memory:" )
16
+ connection = await db . connect ( )
17
+
18
+ // Verify connection is working
19
+ await connection . exec ( "SELECT 1" )
19
20
20
- return { success : true , databaseInstance : instance }
21
+ return { success : true , databaseInstance : db }
21
22
} catch ( error ) {
22
23
return { success : false , error : `Failed to initialize DuckDB: ${ error } ` }
23
24
}
24
25
}
25
26
27
+ export async function closeDuckDB ( ) {
28
+ if ( db ) {
29
+ await db . close ( )
30
+ }
31
+ }
32
+
26
33
/**
27
34
* Creates a table in the DuckDB database using raw SQL
28
35
* @param createTableSQL The SQL CREATE TABLE statement
29
36
* @returns Object indicating success or failure
30
37
*/
31
- export async function createTableWithSQL ( createTableSQL : string ) {
32
- if ( ! instance || ! connection ) {
38
+ export async function createTableWithSQL ( createTableSQL : string ) : Promise < { success : boolean ; message : string } > {
39
+ if ( ! db || ! connection ) {
33
40
return {
34
41
success : false ,
35
42
message : "Database not initialized. Call initializeDuckDB first." ,
36
43
}
37
44
}
38
45
39
46
try {
40
- await connection . run ( createTableSQL )
41
-
47
+ await connection . exec ( createTableSQL )
42
48
return {
43
49
success : true ,
44
50
message : "Table created successfully" ,
@@ -61,8 +67,8 @@ export async function createTableWithSQL(createTableSQL: string) {
61
67
export async function createProjectionTable (
62
68
tableName : string ,
63
69
columns : Array < { name : string ; type : string ; constraints ?: string } > ,
64
- ) {
65
- if ( ! instance || ! connection ) {
70
+ ) : Promise < { success : boolean ; message : string } > {
71
+ if ( ! db || ! connection ) {
66
72
return {
67
73
success : false ,
68
74
message : "Database not initialized. Call initializeDuckDB first." ,
@@ -77,7 +83,7 @@ export async function createProjectionTable(
77
83
78
84
const createTableSQL = `CREATE TABLE IF NOT EXISTS ${ tableName } (${ columnsSQL } )`
79
85
80
- await connection . run ( createTableSQL )
86
+ await connection . exec ( createTableSQL )
81
87
82
88
return {
83
89
success : true ,
@@ -97,8 +103,11 @@ export async function createProjectionTable(
97
103
* @param record Record to insert
98
104
* @returns Object indicating success or failure
99
105
*/
100
- export async function insertRecordIntoTable ( tableName : string , record : Record < string , unknown > ) {
101
- if ( ! instance || ! connection ) {
106
+ export async function insertRecordIntoTable (
107
+ tableName : string ,
108
+ record : Record < string , unknown > ,
109
+ ) : Promise < { success : boolean ; message : string } > {
110
+ if ( ! db || ! connection ) {
102
111
return {
103
112
success : false ,
104
113
message : "Database not initialized. Call initializeDuckDB first." ,
@@ -107,46 +116,35 @@ export async function insertRecordIntoTable(tableName: string, record: Record<st
107
116
108
117
try {
109
118
const columnNames = Object . keys ( record ) . join ( ", " )
110
- const placeholders = Object . keys ( record )
111
- . map ( ( _ , index ) => `$${ index + 1 } ` )
112
- . join ( ", " )
113
- const values = Object . values ( record )
114
119
115
- const insertSQL = `INSERT INTO ${ tableName } (${ columnNames } ) VALUES (${ placeholders } )`
116
-
117
- try {
118
- const prepared = await connection . prepare ( insertSQL )
120
+ // Format values according to their type
121
+ const formattedValues = Object . values ( record )
122
+ . map ( ( value ) => {
123
+ if ( value === null ) {
124
+ return "NULL"
125
+ }
119
126
120
- // Bind values based on their types
121
- values . forEach ( ( value , index ) => {
122
- const paramIndex = index + 1
123
127
if ( typeof value === "string" ) {
124
- prepared . bindVarchar ( paramIndex , value )
125
- } else if ( typeof value === "number" ) {
126
- if ( Number . isInteger ( value ) ) {
127
- prepared . bindInteger ( paramIndex , value )
128
- } else {
129
- prepared . bindDouble ( paramIndex , value )
130
- }
131
- } else if ( typeof value === "boolean" ) {
132
- prepared . bindBoolean ( paramIndex , value )
133
- } else if ( value === null ) {
134
- prepared . bindNull ( paramIndex )
135
- } else if ( typeof value === "bigint" ) {
136
- prepared . bindBigInt ( paramIndex , value )
137
- } else {
138
- // For other types, convert to string
139
- prepared . bindVarchar ( paramIndex , String ( value ) )
128
+ // Escape single quotes in strings
129
+ return `'${ String ( value ) . replace ( / ' / g, "''" ) } '`
140
130
}
131
+
132
+ if ( typeof value === "boolean" ) {
133
+ return value ? "TRUE" : "FALSE"
134
+ }
135
+
136
+ if ( value instanceof Date ) {
137
+ return `'${ value . toISOString ( ) } '`
138
+ }
139
+
140
+ return String ( value )
141
141
} )
142
+ . join ( ", " )
143
+
144
+ const insertSQL = `INSERT INTO ${ tableName } (${ columnNames } ) VALUES (${ formattedValues } )`
145
+
146
+ await connection . exec ( insertSQL )
142
147
143
- await prepared . run ( )
144
- } catch ( error ) {
145
- return {
146
- success : false ,
147
- message : `Failed to insert record into ${ tableName } : ${ error } ` ,
148
- }
149
- }
150
148
return {
151
149
success : true ,
152
150
message : `Record inserted into ${ tableName } successfully` ,
@@ -164,8 +162,10 @@ export async function insertRecordIntoTable(tableName: string, record: Record<st
164
162
* @param query SQL query to execute
165
163
* @returns Object with query results or error
166
164
*/
167
- export async function queryDatabase ( query : string ) {
168
- if ( ! instance || ! connection ) {
165
+ export async function queryDatabase (
166
+ query : string ,
167
+ ) : Promise < { success : boolean ; message : string ; results : unknown [ ] | null } > {
168
+ if ( ! db || ! connection ) {
169
169
return {
170
170
success : false ,
171
171
message : "Database not initialized. Call initializeDuckDB first." ,
@@ -174,11 +174,12 @@ export async function queryDatabase(query: string) {
174
174
}
175
175
176
176
try {
177
- const result = await connection . run ( query )
177
+ const rows = await connection . all ( query )
178
+
178
179
return {
179
180
success : true ,
180
181
message : "Query executed successfully" ,
181
- results : result ,
182
+ results : rows ,
182
183
}
183
184
} catch ( error ) {
184
185
return {
@@ -194,7 +195,7 @@ export async function queryDatabase(query: string) {
194
195
* @returns The database instance or null if not initialized
195
196
*/
196
197
export function getDatabaseInstance ( ) {
197
- return instance
198
+ return db
198
199
}
199
200
200
201
/**
0 commit comments