Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * @f mapi
15 : * @a M.L. Kersten, K.S. Mullender, Fabian Groffen
16 : * @v 2.0
17 : * @* The MonetDB Programming Interface
18 : * @+ The Mapi Library
19 : *
20 : * The easiest way to extend the functionality of MonetDB is to construct
21 : * an independent application, which communicates with a running server
22 : * using a database driver with a simple API and a textual protocol. The
23 : * effectiveness of such an approach has been demonstrated by the wide
24 : * use of database API implementations, such as Perl DBI, PHP, ODBC,...
25 : *
26 : * @menu
27 : * * An Example:: a C/C++ code example to get going.
28 : * * Command Summary:: the list of API functions.
29 : * * Library Synopsis:: an short explanation of how MAPI works.
30 : * * Mapi Function Reference:: per API function its parameters and expected results.
31 : * @end menu
32 : *
33 : * @ifclear XQRYmanual
34 : * @node An Example, Command Summary, The Mapi Library, The Mapi Library
35 : * @subsection Sample MAPI Application
36 : *
37 : * The database driver implementation given in this document focuses on
38 : * developing applications in C. The command collection has been
39 : * chosen to align with common practice, i.e. queries follow a prepare,
40 : * execute, and fetch_row paradigm. The output is considered a regular
41 : * table. An example of a mini application below illustrates the main
42 : * operations.
43 : *
44 : * @example
45 : * @verbatim
46 : * #include <mapi.h>
47 : * #include <stdio.h>
48 : * #include <stdlib.h>
49 : *
50 : * void die(Mapi dbh, MapiHdl hdl)
51 : * {
52 : * if (hdl != NULL) {
53 : * mapi_explain_query(hdl, stderr);
54 : * do {
55 : * if (mapi_result_error(hdl) != NULL)
56 : * mapi_explain_result(hdl, stderr);
57 : * } while (mapi_next_result(hdl) == 1);
58 : * mapi_close_handle(hdl);
59 : * mapi_destroy(dbh);
60 : * } else if (dbh != NULL) {
61 : * mapi_explain(dbh, stderr);
62 : * mapi_destroy(dbh);
63 : * } else {
64 : * fprintf(stderr, "command failed\n");
65 : * }
66 : * exit(-1);
67 : * }
68 : *
69 : * MapiHdl query(Mapi dbh, char *q)
70 : * {
71 : * MapiHdl ret = NULL;
72 : * if ((ret = mapi_query(dbh, q)) == NULL || mapi_error(dbh) != MOK)
73 : * die(dbh, ret);
74 : * return(ret);
75 : * }
76 : *
77 : * void update(Mapi dbh, char *q)
78 : * {
79 : * MapiHdl ret = query(dbh, q);
80 : * if (mapi_close_handle(ret) != MOK)
81 : * die(dbh, ret);
82 : * }
83 : *
84 : * int main(int argc, char *argv[])
85 : * {
86 : * Mapi dbh;
87 : * MapiHdl hdl = NULL;
88 : * char *name;
89 : * char *age;
90 : *
91 : * dbh = mapi_connect("localhost", 50000, "monetdb", "monetdb", "sql", "demo");
92 : * if (mapi_error(dbh))
93 : * die(dbh, hdl);
94 : *
95 : * update(dbh, "CREATE TABLE emp (name VARCHAR(20), age INT)");
96 : * update(dbh, "INSERT INTO emp VALUES ('John', 23)");
97 : * update(dbh, "INSERT INTO emp VALUES ('Mary', 22)");
98 : *
99 : * hdl = query(dbh, "SELECT * FROM emp");
100 : *
101 : * while (mapi_fetch_row(hdl)) {
102 : * name = mapi_fetch_field(hdl, 0);
103 : * age = mapi_fetch_field(hdl, 1);
104 : * printf("%s is %s\n", name, age);
105 : * }
106 : *
107 : * mapi_close_handle(hdl);
108 : * mapi_destroy(dbh);
109 : *
110 : * return(0);
111 : * }
112 : * @end verbatim
113 : * @end example
114 : *
115 : * The @code{mapi_connect()} operation establishes a communication channel with
116 : * a running server.
117 : * The query language interface is either "sql" or "mal".
118 : *
119 : * Errors on the interaction can be captured using @code{mapi_error()},
120 : * possibly followed by a request to dump a short error message
121 : * explanation on a standard file location. It has been abstracted away
122 : * in a macro.
123 : *
124 : * Provided we can establish a connection, the interaction proceeds as in
125 : * many similar application development packages. Queries are shipped for
126 : * execution using @code{mapi_query()} and an answer table can be consumed one
127 : * row at a time. In many cases these functions suffice.
128 : *
129 : * The Mapi interface provides caching of rows at the client side.
130 : * @code{mapi_query()} will load tuples into the cache, after which they can be
131 : * read repeatedly using @code{mapi_fetch_row()} or directly accessed
132 : * (@code{mapi_seek_row()}). This facility is particularly handy when small,
133 : * but stable query results are repeatedly used in the client program.
134 : *
135 : * To ease communication between application code and the cache entries,
136 : * the user can bind the C-variables both for input and output to the
137 : * query parameters, and output columns, respectively. The query
138 : * parameters are indicated by '?' and may appear anywhere in the query
139 : * template.
140 : *
141 : * The Mapi library expects complete lines from the server as answers to
142 : * query actions. Incomplete lines leads to Mapi waiting forever on the
143 : * server. Thus formatted printing is discouraged in favor of tabular
144 : * printing as offered by the @code{table.print()} commands.
145 : * @end ifclear
146 : *
147 : * @ifset XQRYmanual
148 : * @node An Example
149 : * @subsection An Example
150 : *
151 : * C and C++ programs can use the MAPI library to execute queries on MonetDB.
152 : *
153 : * We give a short example with a minimal Mapi program:
154 : * @itemize
155 : * @item @code{mapi_connect()} and @code{mapi_disconnect()}: make a connection to a database server (@code{Mapi mid;}).
156 : * @strong{note:} pass the value @code{"sql"} in the @code{language} parameter, when connecting.
157 : * @item @code{mapi_error()} and @code{mapi_error_str()}: check for and print connection errors (on @code{Mapi mid}).
158 : * @item @code{mapi_query()} and @code{mapi_close_handle()} do a query and get a handle to it (@code{MapiHdl hdl}).
159 : * @item @code{mapi_result_error()}: check for query evaluation errors (on @code{MapiHdl hdl}).
160 : * @item @code{mapi_fetch_line()}: get a line of (result or error) output from the server (on @code{MapiHdl hdl}).
161 : * @strong{note:} output lines are prefixed with a @code{'='} character that must be escaped.
162 : * @end itemize
163 : *
164 : * @example
165 : * @verbatim
166 : * #include <stdio.h>
167 : * #include <mapi.h>
168 : * #include <stdlib.h>
169 : *
170 : * int
171 : * main(int argc, char** argv) {
172 : * const char *prog = argv[0];
173 : * const char *host = argv[1]; // where Mserver is started, e.g. localhost
174 : * const char *db = argv[2]; // database name e.g. demo
175 : * int port = atoi(argv[3]); // mapi_port e.g. 50000
176 : * char *mode = argv[4]; // output format e.g. xml
177 : * const char *query = argv[5]; // single-line query e.g. '1+1' (use quotes)
178 : * FILE *fp = stderr;
179 : * char *line;
180 : *
181 : * if (argc != 6) {
182 : * fprintf(fp, "usage: %s <host> <db> <port> <mode> <query>\n", prog);
183 : * fprintf(fp, " e.g. %s localhost demo 50000 xml '1+1'\n", prog);
184 : * } else {
185 : * // CONNECT TO SERVER, default unsecure user/password, language="sql"
186 : * Mapi mid = mapi_connect(host, port, "monetdb", "monetdb", "sql", db);
187 : * MapiHdl hdl;
188 : * if (mid == NULL) {
189 : * fprintf(fp, "%s: failed to connect.\n", prog);
190 : * } else {
191 : * hdl = mapi_query(mid, query); // FIRE OFF A QUERY
192 : *
193 : * if (hdl == NULL || mapi_error(mid) != MOK) // CHECK CONNECTION ERROR
194 : * fprintf(fp, "%s: connection error: %s\n", prog, mapi_error_str(mid)); // GET CONNECTION ERROR STRING
195 : * if (hdl) {
196 : * if (mapi_result_error(hdl) != MOK) // CHECK QUERY ERROR
197 : * fprintf(fp, "%s: query error\n", prog);
198 : * else
199 : * fp = stdout; // success: connection&query went ok
200 : *
201 : * // FETCH SERVER QUERY ANSWER LINE-BY-LINE
202 : * while((line = mapi_fetch_line(hdl)) != NULL) {
203 : * if (*line == '=') line++; // XML result lines start with '='
204 : * fprintf(fp, "%s\n", line);
205 : * }
206 : * }
207 : * mapi_close_handle(hdl); // CLOSE QUERY HANDLE
208 : * }
209 : * mapi_disconnect(mid); // CLOSE CONNECTION
210 : * }
211 : * return (fp == stdout)? 0 : -1;
212 : * }
213 : * @end verbatim
214 : * @end example
215 : * @end ifset
216 : *
217 : * The following action is needed to get a working program.
218 : * Compilation of the application relies on the @emph{monetdb-config}
219 : * program shipped with the distribution.
220 : * It localizes the include files and library directories.
221 : * Once properly installed, the application can be compiled and linked as
222 : * follows:
223 : * @example
224 : * @verbatim
225 : * cc sample.c `monetdb-clients-config --cflags --libs` -lmapi -o sample
226 : * ./sample
227 : * @end verbatim
228 : * @end example
229 : *
230 : * It assumes that the dynamic loadable libraries are in public places.
231 : * If, however, the system is installed in your private environment
232 : * then the following option can be used on most ELF platforms.
233 : *
234 : * @example
235 : * @verbatim
236 : * cc sample.c `monetdb-clients-config --cflags --libs` -lmapi -o sample \
237 : * `monetdb-clients-config --libs | sed -e's:-L:-R:g'`
238 : * ./sample
239 : * @end verbatim
240 : * @end example
241 : *
242 : * The compilation on Windows is slightly more complicated. It requires
243 : * more attention towards the location of the include files and libraries.
244 : *
245 : * @ifclear XQRYmanual
246 : * @node Command Summary, Library Synopsis, An Example, The Mapi Library
247 : * @subsection Command Summary
248 : * @end ifclear
249 : * @ifset XQRYmanual
250 : * @node Command Summary
251 : * @subsection Command Summary
252 : * @end ifset
253 : *
254 : * The quick reference guide to the Mapi library is given below. More
255 : * details on their constraints and defaults are given in the next
256 : * section.
257 : *
258 : *
259 : * @multitable @columnfractions 0.25 0.75
260 : * @item mapi_bind() @tab Bind string C-variable to a field
261 : * @item mapi_bind_numeric() @tab Bind numeric C-variable to field
262 : * @item mapi_bind_var() @tab Bind typed C-variable to a field
263 : * @item mapi_cache_freeup() @tab Forcefully shuffle fraction for cache refreshment
264 : * @item mapi_cache_limit() @tab Set the tuple cache limit
265 : * @item mapi_clear_bindings() @tab Clear all field bindings
266 : * @item mapi_clear_params() @tab Clear all parameter bindings
267 : * @item mapi_close_handle() @tab Close query handle and free resources
268 : * @item mapi_connect() @tab Connect to a Mserver
269 : * @item mapi_destroy() @tab Free handle resources
270 : * @item mapi_disconnect() @tab Disconnect from server
271 : * @item mapi_error() @tab Test for error occurrence
272 : * @item mapi_execute() @tab Execute a query
273 : * @item mapi_explain() @tab Display error message and context on stream
274 : * @item mapi_explain_query() @tab Display error message and context on stream
275 : * @item mapi_fetch_all_rows() @tab Fetch all answers from server into cache
276 : * @item mapi_fetch_field() @tab Fetch a field from the current row
277 : * @item mapi_fetch_field_len() @tab Fetch the length of a field from the current row
278 : * @item mapi_fetch_line() @tab Retrieve the next line
279 : * @item mapi_fetch_reset() @tab Set the cache reader to the beginning
280 : * @item mapi_fetch_row() @tab Fetch row of values
281 : * @item mapi_finish() @tab Terminate the current query
282 : * @item mapi_get_dbname() @tab Database being served
283 : * @item mapi_get_field_count() @tab Number of fields in current row
284 : * @item mapi_get_host() @tab Host name of server
285 : * @item mapi_get_query() @tab Query being executed
286 : * @item mapi_get_language() @tab Query language name
287 : * @item mapi_get_mapi_version() @tab Mapi version name
288 : * @item mapi_get_monet_version() @tab MonetDB version name
289 : * @item mapi_get_motd() @tab Get server welcome message
290 : * @item mapi_get_row_count() @tab Number of rows in cache or -1
291 : * @item mapi_get_last_id() @tab last inserted id of an auto_increment (or alike) column
292 : * @item mapi_get_from() @tab Get the stream 'from'
293 : * @item mapi_get_to() @tab Get the stream 'to'
294 : * @item mapi_get_trace() @tab Get trace flag
295 : * @item mapi_get_user() @tab Current user name
296 : * @item mapi_log() @tab Keep log of client/server interaction
297 : * @item mapi_next_result() @tab Go to next result set
298 : * @item mapi_needmore() @tab Return whether more data is needed
299 : * @item mapi_ping() @tab Test server for accessibility
300 : * @item mapi_prepare() @tab Prepare a query for execution
301 : * @item mapi_query() @tab Send a query for execution
302 : * @item mapi_query_handle() @tab Send a query for execution
303 : * @item mapi_quote() @tab Escape characters
304 : * @item mapi_reconnect() @tab Reconnect with a clean session context
305 : * @item mapi_rows_affected() @tab Obtain number of rows changed
306 : * @item mapi_seek_row() @tab Move row reader to specific location in cache
307 : * @item mapi_setAutocommit() @tab Set auto-commit flag
308 : * @item mapi_table() @tab Get current table name
309 : * @item mapi_timeout() @tab Set timeout for long-running queries[TODO]
310 : * @item mapi_trace() @tab Set trace flag
311 : * @item mapi_unquote() @tab remove escaped characters
312 : * @end multitable
313 : *
314 : * @ifclear XQRYmanual
315 : * @node Library Synopsis, Mapi Function Reference, Command Summary, The Mapi Library
316 : * @subsection Library Synopsis
317 : * @end ifclear
318 : * @ifset XQRYmanual
319 : * @node Library Synopsis
320 : * @subsection Library Synopsis
321 : * @end ifset
322 : *
323 : * The routines to build a MonetDB application are grouped in the library
324 : * MonetDB Programming Interface, or shorthand Mapi.
325 : *
326 : * The protocol information is stored in a Mapi interface descriptor
327 : * (mid). This descriptor can be used to ship queries, which return a
328 : * MapiHdl to represent the query answer. The application can set up
329 : * several channels with the same or a different @code{mserver}. It is the
330 : * programmer's responsibility not to mix the descriptors in retrieving
331 : * the results.
332 : *
333 : * The application may be multi-threaded as long as the user respects the
334 : * individual connections represented by the database handlers.
335 : *
336 : * The interface assumes a cautious user, who understands and has
337 : * experience with the query or programming language model. It should also be
338 : * clear that references returned by the API point directly into the
339 : * administrative structures of Mapi. This means that they are valid
340 : * only for a short period, mostly between successive @code{mapi_fetch_row()}
341 : * commands. It also means that it the values are to retained, they have
342 : * to be copied. A defensive programming style is advised.
343 : *
344 : * Upon an error, the routines @code{mapi_explain()} and @code{mapi_explain_query()}
345 : * give information about the context of the failed call, including the
346 : * expression shipped and any response received. The side-effect is
347 : * clearing the error status.
348 : *
349 : * @subsection Error Message
350 : * Almost every call can fail since the connection with the database
351 : * server can fail at any time. Functions that return a handle (either
352 : * @code{Mapi} or @code{MapiHdl}) may return NULL on failure, or they may return the
353 : * handle with the error flag set. If the function returns a non-NULL
354 : * handle, always check for errors with mapi_error.
355 : *
356 : *
357 : * Functions that return MapiMsg indicate success and failure with the
358 : * following codes.
359 : *
360 : * @multitable @columnfractions 0.15 0.7
361 : * @item MOK @tab No error
362 : * @item MERROR @tab Mapi internal error.
363 : * @item MTIMEOUT @tab Error communicating with the server.
364 : * @end multitable
365 : *
366 : * When these functions return MERROR or MTIMEOUT, an explanation of the
367 : * error can be had by calling one of the functions @code{mapi_error_str()},
368 : * @code{mapi_explain()}, or @code{mapi_explain_query()}.
369 : *
370 : * To check for error messages from the server, call @code{mapi_result_error()}.
371 : * This function returns NULL if there was no error, or the error message
372 : * if there was. A user-friendly message can be printed using
373 : * @code{map_explain_result()}. Typical usage is:
374 : * @verbatim
375 : * do {
376 : * if ((error = mapi_result_error(hdl)) != NULL)
377 : * mapi_explain_result(hdl, stderr);
378 : * while ((line = mapi_fetch_line(hdl)) != NULL)
379 : * ; // use output
380 : * } while (mapi_next_result(hdl) == 1);
381 : * @end verbatim
382 : *
383 : * @ifclear XQRYmanual
384 : * @node Mapi Function Reference, The Perl Library , Library Synopsis, The Mapi Library
385 : * @subsection Mapi Function Reference
386 : * @end ifclear
387 : * @ifset XQRYmanual
388 : * @node Mapi Function Reference
389 : * @subsection Mapi Function Reference
390 : * @end ifset
391 : *
392 : * @subsection Connecting and Disconnecting
393 : * @itemize
394 : * @item Mapi mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
395 : *
396 : * Setup a connection with a Mserver at a @emph{host}:@emph{port} and login
397 : * with @emph{username} and @emph{password}. If host == NULL, the local
398 : * host is accessed. If host starts with a '/' and the system supports it,
399 : * host is the directory where should be searched for UNIX domain
400 : * sockets. Port is not ignored, but used to identify which socket to
401 : * use. If port == 0, a default port is used.
402 : * The preferred query language is
403 : * @verb{ { }sql,mal @verb{ } }. On success, the function returns a
404 : * pointer to a structure with administration about the connection.
405 : *
406 : * @item MapiMsg mapi_disconnect(Mapi mid)
407 : *
408 : * Terminate the session described by @emph{mid}. The only possible uses
409 : * of the handle after this call is @emph{mapi_destroy()} and
410 : * @code{mapi_reconnect()}.
411 : * Other uses lead to failure.
412 : *
413 : * @item MapiMsg mapi_destroy(Mapi mid)
414 : *
415 : * Terminate the session described by @emph{ mid} if not already done so,
416 : * and free all resources. The handle cannot be used anymore.
417 : *
418 : * @item MapiMsg mapi_reconnect(Mapi mid)
419 : *
420 : * Close the current channel (if still open) and re-establish a fresh
421 : * connection. This will remove all global session variables.
422 : *
423 : * @item MapiMsg mapi_ping(Mapi mid)
424 : *
425 : * Test availability of the server. Returns zero upon success.
426 : * @end itemize
427 : *
428 : * @subsection Sending Queries
429 : * @itemize
430 : * @item MapiHdl mapi_query(Mapi mid, const char *Command)
431 : *
432 : * Send the Command to the database server represented by mid. This
433 : * function returns a query handle with which the results of the query
434 : * can be retrieved. The handle should be closed with
435 : * @code{mapi_close_handle()}. The command response is buffered for
436 : * consumption, c.f. mapi\_fetch\_row().
437 : *
438 : * @item MapiMsg mapi_query_handle(MapiHdl hdl, const char *Command)
439 : *
440 : * Send the Command to the database server represented by hdl, reusing
441 : * the handle from a previous query. If Command is zero it takes the
442 : * last query string kept around. The command response is buffered for
443 : * consumption, e.g. @code{mapi_fetch_row()}.
444 : *
445 : * @item MapiHdl mapi_prepare(Mapi mid, const char *Command)
446 : *
447 : * Move the query to a newly allocated query handle (which is returned).
448 : * Possibly interact with the back-end to prepare the query for
449 : * execution.
450 : *
451 : * @item MapiMsg mapi_execute(MapiHdl hdl)
452 : *
453 : * Ship a previously prepared command to the backend for execution. A
454 : * single answer is pre-fetched to detect any runtime error. MOK is
455 : * returned upon success.
456 : *
457 : * @item MapiMsg mapi_finish(MapiHdl hdl)
458 : *
459 : * Terminate a query. This routine is used in the rare cases that
460 : * consumption of the tuple stream produced should be prematurely
461 : * terminated. It is automatically called when a new query using the same
462 : * query handle is shipped to the database and when the query handle is
463 : * closed with @code{mapi_close_handle()}.
464 : *
465 : * @subsection Getting Results
466 : * @itemize
467 : * @item int mapi_get_field_count(MapiHdl mid)
468 : *
469 : * Return the number of fields in the current row.
470 : *
471 : * @item int64_t mapi_get_row_count(MapiHdl mid)
472 : *
473 : * If possible, return the number of rows in the last select call. A -1
474 : * is returned if this information is not available.
475 : *
476 : * @item int64_t mapi_get_last_id(MapiHdl mid)
477 : *
478 : * If possible, return the last inserted id of auto_increment (or alike) column.
479 : * A -1 is returned if this information is not available. We restrict this to
480 : * single row inserts and one auto_increment column per table. If the restrictions
481 : * do not hold, the result is unspecified.
482 : *
483 : * @item int64_t mapi_rows_affected(MapiHdl hdl)
484 : *
485 : * Return the number of rows affected by a database update command
486 : * such as SQL's INSERT/DELETE/UPDATE statements.
487 : *
488 : * @item int mapi_fetch_row(MapiHdl hdl)
489 : *
490 : * Retrieve a row from the server. The text retrieved is kept around in
491 : * a buffer linked with the query handle from which selective fields can
492 : * be extracted. It returns the number of fields recognized. A zero is
493 : * returned upon encountering end of sequence or error. This can be
494 : * analyzed in using @code{mapi_error()}.
495 : *
496 : * @item int64_t mapi_fetch_all_rows(MapiHdl hdl)
497 : *
498 : * All rows are cached at the client side first. Subsequent calls to
499 : * @code{mapi_fetch_row()} will take the row from the cache. The number or
500 : * rows cached is returned.
501 : *
502 : * @item MapiMsg mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
503 : *
504 : * Reset the row pointer to the requested row number. If whence is
505 : * @code{MAPI_SEEK_SET}, rownr is the absolute row number (0 being the
506 : * first row); if whence is @code{MAPI_SEEK_CUR}, rownr is relative to the
507 : * current row; if whence is @code{MAPI_SEEK_END}, rownr is relative to
508 : * the last row.
509 : *
510 : * @item MapiMsg mapi_fetch_reset(MapiHdl hdl)
511 : *
512 : * Reset the row pointer to the first line in the cache. This need not
513 : * be a tuple. This is mostly used in combination with fetching all
514 : * tuples at once.
515 : *
516 : * @item char *mapi_fetch_field(MapiHdl hdl, int fnr)
517 : *
518 : * Return a pointer a C-string representation of the value returned. A
519 : * zero is returned upon encountering an error or when the database value
520 : * is NULL; this can be analyzed in using @code{mapi\_error()}.
521 : *
522 : * @item size_t mapi_fetch_fiels_len(MapiHdl hdl, int fnr)
523 : *
524 : * Return the length of the C-string representation excluding trailing NULL
525 : * byte of the value. Zero is returned upon encountering an error, when the
526 : * database value is NULL, of when the string is the empty string. This can
527 : * be analyzed by using @code{mapi\_error()} and @code{mapi\_fetch\_field()}.
528 : *
529 : * @item MapiMsg mapi_next_result(MapiHdl hdl)
530 : *
531 : * Go to the next result set, discarding the rest of the output of the
532 : * current result set.
533 : * @end itemize
534 : *
535 : * @subsection Errors
536 : * @itemize
537 : * @item MapiMsg mapi_error(Mapi mid)
538 : *
539 : * Return the last error code or 0 if there is no error.
540 : *
541 : * @item char *mapi_error_str(Mapi mid)
542 : *
543 : * Return a pointer to the last error message.
544 : *
545 : * @item char *mapi_result_error(MapiHdl hdl)
546 : *
547 : * Return a pointer to the last error message from the server.
548 : *
549 : * @item void mapi_explain(Mapi mid, FILE *fd)
550 : *
551 : * Write the error message obtained from @code{mserver} to a file.
552 : *
553 : * @item void mapi_explain_query(MapiHdl hdl, FILE *fd)
554 : *
555 : * Write the error message obtained from @code{mserver} to a file.
556 : *
557 : * @item void mapi_explain_result(MapiHdl hdl, FILE *fd)
558 : *
559 : * Write the error message obtained from @code{mserver} to a file.
560 : * @end itemize
561 : *
562 : * @subsection Parameters
563 : *
564 : * @itemize
565 : * @item MapiMsg mapi_bind(MapiHdl hdl, int fldnr, char **val)
566 : *
567 : * Bind a string variable with a field in the return table. Upon a
568 : * successful subsequent @code{mapi\_fetch\_row()} the indicated field is stored
569 : * in the space pointed to by val. Returns an error if the field
570 : * identified does not exist.
571 : *
572 : * @item MapiMsg mapi_bind_var(MapiHdl hdl, int fldnr, int type, void *val)
573 : *
574 : * Bind a variable to a field in the return table. Upon a successful
575 : * subsequent @code{mapi\_fetch\_row()}, the indicated field is converted to the
576 : * given type and stored in the space pointed to by val. The types
577 : * recognized are @verb{ { } @code{MAPI\_TINY, MAPI\_UTINY, MAPI\_SHORT, MAPI\_USHORT,
578 : * MAPI_INT, MAPI_UINT, MAPI_LONG, MAPI_ULONG, MAPI_LONGLONG,
579 : * MAPI_ULONGLONG, MAPI_CHAR, MAPI_VARCHAR, MAPI_FLOAT, MAPI_DOUBLE,
580 : * MAPI_DATE, MAPI_TIME, MAPI_DATETIME} @verb{ } }. The binding operations
581 : * should be performed after the mapi_execute command. Subsequently all
582 : * rows being fetched also involve delivery of the field values in the
583 : * C-variables using proper conversion. For variable length strings a
584 : * pointer is set into the cache.
585 : *
586 : * @item MapiMsg mapi_bind_numeric(MapiHdl hdl, int fldnr, int scale, int precision, void *val)
587 : *
588 : * Bind to a numeric variable, internally represented by MAPI_INT
589 : * Describe the location of a numeric parameter in a query template.
590 : *
591 : * @item MapiMsg mapi_clear_bindings(MapiHdl hdl)
592 : *
593 : * Clear all field bindings.
594 : *
595 : * @item MapiMsg mapi_param(MapiHdl hdl, int fldnr, char **val)
596 : *
597 : * Bind a string variable with the n-th placeholder in the query
598 : * template. No conversion takes place.
599 : *
600 : * @item MapiMsg mapi_param_type(MapiHdl hdl, int fldnr, int ctype, int sqltype, void *val)
601 : *
602 : * Bind a variable whose type is described by ctype to a parameter whose
603 : * type is described by sqltype.
604 : *
605 : * @item MapiMsg mapi_param_numeric(MapiHdl hdl, int fldnr, int scale, int precision, void *val)
606 : *
607 : * Bind to a numeric variable, internally represented by MAPI_INT.
608 : *
609 : * @item MapiMsg mapi_param_string(MapiHdl hdl, int fldnr, int sqltype, char *val, int *sizeptr)
610 : *
611 : * Bind a string variable, internally represented by MAPI_VARCHAR, to a
612 : * parameter. The sizeptr parameter points to the length of the string
613 : * pointed to by val. If sizeptr == NULL or *sizeptr == -1, the string
614 : * is NULL-terminated.
615 : *
616 : * @item MapiMsg mapi_clear_params(MapiHdl hdl)
617 : *
618 : * Clear all parameter bindings.
619 : * @end itemize
620 : *
621 : * @subsection Miscellaneous
622 : * @itemize
623 : * @item MapiMsg mapi_setAutocommit(Mapi mid, bool autocommit)
624 : *
625 : * Set the autocommit flag (default is on). This only has an effect
626 : * when the language is SQL. In that case, the server commits after each
627 : * statement sent to the server.
628 : *
629 : * @item MapiMsg mapi_cache_limit(Mapi mid, int maxrows)
630 : *
631 : * A limited number of tuples are pre-fetched after each @code{execute()}. If
632 : * maxrows is negative, all rows will be fetched before the application
633 : * is permitted to continue. Once the cache is filled, a number of tuples
634 : * are shuffled to make room for new ones, but taking into account
635 : * non-read elements. Filling the cache quicker than reading leads to an
636 : * error.
637 : *
638 : * @item MapiMsg mapi_cache_freeup(MapiHdl hdl, int percentage)
639 : *
640 : * Forcefully shuffle the cache making room for new rows. It ignores the
641 : * read counter, so rows may be lost.
642 : *
643 : * @item char * mapi_quote(const char *str, int size)
644 : *
645 : * Escape special characters such as @code{\n}, @code{\t} in str with
646 : * backslashes. The returned value is a newly allocated string which
647 : * should be freed by the caller.
648 : *
649 : * @item char * mapi_unquote(const char *name)
650 : *
651 : * The reverse action of @code{mapi_quote()}, turning the database
652 : * representation into a C-representation. The storage space is
653 : * dynamically created and should be freed after use.
654 : *
655 : * @item MapiMsg mapi_trace(Mapi mid, bool flag)
656 : *
657 : * Set the trace flag to monitor interaction of the client
658 : * with the library. It is primarilly used for debugging
659 : * Mapi applications.
660 : *
661 : * @item int mapi_get_trace(Mapi mid)
662 : *
663 : * Return the current value of the trace flag.
664 : *
665 : * @item MapiMsg mapi\_log(Mapi mid, const char *fname)
666 : *
667 : * Log the interaction between the client and server for offline
668 : * inspection. Beware that the log file overwrites any previous log.
669 : * For detailed interaction trace with the Mapi library itself use mapi\_trace().
670 : * @end itemize
671 : * The remaining operations are wrappers around the data structures
672 : * maintained. Note that column properties are derived from the table
673 : * output returned from the server.
674 : * @itemize
675 : * @item char *mapi_get_name(MapiHdl hdl, int fnr)
676 : * @item char *mapi_get_type(MapiHdl hdl, int fnr)
677 : * @item char *mapi_get_table(MapiHdl hdl, int fnr)
678 : * @item int mapi_get_len(Mapi mid, int fnr)
679 : *
680 : * @item const char *mapi_get_dbname(Mapi mid)
681 : * @item const char *mapi_get_host(Mapi mid)
682 : * @item const char *mapi_get_user(Mapi mid)
683 : * @item const char *mapi_get_lang(Mapi mid)
684 : * @item const char *mapi_get_motd(Mapi mid)
685 : *
686 : * @end itemize
687 : * @- Implementation
688 : */
689 :
690 : #include "monetdb_config.h"
691 : #include "stream.h" /* include before mapi.h */
692 : #include "stream_socket.h"
693 : #include "mapi.h"
694 : #include "mapi_prompt.h"
695 : #include "mcrypt.h"
696 : #include "matomic.h"
697 : #include "mstring.h"
698 : #include "mutils.h"
699 :
700 : #include "mapi_intern.h"
701 :
702 : #ifndef INVALID_SOCKET
703 : #define INVALID_SOCKET (-1)
704 : #endif
705 :
706 : #define MAPIBLKSIZE 256 /* minimum buffer shipped */
707 :
708 : #define MAPI_AUTO 0 /* automatic type detection */
709 : #define MAPI_TINY 1
710 : #define MAPI_UTINY 2
711 : #define MAPI_SHORT 3
712 : #define MAPI_USHORT 4
713 : #define MAPI_INT 5
714 : #define MAPI_UINT 6
715 : #define MAPI_LONG 7
716 : #define MAPI_ULONG 8
717 : #define MAPI_LONGLONG 9
718 : #define MAPI_ULONGLONG 10
719 : #define MAPI_CHAR 11
720 : #define MAPI_VARCHAR 12
721 : #define MAPI_FLOAT 13
722 : #define MAPI_DOUBLE 14
723 : #define MAPI_DATE 15
724 : #define MAPI_TIME 16
725 : #define MAPI_DATETIME 17
726 : #define MAPI_NUMERIC 18
727 :
728 : #define PLACEHOLDER '?'
729 :
730 :
731 : #ifdef DEBUG
732 : #define debugprint(fmt,arg) printf(fmt,arg)
733 : #else
734 : #define debugprint(fmt,arg) ((void) 0)
735 : #endif
736 :
737 : /*
738 : * All external calls to the library should pass the mapi-check
739 : * routine. It assures a working connection and proper reset of
740 : * the error status of the Mapi structure.
741 : */
742 : #define mapi_check(X) \
743 : do { \
744 : debugprint("entering %s\n", __func__); \
745 : assert(X); \
746 : if (!(X)->connected) { \
747 : mapi_setError((X), "Connection lost", \
748 : __func__, MERROR); \
749 : return (X)->error; \
750 : } \
751 : mapi_clrError(X); \
752 : } while (0)
753 : #define mapi_check0(X) \
754 : do { \
755 : debugprint("entering %s\n", __func__); \
756 : assert(X); \
757 : if (!(X)->connected) { \
758 : mapi_setError((X), "Connection lost", \
759 : __func__, MERROR); \
760 : return 0; \
761 : } \
762 : mapi_clrError(X); \
763 : } while (0)
764 : #define mapi_hdl_check(X) \
765 : do { \
766 : debugprint("entering %s\n", __func__); \
767 : assert(X); \
768 : assert((X)->mid); \
769 : if (!(X)->mid->connected) { \
770 : mapi_setError((X)->mid, "Connection lost", \
771 : __func__, MERROR); \
772 : return (X)->mid->error; \
773 : } \
774 : mapi_clrError((X)->mid); \
775 : } while (0)
776 : #define mapi_hdl_check0(X) \
777 : do { \
778 : debugprint("entering %s\n", __func__); \
779 : assert(X); \
780 : assert((X)->mid); \
781 : if (!(X)->mid->connected) { \
782 : mapi_setError((X)->mid, "Connection lost", \
783 : __func__, MERROR); \
784 : return 0; \
785 : } \
786 : mapi_clrError((X)->mid); \
787 : } while (0)
788 :
789 : static int mapi_extend_bindings(MapiHdl hdl, int minbindings);
790 : static int mapi_extend_params(MapiHdl hdl, int minparams);
791 : static int unquote(const char *msg, char **start, const char **next, int endchar, size_t *lenp);
792 : static int mapi_slice_row(struct MapiResultSet *result, int cr);
793 : static void mapi_store_bind(struct MapiResultSet *result, int cr);
794 :
795 : static ATOMIC_FLAG mapi_initialized = ATOMIC_FLAG_INIT;
796 : /*
797 : * Blocking
798 : * --------
799 : *
800 : * The server side code works with a common/stream package, a fast
801 : * buffered IO scheme. Nowadays this should be the only protocol used,
802 : * while historical uses were line-based instead.
803 : *
804 : *
805 : * Error Handling
806 : * --------------
807 : *
808 : * All externally visible functions should first call mapi_clrError (usually
809 : * though a call to one of the check macros above) to clear the error flag.
810 : * When an error is detected, the library calls mapi_setError to set the error
811 : * flag. The application can call mapi_error or mapi_error_str to check for
812 : * errors, and mapi_explain or mapi_explain_query to print a formatted error
813 : * report.
814 : */
815 : char mapi_nomem[] = "Memory allocation failed";
816 :
817 : void
818 13345692 : mapi_clrError(Mapi mid)
819 : {
820 13345692 : assert(mid);
821 13345692 : if (mid->errorstr && mid->errorstr != mapi_nomem)
822 1067 : free(mid->errorstr);
823 13345692 : mid->action = 0; /* contains references to constants */
824 13345692 : mid->error = 0;
825 13345692 : mid->errorstr = 0;
826 13345692 : }
827 :
828 : MapiMsg
829 1071 : mapi_setError(Mapi mid, const char *msg, const char *action, MapiMsg error)
830 : {
831 1071 : assert(msg);
832 1071 : REALLOC(mid->errorstr, strlen(msg) + 1);
833 1071 : if (mid->errorstr == NULL)
834 0 : mid->errorstr = mapi_nomem;
835 : else
836 1071 : strcpy(mid->errorstr, msg);
837 1071 : mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
838 1071 : mid->error = error;
839 1071 : mid->action = action;
840 1071 : return mid->error;
841 : }
842 :
843 13 : MapiMsg mapi_printError(Mapi mid, const char *action, MapiMsg error, const char *fmt, ...)
844 : {
845 13 : size_t size = 81; // just a guess
846 :
847 19 : while (1) {
848 16 : REALLOC(mid->errorstr, size);
849 16 : if (mid->errorstr == NULL) {
850 0 : mid->errorstr = mapi_nomem;
851 0 : break;
852 : }
853 16 : va_list ap;
854 16 : va_start(ap, fmt);
855 16 : int n = vsnprintf(mid->errorstr, size, fmt, ap);
856 16 : va_end(ap);
857 16 : if (n < 0) {
858 : // is it even possible for vsnprintf to fail?
859 : break;
860 : }
861 16 : if ((size_t)n < size) {
862 : // it fit
863 : break;
864 : } else {
865 : // try again larger
866 3 : size = (size_t)n + 1;
867 : }
868 : }
869 13 : mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
870 13 : mid->error = error;
871 13 : mid->action = action;
872 13 : return mid->error;
873 : }
874 :
875 : MapiMsg
876 190405 : mapi_error(Mapi mid)
877 : {
878 190405 : assert(mid);
879 190405 : return mid->error;
880 : }
881 :
882 : const char *
883 19 : mapi_error_str(Mapi mid)
884 : {
885 19 : assert(mid);
886 19 : return mid->errorstr;
887 : }
888 :
889 : #ifdef _MSC_VER
890 : static const struct {
891 : int e;
892 : const char *m;
893 : } wsaerrlist[] = {
894 : { WSA_INVALID_HANDLE, "Specified event object handle is invalid" },
895 : { WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available" },
896 : { WSA_INVALID_PARAMETER, "One or more parameters are invalid" },
897 : { WSA_OPERATION_ABORTED, "Overlapped operation aborted" },
898 : { WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state" },
899 : { WSA_IO_PENDING, "Overlapped operations will complete later" },
900 : { WSAEINTR, "Interrupted function call" },
901 : { WSAEBADF, "File handle is not valid" },
902 : { WSAEACCES, "Permission denied" },
903 : { WSAEFAULT, "Bad address" },
904 : { WSAEINVAL, "Invalid argument" },
905 : { WSAEMFILE, "Too many open files" },
906 : { WSAEWOULDBLOCK, "Resource temporarily unavailable" },
907 : { WSAEINPROGRESS, "Operation now in progress" },
908 : { WSAEALREADY, "Operation already in progress" },
909 : { WSAENOTSOCK, "Socket operation on nonsocket" },
910 : { WSAEDESTADDRREQ, "Destination address required" },
911 : { WSAEMSGSIZE, "Message too long" },
912 : { WSAEPROTOTYPE, "Protocol wrong type for socket" },
913 : { WSAENOPROTOOPT, "Bad protocol option" },
914 : { WSAEPROTONOSUPPORT, "Protocol not supported" },
915 : { WSAESOCKTNOSUPPORT, "Socket type not supported" },
916 : { WSAEOPNOTSUPP, "Operation not supported" },
917 : { WSAEPFNOSUPPORT, "Protocol family not supported" },
918 : { WSAEAFNOSUPPORT, "Address family not supported by protocol family" },
919 : { WSAEADDRINUSE, "Address already in use" },
920 : { WSAEADDRNOTAVAIL, "Cannot assign requested address" },
921 : { WSAENETDOWN, "Network is down" },
922 : { WSAENETUNREACH, "Network is unreachable" },
923 : { WSAENETRESET, "Network dropped connection on reset" },
924 : { WSAECONNABORTED, "Software caused connection abort" },
925 : { WSAECONNRESET, "Connection reset by peer" },
926 : { WSAENOBUFS, "No buffer space available" },
927 : { WSAEISCONN, "Socket is already connected" },
928 : { WSAENOTCONN, "Socket is not connected" },
929 : { WSAESHUTDOWN, "Cannot send after socket shutdown" },
930 : { WSAETOOMANYREFS, "Too many references" },
931 : { WSAETIMEDOUT, "Connection timed out" },
932 : { WSAECONNREFUSED, "Connection refused" },
933 : { WSAELOOP, "Cannot translate name" },
934 : { WSAENAMETOOLONG, "Name too long" },
935 : { WSAEHOSTDOWN, "Host is down" },
936 : { WSAEHOSTUNREACH, "No route to host" },
937 : { WSAENOTEMPTY, "Directory not empty" },
938 : { WSAEPROCLIM, "Too many processes" },
939 : { WSAEUSERS, "User quota exceeded" },
940 : { WSAEDQUOT, "Disk quota exceeded" },
941 : { WSAESTALE, "Stale file handle reference" },
942 : { WSAEREMOTE, "Item is remote" },
943 : { WSASYSNOTREADY, "Network subsystem is unavailable" },
944 : { WSAVERNOTSUPPORTED, "Winsock.dll version out of range" },
945 : { WSANOTINITIALISED, "Successful WSAStartup not yet performed" },
946 : { WSAEDISCON, "Graceful shutdown in progress" },
947 : { WSAENOMORE, "No more results" },
948 : { WSAECANCELLED, "Call has been canceled" },
949 : { WSAEINVALIDPROCTABLE, "Procedure call table is invalid" },
950 : { WSAEINVALIDPROVIDER, "Service provider is invalid" },
951 : { WSAEPROVIDERFAILEDINIT, "Service provider failed to initialize" },
952 : { WSASYSCALLFAILURE, "System call failure" },
953 : { WSASERVICE_NOT_FOUND, "Service not found" },
954 : { WSATYPE_NOT_FOUND, "Class type not found" },
955 : { WSA_E_NO_MORE, "No more results" },
956 : { WSA_E_CANCELLED, "Call was canceled" },
957 : { WSAEREFUSED, "Database query was refused" },
958 : { WSAHOST_NOT_FOUND, "Host not found" },
959 : { WSATRY_AGAIN, "Nonauthoritative host not found" },
960 : { WSANO_RECOVERY, "This is a nonrecoverable error" },
961 : { WSANO_DATA, "Valid name, no data record of requested type" },
962 : { WSA_QOS_RECEIVERS, "QOS receivers" },
963 : { WSA_QOS_SENDERS, "QOS senders" },
964 : { WSA_QOS_NO_SENDERS, "No QOS senders" },
965 : { WSA_QOS_NO_RECEIVERS, "QOS no receivers" },
966 : { WSA_QOS_REQUEST_CONFIRMED, "QOS request confirmed" },
967 : { WSA_QOS_ADMISSION_FAILURE, "QOS admission error" },
968 : { WSA_QOS_POLICY_FAILURE, "QOS policy failure" },
969 : { WSA_QOS_BAD_STYLE, "QOS bad style" },
970 : { WSA_QOS_BAD_OBJECT, "QOS bad object" },
971 : { WSA_QOS_TRAFFIC_CTRL_ERROR, "QOS traffic control error" },
972 : { WSA_QOS_GENERIC_ERROR, "QOS generic error" },
973 : { WSA_QOS_ESERVICETYPE, "QOS service type error" },
974 : { WSA_QOS_EFLOWSPEC, "QOS flowspec error" },
975 : { WSA_QOS_EPROVSPECBUF, "Invalid QOS provider buffer" },
976 : { WSA_QOS_EFILTERSTYLE, "Invalid QOS filter style" },
977 : { WSA_QOS_EFILTERTYPE, "Invalid QOS filter type" },
978 : { WSA_QOS_EFILTERCOUNT, "Incorrect QOS filter count" },
979 : { WSA_QOS_EOBJLENGTH, "Invalid QOS object length" },
980 : { WSA_QOS_EFLOWCOUNT, "Incorrect QOS flow count" },
981 : { WSA_QOS_EUNKOWNPSOBJ, "Unrecognized QOS object" },
982 : { WSA_QOS_EPOLICYOBJ, "Invalid QOS policy object" },
983 : { WSA_QOS_EFLOWDESC, "Invalid QOS flow descriptor" },
984 : { WSA_QOS_EPSFLOWSPEC, "Invalid QOS provider-specific flowspec" },
985 : { WSA_QOS_EPSFILTERSPEC, "Invalid QOS provider-specific filterspec" },
986 : { WSA_QOS_ESDMODEOBJ, "Invalid QOS shape discard mode object" },
987 : { WSA_QOS_ESHAPERATEOBJ, "Invalid QOS shaping rate object" },
988 : { WSA_QOS_RESERVED_PETYPE, "Reserved policy QOS element type" },
989 : };
990 : const char *
991 : wsaerror(int err)
992 : {
993 : int i;
994 :
995 : for (i = 0; i < NELEM(wsaerrlist); i++)
996 : if (wsaerrlist[i].e == err)
997 : return wsaerrlist[i].m;
998 : return "Unknown error";
999 : }
1000 : #endif
1001 :
1002 : static void
1003 0 : clean_print(char *msg, const char *prefix, FILE *fd)
1004 : {
1005 0 : size_t len = strlen(prefix);
1006 :
1007 0 : while (msg && *msg) {
1008 : /* cut by line */
1009 0 : char *p = strchr(msg, '\n');
1010 :
1011 0 : if (p)
1012 0 : *p++ = 0;
1013 :
1014 : /* skip over prefix */
1015 0 : if (strncmp(msg, prefix, len) == 0)
1016 0 : msg += len;
1017 :
1018 : /* output line */
1019 0 : fputs(msg, fd);
1020 0 : fputc('\n', fd);
1021 0 : msg = p;
1022 : }
1023 0 : }
1024 :
1025 : static void
1026 179 : indented_print(const char *msg, const char *prefix, FILE *fd)
1027 : {
1028 : /* for multiline error messages, indent all subsequent
1029 : lines with the space it takes to print "ERROR = " */
1030 179 : const char *s = prefix, *p = msg, *q;
1031 179 : const int len = (int) strlen(s);
1032 179 : const char t = s[len - 1];
1033 :
1034 303 : while (p && *p) {
1035 190 : fprintf(fd, "%*.*s%c", len - 1, len - 1, s, t);
1036 190 : s = "";
1037 :
1038 190 : q = strchr(p, '\n');
1039 190 : if (q) {
1040 124 : q++; /* also print the newline */
1041 124 : fprintf(fd, "%.*s", (int) (q - p), p);
1042 : } else {
1043 : /* print bit after last newline,
1044 : adding one ourselves */
1045 66 : fprintf(fd, "%s\n", p);
1046 66 : break; /* nothing more to do */
1047 : }
1048 124 : p = q;
1049 : }
1050 179 : }
1051 :
1052 : void
1053 60 : mapi_noexplain(Mapi mid, const char *errorprefix)
1054 : {
1055 60 : assert(mid);
1056 60 : if (mid->noexplain)
1057 0 : free(mid->noexplain);
1058 60 : mid->noexplain = errorprefix ? strdup(errorprefix) : NULL;
1059 60 : }
1060 :
1061 : void
1062 0 : mapi_explain(Mapi mid, FILE *fd)
1063 : {
1064 0 : assert(mid);
1065 0 : if (mid->noexplain == NULL) {
1066 0 : const char *host = msetting_string(mid->settings, MP_HOST);
1067 0 : const char *user = msetting_string(mid->settings, MP_USER);
1068 0 : int port = msetting_long(mid->settings, MP_PORT);
1069 0 : if (host[0] == '/')
1070 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1071 : else
1072 0 : fprintf(fd, "MAPI = %s@%s:%d\n",
1073 : user, host, port);
1074 0 : if (mid->action)
1075 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1076 0 : if (mid->errorstr)
1077 0 : indented_print(mid->errorstr, "ERROR = !", fd);
1078 0 : } else if (mid->errorstr) {
1079 0 : clean_print(mid->errorstr, mid->noexplain, fd);
1080 : }
1081 0 : fflush(fd);
1082 0 : mapi_clrError(mid);
1083 0 : }
1084 :
1085 : void
1086 0 : mapi_explain_query(MapiHdl hdl, FILE *fd)
1087 : {
1088 0 : Mapi mid;
1089 :
1090 0 : assert(hdl);
1091 0 : mid = hdl->mid;
1092 0 : assert(mid);
1093 0 : if (mid->noexplain == NULL) {
1094 0 : const char *host = msetting_string(mid->settings, MP_HOST);
1095 0 : const char *user = msetting_string(mid->settings, MP_USER);
1096 0 : int port = msetting_long(mid->settings, MP_PORT);
1097 0 : if (host[0] == '/')
1098 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1099 : else
1100 0 : fprintf(fd, "MAPI = %s@%s:%d\n",
1101 : user, host, port);
1102 0 : if (mid->action)
1103 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1104 0 : if (hdl->query)
1105 0 : indented_print(hdl->query, "QUERY = ", fd);
1106 0 : if (mid->errorstr)
1107 0 : indented_print(mid->errorstr, "ERROR = !", fd);
1108 0 : } else if (mid->errorstr) {
1109 0 : clean_print(mid->errorstr, mid->noexplain, fd);
1110 : }
1111 0 : fflush(fd);
1112 0 : mapi_clrError(mid);
1113 0 : }
1114 :
1115 : void
1116 60 : mapi_explain_result(MapiHdl hdl, FILE *fd)
1117 : {
1118 60 : Mapi mid;
1119 :
1120 60 : if (hdl == NULL ||
1121 60 : hdl->result == NULL ||
1122 60 : hdl->result->errorstr == NULL)
1123 : return;
1124 60 : assert(hdl);
1125 60 : assert(hdl->result);
1126 60 : assert(hdl->result->errorstr);
1127 60 : mid = hdl->mid;
1128 60 : assert(mid);
1129 60 : if (mid->noexplain == NULL) {
1130 60 : const char *host = msetting_string(mid->settings, MP_HOST);
1131 60 : const char *user = msetting_string(mid->settings, MP_USER);
1132 60 : int port = msetting_long(mid->settings, MP_PORT);
1133 60 : if (host[0] == '/')
1134 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1135 : else
1136 60 : fprintf(fd, "MAPI = %s@%s:%d\n",
1137 : user, host, port);
1138 60 : if (mid->action)
1139 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1140 60 : if (hdl->query)
1141 60 : indented_print(hdl->query, "QUERY = ", fd);
1142 60 : indented_print(hdl->result->errorstr, "ERROR = !", fd);
1143 60 : if (msettings_lang_is_sql(mid->settings) && hdl->result->sqlstate[0])
1144 59 : indented_print(hdl->result->sqlstate, "CODE = ", fd);
1145 : } else {
1146 0 : clean_print(hdl->result->errorstr, mid->noexplain, fd);
1147 : }
1148 60 : fflush(fd);
1149 : }
1150 :
1151 : stream *
1152 2788 : mapi_get_to(Mapi mid)
1153 : {
1154 2788 : mapi_check0(mid);
1155 2787 : return mid->to;
1156 : }
1157 :
1158 : stream *
1159 1392 : mapi_get_from(Mapi mid)
1160 : {
1161 1392 : mapi_check0(mid);
1162 1394 : return mid->from;
1163 : }
1164 :
1165 : bool
1166 0 : mapi_get_trace(Mapi mid)
1167 : {
1168 0 : mapi_check0(mid);
1169 0 : return mid->trace;
1170 : }
1171 :
1172 : bool
1173 0 : mapi_get_autocommit(Mapi mid)
1174 : {
1175 0 : mapi_check0(mid);
1176 0 : return msetting_bool(mid->settings, MP_AUTOCOMMIT);
1177 : }
1178 :
1179 : bool
1180 0 : mapi_get_columnar_protocol(Mapi mid)
1181 : {
1182 0 : mapi_check0(mid);
1183 0 : return mid->columnar_protocol;
1184 : }
1185 :
1186 : int
1187 0 : mapi_get_time_zone(Mapi mid)
1188 : {
1189 0 : mapi_check0(mid);
1190 0 : return msetting_long(mid->settings, MP_TIMEZONE);
1191 : }
1192 :
1193 : static int64_t
1194 0 : usec(void)
1195 : {
1196 : #ifdef HAVE_GETTIMEOFDAY
1197 0 : struct timeval tp;
1198 :
1199 0 : gettimeofday(&tp, NULL);
1200 0 : return ((int64_t) tp.tv_sec) * 1000000 + (int64_t) tp.tv_usec;
1201 : #else
1202 : #ifdef HAVE_FTIME
1203 : struct timeb tb;
1204 :
1205 : ftime(&tb);
1206 : return ((int64_t) tb.time) * 1000000 + ((int64_t) tb.millitm) * 1000;
1207 : #endif
1208 : #endif
1209 : }
1210 :
1211 : static void
1212 0 : mapi_log_header(Mapi mid, const char *funcname, long line, const char *mark1, const char *mark2)
1213 : {
1214 0 : int64_t now = usec();
1215 0 : static int64_t firstcall = 0;
1216 0 : if (firstcall == 0)
1217 0 : firstcall = now;
1218 0 : double seconds = (double)(now - firstcall) / 1e6;
1219 0 : mnstr_printf(mid->tracelog, "\342\226\266 [%u] t=%.3fs %s%s %s(), line %ld\n", mid->index, seconds, mark1, mark2, funcname, line); /* U+25B6: right-pointing triangle */
1220 0 : }
1221 :
1222 : void
1223 0 : mapi_impl_log_record(Mapi mid, const char *funcname, long line, const char *mark, const char *fmt, ...)
1224 : {
1225 0 : va_list ap;
1226 :
1227 0 : if (mid->tracelog == NULL)
1228 0 : return;
1229 :
1230 : size_t needed = 128;
1231 0 : size_t to_print;
1232 0 : while (1) {
1233 0 : if (mid->tracebuffersize < needed) {
1234 0 : free(mid->tracebuffer);
1235 0 : mid->tracebuffer = malloc(needed);
1236 0 : if (mid->tracebuffer) {
1237 0 : mid->tracebuffersize = needed;
1238 : } else {
1239 0 : mid->tracebuffersize = 0;
1240 0 : to_print = 0;
1241 0 : break;
1242 : }
1243 : }
1244 0 : va_start(ap, fmt);
1245 0 : int n = vsnprintf(mid->tracebuffer, mid->tracebuffersize, fmt, ap);
1246 0 : va_end(ap);
1247 0 : if (n < 0) {
1248 : to_print = 0;
1249 : break;
1250 : }
1251 0 : if ((size_t)n < mid->tracebuffersize) {
1252 : to_print = n;
1253 : break;
1254 : }
1255 : // need to reallocate
1256 0 : needed = n + 1;
1257 : }
1258 :
1259 0 : mapi_log_header(mid, funcname, line, mark, "");
1260 0 : if (to_print > 0) {
1261 0 : mnstr_write(mid->tracelog, mid->tracebuffer, to_print, 1);
1262 0 : mnstr_writeChr(mid->tracelog, '\n');
1263 : }
1264 0 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
1265 : }
1266 :
1267 : #define MAPILOG_OPEN "\tb'"
1268 : #define MAPILOG_CLOSE "'"
1269 :
1270 : void
1271 0 : mapi_impl_log_data(Mapi mid, const char *filename, long line, const char *mark, const char *start, size_t len)
1272 : {
1273 0 : const char hexdigits[] = "0123456789abcdef";
1274 0 : if (mid->tracelog == NULL)
1275 0 : return;
1276 :
1277 0 : mapi_log_header(mid, filename, line, mark, " (DATA)");
1278 :
1279 0 : const size_t margin = strlen("\\xNN" MAPILOG_CLOSE "\n," MAPILOG_OPEN);
1280 0 : char buffer[128] = { 0 };
1281 0 : char *pos = buffer;
1282 0 : mnstr_writeStr(mid->tracelog, MAPILOG_OPEN);
1283 0 : bool inside = true;
1284 0 : for (unsigned char *p = (unsigned char*)start; (char*)p < start + len; p++) {
1285 0 : unsigned char c = *p;
1286 0 : if (!inside) {
1287 0 : for (char *text = "\n" MAPILOG_OPEN; *text; text++)
1288 0 : *pos++ = *text;
1289 : inside = true;
1290 : }
1291 0 : if (pos >= buffer + sizeof(buffer) - margin) {
1292 0 : mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
1293 0 : pos = buffer;
1294 : }
1295 0 : switch (c) {
1296 0 : case '\\':
1297 : case '\'':
1298 0 : *pos++ = '\\';
1299 0 : *pos++ = c;
1300 0 : break;
1301 0 : case '\t':
1302 0 : *pos++ = '\\';
1303 0 : *pos++ = 't';
1304 0 : break;
1305 : case '\n':
1306 0 : for (char *text = "\\n" MAPILOG_CLOSE; *text; text++)
1307 0 : *pos++ = *text;
1308 : inside = false;
1309 : break;
1310 0 : default:
1311 0 : if (c >= 32 && c < 127) {
1312 0 : *pos++ = c;
1313 : } else {
1314 0 : *pos++ = '\\';
1315 0 : *pos++ = 'x';
1316 0 : *pos++ = hexdigits[c / 16];
1317 0 : *pos++ = hexdigits[c % 16];
1318 : }
1319 : break;
1320 : }
1321 : }
1322 0 : if (inside) {
1323 0 : for (char *text = MAPILOG_CLOSE; *text; text++)
1324 0 : *pos++ = *text;
1325 : }
1326 0 : *pos++ = ',';
1327 0 : *pos++ = '\n';
1328 0 : mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
1329 :
1330 0 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
1331 : }
1332 :
1333 : MapiMsg
1334 0 : mapi_log(Mapi mid, const char *nme)
1335 : {
1336 0 : mapi_clrError(mid);
1337 0 : if (mid->tracelog) {
1338 0 : close_stream(mid->tracelog);
1339 0 : mid->tracelog = NULL;
1340 : }
1341 0 : if (nme == NULL)
1342 : return MOK;
1343 0 : if (nme[0] == '-' && nme[1] == '\0')
1344 0 : mid->tracelog = stderr_wastream();
1345 : else
1346 0 : mid->tracelog = open_wastream(nme);
1347 0 : if (mid->tracelog == NULL || mnstr_errnr(mid->tracelog) != MNSTR_NO__ERROR) {
1348 0 : if (mid->tracelog)
1349 0 : close_stream(mid->tracelog);
1350 0 : mid->tracelog = NULL;
1351 0 : return mapi_setError(mid, "Could not create log file", __func__, MERROR);
1352 : }
1353 : return MOK;
1354 : }
1355 :
1356 : /* send a dummy request to the server to see whether the connection is
1357 : still alive */
1358 : MapiMsg
1359 0 : mapi_ping(Mapi mid)
1360 : {
1361 0 : mapi_check(mid);
1362 :
1363 0 : MapiHdl hdl;
1364 0 : if (msettings_lang_is_sql(mid->settings))
1365 0 : hdl = mapi_query(mid, "select true;");
1366 0 : else if (msettings_lang_is_mal(mid->settings))
1367 0 : hdl = mapi_query(mid, "io.print(1);");
1368 : else
1369 : hdl = NULL;
1370 :
1371 0 : if (hdl)
1372 0 : mapi_close_handle(hdl);
1373 0 : return mid->error;
1374 : }
1375 :
1376 : /* allocate a new structure to represent a result set */
1377 : static struct MapiResultSet *
1378 31790 : new_result(MapiHdl hdl)
1379 : {
1380 31790 : struct MapiResultSet *result;
1381 :
1382 31790 : assert((hdl->lastresult == NULL && hdl->result == NULL) ||
1383 : (hdl->result != NULL && hdl->lastresult != NULL && hdl->lastresult->next == NULL));
1384 :
1385 31790 : if (hdl->mid->trace)
1386 0 : printf("allocating new result set\n");
1387 : /* append a newly allocated struct to the end of the linked list */
1388 31790 : result = malloc(sizeof(*result));
1389 31790 : if (result == NULL)
1390 : return NULL;
1391 63580 : *result = (struct MapiResultSet) {
1392 : .hdl = hdl,
1393 : .tableid = -1,
1394 : .querytype = -1,
1395 : .last_id = -1,
1396 31790 : .cache.rowlimit = msetting_long(hdl->mid->settings, MP_REPLYSIZE),
1397 : .cache.reader = -1,
1398 : .commentonly = true,
1399 : };
1400 31790 : if (hdl->lastresult == NULL)
1401 31735 : hdl->result = hdl->lastresult = result;
1402 : else {
1403 55 : hdl->lastresult->next = result;
1404 55 : hdl->lastresult = result;
1405 : }
1406 :
1407 : return result;
1408 : }
1409 :
1410 : /* close a result set, discarding any unread results */
1411 : static MapiMsg
1412 31790 : close_result(MapiHdl hdl)
1413 : {
1414 31790 : struct MapiResultSet *result;
1415 31790 : Mapi mid;
1416 31790 : int i;
1417 :
1418 31790 : result = hdl->result;
1419 31790 : if (result == NULL)
1420 : return MERROR;
1421 31790 : mid = hdl->mid;
1422 31790 : assert(mid != NULL);
1423 31790 : if (mid->trace)
1424 0 : printf("closing result set\n");
1425 31790 : if (result->tableid >= 0 && result->querytype != Q_PREPARE) {
1426 28365 : if (mid->active &&
1427 2 : result->next == NULL &&
1428 2 : !mid->active->needmore &&
1429 1 : read_into_cache(mid->active, -1) != MOK)
1430 : return MERROR;
1431 28365 : assert(hdl->npending_close == 0 ||
1432 : (hdl->npending_close > 0 && hdl->pending_close != NULL));
1433 28365 : if (mid->active &&
1434 1 : (mid->active->active != result ||
1435 0 : result->cache.tuplecount < result->row_count)) {
1436 : /* results for which we got all tuples at the initial
1437 : * response, need not to be closed as the server already
1438 : * did that immediately */
1439 1 : if (result->row_count > result->tuple_count) {
1440 : /* can't write "X" commands now, so save for later */
1441 0 : REALLOC(hdl->pending_close, hdl->npending_close + 1);
1442 0 : hdl->pending_close[hdl->npending_close] = result->tableid;
1443 0 : hdl->npending_close++;
1444 : }
1445 28364 : } else if (mid->to != NULL) {
1446 : /* first close saved up to-be-closed tables */
1447 28364 : for (i = 0; i < hdl->npending_close; i++) {
1448 0 : char msg[256];
1449 :
1450 0 : snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
1451 0 : mapi_log_record(mid, "CMD", "%s", msg);
1452 0 : mid->active = hdl;
1453 0 : if (mnstr_printf(mid->to, "%s", msg) < 0 ||
1454 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
1455 0 : close_connection(mid);
1456 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
1457 0 : break;
1458 : }
1459 0 : read_into_cache(hdl, 0);
1460 : }
1461 28364 : hdl->npending_close = 0;
1462 28364 : if (hdl->pending_close)
1463 0 : free(hdl->pending_close);
1464 28364 : hdl->pending_close = NULL;
1465 28364 : if (mid->to != NULL && result->tuple_count < result->row_count) {
1466 11 : char msg[256];
1467 :
1468 11 : snprintf(msg, sizeof(msg), "Xclose %d\n", result->tableid);
1469 11 : mapi_log_record(mid, "CMD", "%s", msg);
1470 11 : mid->active = hdl;
1471 22 : if (mnstr_printf(mid->to, "%s", msg) < 0 ||
1472 11 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
1473 0 : close_connection(mid);
1474 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
1475 : } else
1476 11 : read_into_cache(hdl, 0);
1477 : }
1478 : }
1479 28365 : result->tableid = -1;
1480 : }
1481 31790 : if (mid->active == hdl &&
1482 3 : hdl->active == result &&
1483 0 : read_into_cache(hdl, -1) != MOK)
1484 : return MERROR;
1485 31790 : if( hdl->active == result)
1486 : return MERROR;
1487 : //assert(hdl->active != result);
1488 31790 : if (result->fields) {
1489 89315 : for (i = 0; i < result->maxfields; i++) {
1490 60633 : if (result->fields[i].tablename)
1491 60434 : free(result->fields[i].tablename);
1492 60633 : if (result->fields[i].columnname)
1493 60434 : free(result->fields[i].columnname);
1494 60633 : if (result->fields[i].columntype)
1495 60442 : free(result->fields[i].columntype);
1496 : }
1497 28682 : free(result->fields);
1498 : }
1499 31790 : result->fields = NULL;
1500 31790 : result->maxfields = result->fieldcnt = 0;
1501 31790 : if (result->cache.line) {
1502 174927 : for (i = 0; i < result->cache.writer; i++) {
1503 146244 : if (result->cache.line[i].rows)
1504 146244 : free(result->cache.line[i].rows);
1505 146244 : if (result->cache.line[i].anchors) {
1506 : int j;
1507 :
1508 92484 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
1509 80768 : if (result->cache.line[i].anchors[j]) {
1510 58968 : free(result->cache.line[i].anchors[j]);
1511 58968 : result->cache.line[i].anchors[j] = NULL;
1512 : }
1513 11716 : free(result->cache.line[i].anchors);
1514 : }
1515 146244 : if (result->cache.line[i].lens)
1516 11716 : free(result->cache.line[i].lens);
1517 : }
1518 28683 : free(result->cache.line);
1519 28683 : result->cache.line = NULL;
1520 28683 : result->cache.tuplecount = 0;
1521 : }
1522 31790 : if (result->errorstr && result->errorstr != mapi_nomem)
1523 66 : free(result->errorstr);
1524 31790 : result->errorstr = NULL;
1525 31790 : memset(result->sqlstate, 0, sizeof(result->sqlstate));
1526 31790 : result->hdl = NULL;
1527 31790 : hdl->result = result->next;
1528 31790 : if (hdl->result == NULL)
1529 31735 : hdl->lastresult = NULL;
1530 31790 : result->next = NULL;
1531 31790 : free(result);
1532 31790 : return MOK;
1533 : }
1534 :
1535 : static void
1536 76 : add_error(struct MapiResultSet *result, char *error)
1537 : {
1538 : /* concatenate the error messages */
1539 76 : size_t size = result->errorstr ? strlen(result->errorstr) : 0;
1540 :
1541 76 : if (strlen(error) > 6 && error[5] == '!' &&
1542 62 : (isdigit((unsigned char) error[0]) ||
1543 0 : (error[0] >= 'A' && error[0] <= 'Z')) &&
1544 62 : (isdigit((unsigned char) error[1]) ||
1545 1 : (error[1] >= 'A' && error[1] <= 'Z')) &&
1546 62 : (isdigit((unsigned char) error[2]) ||
1547 1 : (error[2] >= 'A' && error[2] <= 'Z')) &&
1548 62 : (isdigit((unsigned char) error[3]) ||
1549 0 : (error[3] >= 'A' && error[3] <= 'Z')) &&
1550 62 : (isdigit((unsigned char) error[4]) ||
1551 0 : (error[4] >= 'A' && error[4] <= 'Z'))) {
1552 62 : if (result->errorstr == NULL) {
1553 : /* remeber SQLSTATE for first error */
1554 60 : strcpy_len(result->sqlstate, error,
1555 : sizeof(result->sqlstate));
1556 : }
1557 : /* skip SQLSTATE */
1558 62 : error += 6;
1559 : }
1560 76 : REALLOC(result->errorstr, size + strlen(error) + 2);
1561 76 : if (result->errorstr == NULL)
1562 0 : result->errorstr = mapi_nomem;
1563 : else {
1564 76 : strcpy(result->errorstr + size, error);
1565 76 : strcat(result->errorstr + size, "\n");
1566 : }
1567 76 : }
1568 :
1569 : const char *
1570 10118 : mapi_result_error(MapiHdl hdl)
1571 : {
1572 10118 : return hdl && hdl->result ? hdl->result->errorstr : NULL;
1573 : }
1574 :
1575 : const char *
1576 1 : mapi_result_errorcode(MapiHdl hdl)
1577 : {
1578 1 : return hdl && hdl->result && hdl->result->sqlstate[0] ? hdl->result->sqlstate : NULL;
1579 : }
1580 :
1581 : /* Go to the next result set, if any, and close the current result
1582 : set. This function returns 1 if there are more result sets after
1583 : the one that was closed, otherwise, if more input is needed, return
1584 : MMORE, else, return MOK */
1585 : MapiMsg
1586 3840 : mapi_next_result(MapiHdl hdl)
1587 : {
1588 3840 : mapi_hdl_check(hdl);
1589 :
1590 7095 : while (hdl->result != NULL) {
1591 3310 : if (close_result(hdl) != MOK)
1592 : return MERROR;
1593 3310 : if (hdl->result &&
1594 55 : (hdl->result->querytype == -1 ||
1595 : /* basically exclude Q_PARSE and Q_BLOCK */
1596 : (hdl->result->querytype >= Q_TABLE &&
1597 0 : hdl->result->querytype <= Q_PREPARE) ||
1598 0 : hdl->result->errorstr != NULL))
1599 : return 1;
1600 : }
1601 3785 : return hdl->needmore ? MMORE : MOK;
1602 : }
1603 :
1604 : MapiMsg
1605 13 : mapi_needmore(MapiHdl hdl)
1606 : {
1607 13 : return hdl->needmore ? MMORE : MOK;
1608 : }
1609 :
1610 : bool
1611 2007 : mapi_more_results(MapiHdl hdl)
1612 : {
1613 2007 : struct MapiResultSet *result;
1614 :
1615 2007 : mapi_hdl_check(hdl);
1616 :
1617 2007 : if ((result = hdl->result) == 0) {
1618 : /* there are no results at all */
1619 : return false;
1620 : }
1621 1999 : if (result->querytype == Q_TABLE && hdl->mid->active == hdl) {
1622 : /* read until next result (if any) */
1623 0 : read_into_cache(hdl, -1);
1624 : }
1625 1999 : if (hdl->needmore) {
1626 : /* assume the application will provide more data and
1627 : that we will then have a result */
1628 : return true;
1629 : }
1630 1999 : while (result->next) {
1631 0 : result = result->next;
1632 0 : if (result->querytype == -1 ||
1633 : /* basically exclude Q_PARSE and Q_BLOCK */
1634 0 : (hdl->result->querytype >= Q_TABLE &&
1635 0 : hdl->result->querytype <= Q_PREPARE) ||
1636 0 : result->errorstr != NULL)
1637 : return true;
1638 : }
1639 : /* no more results */
1640 : return false;
1641 : }
1642 :
1643 : MapiHdl
1644 35539 : mapi_new_handle(Mapi mid)
1645 : {
1646 35539 : MapiHdl hdl;
1647 :
1648 35539 : mapi_check0(mid);
1649 :
1650 35538 : hdl = malloc(sizeof(*hdl));
1651 35538 : if (hdl == NULL) {
1652 0 : mapi_setError(mid, "Memory allocation failure", __func__, MERROR);
1653 0 : return NULL;
1654 : }
1655 : /* initialize and add to doubly-linked list */
1656 35538 : *hdl = (struct MapiStatement) {
1657 : .mid = mid,
1658 35538 : .next = mid->first,
1659 : };
1660 35538 : mid->first = hdl;
1661 35538 : if (hdl->next)
1662 4450 : hdl->next->prev = hdl;
1663 : return hdl;
1664 : }
1665 :
1666 : /* close all result sets on the handle but don't close the handle itself */
1667 : static MapiMsg
1668 70081 : finish_handle(MapiHdl hdl)
1669 : {
1670 70081 : Mapi mid;
1671 70081 : int i;
1672 :
1673 70081 : if (hdl == NULL)
1674 : return MERROR;
1675 70081 : mid = hdl->mid;
1676 70375 : if (mid->active == hdl && !hdl->needmore && !mnstr_eof(mid->from) &&
1677 294 : read_into_cache(hdl, 0) != MOK)
1678 : return MERROR;
1679 70081 : if (mid->to) {
1680 70080 : if (hdl->needmore) {
1681 0 : assert(mid->active == NULL || mid->active == hdl);
1682 0 : hdl->needmore = false;
1683 0 : mid->active = hdl;
1684 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
1685 0 : check_stream(mid, mid->to, "write error on stream", mid->error);
1686 0 : read_into_cache(hdl, 0);
1687 : }
1688 70080 : for (i = 0; i < hdl->npending_close; i++) {
1689 1 : char msg[256];
1690 :
1691 1 : snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
1692 1 : mapi_log_record(mid, "CMD", "%s", msg);
1693 1 : mid->active = hdl;
1694 1 : if (mnstr_printf(mid->to, "%s", msg) < 0 ||
1695 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
1696 0 : close_connection(mid);
1697 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
1698 0 : break;
1699 : }
1700 0 : read_into_cache(hdl, 0);
1701 : }
1702 : }
1703 70080 : hdl->npending_close = 0;
1704 70080 : if (hdl->pending_close)
1705 0 : free(hdl->pending_close);
1706 70080 : hdl->pending_close = NULL;
1707 168640 : while (hdl->result) {
1708 28477 : if (close_result(hdl) != MOK)
1709 : return MERROR;
1710 28480 : if (hdl->needmore) {
1711 0 : assert(mid->active == NULL || mid->active == hdl);
1712 0 : hdl->needmore = false;
1713 0 : mid->active = hdl;
1714 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
1715 0 : check_stream(mid, mid->to, "write error on stream", mid->error);
1716 0 : read_into_cache(hdl, 0);
1717 : }
1718 : }
1719 : return MOK;
1720 : }
1721 :
1722 : /* Close a statement handle, discarding any unread output. */
1723 : MapiMsg
1724 35539 : mapi_close_handle(MapiHdl hdl)
1725 : {
1726 35539 : if (hdl == NULL)
1727 : return MOK;
1728 35539 : debugprint("entering %s\n", "mapi_close_handle");
1729 :
1730 : /* don't use mapi_check_hdl: it's ok if we're not connected */
1731 35539 : mapi_clrError(hdl->mid);
1732 :
1733 35535 : (void) finish_handle(hdl);
1734 35539 : hdl->npending_close = 0;
1735 35539 : if (hdl->pending_close)
1736 0 : free(hdl->pending_close);
1737 35539 : hdl->pending_close = NULL;
1738 35539 : if (hdl->bindings)
1739 0 : free(hdl->bindings);
1740 35539 : hdl->bindings = NULL;
1741 35539 : hdl->maxbindings = 0;
1742 35539 : if (hdl->params)
1743 0 : free(hdl->params);
1744 35539 : hdl->params = NULL;
1745 35539 : hdl->maxparams = 0;
1746 35539 : if (hdl->query)
1747 33821 : free(hdl->query);
1748 35539 : hdl->query = NULL;
1749 35539 : if (hdl->template)
1750 0 : free(hdl->template);
1751 35539 : hdl->template = NULL;
1752 : /* remove from doubly-linked list */
1753 35539 : if (hdl->prev)
1754 1 : hdl->prev->next = hdl->next;
1755 35539 : if (hdl->next)
1756 4449 : hdl->next->prev = hdl->prev;
1757 35539 : if (hdl->mid->first == hdl)
1758 35540 : hdl->mid->first = hdl->next;
1759 35539 : hdl->prev = NULL;
1760 35539 : hdl->next = NULL;
1761 35539 : hdl->mid = NULL;
1762 35539 : free(hdl);
1763 35539 : return MOK;
1764 : }
1765 :
1766 : const struct MapiStruct MapiStructDefaults = {
1767 : .error = MOK,
1768 : .redirmax = 10,
1769 : .blk.eos = false,
1770 : .blk.lim = BLOCK,
1771 : };
1772 :
1773 : /* Allocate a new connection handle. */
1774 : Mapi
1775 392 : mapi_new(msettings *settings)
1776 : {
1777 392 : Mapi mid;
1778 392 : static ATOMIC_TYPE index = ATOMIC_VAR_INIT(0);
1779 :
1780 392 : mid = malloc(sizeof(*mid));
1781 392 : if (mid == NULL)
1782 : return NULL;
1783 392 : if (settings == NULL)
1784 393 : settings = msettings_create();
1785 392 : if (settings == NULL) {
1786 0 : free(mid);
1787 0 : return NULL;
1788 : }
1789 :
1790 : /* then fill in some details */
1791 391 : *mid = MapiStructDefaults;
1792 391 : mid->settings = settings;
1793 391 : mid->index = (uint32_t) ATOMIC_ADD(&index, 1); /* for distinctions in log records */
1794 391 : if ((mid->blk.buf = malloc(mid->blk.lim + 1)) == NULL) {
1795 0 : mapi_destroy(mid);
1796 0 : return NULL;
1797 : }
1798 391 : mid->blk.buf[0] = 0;
1799 391 : mid->blk.buf[mid->blk.lim] = 0;
1800 :
1801 : /* also the current timezone, seconds EAST of UTC */
1802 391 : long time_zone;
1803 : #if defined(_MSC_VER)
1804 : DYNAMIC_TIME_ZONE_INFORMATION tzinf;
1805 :
1806 : /* documentation says: UTC = localtime + Bias (in minutes),
1807 : * but experimentation during DST period says, UTC = localtime
1808 : * + Bias + DaylightBias, and presumably during non DST
1809 : * period, UTC = localtime + Bias */
1810 : switch (GetDynamicTimeZoneInformation(&tzinf)) {
1811 : case TIME_ZONE_ID_STANDARD: /* using standard time */
1812 : case TIME_ZONE_ID_UNKNOWN: /* no daylight saving time in this zone */
1813 : time_zone = -(int) tzinf.Bias * 60;
1814 : break;
1815 : case TIME_ZONE_ID_DAYLIGHT: /* using daylight saving time */
1816 : time_zone = -(int) (tzinf.Bias + tzinf.DaylightBias) * 60;
1817 : break;
1818 : default: /* aka TIME_ZONE_ID_INVALID */
1819 : /* call failed, we don't know the time zone */
1820 : time_zone = 0;
1821 : break;
1822 : }
1823 : #else
1824 391 : time_t t = time(NULL);
1825 392 : struct tm *local_tm = localtime_r(&t, &(struct tm){0});
1826 : #ifdef HAVE_TM_GMTOFF
1827 393 : time_zone = local_tm->tm_gmtoff;
1828 : #else
1829 : struct tm *gm_tm = gmtime_r(&t, &(struct tm){0});
1830 : time_t gt = mktime(gm_tm);
1831 : local_tm->tm_isdst=0; /* We need the difference without dst */
1832 : time_t lt = mktime(local_tm);
1833 : assert((int64_t) gt - (int64_t) lt >= (int64_t) INT_MIN && (int64_t) gt - (int64_t) lt <= (int64_t) INT_MAX);
1834 : time_zone = (long) (lt - gt);
1835 : #endif
1836 : #endif
1837 393 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
1838 393 : if (err)
1839 0 : mapi_setError(mid, err, __func__, MERROR);
1840 :
1841 : return mid;
1842 : }
1843 :
1844 : /* construct the uri field of a Mapi struct */
1845 : void
1846 202 : set_uri(Mapi mid)
1847 : {
1848 202 : const char *host = msetting_string(mid->settings, MP_HOST);
1849 200 : const char *database = msetting_string(mid->settings, MP_DATABASE);
1850 201 : int port = msetting_long(mid->settings, MP_PORT);
1851 202 : size_t urilen = strlen(host) + (database ? strlen(database) : 0) + 32;
1852 202 : char *uri = malloc(urilen);
1853 :
1854 : /* uri looks as follows:
1855 : * mapi:monetdb://host:port/database
1856 : * or
1857 : * mapi:monetdb:///some/path/to?database=database
1858 : */
1859 :
1860 202 : if (database != NULL) {
1861 202 : if (host[0] == '/') {
1862 0 : snprintf(uri, urilen, "mapi:monetdb://%s?database=%s",
1863 : host, database);
1864 : } else {
1865 202 : snprintf(uri, urilen, "mapi:monetdb://%s:%d/%s",
1866 : host, port, database);
1867 : }
1868 : } else {
1869 0 : if (host[0] == '/') {
1870 0 : snprintf(uri, urilen, "mapi:monetdb://%s",
1871 : host);
1872 : } else {
1873 0 : snprintf(uri, urilen, "mapi:monetdb://%s:%d",
1874 : host, port);
1875 : }
1876 : }
1877 :
1878 202 : if (mid->uri != NULL)
1879 0 : free(mid->uri);
1880 202 : mid->uri = uri;
1881 202 : }
1882 :
1883 : Mapi
1884 197 : mapi_mapiuri(const char *url, const char *user, const char *pass, const char *lang)
1885 : {
1886 197 : Mapi mid;
1887 :
1888 197 : if (!ATOMIC_TAS(&mapi_initialized)) {
1889 41 : if (mnstr_init() < 0)
1890 : return NULL;
1891 : }
1892 :
1893 197 : mid = mapi_new(NULL);
1894 202 : if (mid == NULL)
1895 : return NULL;
1896 :
1897 202 : if (url == NULL) {
1898 0 : mapi_setError(mid, "url is null", __func__, MERROR);
1899 0 : return mid;
1900 : }
1901 202 : if (user == NULL) {
1902 0 : mapi_setError(mid, "user is null", __func__, MERROR);
1903 0 : return mid;
1904 : }
1905 202 : if (pass == NULL) {
1906 0 : mapi_setError(mid, "pass is null", __func__, MERROR);
1907 0 : return mid;
1908 : }
1909 202 : if (lang == NULL) {
1910 0 : mapi_setError(mid, "lang is null", __func__, MERROR);
1911 0 : return mid;
1912 : }
1913 :
1914 202 : msettings_error err = NULL;
1915 202 : if ( (err = msetting_set_string(mid->settings, MP_USER, user))
1916 202 : || (err = msetting_set_string(mid->settings, MP_PASSWORD, pass))
1917 202 : || (err = msetting_set_string(mid->settings, MP_LANGUAGE, lang))
1918 : ) {
1919 : // in the old implementation we returned NULL but that doesn't
1920 : // make sense to me because above we already started returning
1921 : // mid with the error set.
1922 0 : mapi_setError(mid, err, __func__, MERROR);
1923 0 : return mid;
1924 : }
1925 :
1926 202 : char *error_message = NULL;
1927 202 : if (!msettings_parse_url(mid->settings, url, &error_message)) {
1928 0 : char *msg = error_message ? error_message : "malloc failed";
1929 0 : mapi_setError(mid, msg, __func__, MERROR);
1930 0 : free(error_message);
1931 0 : return mid;
1932 : }
1933 :
1934 201 : set_uri(mid);
1935 :
1936 201 : return mid;
1937 : }
1938 :
1939 : /* Allocate a new connection handle and fill in the information needed
1940 : to connect to a server, but don't connect yet. */
1941 : Mapi
1942 191 : mapi_mapi(const char *host, int port, const char *username,
1943 : const char *password, const char *lang, const char *dbname)
1944 : {
1945 191 : Mapi mid;
1946 :
1947 191 : if (!ATOMIC_TAS(&mapi_initialized)) {
1948 186 : if (mnstr_init() < 0)
1949 : return NULL;
1950 : }
1951 :
1952 191 : mid = mapi_new(NULL);
1953 191 : if (mid == NULL)
1954 : return NULL;
1955 191 : msettings *settings = mid->settings;
1956 :
1957 191 : if (lang == NULL)
1958 0 : lang = "sql";
1959 :
1960 191 : const char *sockdir = NULL;
1961 191 : if (host && host[0] == '/') {
1962 : sockdir = host;
1963 : host = NULL;
1964 : }
1965 :
1966 126 : msettings_error err = NULL;
1967 317 : do {
1968 126 : if (host && (err = msetting_set_string(settings, MP_HOST, host)))
1969 : break;
1970 191 : if (sockdir && (err = msetting_set_string(settings, MP_SOCKDIR, sockdir)))
1971 : break;
1972 191 : if (username && (err = msetting_set_string(settings, MP_USER, username)))
1973 : break;
1974 191 : if (password && (err = msetting_set_string(settings, MP_PASSWORD, password)))
1975 : break;
1976 191 : if (lang && (err = msetting_set_string(settings, MP_LANGUAGE, lang)))
1977 : break;
1978 191 : if (dbname && (err = msetting_set_string(settings, MP_DATABASE, dbname)))
1979 : break;
1980 191 : if (port > 0 && (err = msetting_set_long(settings, MP_PORT, port)))
1981 : break;
1982 : } while (0);
1983 191 : if (err) {
1984 0 : mapi_setError(mid, err, __func__, MERROR);
1985 0 : return mid;
1986 : }
1987 :
1988 : return mid;
1989 : }
1990 :
1991 : Mapi
1992 0 : mapi_settings(msettings *settings)
1993 : {
1994 0 : assert(settings);
1995 0 : Mapi mid = mapi_new(settings);
1996 0 : if (mid == NULL)
1997 : return mid;
1998 :
1999 0 : set_uri(mid);
2000 0 : return mid;
2001 : }
2002 :
2003 :
2004 : /* Close a connection and free all memory associated with the
2005 : connection handle. */
2006 : MapiMsg
2007 372 : mapi_destroy(Mapi mid)
2008 : {
2009 372 : char **r;
2010 :
2011 372 : mapi_clrError(mid);
2012 :
2013 373 : while (mid->first)
2014 1 : mapi_close_handle(mid->first);
2015 372 : if (mid->connected)
2016 173 : (void) mapi_disconnect(mid);
2017 372 : if (mid->tracelog)
2018 0 : close_stream(mid->tracelog);
2019 :
2020 372 : free(mid->blk.buf);
2021 372 : free(mid->motd);
2022 372 : free(mid->server);
2023 372 : free(mid->uri);
2024 372 : free(mid->tracebuffer);
2025 372 : free(mid->noexplain);
2026 372 : if (mid->errorstr && mid->errorstr != mapi_nomem)
2027 0 : free(mid->errorstr);
2028 :
2029 372 : msettings_destroy(mid->settings);
2030 :
2031 372 : r = mid->redirects;
2032 372 : while (*r) {
2033 0 : free(*r);
2034 0 : r++;
2035 : }
2036 :
2037 372 : free(mid);
2038 372 : return MOK;
2039 : }
2040 :
2041 : /* Create a connection handle and connect to the server using the
2042 : specified parameters. */
2043 : Mapi
2044 11 : mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
2045 : {
2046 11 : Mapi mid;
2047 :
2048 11 : mid = mapi_mapi(host, port, username, password, lang, dbname);
2049 11 : if (mid && mid->error == MOK)
2050 11 : mapi_reconnect(mid); /* actually, initial connect */
2051 11 : return mid;
2052 : }
2053 :
2054 : /* Returns an malloced NULL-terminated array with redirects */
2055 : char **
2056 0 : mapi_resolve(const char *host, int port, const char *pattern)
2057 : {
2058 0 : int rmax;
2059 0 : Mapi mid;
2060 :
2061 : /* if it doesn't make sense, don't try to crash */
2062 0 : if (pattern == NULL)
2063 : return NULL;
2064 :
2065 0 : mid = mapi_mapi(host, port, "mero", "mero", "resolve", pattern);
2066 0 : if (mid) {
2067 0 : if (mid->error == MOK) {
2068 0 : rmax = mid->redirmax;
2069 0 : mid->redirmax = 0;
2070 0 : mapi_reconnect(mid); /* real connect, don't follow redirects */
2071 0 : mid->redirmax = rmax;
2072 0 : if (mid->error == MOK) {
2073 0 : close_connection(mid); /* we didn't expect a connection actually */
2074 : } else {
2075 0 : char **ret = malloc(sizeof(char *) * MAXREDIR);
2076 0 : memcpy(ret, mid->redirects, sizeof(char *) * MAXREDIR);
2077 0 : mid->redirects[0] = NULL; /* make sure the members aren't freed */
2078 0 : mapi_destroy(mid);
2079 0 : return ret;
2080 : }
2081 : }
2082 0 : mapi_destroy(mid);
2083 : }
2084 : return NULL;
2085 : }
2086 :
2087 : void
2088 1376 : close_connection(Mapi mid)
2089 : {
2090 1376 : MapiHdl hdl;
2091 1376 : struct MapiResultSet *result;
2092 :
2093 1376 : mid->connected = false;
2094 1376 : mid->active = NULL;
2095 1377 : for (hdl = mid->first; hdl; hdl = hdl->next) {
2096 1 : hdl->active = NULL;
2097 1 : for (result = hdl->result; result; result = result->next)
2098 0 : result->tableid = -1;
2099 : }
2100 : /* finish channels */
2101 : /* Make sure that the write- (to-) stream is closed first,
2102 : * as the related read- (from-) stream closes the shared
2103 : * socket; see also src/common/stream.c:socket_close .
2104 : */
2105 1376 : if (mid->to) {
2106 1376 : close_stream(mid->to);
2107 1376 : mid->to = 0;
2108 : }
2109 1376 : if (mid->from) {
2110 1376 : close_stream(mid->from);
2111 1376 : mid->from = 0;
2112 : }
2113 1376 : mid->redircnt = 0;
2114 1376 : mapi_log_record(mid, "C", "Connection closed");
2115 1376 : }
2116 :
2117 : MapiMsg
2118 1366 : mapi_disconnect(Mapi mid)
2119 : {
2120 1366 : mapi_check(mid);
2121 :
2122 1366 : close_connection(mid);
2123 1366 : return MOK;
2124 : }
2125 :
2126 : /* Set callback function to retrieve or send file content for COPY
2127 : * INTO queries.
2128 : *
2129 : * char *getfile(void *private, const char *filename, bool binary,
2130 : * uint64_6 offset, size_t *size);
2131 : * Retrieve data from a file.
2132 : *
2133 : * The arguments are:
2134 : * private - the value of the filecontentprivate argument to
2135 : * mapi_setfilecallback;
2136 : * filename - the file to read (the application is free to interpret
2137 : * this any way it wants, including getting data over the
2138 : * Internet);
2139 : * binary - if set, the file is expected to contain binary data and
2140 : * should therefore be opened in binary mode, otherwise the
2141 : * file is expected to contain data in the UTF-8 encoding (of
2142 : * course, the application is free to transparently convert
2143 : * from the actual encoding to UTF-8);
2144 : * offset - the line number of the first line to be retrieved (this is
2145 : * one-based, i.e. the start of the file has line number one;
2146 : * lines are terminated by '\n');
2147 : * size - pointer in which to return the size of the chunk that is
2148 : * being returned.
2149 : *
2150 : * The callback function is expected to return data in chunks until it
2151 : * indicates to the caller that there is no more data or an error has
2152 : * occurred. Chunks can be any size. The caller does not modify or
2153 : * free the data returned. The size of the chunk being returned is
2154 : * stored in the size argument. Errors are indicated by returning a
2155 : * string containing an error message and setting *size to zero. The
2156 : * error message should not contain any newlines. Any call to the
2157 : * callback function is allowed to return an error.
2158 : *
2159 : * The first call to the callback function contains values for
2160 : * filename, binary, and offset. These parameters are all 0 for all
2161 : * subsequent calls for continuation data from the same file.
2162 : *
2163 : * If the caller has retrieved enough data before the file is
2164 : * exhausted, it calls the callback function one more time with a NULL
2165 : * pointer for the size argument. This gives the callback function
2166 : * the opportunity to free its resources (e.g. close the file).
2167 : *
2168 : * If there is no more data to be returned, the callback function
2169 : * returns a NULL pointer and sets *size to zero. No more calls for
2170 : * the current file will be made.
2171 : *
2172 : * Note that if the file to be read is empty, or contains fewer lines
2173 : * than the requested offset, the first call to the callback function
2174 : * may return NULL.
2175 : *
2176 : * char *putfile(void *private, const char *filename, bool binary,
2177 : * const void *data, size_t size);
2178 : * Send data to a file.
2179 : *
2180 : * The arguments are:
2181 : * private - the value of the filecontentprivate argument to
2182 : * mapi_setfilecallback;
2183 : * filename - the file to be written;
2184 : * binary - if set, the data to be written is binary and the file
2185 : * should therefore be opened in binary mode, otherwise the
2186 : * data is UTF-8 encoded text;
2187 : * data - the data to be written;
2188 : * size - the size of the data to be written.
2189 : *
2190 : * The callback is called multiple time to write a single file. The
2191 : * first time, a filename is specified, all subsequent times, the
2192 : * filename argument is NULL. When all data has been written, the
2193 : * callback function is called one last time with NULL pointer for the
2194 : * data argument so that the callback function can free any resources.
2195 : *
2196 : * When an error occurs, the callback function returns a string
2197 : * containing an error message after which the callback will not be
2198 : * called again for the same file. Otherwise, the callback function
2199 : * returns NULL.
2200 : *
2201 : * Note also that multibyte sequences may be split over two calls.
2202 : */
2203 : void
2204 145 : mapi_setfilecallback2(Mapi mid,
2205 : char *(*getfilecontent)(void *,
2206 : const char *, bool,
2207 : uint64_t, size_t *),
2208 : char *(*putfilecontent)(void *,
2209 : const char *, bool,
2210 : const void *, size_t),
2211 : void *filecontentprivate)
2212 : {
2213 145 : mid->getfilecontent = getfilecontent;
2214 145 : mid->putfilecontent = putfilecontent;
2215 145 : mid->filecontentprivate = filecontentprivate;
2216 145 : mid->putfilecontent_old = NULL;
2217 145 : mid->filecontentprivate_old = NULL;
2218 145 : }
2219 :
2220 : static char *
2221 0 : putfilecontent_wrap(void *priv, const char *filename, bool binary, const void *data, size_t size)
2222 : {
2223 0 : Mapi mid = priv;
2224 0 : void *priv_old = mid->filecontentprivate_old;
2225 0 : if (filename && binary)
2226 : return "Client does not support writing binary files";
2227 0 : return mid->putfilecontent_old(priv_old, filename, data, size);
2228 : }
2229 :
2230 : /* DEPRECATED. Set callback function to retrieve or send file content for COPY
2231 : * INTO queries.
2232 : *
2233 : * Deprecated because it does not support binary downloads.
2234 : * Use mapi_setfilecallback2 instead.
2235 : */
2236 : void
2237 0 : mapi_setfilecallback(Mapi mid,
2238 : char *(*getfilecontent)(void *,
2239 : const char *, bool,
2240 : uint64_t, size_t *),
2241 : char *(*putfilecontent)(void *,
2242 : const char *,
2243 : const void *, size_t),
2244 : void *filecontentprivate)
2245 : {
2246 0 : mid->getfilecontent = getfilecontent;
2247 0 : mid->putfilecontent = putfilecontent_wrap;
2248 0 : mid->filecontentprivate = mid;
2249 0 : mid->putfilecontent_old = putfilecontent;
2250 0 : mid->filecontentprivate_old = filecontentprivate;
2251 0 : }
2252 :
2253 : #define testBinding(hdl,fnr) \
2254 : do { \
2255 : mapi_hdl_check(hdl); \
2256 : if (fnr < 0) { \
2257 : return mapi_setError(hdl->mid, \
2258 : "Illegal field number", \
2259 : __func__, MERROR); \
2260 : } \
2261 : /* make sure there is enough space */ \
2262 : if (fnr >= hdl->maxbindings) \
2263 : mapi_extend_bindings(hdl, fnr); \
2264 : } while (0)
2265 :
2266 : #define testParam(hdl, fnr) \
2267 : do { \
2268 : mapi_hdl_check(hdl); \
2269 : if (fnr < 0) { \
2270 : return mapi_setError(hdl->mid, \
2271 : "Illegal param number", \
2272 : __func__, MERROR); \
2273 : } \
2274 : if (fnr >= hdl->maxparams) \
2275 : mapi_extend_params(hdl, fnr); \
2276 : } while (0)
2277 :
2278 : MapiMsg
2279 0 : mapi_bind(MapiHdl hdl, int fnr, char **ptr)
2280 : {
2281 0 : testBinding(hdl, fnr);
2282 0 : hdl->bindings[fnr].outparam = ptr;
2283 :
2284 0 : hdl->bindings[fnr].outtype = MAPI_AUTO;
2285 0 : return MOK;
2286 : }
2287 :
2288 : MapiMsg
2289 0 : mapi_bind_var(MapiHdl hdl, int fnr, int type, void *ptr)
2290 : {
2291 0 : testBinding(hdl, fnr);
2292 0 : hdl->bindings[fnr].outparam = ptr;
2293 :
2294 0 : if (type >= 0 && type < MAPI_NUMERIC)
2295 0 : hdl->bindings[fnr].outtype = type;
2296 : else
2297 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2298 0 : return MOK;
2299 : }
2300 :
2301 : MapiMsg
2302 0 : mapi_bind_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2303 : {
2304 0 : if (mapi_bind_var(hdl, fnr, MAPI_NUMERIC, ptr))
2305 0 : return hdl->mid->error;
2306 :
2307 0 : hdl->bindings[fnr].scale = scale;
2308 0 : hdl->bindings[fnr].precision = prec;
2309 0 : return MOK;
2310 : }
2311 :
2312 : MapiMsg
2313 0 : mapi_clear_bindings(MapiHdl hdl)
2314 : {
2315 0 : mapi_hdl_check(hdl);
2316 0 : if (hdl->bindings)
2317 0 : memset(hdl->bindings, 0, hdl->maxbindings * sizeof(*hdl->bindings));
2318 : return MOK;
2319 : }
2320 :
2321 : MapiMsg
2322 0 : mapi_param_type(MapiHdl hdl, int fnr, int ctype, int sqltype, void *ptr)
2323 : {
2324 0 : testParam(hdl, fnr);
2325 0 : hdl->params[fnr].inparam = ptr;
2326 :
2327 0 : if (ctype >= 0 && ctype < MAPI_NUMERIC)
2328 0 : hdl->params[fnr].intype = ctype;
2329 : else
2330 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2331 0 : hdl->params[fnr].sizeptr = NULL;
2332 0 : hdl->params[fnr].outtype = sqltype;
2333 0 : hdl->params[fnr].scale = 0;
2334 0 : hdl->params[fnr].precision = 0;
2335 0 : return MOK;
2336 : }
2337 :
2338 : MapiMsg
2339 0 : mapi_param_string(MapiHdl hdl, int fnr, int sqltype, char *ptr, int *sizeptr)
2340 : {
2341 0 : testParam(hdl, fnr);
2342 0 : hdl->params[fnr].inparam = (void *) ptr;
2343 :
2344 0 : hdl->params[fnr].intype = MAPI_VARCHAR;
2345 0 : hdl->params[fnr].sizeptr = sizeptr;
2346 0 : hdl->params[fnr].outtype = sqltype;
2347 0 : hdl->params[fnr].scale = 0;
2348 0 : hdl->params[fnr].precision = 0;
2349 0 : return MOK;
2350 : }
2351 :
2352 : MapiMsg
2353 0 : mapi_param(MapiHdl hdl, int fnr, char **ptr)
2354 : {
2355 0 : return mapi_param_type(hdl, fnr, MAPI_AUTO, MAPI_AUTO, ptr);
2356 : }
2357 :
2358 : MapiMsg
2359 0 : mapi_param_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2360 : {
2361 0 : if (mapi_param_type(hdl, fnr, MAPI_NUMERIC, MAPI_NUMERIC, ptr))
2362 0 : return hdl->mid->error;
2363 :
2364 0 : hdl->params[fnr].scale = scale;
2365 0 : hdl->params[fnr].precision = prec;
2366 0 : return MOK;
2367 : }
2368 :
2369 : MapiMsg
2370 2 : mapi_clear_params(MapiHdl hdl)
2371 : {
2372 2 : mapi_hdl_check(hdl);
2373 2 : if (hdl->params)
2374 0 : memset(hdl->params, 0, hdl->maxparams * sizeof(*hdl->params));
2375 : return MOK;
2376 : }
2377 :
2378 : static MapiHdl
2379 32491 : prepareQuery(MapiHdl hdl, const char *cmd)
2380 : {
2381 32491 : if (hdl && cmd) {
2382 32494 : if (hdl->query)
2383 2098 : free(hdl->query);
2384 32494 : hdl->query = strdup(cmd);
2385 32494 : assert(hdl->query);
2386 32494 : if (hdl->template) {
2387 0 : free(hdl->template);
2388 0 : hdl->template = NULL;
2389 : }
2390 : }
2391 32491 : return hdl;
2392 : }
2393 :
2394 :
2395 : MapiMsg
2396 6 : mapi_set_timeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
2397 : {
2398 6 : mapi_check(mid);
2399 6 : if (mid->trace)
2400 0 : printf("Set timeout to %u\n", timeout);
2401 6 : mnstr_settimeout(mid->to, timeout, callback, callback_data);
2402 6 : mnstr_settimeout(mid->from, timeout, callback, callback_data);
2403 6 : return MOK;
2404 : }
2405 :
2406 : MapiMsg
2407 6 : mapi_timeout(Mapi mid, unsigned int timeout)
2408 : {
2409 6 : return mapi_set_timeout(mid, timeout, NULL, NULL);
2410 : }
2411 :
2412 : MapiMsg
2413 10 : mapi_Xcommand(Mapi mid, const char *cmdname, const char *cmdvalue)
2414 : {
2415 10 : MapiHdl hdl;
2416 :
2417 10 : mapi_check(mid);
2418 10 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
2419 : return MERROR;
2420 20 : if (mnstr_printf(mid->to, "X" "%s %s\n", cmdname, cmdvalue) < 0 ||
2421 10 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
2422 0 : close_connection(mid);
2423 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
2424 0 : return MERROR;
2425 : }
2426 10 : mapi_log_record(mid, "X", "X" "%s %s\n", cmdname, cmdvalue);
2427 10 : hdl = prepareQuery(mapi_new_handle(mid), "Xcommand");
2428 10 : if (hdl == NULL)
2429 : return MERROR;
2430 10 : mid->active = hdl;
2431 10 : read_into_cache(hdl, 0);
2432 10 : mapi_close_handle(hdl); /* reads away any output */
2433 10 : return MOK;
2434 : }
2435 :
2436 : MapiMsg
2437 0 : mapi_prepare_handle(MapiHdl hdl, const char *cmd)
2438 : {
2439 0 : mapi_hdl_check(hdl);
2440 0 : if (finish_handle(hdl) != MOK)
2441 : return MERROR;
2442 0 : prepareQuery(hdl, cmd);
2443 0 : hdl->template = strdup(hdl->query);
2444 0 : assert(hdl->template);
2445 0 : return hdl->mid->error;
2446 : }
2447 :
2448 : MapiHdl
2449 0 : mapi_prepare(Mapi mid, const char *cmd)
2450 : {
2451 0 : MapiHdl hdl;
2452 :
2453 0 : mapi_check0(mid);
2454 0 : hdl = mapi_new_handle(mid);
2455 0 : if (hdl == NULL)
2456 : return NULL;
2457 0 : mapi_prepare_handle(hdl, cmd);
2458 0 : return hdl;
2459 : }
2460 :
2461 : /*
2462 : * Building the query string using replacement of values requires
2463 : * some care to not overflow the space allocated.
2464 : */
2465 : #define checkSpace(len) \
2466 : do { \
2467 : /* note: k==strlen(hdl->query) */ \
2468 : if (k+len >= lim) { \
2469 : lim = k + len + MAPIBLKSIZE; \
2470 : char *q = realloc(hdl->query, lim); \
2471 : if (q == NULL) { \
2472 : free(hdl->query); \
2473 : hdl->query = NULL; \
2474 : return; \
2475 : } \
2476 : hdl->query = q; \
2477 : } \
2478 : } while (0)
2479 :
2480 : static void
2481 32437 : mapi_param_store(MapiHdl hdl)
2482 : {
2483 32437 : char *val, buf[MAPIBLKSIZE];
2484 32437 : char *p = hdl->template, *q;
2485 32437 : int i;
2486 32437 : size_t k;
2487 32437 : size_t lim;
2488 :
2489 32437 : if (hdl->template == 0)
2490 : return;
2491 :
2492 0 : lim = strlen(hdl->template) + MAPIBLKSIZE;
2493 0 : REALLOC(hdl->query, lim);
2494 0 : if (hdl->query == NULL)
2495 : return;
2496 0 : hdl->query[0] = 0;
2497 0 : k = 0;
2498 :
2499 0 : q = strchr(hdl->template, PLACEHOLDER);
2500 0 : i = 0;
2501 : /* loop invariant: k == strlen(hdl->query) */
2502 0 : while (q && i < hdl->maxparams) {
2503 0 : if (q > p && *(q - 1) == '\\') {
2504 0 : q = strchr(q + 1, PLACEHOLDER);
2505 0 : continue;
2506 : }
2507 :
2508 0 : if (k + (q - p) >= lim) {
2509 0 : lim += MAPIBLKSIZE;
2510 0 : REALLOC(hdl->query, lim);
2511 0 : if (hdl->query == NULL)
2512 : return;
2513 : }
2514 0 : memcpy(hdl->query + k, p, q - p);
2515 0 : k += q - p;
2516 0 : hdl->query[k] = 0;
2517 :
2518 0 : if (hdl->params[i].inparam == 0) {
2519 0 : char *nullstr = "NULL";
2520 0 : checkSpace(5);
2521 0 : if (msettings_lang_is_mal(hdl->mid->settings))
2522 0 : nullstr = "nil";
2523 0 : strcpy(hdl->query + k, nullstr);
2524 : } else {
2525 0 : void *src = hdl->params[i].inparam; /* abbrev */
2526 :
2527 0 : switch (hdl->params[i].intype) {
2528 0 : case MAPI_TINY:
2529 0 : checkSpace(5);
2530 0 : snprintf(hdl->query + k, lim - k, "%hhd", *(signed char *) src);
2531 0 : break;
2532 0 : case MAPI_UTINY:
2533 0 : checkSpace(5);
2534 0 : snprintf(hdl->query + k, lim - k, "%hhu", *(unsigned char *) src);
2535 0 : break;
2536 0 : case MAPI_SHORT:
2537 0 : checkSpace(10);
2538 0 : snprintf(hdl->query + k, lim - k, "%hd", *(short *) src);
2539 0 : break;
2540 0 : case MAPI_USHORT:
2541 0 : checkSpace(10);
2542 0 : snprintf(hdl->query + k, lim - k, "%hu", *(unsigned short *) src);
2543 0 : break;
2544 0 : case MAPI_INT:
2545 0 : checkSpace(20);
2546 0 : snprintf(hdl->query + k, lim - k, "%d", *(int *) src);
2547 0 : break;
2548 0 : case MAPI_UINT:
2549 0 : checkSpace(20);
2550 0 : snprintf(hdl->query + k, lim - k, "%u", *(unsigned int *) src);
2551 0 : break;
2552 0 : case MAPI_LONG:
2553 0 : checkSpace(20);
2554 0 : snprintf(hdl->query + k, lim - k, "%ld", *(long *) src);
2555 0 : break;
2556 0 : case MAPI_ULONG:
2557 0 : checkSpace(20);
2558 0 : snprintf(hdl->query + k, lim - k, "%lu", *(unsigned long *) src);
2559 0 : break;
2560 0 : case MAPI_LONGLONG:
2561 0 : checkSpace(30);
2562 0 : snprintf(hdl->query + k, lim - k, "%"PRId64, *(int64_t *) src);
2563 0 : break;
2564 0 : case MAPI_ULONGLONG:
2565 0 : checkSpace(30);
2566 0 : snprintf(hdl->query + k, lim - k, "%"PRIu64, *(uint64_t *) src);
2567 0 : break;
2568 0 : case MAPI_FLOAT:
2569 0 : checkSpace(30);
2570 0 : snprintf(hdl->query + k, lim - k, "%.9g", *(float *) src);
2571 0 : break;
2572 0 : case MAPI_DOUBLE:
2573 0 : checkSpace(30);
2574 0 : snprintf(hdl->query + k, lim - k, "%.17g", *(double *) src);
2575 0 : break;
2576 0 : case MAPI_DATE:
2577 0 : checkSpace(50);
2578 0 : snprintf(hdl->query + k, lim - k,
2579 : "DATE '%04hd-%02hu-%02hu'",
2580 0 : ((MapiDate *) src)->year,
2581 0 : ((MapiDate *) src)->month,
2582 0 : ((MapiDate *) src)->day);
2583 0 : break;
2584 0 : case MAPI_TIME:
2585 0 : checkSpace(60);
2586 0 : snprintf(hdl->query + k, lim - k,
2587 : "TIME '%02hu:%02hu:%02hu'",
2588 0 : ((MapiTime *) src)->hour,
2589 0 : ((MapiTime *) src)->minute,
2590 0 : ((MapiTime *) src)->second);
2591 0 : break;
2592 0 : case MAPI_DATETIME:
2593 0 : checkSpace(110);
2594 0 : snprintf(hdl->query + k, lim - k,
2595 : "TIMESTAMP '%04hd-%02hu-%02hu %02hu:%02hu:%02hu.%09u'",
2596 0 : ((MapiDateTime *) src)->year,
2597 0 : ((MapiDateTime *) src)->month,
2598 0 : ((MapiDateTime *) src)->day,
2599 0 : ((MapiDateTime *) src)->hour,
2600 0 : ((MapiDateTime *) src)->minute,
2601 0 : ((MapiDateTime *) src)->second,
2602 : ((MapiDateTime *) src)->fraction);
2603 0 : break;
2604 0 : case MAPI_CHAR:
2605 0 : buf[0] = *(char *) src;
2606 0 : buf[1] = 0;
2607 0 : val = mapi_quote(buf, 1);
2608 : /* note: k==strlen(hdl->query) */
2609 0 : if (k + strlen(val) + 3 >= lim) {
2610 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2611 0 : char *q = realloc(hdl->query, lim);
2612 0 : if (q == NULL) {
2613 0 : free(hdl->query);
2614 0 : hdl->query = NULL;
2615 0 : free(val);
2616 0 : return;
2617 : }
2618 0 : hdl->query = q;
2619 : }
2620 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2621 0 : free(val);
2622 0 : break;
2623 0 : case MAPI_VARCHAR:
2624 0 : val = mapi_quote((char *) src, hdl->params[i].sizeptr ? *hdl->params[i].sizeptr : -1);
2625 : /* note: k==strlen(hdl->query) */
2626 0 : if (k + strlen(val) + 3 >= lim) {
2627 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2628 0 : char *q = realloc(hdl->query, lim);
2629 0 : if (q == NULL) {
2630 0 : free(hdl->query);
2631 0 : hdl->query = NULL;
2632 0 : free(val);
2633 0 : return;
2634 : }
2635 0 : hdl->query = q;
2636 : }
2637 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2638 0 : free(val);
2639 0 : break;
2640 0 : default:
2641 0 : strcpy_len(hdl->query + k, src, lim - k);
2642 0 : break;
2643 : }
2644 : }
2645 0 : k += strlen(hdl->query + k);
2646 :
2647 0 : i++;
2648 0 : p = q + 1;
2649 0 : q = strchr(p, PLACEHOLDER);
2650 : }
2651 0 : checkSpace(strlen(p) + 1);
2652 0 : strcpy(hdl->query + k, p);
2653 0 : if (hdl->mid->trace)
2654 0 : printf("param_store: result=%s\n", hdl->query);
2655 : return;
2656 : }
2657 :
2658 : /* Read one more line from the input stream and return it. This
2659 : returns a pointer into the input buffer, so the data needs to be
2660 : copied if it is to be retained. */
2661 : static char *
2662 1763421 : read_line(Mapi mid)
2663 : {
2664 1763421 : char *reply;
2665 1763421 : char *nl;
2666 1763421 : char *s; /* from where to search for newline */
2667 :
2668 1763421 : if (mid->active == NULL)
2669 : return NULL;
2670 :
2671 : /* check if we need to read more blocks to get a new line */
2672 1763421 : mid->blk.eos = false;
2673 1763421 : s = mid->blk.buf + mid->blk.nxt;
2674 2072845 : while ((nl = strchr(s, '\n')) == NULL && !mid->blk.eos) {
2675 309415 : ssize_t len;
2676 :
2677 309415 : if (mid->blk.lim - mid->blk.end < BLOCK) {
2678 15991 : int len;
2679 :
2680 15991 : len = mid->blk.lim;
2681 15991 : if (mid->blk.nxt <= BLOCK) {
2682 : /* extend space */
2683 3766 : len += BLOCK;
2684 : }
2685 15991 : REALLOC(mid->blk.buf, len + 1);
2686 15991 : if (mid->blk.nxt > 0) {
2687 12632 : memmove(mid->blk.buf, mid->blk.buf + mid->blk.nxt, mid->blk.end - mid->blk.nxt + 1);
2688 12632 : mid->blk.end -= mid->blk.nxt;
2689 12632 : mid->blk.nxt = 0;
2690 : }
2691 15991 : mid->blk.lim = len;
2692 : }
2693 :
2694 309415 : s = mid->blk.buf + mid->blk.end;
2695 :
2696 : /* fetch one more block */
2697 309415 : if (mid->trace)
2698 0 : printf("fetch next block: start at:%d\n", mid->blk.end);
2699 309415 : for (;;) {
2700 309415 : len = mnstr_read(mid->from, mid->blk.buf + mid->blk.end, 1, BLOCK);
2701 309423 : if (len == -1 && mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
2702 0 : mnstr_clearerr(mid->from);
2703 0 : if (mid->oobintr && !mid->active->aborted) {
2704 0 : mid->active->aborted = true;
2705 0 : mnstr_putoob(mid->to, 1);
2706 : }
2707 : } else
2708 : break;
2709 : }
2710 309423 : check_stream(mid, mid->from, "Connection terminated during read line", (mid->blk.eos = true, (char *) 0));
2711 309425 : mapi_log_data(mid, "RECV", mid->blk.buf + mid->blk.end, len);
2712 309426 : mid->blk.buf[mid->blk.end + len] = 0;
2713 309426 : if (mid->trace) {
2714 0 : printf("got next block: length:%zd\n", len);
2715 0 : printf("text:%s\n", mid->blk.buf + mid->blk.end);
2716 : }
2717 309426 : if (len == 0) { /* add prompt */
2718 142740 : if (mnstr_eof(mid->from))
2719 : return NULL;
2720 142738 : if (mid->blk.end > mid->blk.nxt) {
2721 : /* add fake newline since newline was
2722 : * missing from server */
2723 5 : nl = mid->blk.buf + mid->blk.end;
2724 5 : *nl = '\n';
2725 5 : mid->blk.end++;
2726 : }
2727 142738 : len = 2;
2728 142738 : mid->blk.buf[mid->blk.end] = PROMPTBEG;
2729 142738 : mid->blk.buf[mid->blk.end + 1] = '\n';
2730 142738 : mid->blk.buf[mid->blk.end + 2] = 0;
2731 : }
2732 309424 : mid->blk.end += (int) len;
2733 : }
2734 1763430 : if (mid->trace) {
2735 0 : printf("got complete block: \n");
2736 0 : printf("text:%s\n", mid->blk.buf + mid->blk.nxt);
2737 : }
2738 :
2739 : /* we have a complete line in the buffer */
2740 1763430 : assert(nl);
2741 1763430 : *nl++ = 0;
2742 1763430 : reply = mid->blk.buf + mid->blk.nxt;
2743 1763430 : mid->blk.nxt = (int) (nl - mid->blk.buf);
2744 :
2745 1763430 : if (mid->trace)
2746 0 : printf("read_line:%s\n", reply);
2747 : return reply;
2748 : }
2749 :
2750 : /* set or unset the autocommit flag in the server */
2751 : MapiMsg
2752 1179 : mapi_setAutocommit(Mapi mid, bool autocommit)
2753 : {
2754 1179 : if (msetting_bool(mid->settings, MP_AUTOCOMMIT) == autocommit)
2755 : return MOK;
2756 8 : if (!msettings_lang_is_sql(mid->settings)) {
2757 0 : mapi_setError(mid, "autocommit only supported in SQL", __func__, MERROR);
2758 0 : return MERROR;
2759 : }
2760 8 : msettings_error err = msetting_set_bool(mid->settings, MP_AUTOCOMMIT, autocommit);
2761 8 : if (err)
2762 0 : return mapi_setError(mid, err, __func__, MERROR);
2763 8 : if (!mid->connected)
2764 : return MOK;
2765 8 : if (autocommit)
2766 1 : return mapi_Xcommand(mid, "auto_commit", "1");
2767 : else
2768 7 : return mapi_Xcommand(mid, "auto_commit", "0");
2769 : }
2770 :
2771 : MapiMsg
2772 24 : mapi_set_time_zone(Mapi mid, int time_zone)
2773 : {
2774 24 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
2775 24 : if (err)
2776 0 : return mapi_setError(mid, err, __func__, MERROR);
2777 24 : if (!mid->connected)
2778 : return MOK;
2779 :
2780 0 : char buf[100];
2781 0 : if (time_zone < 0)
2782 0 : snprintf(buf, sizeof(buf),
2783 : "SET TIME ZONE INTERVAL '-%02d:%02d' HOUR TO MINUTE",
2784 0 : -time_zone / 3600, (-time_zone % 3600) / 60);
2785 : else
2786 0 : snprintf(buf, sizeof(buf),
2787 : "SET TIME ZONE INTERVAL '+%02d:%02d' HOUR TO MINUTE",
2788 0 : time_zone / 3600, (time_zone % 3600) / 60);
2789 :
2790 0 : MapiHdl hdl = mapi_query(mid, buf);
2791 0 : if (hdl == NULL)
2792 0 : return mid->error;
2793 0 : mapi_close_handle(hdl);
2794 :
2795 0 : return MOK;
2796 : }
2797 :
2798 : MapiMsg
2799 0 : mapi_set_columnar_protocol(Mapi mid, bool columnar_protocol)
2800 : {
2801 0 : if (mid->columnar_protocol == columnar_protocol)
2802 : return MOK;
2803 0 : mid->columnar_protocol = columnar_protocol;
2804 0 : if (!mid->connected)
2805 : return MOK;
2806 0 : if (columnar_protocol)
2807 0 : return mapi_Xcommand(mid, "columnar_protocol", "1");
2808 : else
2809 0 : return mapi_Xcommand(mid, "columnar_protocol", "0");
2810 : }
2811 :
2812 : MapiMsg
2813 172 : mapi_set_size_header(Mapi mid, bool value)
2814 : {
2815 172 : if (!msettings_lang_is_sql(mid->settings)) {
2816 0 : mapi_setError(mid, "size header only supported in SQL", __func__, MERROR);
2817 0 : return MERROR;
2818 : }
2819 172 : if (mid->sizeheader == value)
2820 : return MOK;
2821 15 : mid->sizeheader = value;
2822 15 : if (!mid->connected)
2823 : return MOK;
2824 0 : if (value)
2825 0 : return mapi_Xcommand(mid, "sizeheader", "1");
2826 : else
2827 0 : return mapi_Xcommand(mid, "sizeheader", "0");
2828 : }
2829 :
2830 : MapiMsg
2831 2 : mapi_release_id(Mapi mid, int id)
2832 : {
2833 2 : char buf[10];
2834 :
2835 2 : if (!msettings_lang_is_sql(mid->settings)) {
2836 0 : mapi_setError(mid, "release only supported in SQL", __func__, MERROR);
2837 0 : return MERROR;
2838 : }
2839 2 : snprintf(buf, sizeof(buf), "%d", id);
2840 2 : return mapi_Xcommand(mid, "release", buf);
2841 : }
2842 :
2843 : void
2844 169 : mapi_trace(Mapi mid, bool flag)
2845 : {
2846 169 : mapi_clrError(mid);
2847 169 : mid->trace = flag;
2848 169 : }
2849 :
2850 :
2851 : static int
2852 1462610 : slice_row(const char *reply, char *null, char ***anchorsp, size_t **lensp, int length, int endchar)
2853 : {
2854 : /* This function does the actual work for splicing a real,
2855 : multi-column row into columns. It skips over the first
2856 : character and ends at the end of the string or at endchar,
2857 : whichever comes first. */
2858 1462610 : char *start;
2859 1462610 : char **anchors;
2860 1462610 : int i;
2861 1462610 : size_t len;
2862 1462610 : size_t *lens;
2863 :
2864 1462610 : reply++; /* skip over initial char (usually '[') */
2865 1462610 : i = 0;
2866 1462610 : anchors = length == 0 ? NULL : malloc(length * sizeof(*anchors));
2867 1462420 : lens = length == 0 ? NULL : malloc(length * sizeof(*lens));
2868 4959996 : for (;;) {
2869 4959996 : if (i >= length) {
2870 17824 : length = i + 1;
2871 17824 : REALLOC(anchors, length);
2872 17824 : REALLOC(lens, length);
2873 : }
2874 4959996 : if (!unquote(reply, &start, &reply, endchar, &len) && null && strcmp(start, null) == 0) {
2875 : /* indicate NULL/nil with NULL pointer */
2876 440594 : free(start);
2877 440594 : start = NULL;
2878 440594 : len = 0;
2879 : }
2880 4959996 : lens[i] = len;
2881 4959996 : anchors[i++] = start;
2882 4959996 : if (reply == NULL)
2883 : break;
2884 4959996 : while (*reply && isspace((unsigned char) *reply))
2885 0 : reply++;
2886 4959996 : if (*reply == ',') {
2887 3497382 : reply++;
2888 6994764 : while (*reply && isspace((unsigned char) *reply))
2889 3497382 : reply++;
2890 1462614 : } else if (*reply == 0 || *reply == endchar)
2891 : break;
2892 : }
2893 1462610 : *anchorsp = anchors;
2894 1462610 : *lensp = lens;
2895 1462610 : return i;
2896 : }
2897 :
2898 : static MapiMsg
2899 14916 : mapi_cache_freeup_internal(struct MapiResultSet *result, int k)
2900 : {
2901 14916 : int i; /* just a counter */
2902 14916 : int64_t n = 0; /* # of tuples being deleted from front */
2903 :
2904 14916 : result->cache.tuplecount = 0;
2905 14916 : for (i = 0; i < result->cache.writer - k; i++) {
2906 0 : if (result->cache.line[i].rows) {
2907 0 : if (result->cache.line[i].rows[0] == '[' ||
2908 : result->cache.line[i].rows[0] == '=')
2909 0 : n++;
2910 0 : free(result->cache.line[i].rows);
2911 : }
2912 0 : result->cache.line[i].rows = result->cache.line[i + k].rows;
2913 0 : result->cache.line[i + k].rows = 0;
2914 0 : if (result->cache.line[i].anchors) {
2915 : int j = 0;
2916 :
2917 0 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2918 0 : free(result->cache.line[i].anchors[j]);
2919 0 : free(result->cache.line[i].anchors);
2920 : }
2921 0 : if (result->cache.line[i].lens)
2922 0 : free(result->cache.line[i].lens);
2923 0 : result->cache.line[i].anchors = result->cache.line[i + k].anchors;
2924 0 : result->cache.line[i + k].anchors = 0;
2925 0 : result->cache.line[i].lens = result->cache.line[i + k].lens;
2926 0 : result->cache.line[i + k].lens = 0;
2927 0 : result->cache.line[i].fldcnt = result->cache.line[i + k].fldcnt;
2928 0 : if (result->cache.line[i].rows &&
2929 0 : (result->cache.line[i].rows[0] == '[' ||
2930 : result->cache.line[i].rows[0] == '=')) {
2931 0 : result->cache.line[i].tuplerev = result->cache.tuplecount;
2932 0 : result->cache.line[result->cache.tuplecount++].tupleindex = i;
2933 : }
2934 : }
2935 : /* after the previous loop, i == result->cache.writer - k, and
2936 : the last (result->cache.writer - k) cache entries have been
2937 : cleared already , so we don't need to go the Full Monty
2938 : here */
2939 1352499 : for ( /*i = result->cache.writer - k */ ; i < k /*result->cache.writer */ ; i++) {
2940 1337583 : if (result->cache.line[i].rows) {
2941 1337583 : if (result->cache.line[i].rows[0] == '[' ||
2942 : result->cache.line[i].rows[0] == '=')
2943 1336867 : n++;
2944 1337583 : free(result->cache.line[i].rows);
2945 : }
2946 1337583 : result->cache.line[i].rows = 0;
2947 1337583 : if (result->cache.line[i].anchors) {
2948 : int j = 0;
2949 :
2950 5973431 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2951 4636564 : free(result->cache.line[i].anchors[j]);
2952 1336867 : free(result->cache.line[i].anchors);
2953 : }
2954 1337583 : if (result->cache.line[i].lens)
2955 1336867 : free(result->cache.line[i].lens);
2956 1337583 : result->cache.line[i].anchors = 0;
2957 1337583 : result->cache.line[i].lens = 0;
2958 1337583 : result->cache.line[i].fldcnt = 0;
2959 : }
2960 14916 : result->cache.reader -= k;
2961 14916 : if (result->cache.reader < 0)
2962 14916 : result->cache.reader = -1;
2963 14916 : result->cache.writer -= k;
2964 14916 : if (result->cache.writer < 0) /* "cannot happen" */
2965 0 : result->cache.writer = 0;
2966 14916 : result->cache.first += n;
2967 :
2968 14916 : return MOK;
2969 : }
2970 :
2971 : static void
2972 42036 : mapi_extend_cache(struct MapiResultSet *result, int cacheall)
2973 : {
2974 42036 : int incr, newsize, oldsize = result->cache.limit, i;
2975 :
2976 : /* if there are read entries, delete them */
2977 42036 : if (result->cache.reader >= 0) {
2978 13346 : mapi_cache_freeup_internal(result, result->cache.reader + 1);
2979 : /* since we've made space, we can return */
2980 13346 : return;
2981 : }
2982 :
2983 : /* extend row cache */
2984 28690 : retry:;
2985 28692 : if (oldsize == 0)
2986 : incr = 100;
2987 : else
2988 9 : incr = oldsize * 2;
2989 9 : if (incr > 200000)
2990 0 : incr = 20000;
2991 28692 : newsize = oldsize + incr;
2992 28692 : if (result->cache.rowlimit > 0 &&
2993 4 : newsize > result->cache.rowlimit &&
2994 : !cacheall) {
2995 4 : newsize = result->cache.rowlimit;
2996 4 : incr = newsize - oldsize;
2997 4 : if (incr <= 0) {
2998 : /* not enough space, so increase limit and try again */
2999 2 : result->cache.rowlimit += 100;
3000 2 : goto retry;
3001 : }
3002 : }
3003 :
3004 28690 : REALLOC(result->cache.line, newsize + 1);
3005 28690 : assert(result->cache.line);
3006 2928492 : for (i = oldsize; i <= newsize; i++) {
3007 2899802 : result->cache.line[i].fldcnt = 0;
3008 2899802 : result->cache.line[i].rows = NULL;
3009 2899802 : result->cache.line[i].tupleindex = -1;
3010 2899802 : result->cache.line[i].tuplerev = -1;
3011 2899802 : result->cache.line[i].anchors = NULL;
3012 2899802 : result->cache.line[i].lens = NULL;
3013 : }
3014 28690 : result->cache.limit = newsize;
3015 : }
3016 :
3017 : /* store a line in the cache */
3018 : static void
3019 1483827 : add_cache(struct MapiResultSet *result, char *line, int cacheall)
3020 : {
3021 : /* manage the row cache space first */
3022 1483827 : if (result->cache.writer >= result->cache.limit)
3023 42036 : mapi_extend_cache(result, cacheall);
3024 :
3025 1483827 : result->cache.line[result->cache.writer].rows = line;
3026 1483827 : result->cache.line[result->cache.writer].tuplerev = result->cache.tuplecount;
3027 1483827 : result->cache.line[result->cache.writer + 1].tuplerev = result->cache.tuplecount + 1;
3028 1483827 : if (*line == '[' || *line == '=') {
3029 1369751 : result->cache.line[result->cache.tuplecount++].tupleindex = result->cache.writer;
3030 1369751 : if (result->row_count < result->cache.first + result->cache.tuplecount)
3031 202 : result->row_count = result->cache.first + result->cache.tuplecount;
3032 : }
3033 1483827 : result->cache.writer++;
3034 1483827 : }
3035 :
3036 : static struct MapiResultSet *
3037 145616 : parse_header_line(MapiHdl hdl, char *line, struct MapiResultSet *result)
3038 : {
3039 145616 : char *tag, *etag;
3040 145616 : int i, n;
3041 145616 : char **anchors;
3042 145616 : size_t *lens;
3043 :
3044 145616 : if (line[0] == '&') {
3045 31540 : char *nline = line;
3046 31540 : int qt;
3047 31540 : uint64_t queryid;
3048 :
3049 : /* handle fields &qt */
3050 :
3051 31540 : nline++; /* query type */
3052 31540 : qt = (int) strtol(nline, &nline, 0);
3053 :
3054 31540 : if (result == NULL || (qt != Q_BLOCK && !result->commentonly))
3055 31521 : result = new_result(hdl);
3056 31540 : result->querytype = qt;
3057 31540 : result->commentonly = false;
3058 31540 : result->querytime = 0;
3059 31540 : result->maloptimizertime = 0;
3060 31540 : result->sqloptimizertime = 0;
3061 :
3062 31540 : nline++; /* skip space */
3063 31540 : switch (qt) {
3064 578 : case Q_SCHEMA:
3065 578 : result->querytime = strtoll(nline, &nline, 10);
3066 578 : result->maloptimizertime = strtoll(nline, &nline, 10);
3067 578 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3068 578 : break;
3069 228 : case Q_TRANS:
3070 228 : msetting_set_bool(hdl->mid->settings, MP_AUTOCOMMIT, *nline != 'f');
3071 228 : break;
3072 2227 : case Q_UPDATE:
3073 2227 : result->row_count = strtoll(nline, &nline, 10);
3074 2227 : result->last_id = strtoll(nline, &nline, 10);
3075 2227 : queryid = strtoll(nline, &nline, 10);
3076 2227 : result->querytime = strtoll(nline, &nline, 10);
3077 2227 : result->maloptimizertime = strtoll(nline, &nline, 10);
3078 2227 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3079 2227 : break;
3080 28365 : case Q_TABLE:
3081 28365 : if (sscanf(nline,
3082 : "%d %" SCNd64 " %d %" SCNd64 " %" SCNu64
3083 : " %" SCNd64 " %" SCNd64 " %" SCNd64,
3084 : &result->tableid, &result->row_count,
3085 : &result->fieldcnt, &result->tuple_count,
3086 : &queryid, &result->querytime,
3087 : &result->maloptimizertime,
3088 : &result->sqloptimizertime) < 8){
3089 2 : result->querytime = 0;
3090 2 : result->maloptimizertime = 0;
3091 2 : result->sqloptimizertime = 0;
3092 : }
3093 : (void) queryid; /* ignored for now */
3094 : break;
3095 123 : case Q_PREPARE:
3096 123 : sscanf(nline, "%d %" SCNd64 " %d %" SCNd64,
3097 : &result->tableid, &result->row_count,
3098 : &result->fieldcnt, &result->tuple_count);
3099 123 : break;
3100 19 : case Q_BLOCK:
3101 : /* Mapi ignores the Q_BLOCK header, so spoof
3102 : * the querytype back to a Q_TABLE to let it
3103 : * go unnoticed */
3104 19 : result->querytype = Q_TABLE;
3105 19 : break;
3106 : }
3107 :
3108 :
3109 31540 : if (result->fieldcnt > result->maxfields) {
3110 28488 : REALLOC(result->fields, result->fieldcnt);
3111 28488 : memset(result->fields + result->maxfields, 0, (result->fieldcnt - result->maxfields) * sizeof(*result->fields));
3112 28488 : result->maxfields = result->fieldcnt;
3113 : }
3114 :
3115 : /* start of new SQL result */
3116 31540 : return result;
3117 : }
3118 114076 : if (result == NULL)
3119 4 : result = new_result(hdl);
3120 :
3121 114076 : if (line[0] == '#' && !msettings_lang_is_mal(hdl->mid->settings)) {
3122 : /* comment */
3123 : return result;
3124 : }
3125 :
3126 114076 : line = strdup(line); /* make copy we can play with */
3127 114076 : etag = strrchr(line, '#');
3128 114076 : if (etag == 0 || etag == line) {
3129 : /* not a useful header line */
3130 0 : free(line);
3131 0 : return result;
3132 : }
3133 :
3134 114076 : n = slice_row(line, NULL, &anchors, &lens, 10, '#');
3135 :
3136 114076 : result->commentonly = false;
3137 :
3138 114076 : tag = etag + 1;
3139 228144 : while (*tag && isspace((unsigned char) *tag))
3140 114068 : tag++;
3141 :
3142 114076 : if (n > result->fieldcnt) {
3143 8 : result->fieldcnt = n;
3144 8 : if (n > result->maxfields) {
3145 8 : REALLOC(result->fields, n);
3146 8 : memset(result->fields + result->maxfields, 0, (n - result->maxfields) * sizeof(*result->fields));
3147 8 : result->maxfields = n;
3148 : }
3149 : }
3150 :
3151 114076 : if (strcmp(tag, "name") == 0) {
3152 28488 : result->fieldcnt = n;
3153 88922 : for (i = 0; i < n; i++) {
3154 60434 : if (anchors[i]) {
3155 60434 : if (result->fields[i].columnname)
3156 0 : free(result->fields[i].columnname);
3157 60434 : result->fields[i].columnname = anchors[i];
3158 60434 : anchors[i] = NULL;
3159 : }
3160 : }
3161 85588 : } else if (strcmp(tag, "type") == 0) {
3162 28492 : result->fieldcnt = n;
3163 88934 : for (i = 0; i < n; i++) {
3164 60442 : if (anchors[i]) {
3165 60442 : if (result->fields[i].columntype)
3166 0 : free(result->fields[i].columntype);
3167 60442 : result->fields[i].columntype = anchors[i];
3168 60442 : anchors[i] = NULL;
3169 : }
3170 : }
3171 57096 : } else if (strcmp(tag, "length") == 0) {
3172 28488 : result->fieldcnt = n;
3173 88922 : for (i = 0; i < n; i++) {
3174 60434 : if (anchors[i])
3175 60434 : result->fields[i].columnlength = atoi(anchors[i]);
3176 : }
3177 28608 : } else if (strcmp(tag, "table_name") == 0) {
3178 28488 : result->fieldcnt = n;
3179 88922 : for (i = 0; i < n; i++) {
3180 60434 : if (anchors[i]) {
3181 60434 : if (result->fields[i].tablename)
3182 0 : free(result->fields[i].tablename);
3183 60434 : result->fields[i].tablename = anchors[i];
3184 60434 : anchors[i] = NULL;
3185 : }
3186 : }
3187 120 : } else if (strcmp(tag, "typesizes") == 0) {
3188 112 : result->fieldcnt = n;
3189 1073 : for (i = 0; i < n; i++) {
3190 961 : if (anchors[i]) {
3191 961 : char *p;
3192 961 : result->fields[i].digits = atoi(anchors[i]);
3193 961 : p = strchr(anchors[i], ' ');
3194 961 : if (p)
3195 961 : result->fields[i].scale = atoi(p + 1);
3196 : }
3197 : }
3198 : }
3199 :
3200 : /* clean up */
3201 114076 : free(line);
3202 356789 : for (i = 0; i < n; i++)
3203 242713 : if (anchors[i])
3204 61403 : free(anchors[i]);
3205 114076 : free(anchors);
3206 114076 : free(lens);
3207 :
3208 114076 : return result;
3209 : }
3210 :
3211 : static void
3212 75 : write_file(MapiHdl hdl, char *filename, bool binary)
3213 : {
3214 75 : Mapi mid = hdl->mid;
3215 75 : char *line;
3216 75 : char data[BLOCK];
3217 75 : ssize_t len;
3218 :
3219 75 : (void) read_line(mid); /* read flush marker */
3220 75 : if (filename == NULL) {
3221 : /* malloc failure */
3222 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3223 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3224 0 : return;
3225 : }
3226 75 : if (mid->putfilecontent == NULL) {
3227 0 : free(filename);
3228 0 : mnstr_printf(mid->to, "!HY000!cannot send files\n");
3229 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3230 0 : return;
3231 : }
3232 75 : line = mid->putfilecontent(mid->filecontentprivate, filename, binary, NULL, 0);
3233 75 : free(filename);
3234 75 : if (line != NULL) {
3235 0 : if (strchr(line, '\n'))
3236 0 : line = "incorrect response from application";
3237 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", line);
3238 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3239 0 : return;
3240 : }
3241 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3242 180811 : for (;;) {
3243 180811 : len = mnstr_read(mid->from, data, 1, sizeof(data));
3244 180811 : if (len == -1) {
3245 0 : if (mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
3246 0 : mnstr_clearerr(mid->from);
3247 0 : if (mid->oobintr && !hdl->aborted) {
3248 0 : hdl->aborted = true;
3249 0 : mnstr_putoob(mid->to, 1);
3250 : }
3251 : } else {
3252 : break;
3253 : }
3254 180811 : } else if (len == 0) {
3255 : break;
3256 180736 : } else if (line == NULL) {
3257 180736 : line = mid->putfilecontent(mid->filecontentprivate,
3258 : NULL, binary, data, len);
3259 : }
3260 : }
3261 75 : if (line == NULL)
3262 75 : line = mid->putfilecontent(mid->filecontentprivate,
3263 : NULL, binary, NULL, 0);
3264 75 : if (line && strchr(line, '\n'))
3265 : line = "incorrect response from application";
3266 75 : mnstr_printf(mid->to, "%s\n", line ? line : "");
3267 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3268 : }
3269 :
3270 : #define MiB (1 << 20) /* a megabyte */
3271 :
3272 : static void
3273 103 : read_file(MapiHdl hdl, uint64_t off, char *filename, bool binary)
3274 : {
3275 103 : Mapi mid = hdl->mid;
3276 103 : size_t size = 0, flushsize = 0;
3277 103 : char *data, *line;
3278 :
3279 103 : (void) read_line(mid); /* read flush marker */
3280 103 : if (filename == NULL) {
3281 : /* malloc failure */
3282 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3283 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3284 19 : return;
3285 : }
3286 103 : if (mid->getfilecontent == NULL) {
3287 0 : free(filename);
3288 0 : mnstr_printf(mid->to, "!HY000!cannot retrieve files\n");
3289 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3290 0 : return;
3291 : }
3292 103 : data = mid->getfilecontent(mid->filecontentprivate, filename, binary,
3293 : off, &size);
3294 103 : free(filename);
3295 103 : if (data != NULL && size == 0) {
3296 0 : if (strchr(data, '\n'))
3297 0 : data = "incorrect response from application";
3298 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", data);
3299 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3300 0 : return;
3301 : }
3302 103 : mnstr_printf(mid->to, "\n");
3303 23784 : while (data != NULL && size != 0) {
3304 23682 : if (flushsize >= MiB) {
3305 : /* after every MiB give the server the
3306 : * opportunity to stop reading more data */
3307 1414 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3308 : /* at this point we expect to get a PROMPT2 if
3309 : * the server wants more data, or a PROMPT3 if
3310 : * the server had enough; anything else is a
3311 : * protocol violation */
3312 1414 : line = read_line(mid);
3313 1414 : if (line == NULL) {
3314 : /* error */
3315 0 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3316 0 : return;
3317 : }
3318 1414 : assert(line[0] == PROMPTBEG);
3319 1414 : if (line[0] != PROMPTBEG) {
3320 : /* error in protocol */
3321 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3322 : return;
3323 : }
3324 1414 : if (line[1] == PROMPT3[1]) {
3325 : /* done reading: close file */
3326 1 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3327 1 : (void) read_line(mid);
3328 1 : return;
3329 : }
3330 1413 : assert(line[1] == PROMPT2[1]);
3331 1413 : if (line[1] != PROMPT2[1]) {
3332 : /* error in protocol */
3333 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3334 : return;
3335 : }
3336 : /* clear the flush marker */
3337 1413 : (void) read_line(mid);
3338 1413 : flushsize = 0;
3339 : }
3340 23681 : if (size > MiB) {
3341 0 : if (mnstr_write(mid->to, data, 1, MiB) != MiB) {
3342 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3343 0 : return;
3344 : }
3345 0 : size -= MiB;
3346 0 : data += MiB;
3347 0 : flushsize += MiB;
3348 : } else {
3349 23681 : if (mnstr_write(mid->to, data, 1, size) != (ssize_t) size) {
3350 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3351 0 : return;
3352 : }
3353 23681 : flushsize += size;
3354 23681 : data = mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, &size);
3355 : }
3356 : }
3357 102 : if (data != NULL && size == 0) {
3358 : /* some error occurred */
3359 0 : mnstr_clearerr(mid->from);
3360 0 : if (mid->oobintr && !hdl->aborted) {
3361 0 : hdl->aborted = true;
3362 0 : mnstr_putoob(mid->to, 1);
3363 : }
3364 : }
3365 102 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3366 102 : line = read_line(mid);
3367 102 : if (line == NULL)
3368 : return;
3369 102 : assert(line[0] == PROMPTBEG);
3370 102 : if (line[0] != PROMPTBEG)
3371 : return;
3372 102 : if (line[1] == PROMPT3[1]) {
3373 18 : (void) read_line(mid);
3374 18 : return;
3375 : }
3376 84 : assert(line[1] == PROMPT2[1]);
3377 84 : if (line[1] != PROMPT2[1])
3378 : return;
3379 84 : (void) read_line(mid);
3380 84 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3381 84 : line = read_line(mid);
3382 84 : if (line == NULL)
3383 : return;
3384 84 : assert(line[0] == PROMPTBEG);
3385 84 : assert(line[1] == PROMPT3[1]);
3386 84 : (void) read_line(mid);
3387 : }
3388 :
3389 :
3390 : /* Read ahead and cache data read. Depending on the second argument,
3391 : reading may stop at the first non-header and non-error line, or at
3392 : a prompt.
3393 : This function is called either after a command has been sent to the
3394 : server (in which case the second argument is 1), when the
3395 : application asks for a result tuple that hadn't been cached yet (in
3396 : which case the second argument is also 1), or whenever all pending
3397 : data needs to be read in order to send a new command to the server
3398 : (in which case the second argument is 0).
3399 : Header lines result tuples are stored in the cache. Certain header
3400 : lines may cause a new result set to be created in which case all
3401 : subsequent lines are added to that result set.
3402 : */
3403 : MapiMsg
3404 1508367 : read_into_cache(MapiHdl hdl, int lookahead)
3405 : {
3406 1508367 : char *line;
3407 1508367 : Mapi mid;
3408 1508367 : struct MapiResultSet *result;
3409 :
3410 1508367 : mid = hdl->mid;
3411 1508367 : assert(mid->active == hdl);
3412 1508367 : if (hdl->needmore) {
3413 0 : hdl->needmore = false;
3414 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3415 0 : check_stream(mid, mid->to, "write error on stream", mid->error);
3416 : }
3417 1508367 : if ((result = hdl->active) == NULL)
3418 140931 : result = hdl->result; /* may also be NULL */
3419 :
3420 1656575 : for (;;) {
3421 1656575 : line = read_line(mid);
3422 1656579 : if (line == NULL) {
3423 0 : if (mid->from && mnstr_eof(mid->from)) {
3424 0 : return mapi_setError(mid, "unexpected end of file", __func__, MTIMEOUT);
3425 : }
3426 :
3427 0 : return mid->error;
3428 : }
3429 1656579 : switch (*line) {
3430 141136 : case PROMPTBEG: /* \001 */
3431 141136 : mid->active = NULL;
3432 141136 : hdl->active = NULL;
3433 : /* set needmore flag if line equals PROMPT2 up
3434 : to newline */
3435 141136 : if (line[1] == PROMPT2[1] && line[2] == '\0') {
3436 : /* skip end of block */
3437 103291 : mid->active = hdl;
3438 103291 : (void) read_line(mid);
3439 103291 : hdl->needmore = true;
3440 103291 : mid->active = hdl;
3441 37845 : } else if (line[1] == PROMPT3[1] && line[2] == '\0') {
3442 178 : mid->active = hdl;
3443 178 : line = read_line(mid);
3444 178 : bool binary = false;
3445 : /* rb FILE
3446 : * r OFF FILE
3447 : * w FILE
3448 : * wb FILE
3449 : */
3450 178 : switch (*line++) {
3451 103 : case 'r': {
3452 103 : uint64_t off = 0;
3453 103 : if (*line == 'b') {
3454 85 : line++;
3455 85 : binary = true;
3456 : } else {
3457 18 : off = strtoul(line, &line, 10);
3458 : }
3459 103 : if (*line++ != ' ') {
3460 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3461 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3462 0 : break;
3463 : }
3464 103 : read_file(hdl, off, strdup(line), binary);
3465 103 : break;
3466 : }
3467 75 : case 'w':
3468 75 : if (*line == 'b') {
3469 75 : line++;
3470 75 : binary = true;
3471 : }
3472 75 : if (*line++ != ' ') {
3473 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3474 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3475 0 : break;
3476 : }
3477 75 : write_file(hdl, strdup(line), binary);
3478 75 : break;
3479 : }
3480 178 : continue;
3481 : }
3482 140958 : return mid->error;
3483 76 : case '!':
3484 : /* start a new result set if we don't have one
3485 : yet (duh!), or if we've already seen
3486 : normal output for the current one */
3487 76 : if (result == NULL ||
3488 2 : result->cache.writer > 0 ||
3489 2 : result->querytype > 0) {
3490 74 : result = new_result(hdl);
3491 74 : result->commentonly = false;
3492 74 : hdl->active = result;
3493 : }
3494 76 : add_error(result, line + 1 /* skip ! */ );
3495 76 : if (!mid->error)
3496 74 : mid->error = MSERVER;
3497 : break;
3498 145616 : case '%':
3499 : case '#':
3500 : case '&':
3501 145616 : if (lookahead < 0)
3502 0 : lookahead = 1;
3503 145616 : result = parse_header_line(hdl, line, result);
3504 145616 : hdl->active = result;
3505 145616 : if (result && *line != '&')
3506 114076 : add_cache(result, strdup(line), !lookahead);
3507 : break;
3508 1369751 : default:
3509 1369751 : if (result == NULL) {
3510 191 : result = new_result(hdl);
3511 191 : hdl->active = result;
3512 : }
3513 1369751 : add_cache(result, strdup(line), !lookahead);
3514 1369751 : if (lookahead > 0 &&
3515 1368021 : (result->querytype == -1 /* unknown (not SQL) */ ||
3516 608 : result->querytype == Q_TABLE ||
3517 : result->querytype == Q_UPDATE)) {
3518 1367413 : return mid->error;
3519 : }
3520 : break;
3521 : }
3522 : }
3523 : }
3524 :
3525 : static MapiMsg
3526 32438 : mapi_execute_internal(MapiHdl hdl)
3527 : {
3528 32438 : size_t size;
3529 32438 : char *cmd;
3530 32438 : Mapi mid;
3531 :
3532 32438 : mid = hdl->mid;
3533 32438 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3534 : return MERROR;
3535 32438 : assert(mid->active == NULL);
3536 32438 : finish_handle(hdl);
3537 32439 : mapi_param_store(hdl);
3538 32438 : cmd = hdl->query;
3539 32438 : if (cmd == NULL)
3540 : return MERROR;
3541 32438 : size = strlen(cmd);
3542 :
3543 32438 : bool is_sql = msettings_lang_is_sql(mid->settings);
3544 32443 : char *prefix = is_sql ? "s" : "";
3545 4148 : char *suffix = is_sql ? "\n;" : "";
3546 32443 : mapi_log_record(mid, "SEND", "%s%s%s", prefix, cmd, suffix);
3547 :
3548 32444 : if (is_sql) {
3549 : /* indicate to server this is a SQL command */
3550 28294 : mnstr_write(mid->to, "s", 1, 1);
3551 28294 : check_stream(mid, mid->to, "write error on stream", mid->error);
3552 : }
3553 32444 : mnstr_write(mid->to, cmd, 1, size);
3554 32439 : check_stream(mid, mid->to, "write error on stream", mid->error);
3555 : /* all SQL statements should end with a semicolon */
3556 : /* for the other languages it is assumed that the statements are correct */
3557 32440 : if (is_sql) {
3558 28294 : mnstr_write(mid->to, "\n;", 2, 1);
3559 28294 : check_stream(mid, mid->to, "write error on stream", mid->error);
3560 : }
3561 32440 : mnstr_write(mid->to, "\n", 1, 1);
3562 32439 : check_stream(mid, mid->to, "write error on stream", mid->error);
3563 32443 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3564 32439 : check_stream(mid, mid->to, "write error on stream", mid->error);
3565 32437 : mid->active = hdl;
3566 :
3567 32437 : return MOK;
3568 : }
3569 :
3570 : MapiMsg
3571 0 : mapi_execute(MapiHdl hdl)
3572 : {
3573 0 : int ret;
3574 :
3575 0 : mapi_hdl_check(hdl);
3576 0 : if ((ret = mapi_execute_internal(hdl)) == MOK)
3577 0 : return read_into_cache(hdl, 1);
3578 :
3579 : return ret;
3580 : }
3581 :
3582 : /*
3583 : * The routine mapi_query is one of the most heavily used ones.
3584 : * It sends a complete statement for execution
3585 : * (i.e., ending in a newline; possibly including additional newlines).
3586 : * Interaction with the server is sped up using block based interaction.
3587 : * The query is retained in the Mapi structure to repeat shipping.
3588 : */
3589 : MapiHdl
3590 30331 : mapi_query(Mapi mid, const char *cmd)
3591 : {
3592 30331 : int ret;
3593 30331 : MapiHdl hdl;
3594 :
3595 30331 : mapi_check0(mid);
3596 30331 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3597 30336 : ret = mid->error;
3598 30336 : if (ret == MOK)
3599 30333 : ret = mapi_execute_internal(hdl);
3600 30325 : if (ret == MOK)
3601 30327 : ret = read_into_cache(hdl, 1);
3602 : return hdl;
3603 : }
3604 :
3605 : /* version of mapi_query that does not wait for a response */
3606 : MapiHdl
3607 0 : mapi_send(Mapi mid, const char *cmd)
3608 : {
3609 0 : int ret;
3610 0 : MapiHdl hdl;
3611 :
3612 0 : mapi_check0(mid);
3613 0 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3614 0 : ret = mid->error;
3615 0 : if (ret == MOK)
3616 0 : ret = mapi_execute_internal(hdl);
3617 : return hdl;
3618 : }
3619 :
3620 : MapiMsg
3621 0 : mapi_read_response(MapiHdl hdl)
3622 : {
3623 0 : return read_into_cache(hdl, 1);
3624 : }
3625 :
3626 : MapiMsg
3627 2110 : mapi_query_handle(MapiHdl hdl, const char *cmd)
3628 : {
3629 2110 : int ret;
3630 :
3631 2110 : mapi_hdl_check(hdl);
3632 2110 : if (finish_handle(hdl) != MOK)
3633 : return MERROR;
3634 2110 : prepareQuery(hdl, cmd);
3635 2110 : ret = hdl->mid->error;
3636 2110 : if (ret == MOK)
3637 2110 : ret = mapi_execute_internal(hdl);
3638 2110 : if (ret == MOK)
3639 2110 : ret = read_into_cache(hdl, 1);
3640 : return ret;
3641 : }
3642 :
3643 : MapiHdl
3644 3762 : mapi_query_prep(Mapi mid)
3645 : {
3646 3762 : mapi_check0(mid);
3647 3762 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3648 : return NULL;
3649 3762 : assert(mid->active == NULL);
3650 3762 : if (msettings_lang_is_sql(mid->settings)) {
3651 : /* indicate to server this is a SQL command */
3652 3762 : mnstr_write(mid->to, "S", 1, 1);
3653 3762 : mapi_log_data(mid, "SEND", "S", 1);
3654 : }
3655 3762 : return (mid->active = mapi_new_handle(mid));
3656 : }
3657 :
3658 : MapiMsg
3659 106714 : mapi_query_part(MapiHdl hdl, const char *query, size_t size)
3660 : {
3661 106714 : Mapi mid;
3662 :
3663 106714 : mapi_hdl_check(hdl);
3664 106714 : mid = hdl->mid;
3665 106714 : assert(mid->active == NULL || mid->active == hdl);
3666 106714 : mid->active = hdl;
3667 : /* remember the query just for the error messages */
3668 106714 : if (hdl->query == NULL) {
3669 3430 : hdl->query = malloc(size + 1);
3670 3430 : if (hdl->query) {
3671 3430 : strcpy_len(hdl->query, query, size + 1);
3672 : }
3673 : } else {
3674 103284 : size_t sz = strlen(hdl->query);
3675 103284 : char *q;
3676 :
3677 103284 : if (sz < 512 &&
3678 1686 : (q = realloc(hdl->query, sz + size + 1)) != NULL) {
3679 1686 : strcpy_len(q + sz, query, size + 1);
3680 1686 : hdl->query = q;
3681 : }
3682 : }
3683 :
3684 106714 : if (mid->trace) {
3685 0 : printf("mapi_query_part:%zu:%.*s\n", size, (int) size, query);
3686 : }
3687 106714 : hdl->needmore = false;
3688 106714 : mnstr_write(mid->to, query, 1, size);
3689 106714 : if (mid->tracelog) {
3690 0 : mnstr_write(mid->tracelog, query, 1, size);
3691 0 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
3692 : }
3693 106714 : check_stream(mid, mid->to, "write error on stream", mid->error);
3694 106714 : return mid->error;
3695 : }
3696 :
3697 : MapiMsg
3698 107053 : mapi_query_done(MapiHdl hdl)
3699 : {
3700 107053 : int ret;
3701 107053 : Mapi mid;
3702 :
3703 107053 : mapi_hdl_check(hdl);
3704 107053 : mid = hdl->mid;
3705 107053 : assert(mid->active == NULL || mid->active == hdl);
3706 107053 : mid->active = hdl;
3707 107053 : hdl->needmore = false;
3708 107053 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3709 107053 : check_stream(mid, mid->to, "write error on stream", mid->error);
3710 107053 : ret = mid->error;
3711 107053 : if (ret == MOK)
3712 107053 : ret = read_into_cache(hdl, 1);
3713 107053 : return ret == MOK && hdl->needmore ? MMORE : ret;
3714 : }
3715 :
3716 : MapiMsg
3717 0 : mapi_query_abort(MapiHdl hdl, int reason)
3718 : {
3719 0 : Mapi mid;
3720 :
3721 0 : assert(reason > 0 && reason <= 127);
3722 0 : mapi_hdl_check(hdl);
3723 0 : mid = hdl->mid;
3724 0 : assert(mid->active == NULL || mid->active == hdl);
3725 0 : if (mid->oobintr && !hdl->aborted) {
3726 0 : mnstr_putoob(mid->to, reason);
3727 0 : hdl->aborted = true;
3728 0 : return MOK;
3729 : }
3730 : return MERROR;
3731 : }
3732 :
3733 : MapiMsg
3734 207 : mapi_cache_limit(Mapi mid, int limit)
3735 : {
3736 : /* clean out superflous space TODO */
3737 207 : msettings_error err = msetting_set_long(mid->settings, MP_REPLYSIZE, limit);
3738 207 : if (err)
3739 0 : return mapi_setError(mid, err, __func__, MERROR);
3740 207 : if (!mid->connected)
3741 : return MOK;
3742 41 : mapi_check(mid);
3743 : /* if (hdl->cache.rowlimit < hdl->cache.limit) { */
3744 : /* TODO: decide what to do here */
3745 : /* hdl->cache.limit = hdl->cache.rowlimit; *//* arbitrarily throw away cache lines */
3746 : /* if (hdl->cache.writer > hdl->cache.limit) { */
3747 : /* hdl->cache.writer = hdl->cache.limit; */
3748 : /* if (hdl->cache.reader > hdl->cache.writer) */
3749 : /* hdl->cache.reader = hdl->cache.writer; */
3750 : /* } */
3751 : /* } */
3752 41 : if (msettings_lang_is_sql(mid->settings)) {
3753 41 : MapiHdl hdl;
3754 :
3755 41 : if (mid->active)
3756 1 : read_into_cache(mid->active, 0);
3757 :
3758 41 : mapi_log_record(mid, "X", "X" "reply_size %d\n", limit);
3759 82 : if (mnstr_printf(mid->to, "X" "reply_size %d\n", limit) < 0 ||
3760 41 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
3761 0 : close_connection(mid);
3762 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
3763 0 : return MERROR;
3764 : }
3765 41 : hdl = prepareQuery(mapi_new_handle(mid), "reply_size");
3766 41 : if (hdl == NULL)
3767 : return MERROR;
3768 41 : mid->active = hdl;
3769 41 : read_into_cache(hdl, 0);
3770 41 : mapi_close_handle(hdl); /* reads away any output */
3771 : }
3772 : return MOK;
3773 : }
3774 :
3775 : MapiMsg
3776 0 : mapi_fetch_reset(MapiHdl hdl)
3777 : {
3778 0 : mapi_hdl_check(hdl);
3779 0 : if (hdl->result)
3780 0 : hdl->result->cache.reader = -1;
3781 : return MOK;
3782 : }
3783 :
3784 : MapiMsg
3785 2657 : mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
3786 : {
3787 2657 : struct MapiResultSet *result;
3788 :
3789 2657 : mapi_hdl_check(hdl);
3790 2657 : result = hdl->result;
3791 2657 : switch (whence) {
3792 : case MAPI_SEEK_SET:
3793 : break;
3794 0 : case MAPI_SEEK_CUR:
3795 0 : rownr += result->cache.line[result->cache.reader + 1].tuplerev;
3796 0 : break;
3797 0 : case MAPI_SEEK_END:
3798 0 : if (hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3799 : return MERROR;
3800 0 : rownr += result->row_count;
3801 0 : break;
3802 0 : default:
3803 0 : return mapi_setError(hdl->mid, "Illegal whence value", __func__, MERROR);
3804 : }
3805 2657 : if (rownr > result->row_count && hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3806 : return MERROR;
3807 2657 : if (rownr < 0 || rownr > result->row_count)
3808 0 : return mapi_setError(hdl->mid, "Illegal row number", __func__, MERROR);
3809 2657 : if (result->cache.first <= rownr && rownr < result->cache.first + result->cache.tuplecount) {
3810 : /* we've got the requested tuple in the cache */
3811 1087 : result->cache.reader = result->cache.line[rownr - result->cache.first].tupleindex - 1;
3812 : } else {
3813 : /* we don't have the requested tuple in the cache
3814 : reset the cache and at the next fetch we'll get the data */
3815 1570 : if (mapi_cache_freeup(hdl, 100) == MOK) {
3816 1570 : result->cache.first = rownr;
3817 : }
3818 : }
3819 2657 : return hdl->mid->error;
3820 : }
3821 :
3822 : /* Make space in the cache for new tuples, ignore the read pointer */
3823 : MapiMsg
3824 1570 : mapi_cache_freeup(MapiHdl hdl, int percentage)
3825 : {
3826 1570 : struct MapiResultSet *result;
3827 1570 : int k; /* # of cache lines to be deleted from front */
3828 :
3829 1570 : mapi_hdl_check(hdl);
3830 1570 : result = hdl->result;
3831 1570 : if (result == NULL || (result->cache.writer == 0 && result->cache.reader == -1))
3832 : return MOK;
3833 1570 : if (percentage < 0 || percentage > 100)
3834 0 : percentage = 100;
3835 1570 : k = (result->cache.writer * percentage) / 100;
3836 1570 : if (k < 1)
3837 0 : k = 1;
3838 1570 : return mapi_cache_freeup_internal(result, k);
3839 : }
3840 :
3841 : static char *
3842 1511144 : mapi_fetch_line_internal(MapiHdl hdl)
3843 : {
3844 1511144 : Mapi mid;
3845 1511144 : struct MapiResultSet *result;
3846 1511144 : char *reply;
3847 :
3848 : /* try to read a line from the cache */
3849 1511144 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer) {
3850 1371562 : mid = hdl->mid;
3851 1371562 : if (mid->active != hdl || hdl->needmore)
3852 : return NULL;
3853 :
3854 1366995 : if (read_into_cache(hdl, 1) != MOK)
3855 : return NULL;
3856 1366995 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer)
3857 : return NULL;
3858 : }
3859 1482825 : reply = result->cache.line[++result->cache.reader].rows;
3860 1482825 : if (hdl->bindings && (*reply == '[' || *reply == '=')) {
3861 0 : mapi_slice_row(result, result->cache.reader);
3862 0 : mapi_store_bind(result, result->cache.reader);
3863 : }
3864 : return reply;
3865 : }
3866 :
3867 : /*
3868 : * The routine mapi_fetch_line forms the basic interaction with the server.
3869 : * It simply retrieves the next line and stores it in the row cache.
3870 : * The field anchor structure is prepared for subsequent use by
3871 : * mapi_fetch_row.
3872 : * The content received is analyzed further by mapi_getRow()
3873 : */
3874 : char *
3875 1511125 : mapi_fetch_line(MapiHdl hdl)
3876 : {
3877 1511125 : char *reply;
3878 1511125 : struct MapiResultSet *result;
3879 :
3880 1511125 : mapi_hdl_check0(hdl);
3881 1511125 : reply = mapi_fetch_line_internal(hdl);
3882 1511125 : if (reply == NULL &&
3883 56638 : (result = hdl->result) != NULL &&
3884 28319 : msettings_lang_is_sql(hdl->mid->settings) &&
3885 28307 : result->querytype == Q_TABLE &&
3886 28188 : result->row_count > 0 &&
3887 23881 : result->cache.first + result->cache.tuplecount < result->row_count) {
3888 19 : if (hdl->needmore) /* escalate */
3889 : return NULL;
3890 19 : if (hdl->mid->active != NULL)
3891 0 : read_into_cache(hdl->mid->active, 0);
3892 19 : hdl->mid->active = hdl;
3893 19 : hdl->active = result;
3894 19 : mapi_log_record(hdl->mid, "W", "X" "export %d %" PRId64 "\n",
3895 : result->tableid,
3896 : result->cache.first + result->cache.tuplecount);
3897 19 : if (mnstr_printf(hdl->mid->to, "X" "export %d %" PRId64 "\n",
3898 : result->tableid,
3899 38 : result->cache.first + result->cache.tuplecount) < 0 ||
3900 19 : mnstr_flush(hdl->mid->to, MNSTR_FLUSH_DATA))
3901 0 : check_stream(hdl->mid, hdl->mid->to, "sending export command", NULL);
3902 19 : reply = mapi_fetch_line_internal(hdl);
3903 : }
3904 : return reply;
3905 : }
3906 :
3907 : /*
3908 : * To synchronize on a prompt, the low level routine mapi_finish can be used.
3909 : * It discards all output received.
3910 : */
3911 : MapiMsg
3912 0 : mapi_finish(MapiHdl hdl)
3913 : {
3914 0 : mapi_hdl_check(hdl);
3915 0 : return finish_handle(hdl);
3916 : }
3917 :
3918 : /* msg is a string consisting comma-separated values. The list of
3919 : values is terminated by endchar or by the end-of-string NULL byte.
3920 : Values can be quoted strings or unquoted values. Upon return,
3921 : *start points to the start of the first value which is stripped of
3922 : leading and trailing white space, and if it was a quoted string,
3923 : also of the quotes. Also, backslash-escaped characters in the
3924 : quoted string are replaced by the values the escapes represent.
3925 : *next points to either the start of the next value (i.e. after the
3926 : separating comma, possibly to the leading white space of the next
3927 : value), or to the trailing ] or NULL byte if this was the last
3928 : value. *lenp is the number of bytes occupied by the (possibly
3929 : converted) value, excluding final NULL byte.
3930 : msg is *not* a const string: it is altered by this function.
3931 : The function returns true if the string was quoted.
3932 : */
3933 : static int
3934 4959996 : unquote(const char *msg, char **str, const char **next, int endchar, size_t *lenp)
3935 : {
3936 4959996 : const char *p = msg;
3937 4959996 : char quote;
3938 :
3939 : /* first skip over leading white space */
3940 6422598 : while (*p && isspace((unsigned char) *p))
3941 1462602 : p++;
3942 4959996 : quote = *p;
3943 4959996 : if (quote == '\'' || quote == '"') {
3944 4164407 : size_t len = 0;
3945 4164407 : char *s, *start;
3946 :
3947 : /* get quoted string and remove trailing bracket first */
3948 4164407 : p++;
3949 : /* first count how much space we need */
3950 4164407 : msg = p; /* save for later */
3951 174741164 : while (*p && *p != quote) {
3952 170576757 : if (*p == '\\') {
3953 17328 : p++;
3954 17328 : switch (*p) {
3955 0 : case '0':
3956 : case '1':
3957 : case '2':
3958 : case '3':
3959 : /* this could be the start of
3960 : an octal sequence, check it
3961 : out */
3962 0 : if (p[1] && p[2] &&
3963 0 : p[1] >= '0' && p[1] <= '7' &&
3964 0 : p[2] >= '0' && p[2] <= '7') {
3965 0 : p += 2;
3966 0 : break;
3967 : }
3968 : /* fall through */
3969 : default:
3970 : break;
3971 : }
3972 : }
3973 170576757 : p++;
3974 170576757 : len++;
3975 : }
3976 : /* now allocate space and copy string into new space */
3977 4164407 : p = msg; /* start over */
3978 4164407 : start = s = malloc(len + 1);
3979 174741164 : while (*p && *p != quote) {
3980 170576757 : if (*p == '\\') {
3981 17328 : p++;
3982 17328 : switch (*p) {
3983 : /* later
3984 : case '0': case '1': case '2': case '3': case '4':
3985 : case '5': case '6': case '7': case '8': case '9':
3986 : */
3987 1717 : case 'n':
3988 1717 : *s = '\n';
3989 1717 : break;
3990 15 : case 't':
3991 15 : *s = '\t';
3992 15 : break;
3993 0 : case 'r':
3994 0 : *s = '\r';
3995 0 : break;
3996 0 : case 'f':
3997 0 : *s = '\f';
3998 0 : break;
3999 0 : case '0':
4000 : case '1':
4001 : case '2':
4002 : case '3':
4003 : /* this could be the start of
4004 : an octal sequence, check it
4005 : out */
4006 0 : if (p[1] && p[2] &&
4007 0 : p[1] >= '0' && p[1] <= '7' &&
4008 0 : p[2] >= '0' && p[2] <= '7') {
4009 0 : *s = ((p[0] - '0') << 6) | ((p[1] - '0') << 3) | (p[2] - '0');
4010 0 : p += 2;
4011 0 : break;
4012 : }
4013 : /* fall through */
4014 : default:
4015 15596 : *s = *p;
4016 15596 : break;
4017 : }
4018 17328 : p++;
4019 : } else {
4020 170559429 : *s = *p++;
4021 : }
4022 170576757 : s++;
4023 : }
4024 4164407 : *s = 0; /* close string */
4025 4164407 : p++; /* skip over end-of-string quote */
4026 : /* skip over trailing junk (presumably white space) */
4027 5489607 : while (*p && *p != ',' && *p != endchar)
4028 1325200 : p++;
4029 4164407 : if (next)
4030 4164407 : *next = p;
4031 4164407 : *str = start;
4032 4164407 : if (lenp)
4033 4164407 : *lenp = len;
4034 :
4035 4164407 : return 1;
4036 : } else {
4037 : const char *s;
4038 : size_t len;
4039 :
4040 : /* p points at first non-white space character */
4041 54990210 : msg = p; /* record start of value */
4042 : /* find separator or terminator */
4043 54990210 : while (*p && *p != ',' && *p != '\t' && *p != endchar)
4044 54194621 : p++;
4045 : /* search back over trailing white space */
4046 909858 : for (s = p - 1; s > msg && isspace((unsigned char) *s); s--)
4047 : ;
4048 795589 : if (s < msg || !isspace((unsigned char) *s)) /* gone one too far */
4049 795589 : s++;
4050 795589 : if (*p == '\t') {
4051 23151 : p++;
4052 : }
4053 795589 : len = s - msg;
4054 795589 : *str = malloc(len + 1);
4055 795589 : strcpy_len(*str, msg, len + 1);
4056 :
4057 795589 : if (next)
4058 795589 : *next = p;
4059 795589 : if (lenp)
4060 795589 : *lenp = len;
4061 795589 : return 0;
4062 : }
4063 : }
4064 :
4065 : char *
4066 0 : mapi_unquote(char *msg)
4067 : {
4068 0 : char *start;
4069 :
4070 0 : unquote(msg, &start, NULL, ']', NULL);
4071 0 : return start;
4072 : }
4073 :
4074 : char *
4075 0 : mapi_quote(const char *msg, int size)
4076 : {
4077 : /* we absolutely don't need more than this (until we start
4078 : producing octal escapes */
4079 0 : char *s = malloc((size < 0 ? strlen(msg) : (size_t) size) * 2 + 1);
4080 0 : char *t = s;
4081 :
4082 : /* the condition is tricky: if initially size < 0, we must
4083 : continue until a NULL byte, else, size gives the number of
4084 : bytes to be copied */
4085 0 : while (size < 0 ? *msg : size > 0) {
4086 0 : if (size > 0)
4087 0 : size--;
4088 0 : switch (*msg) {
4089 0 : case '\n':
4090 0 : *t++ = '\\';
4091 0 : *t++ = 'n';
4092 0 : break;
4093 0 : case '\t':
4094 0 : *t++ = '\\';
4095 0 : *t++ = 't';
4096 0 : break;
4097 0 : case PLACEHOLDER:
4098 0 : *t++ = '\\';
4099 0 : *t++ = PLACEHOLDER;
4100 0 : break;
4101 0 : case '\\':
4102 0 : *t++ = '\\';
4103 0 : *t++ = '\\';
4104 0 : break;
4105 0 : case '\'':
4106 0 : *t++ = '\\';
4107 0 : *t++ = '\'';
4108 0 : break;
4109 0 : case '"':
4110 0 : *t++ = '\\';
4111 0 : *t++ = '"';
4112 0 : break;
4113 0 : case '\0':
4114 0 : *t++ = '\\';
4115 0 : *t++ = '0';
4116 0 : break;
4117 0 : default:
4118 0 : *t++ = *msg;
4119 0 : break;
4120 : }
4121 0 : msg++;
4122 : /* also deal with binaries */
4123 : }
4124 0 : *t = 0;
4125 0 : return s;
4126 : }
4127 :
4128 : static int
4129 0 : mapi_extend_bindings(MapiHdl hdl, int minbindings)
4130 : {
4131 : /* extend the bindings table */
4132 0 : int nm = hdl->maxbindings + 32;
4133 :
4134 0 : if (nm <= minbindings)
4135 0 : nm = minbindings + 32;
4136 0 : REALLOC(hdl->bindings, nm);
4137 0 : assert(hdl->bindings);
4138 : /* clear new entries */
4139 0 : memset(hdl->bindings + hdl->maxbindings, 0, (nm - hdl->maxbindings) * sizeof(*hdl->bindings));
4140 0 : hdl->maxbindings = nm;
4141 0 : return MOK;
4142 : }
4143 :
4144 : static int
4145 0 : mapi_extend_params(MapiHdl hdl, int minparams)
4146 : {
4147 : /* extend the params table */
4148 0 : int nm = hdl->maxparams + 32;
4149 :
4150 0 : if (nm <= minparams)
4151 0 : nm = minparams + 32;
4152 0 : REALLOC(hdl->params, nm);
4153 0 : assert(hdl->params);
4154 : /* clear new entries */
4155 0 : memset(hdl->params + hdl->maxparams, 0, (nm - hdl->maxparams) * sizeof(*hdl->params));
4156 0 : hdl->maxparams = nm;
4157 0 : return MOK;
4158 : }
4159 :
4160 : static MapiMsg
4161 0 : store_field(struct MapiResultSet *result, int cr, int fnr, int outtype, void *dst)
4162 : {
4163 0 : char *val;
4164 :
4165 0 : val = result->cache.line[cr].anchors[fnr];
4166 :
4167 0 : if (val == 0) {
4168 0 : return mapi_setError(result->hdl->mid, "Field value undefined or nil", __func__, MERROR);
4169 : }
4170 :
4171 : /* auto convert to C-type */
4172 0 : switch (outtype) {
4173 0 : case MAPI_TINY:
4174 0 : *(signed char *) dst = (signed char) strtol(val, NULL, 0);
4175 0 : break;
4176 0 : case MAPI_UTINY:
4177 0 : *(unsigned char *) dst = (unsigned char) strtoul(val, NULL, 0);
4178 0 : break;
4179 0 : case MAPI_SHORT:
4180 0 : *(short *) dst = (short) strtol(val, NULL, 0);
4181 0 : break;
4182 0 : case MAPI_USHORT:
4183 0 : *(unsigned short *) dst = (unsigned short) strtoul(val, NULL, 0);
4184 0 : break;
4185 0 : case MAPI_NUMERIC:
4186 : case MAPI_INT:
4187 0 : *(int *) dst = (int) strtol(val, NULL, 0);
4188 0 : break;
4189 0 : case MAPI_UINT:
4190 0 : *(unsigned int *) dst = (unsigned int) strtoul(val, NULL, 0);
4191 0 : break;
4192 0 : case MAPI_LONG:
4193 0 : *(long *) dst = strtol(val, NULL, 0);
4194 0 : break;
4195 0 : case MAPI_ULONG:
4196 0 : *(unsigned long *) dst = strtoul(val, NULL, 0);
4197 0 : break;
4198 0 : case MAPI_LONGLONG:
4199 0 : *(int64_t *) dst = strtoll(val, NULL, 0);
4200 0 : break;
4201 0 : case MAPI_ULONGLONG:
4202 0 : *(uint64_t *) dst = strtoull(val, NULL, 0);
4203 0 : break;
4204 0 : case MAPI_CHAR:
4205 0 : *(char *) dst = *val;
4206 0 : break;
4207 0 : case MAPI_FLOAT:
4208 0 : *(float *) dst = strtof(val, NULL);
4209 0 : break;
4210 0 : case MAPI_DOUBLE:
4211 0 : *(double *) dst = strtod(val, NULL);
4212 0 : break;
4213 0 : case MAPI_DATE:
4214 0 : sscanf(val, "%hd-%hu-%hu",
4215 : &((MapiDate *) dst)->year,
4216 : &((MapiDate *) dst)->month,
4217 : &((MapiDate *) dst)->day);
4218 0 : break;
4219 0 : case MAPI_TIME:
4220 0 : sscanf(val, "%hu:%hu:%hu",
4221 : &((MapiTime *) dst)->hour,
4222 : &((MapiTime *) dst)->minute,
4223 : &((MapiTime *) dst)->second);
4224 0 : break;
4225 0 : case MAPI_DATETIME:{
4226 0 : int n;
4227 :
4228 0 : ((MapiDateTime *) dst)->fraction = 0;
4229 0 : sscanf(val, "%hd-%hu-%hu %hu:%hu:%hu%n",
4230 : &((MapiDateTime *) dst)->year,
4231 : &((MapiDateTime *) dst)->month,
4232 : &((MapiDateTime *) dst)->day,
4233 : &((MapiDateTime *) dst)->hour,
4234 : &((MapiDateTime *) dst)->minute,
4235 : &((MapiDateTime *) dst)->second,
4236 : &n);
4237 0 : if (val[n] == '.') {
4238 0 : unsigned int fac = 1000000000;
4239 0 : unsigned int nsec = 0;
4240 :
4241 0 : for (n++; isdigit((unsigned char) val[n]); n++) {
4242 0 : fac /= 10;
4243 0 : nsec += (val[n] - '0') * fac;
4244 : }
4245 0 : ((MapiDateTime *) dst)->fraction = nsec;
4246 : }
4247 0 : break;
4248 : }
4249 0 : case MAPI_AUTO:
4250 : case MAPI_VARCHAR:
4251 : default:
4252 0 : *(char **) dst = val;
4253 : }
4254 : return MOK;
4255 : }
4256 :
4257 : MapiMsg
4258 0 : mapi_store_field(MapiHdl hdl, int fnr, int outtype, void *dst)
4259 : {
4260 0 : struct MapiResultSet *result;
4261 :
4262 0 : mapi_hdl_check(hdl);
4263 :
4264 0 : if ((result = hdl->result) == NULL) {
4265 0 : return mapi_setError(hdl->mid, "No data read", __func__, MERROR);
4266 : }
4267 :
4268 0 : if (fnr < 0 || fnr >= result->fieldcnt) {
4269 0 : return mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4270 : }
4271 :
4272 0 : return store_field(result, result->cache.reader, fnr, outtype, dst);
4273 : }
4274 :
4275 : static void
4276 0 : mapi_store_bind(struct MapiResultSet *result, int cr)
4277 : {
4278 0 : int i;
4279 0 : MapiHdl hdl = result->hdl;
4280 :
4281 0 : for (i = 0; i < hdl->maxbindings; i++)
4282 0 : if (hdl->bindings[i].outparam)
4283 0 : store_field(result, cr, i, hdl->bindings[i].outtype, hdl->bindings[i].outparam);
4284 0 : }
4285 :
4286 : /*
4287 : * The low level routine mapi_slice_row breaks the last row received
4288 : * into pieces and binds the field descriptors with their location. All
4289 : * escaped characters are immediately replaced, such that we end with a
4290 : * list of C-strings. It overwrites the contents of the row buffer,
4291 : * because de-escaping only reduces the size. It also silently extends
4292 : * the field descriptor table.
4293 : */
4294 : static int
4295 1348583 : mapi_slice_row(struct MapiResultSet *result, int cr)
4296 : {
4297 1348583 : char *p;
4298 1348583 : int i = 0;
4299 :
4300 1348583 : p = result->cache.line[cr].rows;
4301 1348583 : if (p == NULL)
4302 0 : return mapi_setError(result->hdl->mid, "Current row missing", __func__, MERROR);
4303 1348583 : if (result->cache.line[cr].fldcnt)
4304 : return result->cache.line[cr].fldcnt; /* already sliced */
4305 :
4306 1348583 : if (*p != '[') {
4307 : /* nothing to slice */
4308 49 : i = 1;
4309 49 : REALLOC(result->cache.line[cr].anchors, 1);
4310 49 : REALLOC(result->cache.line[cr].lens, 1);
4311 : /* skip initial '=' if present */
4312 49 : if (*p == '=')
4313 49 : p++;
4314 49 : result->cache.line[cr].anchors[0] = strdup(p);
4315 49 : result->cache.line[cr].lens[0] = strlen(p);
4316 : } else {
4317 : /* work on a copy to preserve the original */
4318 1348534 : p = strdup(p);
4319 1348735 : i = slice_row(p,
4320 1348534 : msettings_lang_is_sql(result->hdl->mid->settings) ? "NULL" : "nil",
4321 : &result->cache.line[cr].anchors,
4322 : &result->cache.line[cr].lens,
4323 : result->fieldcnt, ']');
4324 1348534 : free(p);
4325 : }
4326 1348583 : if (i != result->fieldcnt) {
4327 : int j;
4328 190 : for (j = 0; j < result->fieldcnt; j++) {
4329 0 : if (result->fields[j].columnname)
4330 0 : free(result->fields[j].columnname);
4331 0 : result->fields[j].columnname = NULL;
4332 0 : if (result->fields[j].columntype)
4333 0 : free(result->fields[j].columntype);
4334 0 : result->fields[j].columntype = NULL;
4335 0 : if (result->fields[j].tablename)
4336 0 : free(result->fields[j].tablename);
4337 0 : result->fields[j].tablename = NULL;
4338 0 : result->fields[j].columnlength = 0;
4339 : }
4340 : }
4341 1348583 : if (i > result->fieldcnt) {
4342 190 : result->fieldcnt = i;
4343 190 : if (i > result->maxfields) {
4344 190 : REALLOC(result->fields, i);
4345 190 : memset(result->fields + result->maxfields, 0, (i - result->maxfields) * sizeof(*result->fields));
4346 190 : result->maxfields = i;
4347 : }
4348 : }
4349 1348583 : result->cache.line[cr].fldcnt = i;
4350 1348583 : return i;
4351 : }
4352 :
4353 : /*
4354 : * The rows presented are broken down into pieces to
4355 : * simplify access later on. However, mclient may
4356 : * first want to check the content of the line for
4357 : * useful information (e.g. #EOD)
4358 : */
4359 : int
4360 26245 : mapi_split_line(MapiHdl hdl)
4361 : {
4362 26245 : int n;
4363 26245 : struct MapiResultSet *result;
4364 :
4365 26245 : result = hdl->result;
4366 26245 : assert(result != NULL);
4367 26245 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4368 26245 : n = mapi_slice_row(result, result->cache.reader);
4369 : /* no need to call mapi_store_bind since
4370 : mapi_fetch_line would have done that if needed */
4371 : }
4372 26245 : return n;
4373 : }
4374 :
4375 : int
4376 1327349 : mapi_fetch_row(MapiHdl hdl)
4377 : {
4378 1327349 : char *reply;
4379 1327349 : int n;
4380 1327349 : struct MapiResultSet *result;
4381 :
4382 1327349 : mapi_hdl_check(hdl);
4383 1347430 : do {
4384 1347430 : if ((reply = mapi_fetch_line(hdl)) == NULL)
4385 : return 0;
4386 1342419 : } while (*reply != '[' && *reply != '=');
4387 1322338 : result = hdl->result;
4388 1322338 : assert(result != NULL);
4389 1322338 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4390 1322338 : n = mapi_slice_row(result, result->cache.reader);
4391 : /* no need to call mapi_store_bind since
4392 : mapi_fetch_line would have done that if needed */
4393 : }
4394 : return n;
4395 : }
4396 :
4397 : /*
4398 : * All rows can be cached first as well.
4399 : */
4400 : int64_t
4401 1 : mapi_fetch_all_rows(MapiHdl hdl)
4402 : {
4403 1 : Mapi mid;
4404 1 : struct MapiResultSet *result;
4405 :
4406 1 : mapi_hdl_check(hdl);
4407 :
4408 1 : mid = hdl->mid;
4409 3 : for (;;) {
4410 4 : if ((result = hdl->result) != NULL &&
4411 2 : msettings_lang_is_sql(mid->settings) &&
4412 2 : mid->active == NULL &&
4413 1 : result->row_count > 0 &&
4414 1 : result->cache.first + result->cache.tuplecount < result->row_count) {
4415 0 : mid->active = hdl;
4416 0 : hdl->active = result;
4417 0 : mapi_log_record(mid, "SEND", "X" "export %d %" PRId64 "\n",
4418 : result->tableid, result->cache.first + result->cache.tuplecount);
4419 0 : if (mnstr_printf(mid->to, "X" "export %d %" PRId64 "\n",
4420 0 : result->tableid, result->cache.first + result->cache.tuplecount) < 0 ||
4421 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA))
4422 0 : check_stream(mid, mid->to, "sending export command", 0);
4423 : }
4424 2 : if (mid->active)
4425 1 : read_into_cache(mid->active, 0);
4426 : else
4427 : break;
4428 : }
4429 1 : return result ? result->cache.tuplecount : 0;
4430 : }
4431 :
4432 : char *
4433 4713721 : mapi_fetch_field(MapiHdl hdl, int fnr)
4434 : {
4435 4713721 : int cr;
4436 4713721 : struct MapiResultSet *result;
4437 :
4438 4713721 : mapi_hdl_check0(hdl);
4439 :
4440 4713721 : if ((result = hdl->result) == NULL ||
4441 4713721 : (cr = result->cache.reader) < 0 ||
4442 4713721 : (result->cache.line[cr].rows[0] != '[' &&
4443 : result->cache.line[cr].rows[0] != '=')) {
4444 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4445 0 : return 0;
4446 : }
4447 4713721 : assert(result->cache.line != NULL);
4448 4713721 : if (fnr >= 0) {
4449 : /* slice if needed */
4450 4713721 : if (result->cache.line[cr].fldcnt == 0)
4451 0 : mapi_slice_row(result, cr);
4452 4713721 : if (fnr < result->cache.line[cr].fldcnt)
4453 4713720 : return result->cache.line[cr].anchors[fnr];
4454 : }
4455 1 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4456 1 : return 0;
4457 : }
4458 :
4459 : size_t
4460 746698 : mapi_fetch_field_len(MapiHdl hdl, int fnr)
4461 : {
4462 746698 : int cr;
4463 746698 : struct MapiResultSet *result;
4464 :
4465 746698 : mapi_hdl_check0(hdl);
4466 :
4467 746698 : if ((result = hdl->result) == NULL ||
4468 746698 : (cr = result->cache.reader) < 0 ||
4469 746698 : (result->cache.line[cr].rows[0] != '[' &&
4470 : result->cache.line[cr].rows[0] != '=')) {
4471 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4472 0 : return 0;
4473 : }
4474 746698 : assert(result->cache.line != NULL);
4475 746698 : if (fnr >= 0) {
4476 : /* slice if needed */
4477 746698 : if (result->cache.line[cr].fldcnt == 0)
4478 0 : mapi_slice_row(result, cr);
4479 746698 : if (fnr < result->cache.line[cr].fldcnt)
4480 746698 : return result->cache.line[cr].lens[fnr];
4481 : }
4482 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4483 0 : return 0;
4484 : }
4485 :
4486 : int
4487 2413 : mapi_get_field_count(MapiHdl hdl)
4488 : {
4489 2413 : mapi_hdl_check(hdl);
4490 2413 : if (hdl->result && hdl->result->fieldcnt == 0) {
4491 : /* no rows have been sliced yet, and there was no
4492 : header, so try to figure out how many columns there
4493 : are for ourselves */
4494 : int i;
4495 :
4496 2064 : for (i = 0; i < hdl->result->cache.writer; i++)
4497 0 : if (hdl->result->cache.line[i].rows[0] == '[' ||
4498 : hdl->result->cache.line[i].rows[0] == '=')
4499 0 : mapi_slice_row(hdl->result, i);
4500 : }
4501 2413 : return hdl->result ? hdl->result->fieldcnt : 0;
4502 : }
4503 :
4504 : int64_t
4505 438 : mapi_get_row_count(MapiHdl hdl)
4506 : {
4507 438 : mapi_hdl_check(hdl);
4508 438 : return hdl->result ? hdl->result->row_count : 0;
4509 : }
4510 :
4511 : int64_t
4512 0 : mapi_get_last_id(MapiHdl hdl)
4513 : {
4514 0 : mapi_hdl_check(hdl);
4515 0 : return hdl->result ? hdl->result->last_id : -1;
4516 : }
4517 :
4518 : char *
4519 928 : mapi_get_name(MapiHdl hdl, int fnr)
4520 : {
4521 928 : struct MapiResultSet *result;
4522 :
4523 928 : mapi_hdl_check0(hdl);
4524 928 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4525 928 : return result->fields[fnr].columnname;
4526 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4527 0 : return 0;
4528 : }
4529 :
4530 : char *
4531 4677915 : mapi_get_type(MapiHdl hdl, int fnr)
4532 : {
4533 4677915 : struct MapiResultSet *result;
4534 :
4535 4677915 : mapi_hdl_check0(hdl);
4536 4677915 : if ((result = hdl->result) != 0 &&
4537 4677915 : fnr >= 0 && fnr < result->fieldcnt) {
4538 4677915 : if (result->fields[fnr].columntype == NULL)
4539 : return "unknown";
4540 4677914 : return result->fields[fnr].columntype;
4541 : }
4542 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4543 0 : return 0;
4544 : }
4545 :
4546 : char *
4547 918 : mapi_get_table(MapiHdl hdl, int fnr)
4548 : {
4549 918 : struct MapiResultSet *result;
4550 :
4551 918 : mapi_hdl_check0(hdl);
4552 918 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4553 918 : return result->fields[fnr].tablename;
4554 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4555 0 : return 0;
4556 : }
4557 :
4558 : int
4559 926 : mapi_get_len(MapiHdl hdl, int fnr)
4560 : {
4561 926 : struct MapiResultSet *result;
4562 :
4563 926 : mapi_hdl_check0(hdl);
4564 926 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4565 926 : return result->fields[fnr].columnlength;
4566 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4567 0 : return 0;
4568 : }
4569 :
4570 : int
4571 921 : mapi_get_digits(MapiHdl hdl, int fnr)
4572 : {
4573 921 : struct MapiResultSet *result;
4574 :
4575 921 : mapi_hdl_check0(hdl);
4576 921 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4577 921 : return result->fields[fnr].digits;
4578 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4579 0 : return 0;
4580 : }
4581 :
4582 : int
4583 4 : mapi_get_scale(MapiHdl hdl, int fnr)
4584 : {
4585 4 : struct MapiResultSet *result;
4586 :
4587 4 : mapi_hdl_check0(hdl);
4588 4 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4589 4 : return result->fields[fnr].scale;
4590 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4591 0 : return 0;
4592 : }
4593 :
4594 : char *
4595 1055 : mapi_get_query(MapiHdl hdl)
4596 : {
4597 1055 : mapi_hdl_check0(hdl);
4598 1055 : if (hdl->query != NULL) {
4599 1055 : return strdup(hdl->query);
4600 : } else {
4601 : return NULL;
4602 : }
4603 : }
4604 :
4605 :
4606 : int
4607 9638 : mapi_get_querytype(MapiHdl hdl)
4608 : {
4609 9638 : struct MapiResultSet *result;
4610 :
4611 9638 : mapi_hdl_check0(hdl);
4612 9638 : if ((result = hdl->result) != 0)
4613 8578 : return result->querytype;
4614 1060 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4615 1060 : return 0; /* Q_PARSE! */
4616 : }
4617 :
4618 : int
4619 123 : mapi_get_tableid(MapiHdl hdl)
4620 : {
4621 123 : struct MapiResultSet *result;
4622 :
4623 123 : mapi_hdl_check0(hdl);
4624 123 : if ((result = hdl->result) != 0)
4625 123 : return result->tableid;
4626 0 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4627 0 : return 0;
4628 : }
4629 :
4630 : int64_t
4631 2934 : mapi_rows_affected(MapiHdl hdl)
4632 : {
4633 2934 : struct MapiResultSet *result;
4634 :
4635 2934 : mapi_hdl_check(hdl);
4636 2934 : if ((result = hdl->result) == NULL)
4637 : return 0;
4638 2934 : return result->row_count;
4639 : }
4640 :
4641 : int64_t
4642 3777 : mapi_get_querytime(MapiHdl hdl)
4643 : {
4644 3777 : struct MapiResultSet *result;
4645 :
4646 3777 : mapi_hdl_check(hdl);
4647 3777 : if ((result = hdl->result) == NULL)
4648 : return 0;
4649 3247 : return result->querytime;
4650 : }
4651 :
4652 : int64_t
4653 3777 : mapi_get_maloptimizertime(MapiHdl hdl)
4654 : {
4655 3777 : struct MapiResultSet *result;
4656 :
4657 3777 : mapi_hdl_check(hdl);
4658 3777 : if ((result = hdl->result) == NULL)
4659 : return 0;
4660 3247 : return result->maloptimizertime;
4661 : }
4662 :
4663 : int64_t
4664 3777 : mapi_get_sqloptimizertime(MapiHdl hdl)
4665 : {
4666 3777 : struct MapiResultSet *result;
4667 :
4668 3777 : mapi_hdl_check(hdl);
4669 3777 : if ((result = hdl->result) == NULL)
4670 : return 0;
4671 3247 : return result->sqloptimizertime;
4672 : }
4673 :
4674 : const char *
4675 191 : mapi_get_dbname(Mapi mid)
4676 : {
4677 191 : return msetting_string(mid->settings, MP_DATABASE);
4678 : }
4679 :
4680 : const char *
4681 5 : mapi_get_host(Mapi mid)
4682 : {
4683 5 : return msetting_string(mid->settings, MP_HOST);
4684 : }
4685 :
4686 : const char *
4687 5 : mapi_get_user(Mapi mid)
4688 : {
4689 5 : return msetting_string(mid->settings, MP_USER);;
4690 : }
4691 :
4692 : const char *
4693 0 : mapi_get_lang(Mapi mid)
4694 : {
4695 0 : return msetting_string(mid->settings, MP_LANGUAGE);;
4696 : }
4697 :
4698 : const char *
4699 0 : mapi_get_uri(Mapi mid)
4700 : {
4701 0 : return mid->uri;
4702 : }
4703 :
4704 : const char *
4705 1 : mapi_get_mapi_version(void)
4706 : {
4707 1 : return MAPI_VERSION;
4708 : }
4709 :
4710 : const char *
4711 0 : mapi_get_monet_version(Mapi mid)
4712 : {
4713 0 : mapi_check0(mid);
4714 0 : return mid->server ? mid->server : "";
4715 : }
4716 :
4717 : const char *
4718 0 : mapi_get_motd(Mapi mid)
4719 : {
4720 0 : mapi_check0(mid);
4721 0 : return mid->motd;
4722 : }
4723 :
4724 : bool
4725 0 : mapi_is_connected(Mapi mid)
4726 : {
4727 0 : return mid->connected;
4728 : }
4729 :
4730 : MapiHdl
4731 158 : mapi_get_active(Mapi mid)
4732 : {
4733 158 : return mid->active;
4734 : }
4735 :
4736 : msettings*
4737 0 : mapi_get_settings(Mapi mid)
4738 : {
4739 0 : return mid->settings;
4740 : }
4741 :
4742 :
4743 : MapiMsg
4744 1383 : mapi_wrap_streams(Mapi mid, stream *rstream, stream *wstream)
4745 : {
4746 : // do not use check_stream here yet because the socket is not yet in 'mid'
4747 1383 : const char *error_message;
4748 1383 : stream *error_stream;
4749 :
4750 1383 : assert(!isa_block_stream(rstream));
4751 1383 : assert(!isa_block_stream(wstream));
4752 :
4753 : // First send some NUL bytes. If we're accidentally connecting to the wrong
4754 : // port or protocol this may cause the remote server to close the
4755 : // connection. If we don't do this, the connection will often hang
4756 : // because the server expects us to speak first and we expect the server
4757 : // to speak first.
4758 : //
4759 : // Note that a pair of NUL bytes is a no-op message in MAPI.
4760 : //
4761 : // Surprisingly, it seems sending these NUL bytes makes non-TLS
4762 : // connection setup a little faster rather than slower!
4763 : static const char zeroes[8] = { 0 };
4764 : ssize_t to_write = sizeof(zeroes);
4765 2766 : while (to_write > 0) {
4766 1383 : ssize_t n = mnstr_write(wstream, zeroes, 1, to_write);
4767 1383 : if (n < 0) {
4768 0 : close_connection(mid);
4769 0 : return mapi_printError(mid, __func__, MERROR, "could not send leader block: %s", mnstr_peek_error(wstream));
4770 : }
4771 1383 : to_write -= (size_t)n;
4772 : }
4773 1383 : if (mnstr_flush(wstream, MNSTR_FLUSH_DATA) != 0) {
4774 0 : close_connection(mid);
4775 0 : return mapi_printError(mid, __func__, MERROR, "could not flush leader block: %s", mnstr_peek_error(wstream));
4776 : }
4777 :
4778 :
4779 1383 : stream *brstream = NULL;
4780 1383 : stream *bwstream = NULL;
4781 :
4782 1383 : bwstream = block_stream(wstream);
4783 1383 : if (bwstream == NULL || mnstr_errnr(bwstream) != MNSTR_NO__ERROR) {
4784 0 : error_stream = bwstream;
4785 0 : error_message = "block_stream wstream";
4786 0 : goto bailout;
4787 : }
4788 1383 : brstream = block_stream(rstream);
4789 1383 : if (brstream == NULL || mnstr_errnr(brstream) != MNSTR_NO__ERROR) {
4790 0 : error_stream = brstream;
4791 0 : error_message = "block_stream rstream";
4792 0 : goto bailout;
4793 : }
4794 :
4795 1383 : mid->to = bwstream;
4796 1383 : mid->from = brstream;
4797 1383 : return MOK;
4798 0 : bailout:
4799 : // adapted from the check_stream macro
4800 0 : if (brstream)
4801 0 : mnstr_destroy(brstream);
4802 0 : if (bwstream)
4803 0 : mnstr_destroy(bwstream);
4804 : // malloc failure is the only way these calls could have failed
4805 0 : return mapi_printError(mid, __func__, MERROR, "%s: %s", error_message, mnstr_peek_error(error_stream));
4806 : }
|