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
16static duckdb_database database;
17
18static 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
40static 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
54static 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
123static 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
141static 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
149static 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
157void registerDuckdb(void);
158
159void 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