1 | #include <ctype.h> |
2 | #include <stdio.h> |
3 | #ifndef _MSC_VER |
4 | #include <strings.h> |
5 | #endif |
6 | #include "duckdb.h" |
7 | |
8 | #include <stdlib.h> |
9 | |
10 | #ifndef _WIN32 |
11 | #define LLFMT "%lld" |
12 | #else |
13 | #define LLFMT "%I64d" |
14 | #endif |
15 | |
16 | static duckdb_database database; |
17 | |
18 | static int duckdbConnect(void *NotUsed, /* Argument from DbEngine object. Not used */ |
19 | const char *zConnectStr, /* Connection string */ |
20 | void **ppConn, /* Write completed connection here */ |
21 | const char *zParam /* Value of the -parameters command-line option */ |
22 | ) { |
23 | (void)NotUsed; |
24 | (void)zConnectStr; |
25 | (void)zParam; |
26 | |
27 | duckdb_connection conn; |
28 | |
29 | if (duckdb_open(NULL, &database) != DuckDBSuccess) { |
30 | return 1; |
31 | } |
32 | |
33 | if (duckdb_connect(database, &conn) != DuckDBSuccess) { |
34 | return 1; |
35 | } |
36 | *ppConn = (void *)conn; |
37 | return 0; |
38 | } |
39 | |
40 | static int duckdbStatement(void *pConn, /* Connection created by xConnect */ |
41 | const char *zSql, /* SQL statement to evaluate */ |
42 | int bQuiet /* True to suppress printing errors. */ |
43 | ) { |
44 | if (strncasecmp(zSql, "CREATE INDEX" , 12) == 0) { |
45 | fprintf(stderr, "Ignoring CREATE INDEX statement %s\n" , zSql); |
46 | return 0; |
47 | } |
48 | if (duckdb_query((duckdb_connection)pConn, (char *)zSql, NULL) != DuckDBSuccess) { |
49 | return 1; |
50 | } |
51 | return 0; |
52 | } |
53 | |
54 | static int duckdbQuery(void *pConn, /* Connection created by xConnect */ |
55 | const char *zSql, /* SQL statement to evaluate */ |
56 | const char *zType, /* One character for each column of result */ |
57 | char ***pazResult, /* RETURN: Array of result values */ |
58 | int *pnResult /* RETURN: Number of result values */ |
59 | ) { |
60 | duckdb_result result; |
61 | |
62 | size_t r, c; |
63 | (void)zType; |
64 | // fprintf(stderr, "Quack: %s\n", zSql); |
65 | assert(pConn); |
66 | if (duckdb_query((duckdb_connection)pConn, (char *)zSql, &result) != DuckDBSuccess) { |
67 | return 1; |
68 | } |
69 | |
70 | *pazResult = (char **)malloc(sizeof(char *) * result.row_count * result.column_count); |
71 | if (!*pazResult) { |
72 | return 1; |
73 | } |
74 | for (r = 0; r < result.row_count; r++) { |
75 | for (c = 0; c < result.column_count; c++) { |
76 | duckdb_column actual_column = result.columns[c]; |
77 | char *buffer = (char *)malloc(BUFSIZ); |
78 | |
79 | if (actual_column.nullmask[r]) { |
80 | snprintf(buffer, BUFSIZ, "%s" , "NULL" ); |
81 | } else { |
82 | switch (actual_column.type) { |
83 | case DUCKDB_TYPE_BOOLEAN: |
84 | snprintf(buffer, BUFSIZ, "%s" , ((bool *)actual_column.data)[r] ? "1" : "0" ); |
85 | break; |
86 | case DUCKDB_TYPE_TINYINT: |
87 | snprintf(buffer, BUFSIZ, "%d" , (int)((int8_t *)actual_column.data)[r]); |
88 | break; |
89 | case DUCKDB_TYPE_SMALLINT: |
90 | snprintf(buffer, BUFSIZ, "%d" , (int)((int16_t *)actual_column.data)[r]); |
91 | break; |
92 | case DUCKDB_TYPE_INTEGER: |
93 | snprintf(buffer, BUFSIZ, "%d" , (int)((int32_t *)actual_column.data)[r]); |
94 | break; |
95 | case DUCKDB_TYPE_BIGINT: |
96 | snprintf(buffer, BUFSIZ, "%d" , (int)((int64_t *)actual_column.data)[r]); |
97 | break; |
98 | case DUCKDB_TYPE_FLOAT: |
99 | // cast to INT seems to be the trick here |
100 | snprintf(buffer, BUFSIZ, "%d" , (int)((float *)actual_column.data)[r]); |
101 | break; |
102 | case DUCKDB_TYPE_DOUBLE: |
103 | // cast to INT seems to be the trick here |
104 | snprintf(buffer, BUFSIZ, "%d" , (int)((double *)actual_column.data)[r]); |
105 | break; |
106 | case DUCKDB_TYPE_VARCHAR: { |
107 | char *str = ((char **)actual_column.data)[r]; |
108 | snprintf(buffer, BUFSIZ, "%s" , str ? (str == 0 ? "(empty)" : str) : "NULL" ); |
109 | break; |
110 | } |
111 | default: { fprintf(stderr, "%s\n" , "UNKNOWN" ); } |
112 | } |
113 | } |
114 | (*pazResult)[r * result.column_count + c] = buffer; |
115 | } |
116 | } |
117 | *pnResult = result.column_count * result.row_count; |
118 | duckdb_destroy_result(&result); |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static int duckdbFreeResults(void *pConn, /* Connection created by xConnect */ |
124 | char **azResult, /* The results to be freed */ |
125 | int nResult /* Number of rows of result */ |
126 | ) { |
127 | int i; |
128 | (void)pConn; |
129 | if (!azResult) { |
130 | return 1; |
131 | } |
132 | for (i = 0; i < nResult; i++) { |
133 | if (azResult[i]) { |
134 | free(azResult[i]); |
135 | } |
136 | } |
137 | free(azResult); |
138 | return 0; |
139 | } |
140 | |
141 | static int duckdbDisconnect(void *pConn /* Connection created by xConnect */ |
142 | ) { |
143 | duckdb_connection con = (duckdb_connection)pConn; |
144 | duckdb_disconnect(&con); |
145 | duckdb_close(&database); |
146 | return 0; |
147 | } |
148 | |
149 | static int duckdbGetEngineName(void *pConn, /* Connection created by xConnect */ |
150 | const char **zName /* SQL statement to evaluate */ |
151 | ) { |
152 | (void)pConn; |
153 | *zName = "DuckDB" ; |
154 | return 0; |
155 | } |
156 | |
157 | void registerDuckdb(void); |
158 | |
159 | void registerDuckdb(void) { |
160 | /* |
161 | ** This is the object that defines the database engine interface. |
162 | */ |
163 | static const DbEngine duckdbEngine = { |
164 | "DuckDB" , /* zName */ |
165 | 0, /* pAuxData */ |
166 | duckdbConnect, /* xConnect */ |
167 | duckdbGetEngineName, /* xGetEngineName */ |
168 | duckdbStatement, /* xStatement */ |
169 | duckdbQuery, /* xQuery */ |
170 | duckdbFreeResults, /* xFreeResults */ |
171 | duckdbDisconnect /* xDisconnect */ |
172 | }; |
173 | sqllogictestRegisterEngine(&duckdbEngine); |
174 | } |
175 | |