@@ -123,6 +123,54 @@ static std::string ConvertResultToNDJSON(MaterializedQueryResult &result) {
123
123
return ndjson_output;
124
124
}
125
125
126
+ static std::string ConvertResultToCSV (MaterializedQueryResult &result) {
127
+ std::string csv_output;
128
+
129
+ // Add header row
130
+ for (idx_t col = 0 ; col < result.ColumnCount (); ++col) {
131
+ if (col > 0 ) {
132
+ csv_output += " ," ;
133
+ }
134
+ csv_output += result.ColumnName (col);
135
+ }
136
+ csv_output += " \n " ;
137
+
138
+ // Add data rows
139
+ for (idx_t row = 0 ; row < result.RowCount (); ++row) {
140
+ for (idx_t col = 0 ; col < result.ColumnCount (); ++col) {
141
+ if (col > 0 ) {
142
+ csv_output += " ," ;
143
+ }
144
+ Value value = result.GetValue (col, row);
145
+ if (value.IsNull ()) {
146
+ // Leave empty for NULL values
147
+ continue ;
148
+ }
149
+
150
+ std::string value_str = value.ToString ();
151
+ // Escape quotes and wrap in quotes if contains special characters
152
+ if (value_str.find_first_of (" ,\"\n\r " ) != std::string::npos) {
153
+ std::string escaped;
154
+ escaped.reserve (value_str.length () + 2 );
155
+ escaped += ' "' ;
156
+ for (char c : value_str) {
157
+ if (c == ' "' ) {
158
+ escaped += ' "' ; // Double the quotes to escape
159
+ }
160
+ escaped += c;
161
+ }
162
+ escaped += ' "' ;
163
+ csv_output += escaped;
164
+ } else {
165
+ csv_output += value_str;
166
+ }
167
+ }
168
+ csv_output += " \n " ;
169
+ }
170
+
171
+ return csv_output;
172
+ }
173
+
126
174
// Handle both GET and POST requests
127
175
void HandleHttpRequest (const duckdb_httplib_openssl::Request& req, duckdb_httplib_openssl::Response& res) {
128
176
std::string query;
@@ -165,7 +213,7 @@ void HandleHttpRequest(const duckdb_httplib_openssl::Request& req, duckdb_httpli
165
213
return ;
166
214
}
167
215
168
- // Set default format to JSONCompact
216
+ // Set default format to JSONEachRow
169
217
std::string format = " JSONEachRow" ;
170
218
171
219
// Check for format in URL parameter or header
@@ -209,6 +257,10 @@ void HandleHttpRequest(const duckdb_httplib_openssl::Request& req, duckdb_httpli
209
257
ResultSerializerCompactJson serializer;
210
258
std::string json_output = serializer.Serialize (*result, stats);
211
259
res.set_content (json_output, " application/json" );
260
+ } else if (format == " CSV" ) {
261
+ std::string csv_output = ConvertResultToCSV (*result);
262
+ res.set_header (" Content-Type" , " text/csv" );
263
+ res.set_content (csv_output, " text/csv" );
212
264
} else {
213
265
// Default to NDJSON for DuckDB's own queries
214
266
std::string json_output = ConvertResultToNDJSON (*result);
@@ -217,7 +269,7 @@ void HandleHttpRequest(const duckdb_httplib_openssl::Request& req, duckdb_httpli
217
269
218
270
} catch (const Exception& ex) {
219
271
res.status = 500 ;
220
- std::string error_message = " Code: 59, e.displayText() = DB::Exception: " + std::string (ex.what ());
272
+ std::string error_message = " DB::Exception: " + std::string (ex.what ());
221
273
res.set_content (error_message, " text/plain" );
222
274
}
223
275
}
0 commit comments