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 11851436 : mapi_clrError(Mapi mid)
819 : {
820 11851436 : assert(mid);
821 11851436 : if (mid->errorstr && mid->errorstr != mapi_nomem)
822 1067 : free(mid->errorstr);
823 11851436 : mid->action = 0; /* contains references to constants */
824 11851436 : mid->error = 0;
825 11851436 : mid->errorstr = 0;
826 11851436 : }
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 188124 : mapi_error(Mapi mid)
877 : {
878 188124 : assert(mid);
879 188124 : 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 182 : 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 182 : const char *s = prefix, *p = msg, *q;
1031 182 : const int len = (int) strlen(s);
1032 182 : const char t = s[len - 1];
1033 :
1034 308 : while (p && *p) {
1035 193 : fprintf(fd, "%*.*s%c", len - 1, len - 1, s, t);
1036 193 : s = "";
1037 :
1038 193 : q = strchr(p, '\n');
1039 193 : if (q) {
1040 126 : q++; /* also print the newline */
1041 126 : fprintf(fd, "%.*s", (int) (q - p), p);
1042 : } else {
1043 : /* print bit after last newline,
1044 : adding one ourselves */
1045 67 : fprintf(fd, "%s\n", p);
1046 67 : break; /* nothing more to do */
1047 : }
1048 126 : p = q;
1049 : }
1050 182 : }
1051 :
1052 : void
1053 61 : mapi_noexplain(Mapi mid, const char *errorprefix)
1054 : {
1055 61 : assert(mid);
1056 61 : if (mid->noexplain)
1057 0 : free(mid->noexplain);
1058 61 : mid->noexplain = errorprefix ? strdup(errorprefix) : NULL;
1059 61 : }
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 61 : mapi_explain_result(MapiHdl hdl, FILE *fd)
1117 : {
1118 61 : Mapi mid;
1119 :
1120 61 : if (hdl == NULL ||
1121 61 : hdl->result == NULL ||
1122 61 : hdl->result->errorstr == NULL)
1123 : return;
1124 61 : assert(hdl);
1125 61 : assert(hdl->result);
1126 61 : assert(hdl->result->errorstr);
1127 61 : mid = hdl->mid;
1128 61 : assert(mid);
1129 61 : if (mid->noexplain == NULL) {
1130 61 : const char *host = msetting_string(mid->settings, MP_HOST);
1131 61 : const char *user = msetting_string(mid->settings, MP_USER);
1132 61 : int port = msetting_long(mid->settings, MP_PORT);
1133 61 : if (host[0] == '/')
1134 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1135 : else
1136 61 : fprintf(fd, "MAPI = %s@%s:%d\n",
1137 : user, host, port);
1138 61 : if (mid->action)
1139 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1140 61 : if (hdl->query)
1141 61 : indented_print(hdl->query, "QUERY = ", fd);
1142 61 : indented_print(hdl->result->errorstr, "ERROR = !", fd);
1143 61 : if (msettings_lang_is_sql(mid->settings) && hdl->result->sqlstate[0])
1144 60 : indented_print(hdl->result->sqlstate, "CODE = ", fd);
1145 : } else {
1146 0 : clean_print(hdl->result->errorstr, mid->noexplain, fd);
1147 : }
1148 61 : fflush(fd);
1149 : }
1150 :
1151 : stream *
1152 2792 : mapi_get_to(Mapi mid)
1153 : {
1154 2792 : mapi_check0(mid);
1155 2792 : return mid->to;
1156 : }
1157 :
1158 : stream *
1159 1396 : mapi_get_from(Mapi mid)
1160 : {
1161 1396 : mapi_check0(mid);
1162 1396 : 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 30889 : new_result(MapiHdl hdl)
1379 : {
1380 30889 : struct MapiResultSet *result;
1381 :
1382 30889 : assert((hdl->lastresult == NULL && hdl->result == NULL) ||
1383 : (hdl->result != NULL && hdl->lastresult != NULL && hdl->lastresult->next == NULL));
1384 :
1385 30889 : 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 30889 : result = malloc(sizeof(*result));
1389 30889 : if (result == NULL)
1390 : return NULL;
1391 61778 : *result = (struct MapiResultSet) {
1392 : .hdl = hdl,
1393 : .tableid = -1,
1394 : .querytype = -1,
1395 : .last_id = -1,
1396 30889 : .cache.rowlimit = msetting_long(hdl->mid->settings, MP_REPLYSIZE),
1397 : .cache.reader = -1,
1398 : .commentonly = true,
1399 : };
1400 30889 : if (hdl->lastresult == NULL)
1401 30834 : 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 30889 : close_result(MapiHdl hdl)
1413 : {
1414 30889 : struct MapiResultSet *result;
1415 30889 : Mapi mid;
1416 30889 : int i;
1417 :
1418 30889 : result = hdl->result;
1419 30889 : if (result == NULL)
1420 : return MERROR;
1421 30889 : mid = hdl->mid;
1422 30889 : assert(mid != NULL);
1423 30889 : if (mid->trace)
1424 0 : printf("closing result set\n");
1425 30889 : if (result->tableid >= 0 && result->querytype != Q_PREPARE) {
1426 27575 : 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 27575 : assert(hdl->npending_close == 0 ||
1432 : (hdl->npending_close > 0 && hdl->pending_close != NULL));
1433 27575 : 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 27574 : } else if (mid->to != NULL) {
1446 : /* first close saved up to-be-closed tables */
1447 27574 : 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 27574 : hdl->npending_close = 0;
1462 27574 : if (hdl->pending_close)
1463 0 : free(hdl->pending_close);
1464 27574 : hdl->pending_close = NULL;
1465 27574 : 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 27575 : result->tableid = -1;
1480 : }
1481 30889 : if (mid->active == hdl &&
1482 3 : hdl->active == result &&
1483 0 : read_into_cache(hdl, -1) != MOK)
1484 : return MERROR;
1485 30889 : if( hdl->active == result)
1486 : return MERROR;
1487 : //assert(hdl->active != result);
1488 30889 : if (result->fields) {
1489 85648 : for (i = 0; i < result->maxfields; i++) {
1490 57753 : if (result->fields[i].tablename)
1491 57550 : free(result->fields[i].tablename);
1492 57753 : if (result->fields[i].columnname)
1493 57550 : free(result->fields[i].columnname);
1494 57753 : if (result->fields[i].columntype)
1495 57558 : free(result->fields[i].columntype);
1496 : }
1497 27895 : free(result->fields);
1498 : }
1499 30889 : result->fields = NULL;
1500 30889 : result->maxfields = result->fieldcnt = 0;
1501 30889 : if (result->cache.line) {
1502 169939 : for (i = 0; i < result->cache.writer; i++) {
1503 142043 : if (result->cache.line[i].rows)
1504 142043 : free(result->cache.line[i].rows);
1505 142043 : if (result->cache.line[i].anchors) {
1506 : int j;
1507 :
1508 86299 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
1509 75632 : if (result->cache.line[i].anchors[j]) {
1510 54520 : free(result->cache.line[i].anchors[j]);
1511 54520 : result->cache.line[i].anchors[j] = NULL;
1512 : }
1513 10667 : free(result->cache.line[i].anchors);
1514 : }
1515 142043 : if (result->cache.line[i].lens)
1516 10667 : free(result->cache.line[i].lens);
1517 : }
1518 27896 : free(result->cache.line);
1519 27896 : result->cache.line = NULL;
1520 27896 : result->cache.tuplecount = 0;
1521 : }
1522 30889 : if (result->errorstr && result->errorstr != mapi_nomem)
1523 70 : free(result->errorstr);
1524 30889 : result->errorstr = NULL;
1525 30889 : memset(result->sqlstate, 0, sizeof(result->sqlstate));
1526 30889 : result->hdl = NULL;
1527 30889 : hdl->result = result->next;
1528 30889 : if (hdl->result == NULL)
1529 30834 : hdl->lastresult = NULL;
1530 30889 : result->next = NULL;
1531 30889 : free(result);
1532 30889 : return MOK;
1533 : }
1534 :
1535 : static void
1536 80 : add_error(struct MapiResultSet *result, char *error)
1537 : {
1538 : /* concatenate the error messages */
1539 80 : size_t size = result->errorstr ? strlen(result->errorstr) : 0;
1540 :
1541 80 : if (strlen(error) > 6 && error[5] == '!' &&
1542 63 : (isdigit((unsigned char) error[0]) ||
1543 0 : (error[0] >= 'A' && error[0] <= 'Z')) &&
1544 63 : (isdigit((unsigned char) error[1]) ||
1545 1 : (error[1] >= 'A' && error[1] <= 'Z')) &&
1546 63 : (isdigit((unsigned char) error[2]) ||
1547 1 : (error[2] >= 'A' && error[2] <= 'Z')) &&
1548 63 : (isdigit((unsigned char) error[3]) ||
1549 0 : (error[3] >= 'A' && error[3] <= 'Z')) &&
1550 63 : (isdigit((unsigned char) error[4]) ||
1551 0 : (error[4] >= 'A' && error[4] <= 'Z'))) {
1552 63 : if (result->errorstr == NULL) {
1553 : /* remeber SQLSTATE for first error */
1554 61 : strcpy_len(result->sqlstate, error,
1555 : sizeof(result->sqlstate));
1556 : }
1557 : /* skip SQLSTATE */
1558 63 : error += 6;
1559 : }
1560 80 : REALLOC(result->errorstr, size + strlen(error) + 2);
1561 80 : if (result->errorstr == NULL)
1562 0 : result->errorstr = mapi_nomem;
1563 : else {
1564 80 : strcpy(result->errorstr + size, error);
1565 80 : strcat(result->errorstr + size, "\n");
1566 : }
1567 80 : }
1568 :
1569 : const char *
1570 10061 : mapi_result_error(MapiHdl hdl)
1571 : {
1572 10061 : 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 3726 : mapi_next_result(MapiHdl hdl)
1587 : {
1588 3726 : mapi_hdl_check(hdl);
1589 :
1590 6867 : while (hdl->result != NULL) {
1591 3196 : if (close_result(hdl) != MOK)
1592 : return MERROR;
1593 3196 : 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 3671 : 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 34681 : mapi_new_handle(Mapi mid)
1645 : {
1646 34681 : MapiHdl hdl;
1647 :
1648 34681 : mapi_check0(mid);
1649 :
1650 34681 : hdl = malloc(sizeof(*hdl));
1651 34681 : if (hdl == NULL) {
1652 0 : mapi_setError(mid, "Memory allocation failure", __func__, MERROR);
1653 0 : return NULL;
1654 : }
1655 34681 : *hdl = (struct MapiStatement) {
1656 : .mid = mid,
1657 : .needmore = false,
1658 : };
1659 : /* add to doubly-linked list */
1660 34681 : hdl->next = mid->first;
1661 34681 : mid->first = hdl;
1662 34681 : if (hdl->next)
1663 3704 : hdl->next->prev = hdl;
1664 : return hdl;
1665 : }
1666 :
1667 : /* close all result sets on the handle but don't close the handle itself */
1668 : static MapiMsg
1669 68489 : finish_handle(MapiHdl hdl)
1670 : {
1671 68489 : Mapi mid;
1672 68489 : int i;
1673 :
1674 68489 : if (hdl == NULL)
1675 : return MERROR;
1676 68489 : mid = hdl->mid;
1677 68767 : if (mid->active == hdl && !hdl->needmore && !mnstr_eof(mid->from) &&
1678 278 : read_into_cache(hdl, 0) != MOK)
1679 : return MERROR;
1680 68489 : if (mid->to) {
1681 68488 : if (hdl->needmore) {
1682 0 : assert(mid->active == NULL || mid->active == hdl);
1683 0 : hdl->needmore = false;
1684 0 : mid->active = hdl;
1685 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
1686 0 : check_stream(mid, mid->to, "write error on stream", mid->error);
1687 0 : read_into_cache(hdl, 0);
1688 : }
1689 68488 : for (i = 0; i < hdl->npending_close; i++) {
1690 0 : char msg[256];
1691 :
1692 0 : snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
1693 0 : mapi_log_record(mid, "CMD", "%s", msg);
1694 0 : mid->active = hdl;
1695 0 : if (mnstr_printf(mid->to, "%s", msg) < 0 ||
1696 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
1697 0 : close_connection(mid);
1698 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
1699 0 : break;
1700 : }
1701 0 : read_into_cache(hdl, 0);
1702 : }
1703 : }
1704 68489 : hdl->npending_close = 0;
1705 68489 : if (hdl->pending_close)
1706 0 : free(hdl->pending_close);
1707 68489 : hdl->pending_close = NULL;
1708 164671 : while (hdl->result) {
1709 27693 : if (close_result(hdl) != MOK)
1710 : return MERROR;
1711 27693 : if (hdl->needmore) {
1712 0 : assert(mid->active == NULL || mid->active == hdl);
1713 0 : hdl->needmore = false;
1714 0 : mid->active = hdl;
1715 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
1716 0 : check_stream(mid, mid->to, "write error on stream", mid->error);
1717 0 : read_into_cache(hdl, 0);
1718 : }
1719 : }
1720 : return MOK;
1721 : }
1722 :
1723 : /* Close a statement handle, discarding any unread output. */
1724 : MapiMsg
1725 34681 : mapi_close_handle(MapiHdl hdl)
1726 : {
1727 34681 : if (hdl == NULL)
1728 : return MOK;
1729 34681 : debugprint("entering %s\n", "mapi_close_handle");
1730 :
1731 : /* don't use mapi_check_hdl: it's ok if we're not connected */
1732 34681 : mapi_clrError(hdl->mid);
1733 :
1734 34681 : if (finish_handle(hdl) != MOK)
1735 : return MERROR;
1736 34681 : hdl->npending_close = 0;
1737 34681 : if (hdl->pending_close)
1738 0 : free(hdl->pending_close);
1739 34681 : hdl->pending_close = NULL;
1740 34681 : if (hdl->bindings)
1741 0 : free(hdl->bindings);
1742 34681 : hdl->bindings = NULL;
1743 34681 : hdl->maxbindings = 0;
1744 34681 : if (hdl->params)
1745 0 : free(hdl->params);
1746 34681 : hdl->params = NULL;
1747 34681 : hdl->maxparams = 0;
1748 34681 : if (hdl->query)
1749 32965 : free(hdl->query);
1750 34681 : hdl->query = NULL;
1751 34681 : if (hdl->template)
1752 0 : free(hdl->template);
1753 34681 : hdl->template = NULL;
1754 : /* remove from doubly-linked list */
1755 34681 : if (hdl->prev)
1756 1 : hdl->prev->next = hdl->next;
1757 34681 : if (hdl->next)
1758 3703 : hdl->next->prev = hdl->prev;
1759 34681 : if (hdl->mid->first == hdl)
1760 34680 : hdl->mid->first = hdl->next;
1761 34681 : hdl->prev = NULL;
1762 34681 : hdl->next = NULL;
1763 34681 : hdl->mid = NULL;
1764 34681 : free(hdl);
1765 34681 : return MOK;
1766 : }
1767 :
1768 : const struct MapiStruct MapiStructDefaults = {
1769 : .error = MOK,
1770 : .redirmax = 10,
1771 : .blk.eos = false,
1772 : .blk.lim = BLOCK,
1773 : };
1774 :
1775 : /* Allocate a new connection handle. */
1776 : Mapi
1777 393 : mapi_new(msettings *settings)
1778 : {
1779 393 : Mapi mid;
1780 393 : static ATOMIC_TYPE index = ATOMIC_VAR_INIT(0);
1781 :
1782 393 : mid = malloc(sizeof(*mid));
1783 393 : if (mid == NULL)
1784 : return NULL;
1785 393 : if (settings == NULL)
1786 394 : settings = msettings_create();
1787 394 : if (settings == NULL) {
1788 0 : free(mid);
1789 0 : return NULL;
1790 : }
1791 :
1792 : /* then fill in some details */
1793 393 : *mid = MapiStructDefaults;
1794 393 : mid->settings = settings;
1795 393 : mid->index = (uint32_t) ATOMIC_ADD(&index, 1); /* for distinctions in log records */
1796 393 : if ((mid->blk.buf = malloc(mid->blk.lim + 1)) == NULL) {
1797 0 : mapi_destroy(mid);
1798 0 : return NULL;
1799 : }
1800 393 : mid->blk.buf[0] = 0;
1801 393 : mid->blk.buf[mid->blk.lim] = 0;
1802 :
1803 : /* also the current timezone, seconds EAST of UTC */
1804 393 : long time_zone;
1805 : #if defined(_MSC_VER)
1806 : DYNAMIC_TIME_ZONE_INFORMATION tzinf;
1807 :
1808 : /* documentation says: UTC = localtime + Bias (in minutes),
1809 : * but experimentation during DST period says, UTC = localtime
1810 : * + Bias + DaylightBias, and presumably during non DST
1811 : * period, UTC = localtime + Bias */
1812 : switch (GetDynamicTimeZoneInformation(&tzinf)) {
1813 : case TIME_ZONE_ID_STANDARD: /* using standard time */
1814 : case TIME_ZONE_ID_UNKNOWN: /* no daylight saving time in this zone */
1815 : time_zone = -(int) tzinf.Bias * 60;
1816 : break;
1817 : case TIME_ZONE_ID_DAYLIGHT: /* using daylight saving time */
1818 : time_zone = -(int) (tzinf.Bias + tzinf.DaylightBias) * 60;
1819 : break;
1820 : default: /* aka TIME_ZONE_ID_INVALID */
1821 : /* call failed, we don't know the time zone */
1822 : time_zone = 0;
1823 : break;
1824 : }
1825 : #else
1826 393 : time_t t = time(NULL);
1827 393 : struct tm *local_tm = localtime_r(&t, &(struct tm){0});
1828 : #ifdef HAVE_TM_GMTOFF
1829 394 : time_zone = local_tm->tm_gmtoff;
1830 : #else
1831 : struct tm *gm_tm = gmtime_r(&t, &(struct tm){0});
1832 : time_t gt = mktime(gm_tm);
1833 : local_tm->tm_isdst=0; /* We need the difference without dst */
1834 : time_t lt = mktime(local_tm);
1835 : assert((int64_t) gt - (int64_t) lt >= (int64_t) INT_MIN && (int64_t) gt - (int64_t) lt <= (int64_t) INT_MAX);
1836 : time_zone = (long) (lt - gt);
1837 : #endif
1838 : #endif
1839 394 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
1840 394 : if (err)
1841 0 : mapi_setError(mid, err, __func__, MERROR);
1842 :
1843 : return mid;
1844 : }
1845 :
1846 : /* construct the uri field of a Mapi struct */
1847 : void
1848 206 : set_uri(Mapi mid)
1849 : {
1850 206 : const char *host = msetting_string(mid->settings, MP_HOST);
1851 205 : const char *database = msetting_string(mid->settings, MP_DATABASE);
1852 206 : int port = msetting_long(mid->settings, MP_PORT);
1853 206 : size_t urilen = strlen(host) + (database ? strlen(database) : 0) + 32;
1854 206 : char *uri = malloc(urilen);
1855 :
1856 : /* uri looks as follows:
1857 : * mapi:monetdb://host:port/database
1858 : * or
1859 : * mapi:monetdb:///some/path/to?database=database
1860 : */
1861 :
1862 206 : if (database != NULL) {
1863 206 : if (host[0] == '/') {
1864 0 : snprintf(uri, urilen, "mapi:monetdb://%s?database=%s",
1865 : host, database);
1866 : } else {
1867 206 : snprintf(uri, urilen, "mapi:monetdb://%s:%d/%s",
1868 : host, port, database);
1869 : }
1870 : } else {
1871 0 : if (host[0] == '/') {
1872 0 : snprintf(uri, urilen, "mapi:monetdb://%s",
1873 : host);
1874 : } else {
1875 0 : snprintf(uri, urilen, "mapi:monetdb://%s:%d",
1876 : host, port);
1877 : }
1878 : }
1879 :
1880 206 : if (mid->uri != NULL)
1881 0 : free(mid->uri);
1882 206 : mid->uri = uri;
1883 206 : }
1884 :
1885 : Mapi
1886 205 : mapi_mapiuri(const char *url, const char *user, const char *pass, const char *lang)
1887 : {
1888 205 : Mapi mid;
1889 :
1890 205 : if (!ATOMIC_TAS(&mapi_initialized)) {
1891 42 : if (mnstr_init() < 0)
1892 : return NULL;
1893 : }
1894 :
1895 205 : mid = mapi_new(NULL);
1896 206 : if (mid == NULL)
1897 : return NULL;
1898 :
1899 206 : if (url == NULL) {
1900 0 : mapi_setError(mid, "url is null", __func__, MERROR);
1901 0 : return mid;
1902 : }
1903 206 : if (user == NULL) {
1904 0 : mapi_setError(mid, "user is null", __func__, MERROR);
1905 0 : return mid;
1906 : }
1907 206 : if (pass == NULL) {
1908 0 : mapi_setError(mid, "pass is null", __func__, MERROR);
1909 0 : return mid;
1910 : }
1911 206 : if (lang == NULL) {
1912 0 : mapi_setError(mid, "lang is null", __func__, MERROR);
1913 0 : return mid;
1914 : }
1915 :
1916 206 : msettings_error err = NULL;
1917 206 : if ( (err = msetting_set_string(mid->settings, MP_USER, user))
1918 206 : || (err = msetting_set_string(mid->settings, MP_PASSWORD, pass))
1919 206 : || (err = msetting_set_string(mid->settings, MP_LANGUAGE, lang))
1920 : ) {
1921 : // in the old implementation we returned NULL but that doesn't
1922 : // make sense to me because above we already started returning
1923 : // mid with the error set.
1924 0 : mapi_setError(mid, err, __func__, MERROR);
1925 0 : return mid;
1926 : }
1927 :
1928 206 : char *error_message = NULL;
1929 206 : if (!msettings_parse_url(mid->settings, url, &error_message)) {
1930 0 : char *msg = error_message ? error_message : "malloc failed";
1931 0 : mapi_setError(mid, msg, __func__, MERROR);
1932 0 : free(error_message);
1933 0 : return mid;
1934 : }
1935 :
1936 206 : set_uri(mid);
1937 :
1938 206 : return mid;
1939 : }
1940 :
1941 : /* Allocate a new connection handle and fill in the information needed
1942 : to connect to a server, but don't connect yet. */
1943 : Mapi
1944 188 : mapi_mapi(const char *host, int port, const char *username,
1945 : const char *password, const char *lang, const char *dbname)
1946 : {
1947 188 : Mapi mid;
1948 :
1949 188 : if (!ATOMIC_TAS(&mapi_initialized)) {
1950 183 : if (mnstr_init() < 0)
1951 : return NULL;
1952 : }
1953 :
1954 188 : mid = mapi_new(NULL);
1955 188 : if (mid == NULL)
1956 : return NULL;
1957 188 : msettings *settings = mid->settings;
1958 :
1959 188 : if (lang == NULL)
1960 0 : lang = "sql";
1961 :
1962 188 : const char *sockdir = NULL;
1963 188 : if (host && host[0] == '/') {
1964 : sockdir = host;
1965 : host = NULL;
1966 : }
1967 :
1968 126 : msettings_error err = NULL;
1969 314 : do {
1970 126 : if (host && (err = msetting_set_string(settings, MP_HOST, host)))
1971 : break;
1972 188 : if (sockdir && (err = msetting_set_string(settings, MP_SOCKDIR, sockdir)))
1973 : break;
1974 188 : if (username && (err = msetting_set_string(settings, MP_USER, username)))
1975 : break;
1976 188 : if (password && (err = msetting_set_string(settings, MP_PASSWORD, password)))
1977 : break;
1978 188 : if (lang && (err = msetting_set_string(settings, MP_LANGUAGE, lang)))
1979 : break;
1980 188 : if (dbname && (err = msetting_set_string(settings, MP_DATABASE, dbname)))
1981 : break;
1982 188 : if (port > 0 && (err = msetting_set_long(settings, MP_PORT, port)))
1983 : break;
1984 : } while (0);
1985 188 : if (err) {
1986 0 : mapi_setError(mid, err, __func__, MERROR);
1987 0 : return mid;
1988 : }
1989 :
1990 : return mid;
1991 : }
1992 :
1993 : Mapi
1994 0 : mapi_settings(msettings *settings)
1995 : {
1996 0 : assert(settings);
1997 0 : Mapi mid = mapi_new(settings);
1998 0 : if (mid == NULL)
1999 : return mid;
2000 :
2001 0 : set_uri(mid);
2002 0 : return mid;
2003 : }
2004 :
2005 :
2006 : /* Close a connection and free all memory associated with the
2007 : connection handle. */
2008 : MapiMsg
2009 373 : mapi_destroy(Mapi mid)
2010 : {
2011 373 : char **r;
2012 :
2013 373 : mapi_clrError(mid);
2014 :
2015 374 : while (mid->first)
2016 1 : mapi_close_handle(mid->first);
2017 373 : if (mid->connected)
2018 170 : (void) mapi_disconnect(mid);
2019 373 : if (mid->tracelog)
2020 0 : close_stream(mid->tracelog);
2021 :
2022 373 : free(mid->blk.buf);
2023 373 : free(mid->motd);
2024 373 : free(mid->server);
2025 373 : free(mid->uri);
2026 373 : free(mid->tracebuffer);
2027 373 : free(mid->noexplain);
2028 373 : if (mid->errorstr && mid->errorstr != mapi_nomem)
2029 0 : free(mid->errorstr);
2030 :
2031 373 : msettings_destroy(mid->settings);
2032 :
2033 373 : r = mid->redirects;
2034 373 : while (*r) {
2035 0 : free(*r);
2036 0 : r++;
2037 : }
2038 :
2039 373 : free(mid);
2040 373 : return MOK;
2041 : }
2042 :
2043 : /* Create a connection handle and connect to the server using the
2044 : specified parameters. */
2045 : Mapi
2046 11 : mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
2047 : {
2048 11 : Mapi mid;
2049 :
2050 11 : mid = mapi_mapi(host, port, username, password, lang, dbname);
2051 11 : if (mid && mid->error == MOK)
2052 11 : mapi_reconnect(mid); /* actually, initial connect */
2053 11 : return mid;
2054 : }
2055 :
2056 : /* Returns an malloced NULL-terminated array with redirects */
2057 : char **
2058 0 : mapi_resolve(const char *host, int port, const char *pattern)
2059 : {
2060 0 : int rmax;
2061 0 : Mapi mid;
2062 :
2063 : /* if it doesn't make sense, don't try to crash */
2064 0 : if (pattern == NULL)
2065 : return NULL;
2066 :
2067 0 : mid = mapi_mapi(host, port, "mero", "mero", "resolve", pattern);
2068 0 : if (mid) {
2069 0 : if (mid->error == MOK) {
2070 0 : rmax = mid->redirmax;
2071 0 : mid->redirmax = 0;
2072 0 : mapi_reconnect(mid); /* real connect, don't follow redirects */
2073 0 : mid->redirmax = rmax;
2074 0 : if (mid->error == MOK) {
2075 0 : close_connection(mid); /* we didn't expect a connection actually */
2076 : } else {
2077 0 : char **ret = malloc(sizeof(char *) * MAXREDIR);
2078 0 : memcpy(ret, mid->redirects, sizeof(char *) * MAXREDIR);
2079 0 : mid->redirects[0] = NULL; /* make sure the members aren't freed */
2080 0 : mapi_destroy(mid);
2081 0 : return ret;
2082 : }
2083 : }
2084 0 : mapi_destroy(mid);
2085 : }
2086 : return NULL;
2087 : }
2088 :
2089 : void
2090 1377 : close_connection(Mapi mid)
2091 : {
2092 1377 : MapiHdl hdl;
2093 1377 : struct MapiResultSet *result;
2094 :
2095 1377 : mid->connected = false;
2096 1377 : mid->active = NULL;
2097 1378 : for (hdl = mid->first; hdl; hdl = hdl->next) {
2098 1 : hdl->active = NULL;
2099 1 : for (result = hdl->result; result; result = result->next)
2100 0 : result->tableid = -1;
2101 : }
2102 : /* finish channels */
2103 : /* Make sure that the write- (to-) stream is closed first,
2104 : * as the related read- (from-) stream closes the shared
2105 : * socket; see also src/common/stream.c:socket_close .
2106 : */
2107 1377 : if (mid->to) {
2108 1377 : close_stream(mid->to);
2109 1377 : mid->to = 0;
2110 : }
2111 1377 : if (mid->from) {
2112 1377 : close_stream(mid->from);
2113 1377 : mid->from = 0;
2114 : }
2115 1377 : mid->redircnt = 0;
2116 1377 : mapi_log_record(mid, "C", "Connection closed");
2117 1377 : }
2118 :
2119 : MapiMsg
2120 1367 : mapi_disconnect(Mapi mid)
2121 : {
2122 1367 : mapi_check(mid);
2123 :
2124 1367 : close_connection(mid);
2125 1367 : return MOK;
2126 : }
2127 :
2128 : /* Set callback function to retrieve or send file content for COPY
2129 : * INTO queries.
2130 : *
2131 : * char *getfile(void *private, const char *filename, bool binary,
2132 : * uint64_6 offset, size_t *size);
2133 : * Retrieve data from a file.
2134 : *
2135 : * The arguments are:
2136 : * private - the value of the filecontentprivate argument to
2137 : * mapi_setfilecallback;
2138 : * filename - the file to read (the application is free to interpret
2139 : * this any way it wants, including getting data over the
2140 : * Internet);
2141 : * binary - if set, the file is expected to contain binary data and
2142 : * should therefore be opened in binary mode, otherwise the
2143 : * file is expected to contain data in the UTF-8 encoding (of
2144 : * course, the application is free to transparently convert
2145 : * from the actual encoding to UTF-8);
2146 : * offset - the line number of the first line to be retrieved (this is
2147 : * one-based, i.e. the start of the file has line number one;
2148 : * lines are terminated by '\n');
2149 : * size - pointer in which to return the size of the chunk that is
2150 : * being returned.
2151 : *
2152 : * The callback function is expected to return data in chunks until it
2153 : * indicates to the caller that there is no more data or an error has
2154 : * occurred. Chunks can be any size. The caller does not modify or
2155 : * free the data returned. The size of the chunk being returned is
2156 : * stored in the size argument. Errors are indicated by returning a
2157 : * string containing an error message and setting *size to zero. The
2158 : * error message should not contain any newlines. Any call to the
2159 : * callback function is allowed to return an error.
2160 : *
2161 : * The first call to the callback function contains values for
2162 : * filename, binary, and offset. These parameters are all 0 for all
2163 : * subsequent calls for continuation data from the same file.
2164 : *
2165 : * If the caller has retrieved enough data before the file is
2166 : * exhausted, it calls the callback function one more time with a NULL
2167 : * pointer for the size argument. This gives the callback function
2168 : * the opportunity to free its resources (e.g. close the file).
2169 : *
2170 : * If there is no more data to be returned, the callback function
2171 : * returns a NULL pointer and sets *size to zero. No more calls for
2172 : * the current file will be made.
2173 : *
2174 : * Note that if the file to be read is empty, or contains fewer lines
2175 : * than the requested offset, the first call to the callback function
2176 : * may return NULL.
2177 : *
2178 : * char *putfile(void *private, const char *filename, bool binary,
2179 : * const void *data, size_t size);
2180 : * Send data to a file.
2181 : *
2182 : * The arguments are:
2183 : * private - the value of the filecontentprivate argument to
2184 : * mapi_setfilecallback;
2185 : * filename - the file to be written, files are always written as text
2186 : * files;
2187 : * binary - if set, the data to be written is binary and the file
2188 : * should therefore be opened in binary mode, otherwise the
2189 : * data is UTF-8 encoded text;
2190 : * data - the data to be written;
2191 : * size - the size of the data to be written.
2192 : *
2193 : * The callback is called multiple time to write a single file. The
2194 : * first time, a filename is specified, all subsequent times, the
2195 : * filename argument is NULL. When all data has been written, the
2196 : * callback function is called one last time with NULL pointer for the
2197 : * data argument so that the callback function can free any resources.
2198 : *
2199 : * When an error occurs, the callback function returns a string
2200 : * containing an error message after which the callback will not be
2201 : * called again for the same file. Otherwise, the callback function
2202 : * returns NULL.
2203 : *
2204 : * Note also that multibyte sequences may be split over two calls.
2205 : */
2206 : void
2207 144 : mapi_setfilecallback2(Mapi mid,
2208 : char *(*getfilecontent)(void *,
2209 : const char *, bool,
2210 : uint64_t, size_t *),
2211 : char *(*putfilecontent)(void *,
2212 : const char *, bool,
2213 : const void *, size_t),
2214 : void *filecontentprivate)
2215 : {
2216 144 : mid->getfilecontent = getfilecontent;
2217 144 : mid->putfilecontent = putfilecontent;
2218 144 : mid->filecontentprivate = filecontentprivate;
2219 144 : mid->putfilecontent_old = NULL;
2220 144 : mid->filecontentprivate_old = NULL;
2221 144 : }
2222 :
2223 : static char *
2224 0 : putfilecontent_wrap(void *priv, const char *filename, bool binary, const void *data, size_t size)
2225 : {
2226 0 : Mapi mid = priv;
2227 0 : void *priv_old = mid->filecontentprivate_old;
2228 0 : if (filename && binary)
2229 : return "Client does not support writing binary files";
2230 0 : return mid->putfilecontent_old(priv_old, filename, data, size);
2231 : }
2232 :
2233 : /* DEPRECATED. Set callback function to retrieve or send file content for COPY
2234 : * INTO queries.
2235 : *
2236 : * Deprecated because it does not support binary downloads.
2237 : * Use mapi_setfilecallback2 instead.
2238 : */
2239 : void
2240 0 : mapi_setfilecallback(Mapi mid,
2241 : char *(*getfilecontent)(void *,
2242 : const char *, bool,
2243 : uint64_t, size_t *),
2244 : char *(*putfilecontent)(void *,
2245 : const char *,
2246 : const void *, size_t),
2247 : void *filecontentprivate)
2248 : {
2249 0 : mid->getfilecontent = getfilecontent;
2250 0 : mid->putfilecontent = putfilecontent_wrap;
2251 0 : mid->filecontentprivate = mid;
2252 0 : mid->putfilecontent_old = putfilecontent;
2253 0 : mid->filecontentprivate_old = filecontentprivate;
2254 0 : }
2255 :
2256 : #define testBinding(hdl,fnr) \
2257 : do { \
2258 : mapi_hdl_check(hdl); \
2259 : if (fnr < 0) { \
2260 : return mapi_setError(hdl->mid, \
2261 : "Illegal field number", \
2262 : __func__, MERROR); \
2263 : } \
2264 : /* make sure there is enough space */ \
2265 : if (fnr >= hdl->maxbindings) \
2266 : mapi_extend_bindings(hdl, fnr); \
2267 : } while (0)
2268 :
2269 : #define testParam(hdl, fnr) \
2270 : do { \
2271 : mapi_hdl_check(hdl); \
2272 : if (fnr < 0) { \
2273 : return mapi_setError(hdl->mid, \
2274 : "Illegal param number", \
2275 : __func__, MERROR); \
2276 : } \
2277 : if (fnr >= hdl->maxparams) \
2278 : mapi_extend_params(hdl, fnr); \
2279 : } while (0)
2280 :
2281 : MapiMsg
2282 0 : mapi_bind(MapiHdl hdl, int fnr, char **ptr)
2283 : {
2284 0 : testBinding(hdl, fnr);
2285 0 : hdl->bindings[fnr].outparam = ptr;
2286 :
2287 0 : hdl->bindings[fnr].outtype = MAPI_AUTO;
2288 0 : return MOK;
2289 : }
2290 :
2291 : MapiMsg
2292 0 : mapi_bind_var(MapiHdl hdl, int fnr, int type, void *ptr)
2293 : {
2294 0 : testBinding(hdl, fnr);
2295 0 : hdl->bindings[fnr].outparam = ptr;
2296 :
2297 0 : if (type >= 0 && type < MAPI_NUMERIC)
2298 0 : hdl->bindings[fnr].outtype = type;
2299 : else
2300 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2301 0 : return MOK;
2302 : }
2303 :
2304 : MapiMsg
2305 0 : mapi_bind_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2306 : {
2307 0 : if (mapi_bind_var(hdl, fnr, MAPI_NUMERIC, ptr))
2308 0 : return hdl->mid->error;
2309 :
2310 0 : hdl->bindings[fnr].scale = scale;
2311 0 : hdl->bindings[fnr].precision = prec;
2312 0 : return MOK;
2313 : }
2314 :
2315 : MapiMsg
2316 0 : mapi_clear_bindings(MapiHdl hdl)
2317 : {
2318 0 : mapi_hdl_check(hdl);
2319 0 : if (hdl->bindings)
2320 0 : memset(hdl->bindings, 0, hdl->maxbindings * sizeof(*hdl->bindings));
2321 : return MOK;
2322 : }
2323 :
2324 : MapiMsg
2325 0 : mapi_param_type(MapiHdl hdl, int fnr, int ctype, int sqltype, void *ptr)
2326 : {
2327 0 : testParam(hdl, fnr);
2328 0 : hdl->params[fnr].inparam = ptr;
2329 :
2330 0 : if (ctype >= 0 && ctype < MAPI_NUMERIC)
2331 0 : hdl->params[fnr].intype = ctype;
2332 : else
2333 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2334 0 : hdl->params[fnr].sizeptr = NULL;
2335 0 : hdl->params[fnr].outtype = sqltype;
2336 0 : hdl->params[fnr].scale = 0;
2337 0 : hdl->params[fnr].precision = 0;
2338 0 : return MOK;
2339 : }
2340 :
2341 : MapiMsg
2342 0 : mapi_param_string(MapiHdl hdl, int fnr, int sqltype, char *ptr, int *sizeptr)
2343 : {
2344 0 : testParam(hdl, fnr);
2345 0 : hdl->params[fnr].inparam = (void *) ptr;
2346 :
2347 0 : hdl->params[fnr].intype = MAPI_VARCHAR;
2348 0 : hdl->params[fnr].sizeptr = sizeptr;
2349 0 : hdl->params[fnr].outtype = sqltype;
2350 0 : hdl->params[fnr].scale = 0;
2351 0 : hdl->params[fnr].precision = 0;
2352 0 : return MOK;
2353 : }
2354 :
2355 : MapiMsg
2356 0 : mapi_param(MapiHdl hdl, int fnr, char **ptr)
2357 : {
2358 0 : return mapi_param_type(hdl, fnr, MAPI_AUTO, MAPI_AUTO, ptr);
2359 : }
2360 :
2361 : MapiMsg
2362 0 : mapi_param_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2363 : {
2364 0 : if (mapi_param_type(hdl, fnr, MAPI_NUMERIC, MAPI_NUMERIC, ptr))
2365 0 : return hdl->mid->error;
2366 :
2367 0 : hdl->params[fnr].scale = scale;
2368 0 : hdl->params[fnr].precision = prec;
2369 0 : return MOK;
2370 : }
2371 :
2372 : MapiMsg
2373 2 : mapi_clear_params(MapiHdl hdl)
2374 : {
2375 2 : mapi_hdl_check(hdl);
2376 2 : if (hdl->params)
2377 0 : memset(hdl->params, 0, hdl->maxparams * sizeof(*hdl->params));
2378 : return MOK;
2379 : }
2380 :
2381 : static MapiHdl
2382 31747 : prepareQuery(MapiHdl hdl, const char *cmd)
2383 : {
2384 31747 : if (hdl && cmd) {
2385 31747 : if (hdl->query)
2386 2098 : free(hdl->query);
2387 31747 : hdl->query = strdup(cmd);
2388 31747 : assert(hdl->query);
2389 31747 : if (hdl->template) {
2390 0 : free(hdl->template);
2391 0 : hdl->template = NULL;
2392 : }
2393 : }
2394 31747 : return hdl;
2395 : }
2396 :
2397 :
2398 : MapiMsg
2399 6 : mapi_set_timeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
2400 : {
2401 6 : mapi_check(mid);
2402 6 : if (mid->trace)
2403 0 : printf("Set timeout to %u\n", timeout);
2404 6 : mnstr_settimeout(mid->to, timeout, callback, callback_data);
2405 6 : mnstr_settimeout(mid->from, timeout, callback, callback_data);
2406 6 : return MOK;
2407 : }
2408 :
2409 : MapiMsg
2410 6 : mapi_timeout(Mapi mid, unsigned int timeout)
2411 : {
2412 6 : return mapi_set_timeout(mid, timeout, NULL, NULL);
2413 : }
2414 :
2415 : MapiMsg
2416 10 : mapi_Xcommand(Mapi mid, const char *cmdname, const char *cmdvalue)
2417 : {
2418 10 : MapiHdl hdl;
2419 :
2420 10 : mapi_check(mid);
2421 10 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
2422 : return MERROR;
2423 20 : if (mnstr_printf(mid->to, "X" "%s %s\n", cmdname, cmdvalue) < 0 ||
2424 10 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
2425 0 : close_connection(mid);
2426 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
2427 0 : return MERROR;
2428 : }
2429 10 : mapi_log_record(mid, "X", "X" "%s %s\n", cmdname, cmdvalue);
2430 10 : hdl = prepareQuery(mapi_new_handle(mid), "Xcommand");
2431 10 : if (hdl == NULL)
2432 : return MERROR;
2433 10 : mid->active = hdl;
2434 10 : read_into_cache(hdl, 0);
2435 10 : mapi_close_handle(hdl); /* reads away any output */
2436 10 : return MOK;
2437 : }
2438 :
2439 : MapiMsg
2440 0 : mapi_prepare_handle(MapiHdl hdl, const char *cmd)
2441 : {
2442 0 : mapi_hdl_check(hdl);
2443 0 : if (finish_handle(hdl) != MOK)
2444 : return MERROR;
2445 0 : prepareQuery(hdl, cmd);
2446 0 : hdl->template = strdup(hdl->query);
2447 0 : assert(hdl->template);
2448 0 : return hdl->mid->error;
2449 : }
2450 :
2451 : MapiHdl
2452 0 : mapi_prepare(Mapi mid, const char *cmd)
2453 : {
2454 0 : MapiHdl hdl;
2455 :
2456 0 : mapi_check0(mid);
2457 0 : hdl = mapi_new_handle(mid);
2458 0 : if (hdl == NULL)
2459 : return NULL;
2460 0 : mapi_prepare_handle(hdl, cmd);
2461 0 : return hdl;
2462 : }
2463 :
2464 : /*
2465 : * Building the query string using replacement of values requires
2466 : * some care to not overflow the space allocated.
2467 : */
2468 : #define checkSpace(len) \
2469 : do { \
2470 : /* note: k==strlen(hdl->query) */ \
2471 : if (k+len >= lim) { \
2472 : lim = k + len + MAPIBLKSIZE; \
2473 : char *q = realloc(hdl->query, lim); \
2474 : if (q == NULL) { \
2475 : free(hdl->query); \
2476 : hdl->query = NULL; \
2477 : return; \
2478 : } \
2479 : hdl->query = q; \
2480 : } \
2481 : } while (0)
2482 :
2483 : static void
2484 31698 : mapi_param_store(MapiHdl hdl)
2485 : {
2486 31698 : char *val, buf[MAPIBLKSIZE];
2487 31698 : char *p = hdl->template, *q;
2488 31698 : int i;
2489 31698 : size_t k;
2490 31698 : size_t lim;
2491 :
2492 31698 : if (hdl->template == 0)
2493 : return;
2494 :
2495 0 : lim = strlen(hdl->template) + MAPIBLKSIZE;
2496 0 : REALLOC(hdl->query, lim);
2497 0 : if (hdl->query == NULL)
2498 : return;
2499 0 : hdl->query[0] = 0;
2500 0 : k = 0;
2501 :
2502 0 : q = strchr(hdl->template, PLACEHOLDER);
2503 0 : i = 0;
2504 : /* loop invariant: k == strlen(hdl->query) */
2505 0 : while (q && i < hdl->maxparams) {
2506 0 : if (q > p && *(q - 1) == '\\') {
2507 0 : q = strchr(q + 1, PLACEHOLDER);
2508 0 : continue;
2509 : }
2510 :
2511 0 : if (k + (q - p) >= lim) {
2512 0 : lim += MAPIBLKSIZE;
2513 0 : REALLOC(hdl->query, lim);
2514 0 : if (hdl->query == NULL)
2515 : return;
2516 : }
2517 0 : memcpy(hdl->query + k, p, q - p);
2518 0 : k += q - p;
2519 0 : hdl->query[k] = 0;
2520 :
2521 0 : if (hdl->params[i].inparam == 0) {
2522 0 : char *nullstr = "NULL";
2523 0 : checkSpace(5);
2524 0 : if (msettings_lang_is_mal(hdl->mid->settings))
2525 0 : nullstr = "nil";
2526 0 : strcpy(hdl->query + k, nullstr);
2527 : } else {
2528 0 : void *src = hdl->params[i].inparam; /* abbrev */
2529 :
2530 0 : switch (hdl->params[i].intype) {
2531 0 : case MAPI_TINY:
2532 0 : checkSpace(5);
2533 0 : snprintf(hdl->query + k, lim - k, "%hhd", *(signed char *) src);
2534 0 : break;
2535 0 : case MAPI_UTINY:
2536 0 : checkSpace(5);
2537 0 : snprintf(hdl->query + k, lim - k, "%hhu", *(unsigned char *) src);
2538 0 : break;
2539 0 : case MAPI_SHORT:
2540 0 : checkSpace(10);
2541 0 : snprintf(hdl->query + k, lim - k, "%hd", *(short *) src);
2542 0 : break;
2543 0 : case MAPI_USHORT:
2544 0 : checkSpace(10);
2545 0 : snprintf(hdl->query + k, lim - k, "%hu", *(unsigned short *) src);
2546 0 : break;
2547 0 : case MAPI_INT:
2548 0 : checkSpace(20);
2549 0 : snprintf(hdl->query + k, lim - k, "%d", *(int *) src);
2550 0 : break;
2551 0 : case MAPI_UINT:
2552 0 : checkSpace(20);
2553 0 : snprintf(hdl->query + k, lim - k, "%u", *(unsigned int *) src);
2554 0 : break;
2555 0 : case MAPI_LONG:
2556 0 : checkSpace(20);
2557 0 : snprintf(hdl->query + k, lim - k, "%ld", *(long *) src);
2558 0 : break;
2559 0 : case MAPI_ULONG:
2560 0 : checkSpace(20);
2561 0 : snprintf(hdl->query + k, lim - k, "%lu", *(unsigned long *) src);
2562 0 : break;
2563 0 : case MAPI_LONGLONG:
2564 0 : checkSpace(30);
2565 0 : snprintf(hdl->query + k, lim - k, "%"PRId64, *(int64_t *) src);
2566 0 : break;
2567 0 : case MAPI_ULONGLONG:
2568 0 : checkSpace(30);
2569 0 : snprintf(hdl->query + k, lim - k, "%"PRIu64, *(uint64_t *) src);
2570 0 : break;
2571 0 : case MAPI_FLOAT:
2572 0 : checkSpace(30);
2573 0 : snprintf(hdl->query + k, lim - k, "%.9g", *(float *) src);
2574 0 : break;
2575 0 : case MAPI_DOUBLE:
2576 0 : checkSpace(30);
2577 0 : snprintf(hdl->query + k, lim - k, "%.17g", *(double *) src);
2578 0 : break;
2579 0 : case MAPI_DATE:
2580 0 : checkSpace(50);
2581 0 : snprintf(hdl->query + k, lim - k,
2582 : "DATE '%04hd-%02hu-%02hu'",
2583 0 : ((MapiDate *) src)->year,
2584 0 : ((MapiDate *) src)->month,
2585 0 : ((MapiDate *) src)->day);
2586 0 : break;
2587 0 : case MAPI_TIME:
2588 0 : checkSpace(60);
2589 0 : snprintf(hdl->query + k, lim - k,
2590 : "TIME '%02hu:%02hu:%02hu'",
2591 0 : ((MapiTime *) src)->hour,
2592 0 : ((MapiTime *) src)->minute,
2593 0 : ((MapiTime *) src)->second);
2594 0 : break;
2595 0 : case MAPI_DATETIME:
2596 0 : checkSpace(110);
2597 0 : snprintf(hdl->query + k, lim - k,
2598 : "TIMESTAMP '%04hd-%02hu-%02hu %02hu:%02hu:%02hu.%09u'",
2599 0 : ((MapiDateTime *) src)->year,
2600 0 : ((MapiDateTime *) src)->month,
2601 0 : ((MapiDateTime *) src)->day,
2602 0 : ((MapiDateTime *) src)->hour,
2603 0 : ((MapiDateTime *) src)->minute,
2604 0 : ((MapiDateTime *) src)->second,
2605 : ((MapiDateTime *) src)->fraction);
2606 0 : break;
2607 0 : case MAPI_CHAR:
2608 0 : buf[0] = *(char *) src;
2609 0 : buf[1] = 0;
2610 0 : val = mapi_quote(buf, 1);
2611 : /* note: k==strlen(hdl->query) */
2612 0 : if (k + strlen(val) + 3 >= lim) {
2613 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2614 0 : char *q = realloc(hdl->query, lim);
2615 0 : if (q == NULL) {
2616 0 : free(hdl->query);
2617 0 : hdl->query = NULL;
2618 0 : free(val);
2619 0 : return;
2620 : }
2621 0 : hdl->query = q;
2622 : }
2623 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2624 0 : free(val);
2625 0 : break;
2626 0 : case MAPI_VARCHAR:
2627 0 : val = mapi_quote((char *) src, hdl->params[i].sizeptr ? *hdl->params[i].sizeptr : -1);
2628 : /* note: k==strlen(hdl->query) */
2629 0 : if (k + strlen(val) + 3 >= lim) {
2630 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2631 0 : char *q = realloc(hdl->query, lim);
2632 0 : if (q == NULL) {
2633 0 : free(hdl->query);
2634 0 : hdl->query = NULL;
2635 0 : free(val);
2636 0 : return;
2637 : }
2638 0 : hdl->query = q;
2639 : }
2640 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2641 0 : free(val);
2642 0 : break;
2643 0 : default:
2644 0 : strcpy_len(hdl->query + k, src, lim - k);
2645 0 : break;
2646 : }
2647 : }
2648 0 : k += strlen(hdl->query + k);
2649 :
2650 0 : i++;
2651 0 : p = q + 1;
2652 0 : q = strchr(p, PLACEHOLDER);
2653 : }
2654 0 : checkSpace(strlen(p) + 1);
2655 0 : strcpy(hdl->query + k, p);
2656 0 : if (hdl->mid->trace)
2657 0 : printf("param_store: result=%s\n", hdl->query);
2658 : return;
2659 : }
2660 :
2661 : /* Read one more line from the input stream and return it. This
2662 : returns a pointer into the input buffer, so the data needs to be
2663 : copied if it is to be retained. */
2664 : static char *
2665 1555421 : read_line(Mapi mid)
2666 : {
2667 1555421 : char *reply;
2668 1555421 : char *nl;
2669 1555421 : char *s; /* from where to search for newline */
2670 :
2671 1555421 : if (mid->active == NULL)
2672 : return NULL;
2673 :
2674 : /* check if we need to read more blocks to get a new line */
2675 1555421 : mid->blk.eos = false;
2676 1555421 : s = mid->blk.buf + mid->blk.nxt;
2677 1858114 : while ((nl = strchr(s, '\n')) == NULL && !mid->blk.eos) {
2678 302694 : ssize_t len;
2679 :
2680 302694 : if (mid->blk.lim - mid->blk.end < BLOCK) {
2681 13783 : int len;
2682 :
2683 13783 : len = mid->blk.lim;
2684 13783 : if (mid->blk.nxt <= BLOCK) {
2685 : /* extend space */
2686 3247 : len += BLOCK;
2687 : }
2688 13783 : REALLOC(mid->blk.buf, len + 1);
2689 13783 : if (mid->blk.nxt > 0) {
2690 10940 : memmove(mid->blk.buf, mid->blk.buf + mid->blk.nxt, mid->blk.end - mid->blk.nxt + 1);
2691 10940 : mid->blk.end -= mid->blk.nxt;
2692 10940 : mid->blk.nxt = 0;
2693 : }
2694 13783 : mid->blk.lim = len;
2695 : }
2696 :
2697 302694 : s = mid->blk.buf + mid->blk.end;
2698 :
2699 : /* fetch one more block */
2700 302694 : if (mid->trace)
2701 0 : printf("fetch next block: start at:%d\n", mid->blk.end);
2702 302694 : len = mnstr_read(mid->from, mid->blk.buf + mid->blk.end, 1, BLOCK);
2703 302693 : check_stream(mid, mid->from, "Connection terminated during read line", (mid->blk.eos = true, (char *) 0));
2704 302693 : mapi_log_data(mid, "RECV", mid->blk.buf + mid->blk.end, len);
2705 302693 : mid->blk.buf[mid->blk.end + len] = 0;
2706 302693 : if (mid->trace) {
2707 0 : printf("got next block: length:%zd\n", len);
2708 0 : printf("text:%s\n", mid->blk.buf + mid->blk.end);
2709 : }
2710 302693 : if (len == 0) { /* add prompt */
2711 141616 : if (mnstr_eof(mid->from))
2712 : return NULL;
2713 141616 : if (mid->blk.end > mid->blk.nxt) {
2714 : /* add fake newline since newline was
2715 : * missing from server */
2716 5 : nl = mid->blk.buf + mid->blk.end;
2717 5 : *nl = '\n';
2718 5 : mid->blk.end++;
2719 : }
2720 141616 : len = 2;
2721 141616 : mid->blk.buf[mid->blk.end] = PROMPTBEG;
2722 141616 : mid->blk.buf[mid->blk.end + 1] = '\n';
2723 141616 : mid->blk.buf[mid->blk.end + 2] = 0;
2724 : }
2725 302693 : mid->blk.end += (int) len;
2726 : }
2727 1555420 : if (mid->trace) {
2728 0 : printf("got complete block: \n");
2729 0 : printf("text:%s\n", mid->blk.buf + mid->blk.nxt);
2730 : }
2731 :
2732 : /* we have a complete line in the buffer */
2733 1555420 : assert(nl);
2734 1555420 : *nl++ = 0;
2735 1555420 : reply = mid->blk.buf + mid->blk.nxt;
2736 1555420 : mid->blk.nxt = (int) (nl - mid->blk.buf);
2737 :
2738 1555420 : if (mid->trace)
2739 0 : printf("read_line:%s\n", reply);
2740 : return reply;
2741 : }
2742 :
2743 : /* set or unset the autocommit flag in the server */
2744 : MapiMsg
2745 1178 : mapi_setAutocommit(Mapi mid, bool autocommit)
2746 : {
2747 1178 : if (msetting_bool(mid->settings, MP_AUTOCOMMIT) == autocommit)
2748 : return MOK;
2749 8 : if (!msettings_lang_is_sql(mid->settings)) {
2750 0 : mapi_setError(mid, "autocommit only supported in SQL", __func__, MERROR);
2751 0 : return MERROR;
2752 : }
2753 8 : msettings_error err = msetting_set_bool(mid->settings, MP_AUTOCOMMIT, autocommit);
2754 8 : if (err)
2755 0 : return mapi_setError(mid, err, __func__, MERROR);
2756 8 : if (!mid->connected)
2757 : return MOK;
2758 8 : if (autocommit)
2759 1 : return mapi_Xcommand(mid, "auto_commit", "1");
2760 : else
2761 7 : return mapi_Xcommand(mid, "auto_commit", "0");
2762 : }
2763 :
2764 : MapiMsg
2765 22 : mapi_set_time_zone(Mapi mid, int time_zone)
2766 : {
2767 22 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
2768 22 : if (err)
2769 0 : return mapi_setError(mid, err, __func__, MERROR);
2770 22 : if (!mid->connected)
2771 : return MOK;
2772 :
2773 0 : char buf[100];
2774 0 : if (time_zone < 0)
2775 0 : snprintf(buf, sizeof(buf),
2776 : "SET TIME ZONE INTERVAL '-%02d:%02d' HOUR TO MINUTE",
2777 0 : -time_zone / 3600, (-time_zone % 3600) / 60);
2778 : else
2779 0 : snprintf(buf, sizeof(buf),
2780 : "SET TIME ZONE INTERVAL '+%02d:%02d' HOUR TO MINUTE",
2781 0 : time_zone / 3600, (time_zone % 3600) / 60);
2782 :
2783 0 : MapiHdl hdl = mapi_query(mid, buf);
2784 0 : if (hdl == NULL)
2785 0 : return mid->error;
2786 0 : mapi_close_handle(hdl);
2787 :
2788 0 : return MOK;
2789 : }
2790 :
2791 : MapiMsg
2792 0 : mapi_set_columnar_protocol(Mapi mid, bool columnar_protocol)
2793 : {
2794 0 : if (mid->columnar_protocol == columnar_protocol)
2795 : return MOK;
2796 0 : mid->columnar_protocol = columnar_protocol;
2797 0 : if (!mid->connected)
2798 : return MOK;
2799 0 : if (columnar_protocol)
2800 0 : return mapi_Xcommand(mid, "columnar_protocol", "1");
2801 : else
2802 0 : return mapi_Xcommand(mid, "columnar_protocol", "0");
2803 : }
2804 :
2805 : MapiMsg
2806 171 : mapi_set_size_header(Mapi mid, bool value)
2807 : {
2808 171 : if (!msettings_lang_is_sql(mid->settings)) {
2809 0 : mapi_setError(mid, "size header only supported in SQL", __func__, MERROR);
2810 0 : return MERROR;
2811 : }
2812 171 : if (mid->sizeheader == value)
2813 : return MOK;
2814 15 : mid->sizeheader = value;
2815 15 : if (!mid->connected)
2816 : return MOK;
2817 0 : if (value)
2818 0 : return mapi_Xcommand(mid, "sizeheader", "1");
2819 : else
2820 0 : return mapi_Xcommand(mid, "sizeheader", "0");
2821 : }
2822 :
2823 : MapiMsg
2824 2 : mapi_release_id(Mapi mid, int id)
2825 : {
2826 2 : char buf[10];
2827 :
2828 2 : if (!msettings_lang_is_sql(mid->settings)) {
2829 0 : mapi_setError(mid, "release only supported in SQL", __func__, MERROR);
2830 0 : return MERROR;
2831 : }
2832 2 : snprintf(buf, sizeof(buf), "%d", id);
2833 2 : return mapi_Xcommand(mid, "release", buf);
2834 : }
2835 :
2836 : void
2837 166 : mapi_trace(Mapi mid, bool flag)
2838 : {
2839 166 : mapi_clrError(mid);
2840 166 : mid->trace = flag;
2841 166 : }
2842 :
2843 :
2844 : static int
2845 1256913 : slice_row(const char *reply, char *null, char ***anchorsp, size_t **lensp, int length, int endchar)
2846 : {
2847 : /* This function does the actual work for splicing a real,
2848 : multi-column row into columns. It skips over the first
2849 : character and ends at the end of the string or at endchar,
2850 : whichever comes first. */
2851 1256913 : char *start;
2852 1256913 : char **anchors;
2853 1256913 : int i;
2854 1256913 : size_t len;
2855 1256913 : size_t *lens;
2856 :
2857 1256913 : reply++; /* skip over initial char (usually '[') */
2858 1256913 : i = 0;
2859 1256913 : anchors = length == 0 ? NULL : malloc(length * sizeof(*anchors));
2860 1256719 : lens = length == 0 ? NULL : malloc(length * sizeof(*lens));
2861 4383872 : for (;;) {
2862 4383872 : if (i >= length) {
2863 17508 : length = i + 1;
2864 17508 : REALLOC(anchors, length);
2865 17508 : REALLOC(lens, length);
2866 : }
2867 4383872 : if (!unquote(reply, &start, &reply, endchar, &len) && null && strcmp(start, null) == 0) {
2868 : /* indicate NULL/nil with NULL pointer */
2869 472981 : free(start);
2870 472981 : start = NULL;
2871 472981 : len = 0;
2872 : }
2873 4383872 : lens[i] = len;
2874 4383872 : anchors[i++] = start;
2875 4383872 : if (reply == NULL)
2876 : break;
2877 4383872 : while (*reply && isspace((unsigned char) *reply))
2878 0 : reply++;
2879 4383872 : if (*reply == ',') {
2880 3126955 : reply++;
2881 6253910 : while (*reply && isspace((unsigned char) *reply))
2882 3126955 : reply++;
2883 1256917 : } else if (*reply == 0 || *reply == endchar)
2884 : break;
2885 : }
2886 1256913 : *anchorsp = anchors;
2887 1256913 : *lensp = lens;
2888 1256913 : return i;
2889 : }
2890 :
2891 : static MapiMsg
2892 12901 : mapi_cache_freeup_internal(struct MapiResultSet *result, int k)
2893 : {
2894 12901 : int i; /* just a counter */
2895 12901 : int64_t n = 0; /* # of tuples being deleted from front */
2896 :
2897 12901 : result->cache.tuplecount = 0;
2898 12901 : for (i = 0; i < result->cache.writer - k; i++) {
2899 0 : if (result->cache.line[i].rows) {
2900 0 : if (result->cache.line[i].rows[0] == '[' ||
2901 : result->cache.line[i].rows[0] == '=')
2902 0 : n++;
2903 0 : free(result->cache.line[i].rows);
2904 : }
2905 0 : result->cache.line[i].rows = result->cache.line[i + k].rows;
2906 0 : result->cache.line[i + k].rows = 0;
2907 0 : if (result->cache.line[i].anchors) {
2908 : int j = 0;
2909 :
2910 0 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2911 0 : free(result->cache.line[i].anchors[j]);
2912 0 : free(result->cache.line[i].anchors);
2913 : }
2914 0 : if (result->cache.line[i].lens)
2915 0 : free(result->cache.line[i].lens);
2916 0 : result->cache.line[i].anchors = result->cache.line[i + k].anchors;
2917 0 : result->cache.line[i + k].anchors = 0;
2918 0 : result->cache.line[i].lens = result->cache.line[i + k].lens;
2919 0 : result->cache.line[i + k].lens = 0;
2920 0 : result->cache.line[i].fldcnt = result->cache.line[i + k].fldcnt;
2921 0 : if (result->cache.line[i].rows &&
2922 0 : (result->cache.line[i].rows[0] == '[' ||
2923 : result->cache.line[i].rows[0] == '=')) {
2924 0 : result->cache.line[i].tuplerev = result->cache.tuplecount;
2925 0 : result->cache.line[result->cache.tuplecount++].tupleindex = i;
2926 : }
2927 : }
2928 : /* after the previous loop, i == result->cache.writer - k, and
2929 : the last (result->cache.writer - k) cache entries have been
2930 : cleared already , so we don't need to go the Full Monty
2931 : here */
2932 1148984 : for ( /*i = result->cache.writer - k */ ; i < k /*result->cache.writer */ ; i++) {
2933 1136083 : if (result->cache.line[i].rows) {
2934 1136083 : if (result->cache.line[i].rows[0] == '[' ||
2935 : result->cache.line[i].rows[0] == '=')
2936 1135383 : n++;
2937 1136083 : free(result->cache.line[i].rows);
2938 : }
2939 1136083 : result->cache.line[i].rows = 0;
2940 1136083 : if (result->cache.line[i].anchors) {
2941 : int j = 0;
2942 :
2943 5212495 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2944 4077112 : free(result->cache.line[i].anchors[j]);
2945 1135383 : free(result->cache.line[i].anchors);
2946 : }
2947 1136083 : if (result->cache.line[i].lens)
2948 1135383 : free(result->cache.line[i].lens);
2949 1136083 : result->cache.line[i].anchors = 0;
2950 1136083 : result->cache.line[i].lens = 0;
2951 1136083 : result->cache.line[i].fldcnt = 0;
2952 : }
2953 12901 : result->cache.reader -= k;
2954 12901 : if (result->cache.reader < 0)
2955 12901 : result->cache.reader = -1;
2956 12901 : result->cache.writer -= k;
2957 12901 : if (result->cache.writer < 0) /* "cannot happen" */
2958 0 : result->cache.writer = 0;
2959 12901 : result->cache.first += n;
2960 :
2961 12901 : return MOK;
2962 : }
2963 :
2964 : static void
2965 39234 : mapi_extend_cache(struct MapiResultSet *result, int cacheall)
2966 : {
2967 39234 : int incr, newsize, oldsize = result->cache.limit, i;
2968 :
2969 : /* if there are read entries, delete them */
2970 39234 : if (result->cache.reader >= 0) {
2971 11331 : mapi_cache_freeup_internal(result, result->cache.reader + 1);
2972 : /* since we've made space, we can return */
2973 11331 : return;
2974 : }
2975 :
2976 : /* extend row cache */
2977 27903 : retry:;
2978 27905 : if (oldsize == 0)
2979 : incr = 100;
2980 : else
2981 9 : incr = oldsize * 2;
2982 9 : if (incr > 200000)
2983 0 : incr = 20000;
2984 27905 : newsize = oldsize + incr;
2985 27905 : if (result->cache.rowlimit > 0 &&
2986 4 : newsize > result->cache.rowlimit &&
2987 : !cacheall) {
2988 4 : newsize = result->cache.rowlimit;
2989 4 : incr = newsize - oldsize;
2990 4 : if (incr <= 0) {
2991 : /* not enough space, so increase limit and try again */
2992 2 : result->cache.rowlimit += 100;
2993 2 : goto retry;
2994 : }
2995 : }
2996 :
2997 27903 : REALLOC(result->cache.line, newsize + 1);
2998 27903 : assert(result->cache.line);
2999 2848218 : for (i = oldsize; i <= newsize; i++) {
3000 2820315 : result->cache.line[i].fldcnt = 0;
3001 2820315 : result->cache.line[i].rows = NULL;
3002 2820315 : result->cache.line[i].tupleindex = -1;
3003 2820315 : result->cache.line[i].tuplerev = -1;
3004 2820315 : result->cache.line[i].anchors = NULL;
3005 2820315 : result->cache.line[i].lens = NULL;
3006 : }
3007 27903 : result->cache.limit = newsize;
3008 : }
3009 :
3010 : /* store a line in the cache */
3011 : static void
3012 1278126 : add_cache(struct MapiResultSet *result, char *line, int cacheall)
3013 : {
3014 : /* manage the row cache space first */
3015 1278126 : if (result->cache.writer >= result->cache.limit)
3016 39234 : mapi_extend_cache(result, cacheall);
3017 :
3018 1278126 : result->cache.line[result->cache.writer].rows = line;
3019 1278126 : result->cache.line[result->cache.writer].tuplerev = result->cache.tuplecount;
3020 1278126 : result->cache.line[result->cache.writer + 1].tuplerev = result->cache.tuplecount + 1;
3021 1278126 : if (*line == '[' || *line == '=') {
3022 1167214 : result->cache.line[result->cache.tuplecount++].tupleindex = result->cache.writer;
3023 1167214 : if (result->row_count < result->cache.first + result->cache.tuplecount)
3024 206 : result->row_count = result->cache.first + result->cache.tuplecount;
3025 : }
3026 1278126 : result->cache.writer++;
3027 1278126 : }
3028 :
3029 : static struct MapiResultSet *
3030 141543 : parse_header_line(MapiHdl hdl, char *line, struct MapiResultSet *result)
3031 : {
3032 141543 : char *tag, *etag;
3033 141543 : int i, n;
3034 141543 : char **anchors;
3035 141543 : size_t *lens;
3036 :
3037 141543 : if (line[0] == '&') {
3038 30631 : char *nline = line;
3039 30631 : int qt;
3040 30631 : uint64_t queryid;
3041 :
3042 : /* handle fields &qt */
3043 :
3044 30631 : nline++; /* query type */
3045 30631 : qt = (int) strtol(nline, &nline, 0);
3046 :
3047 30631 : if (result == NULL || (qt != Q_BLOCK && !result->commentonly))
3048 30612 : result = new_result(hdl);
3049 30631 : result->querytype = qt;
3050 30631 : result->commentonly = false;
3051 30631 : result->querytime = 0;
3052 30631 : result->maloptimizertime = 0;
3053 30631 : result->sqloptimizertime = 0;
3054 :
3055 30631 : nline++; /* skip space */
3056 30631 : switch (qt) {
3057 484 : case Q_SCHEMA:
3058 484 : result->querytime = strtoll(nline, &nline, 10);
3059 484 : result->maloptimizertime = strtoll(nline, &nline, 10);
3060 484 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3061 484 : break;
3062 222 : case Q_TRANS:
3063 222 : msetting_set_bool(hdl->mid->settings, MP_AUTOCOMMIT, *nline != 'f');
3064 222 : break;
3065 2209 : case Q_UPDATE:
3066 2209 : result->row_count = strtoll(nline, &nline, 10);
3067 2209 : result->last_id = strtoll(nline, &nline, 10);
3068 2209 : queryid = strtoll(nline, &nline, 10);
3069 2209 : result->querytime = strtoll(nline, &nline, 10);
3070 2209 : result->maloptimizertime = strtoll(nline, &nline, 10);
3071 2209 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3072 2209 : break;
3073 27575 : case Q_TABLE:
3074 27575 : if (sscanf(nline,
3075 : "%d %" SCNd64 " %d %" SCNd64 " %" SCNu64
3076 : " %" SCNd64 " %" SCNd64 " %" SCNd64,
3077 : &result->tableid, &result->row_count,
3078 : &result->fieldcnt, &result->tuple_count,
3079 : &queryid, &result->querytime,
3080 : &result->maloptimizertime,
3081 : &result->sqloptimizertime) < 8){
3082 2 : result->querytime = 0;
3083 2 : result->maloptimizertime = 0;
3084 2 : result->sqloptimizertime = 0;
3085 : }
3086 : (void) queryid; /* ignored for now */
3087 : break;
3088 122 : case Q_PREPARE:
3089 122 : sscanf(nline, "%d %" SCNd64 " %d %" SCNd64,
3090 : &result->tableid, &result->row_count,
3091 : &result->fieldcnt, &result->tuple_count);
3092 122 : break;
3093 19 : case Q_BLOCK:
3094 : /* Mapi ignores the Q_BLOCK header, so spoof
3095 : * the querytype back to a Q_TABLE to let it
3096 : * go unnoticed */
3097 19 : result->querytype = Q_TABLE;
3098 19 : break;
3099 : }
3100 :
3101 :
3102 30631 : if (result->fieldcnt > result->maxfields) {
3103 27697 : REALLOC(result->fields, result->fieldcnt);
3104 27697 : memset(result->fields + result->maxfields, 0, (result->fieldcnt - result->maxfields) * sizeof(*result->fields));
3105 27697 : result->maxfields = result->fieldcnt;
3106 : }
3107 :
3108 : /* start of new SQL result */
3109 30631 : return result;
3110 : }
3111 110912 : if (result == NULL)
3112 4 : result = new_result(hdl);
3113 :
3114 110912 : if (line[0] == '#' && !msettings_lang_is_mal(hdl->mid->settings)) {
3115 : /* comment */
3116 : return result;
3117 : }
3118 :
3119 110912 : line = strdup(line); /* make copy we can play with */
3120 110912 : etag = strrchr(line, '#');
3121 110912 : if (etag == 0 || etag == line) {
3122 : /* not a useful header line */
3123 0 : free(line);
3124 0 : return result;
3125 : }
3126 :
3127 110912 : n = slice_row(line, NULL, &anchors, &lens, 10, '#');
3128 :
3129 110912 : result->commentonly = false;
3130 :
3131 110912 : tag = etag + 1;
3132 221816 : while (*tag && isspace((unsigned char) *tag))
3133 110904 : tag++;
3134 :
3135 110912 : if (n > result->fieldcnt) {
3136 8 : result->fieldcnt = n;
3137 8 : if (n > result->maxfields) {
3138 8 : REALLOC(result->fields, n);
3139 8 : memset(result->fields + result->maxfields, 0, (n - result->maxfields) * sizeof(*result->fields));
3140 8 : result->maxfields = n;
3141 : }
3142 : }
3143 :
3144 110912 : if (strcmp(tag, "name") == 0) {
3145 27697 : result->fieldcnt = n;
3146 85247 : for (i = 0; i < n; i++) {
3147 57550 : if (anchors[i]) {
3148 57550 : if (result->fields[i].columnname)
3149 0 : free(result->fields[i].columnname);
3150 57550 : result->fields[i].columnname = anchors[i];
3151 57550 : anchors[i] = NULL;
3152 : }
3153 : }
3154 83215 : } else if (strcmp(tag, "type") == 0) {
3155 27701 : result->fieldcnt = n;
3156 85259 : for (i = 0; i < n; i++) {
3157 57558 : if (anchors[i]) {
3158 57558 : if (result->fields[i].columntype)
3159 0 : free(result->fields[i].columntype);
3160 57558 : result->fields[i].columntype = anchors[i];
3161 57558 : anchors[i] = NULL;
3162 : }
3163 : }
3164 55514 : } else if (strcmp(tag, "length") == 0) {
3165 27697 : result->fieldcnt = n;
3166 85247 : for (i = 0; i < n; i++) {
3167 57550 : if (anchors[i])
3168 57550 : result->fields[i].columnlength = atoi(anchors[i]);
3169 : }
3170 27817 : } else if (strcmp(tag, "table_name") == 0) {
3171 27697 : result->fieldcnt = n;
3172 85247 : for (i = 0; i < n; i++) {
3173 57550 : if (anchors[i]) {
3174 57550 : if (result->fields[i].tablename)
3175 0 : free(result->fields[i].tablename);
3176 57550 : result->fields[i].tablename = anchors[i];
3177 57550 : anchors[i] = NULL;
3178 : }
3179 : }
3180 120 : } else if (strcmp(tag, "typesizes") == 0) {
3181 112 : result->fieldcnt = n;
3182 1073 : for (i = 0; i < n; i++) {
3183 961 : if (anchors[i]) {
3184 961 : char *p;
3185 961 : result->fields[i].digits = atoi(anchors[i]);
3186 961 : p = strchr(anchors[i], ' ');
3187 961 : if (p)
3188 961 : result->fields[i].scale = atoi(p + 1);
3189 : }
3190 : }
3191 : }
3192 :
3193 : /* clean up */
3194 110912 : free(line);
3195 342089 : for (i = 0; i < n; i++)
3196 231177 : if (anchors[i])
3197 58519 : free(anchors[i]);
3198 110912 : free(anchors);
3199 110912 : free(lens);
3200 :
3201 110912 : return result;
3202 : }
3203 :
3204 : static void
3205 75 : write_file(MapiHdl hdl, char *filename, bool binary)
3206 : {
3207 75 : Mapi mid = hdl->mid;
3208 75 : char *line;
3209 75 : char data[BLOCK];
3210 75 : ssize_t len;
3211 :
3212 75 : (void) read_line(mid); /* read flush marker */
3213 75 : if (filename == NULL) {
3214 : /* malloc failure */
3215 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3216 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3217 0 : return;
3218 : }
3219 75 : if (mid->putfilecontent == NULL) {
3220 0 : free(filename);
3221 0 : mnstr_printf(mid->to, "!HY000!cannot send files\n");
3222 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3223 0 : return;
3224 : }
3225 75 : line = mid->putfilecontent(mid->filecontentprivate, filename, binary, NULL, 0);
3226 75 : free(filename);
3227 75 : if (line != NULL) {
3228 0 : if (strchr(line, '\n'))
3229 0 : line = "incorrect response from application";
3230 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", line);
3231 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3232 0 : return;
3233 : }
3234 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3235 180886 : while ((len = mnstr_read(mid->from, data, 1, sizeof(data))) > 0) {
3236 180736 : if (line == NULL)
3237 180736 : line = mid->putfilecontent(mid->filecontentprivate,
3238 : NULL, binary, data, len);
3239 : }
3240 75 : if (line == NULL)
3241 75 : line = mid->putfilecontent(mid->filecontentprivate,
3242 : NULL, binary, NULL, 0);
3243 75 : if (line && strchr(line, '\n'))
3244 : line = "incorrect response from application";
3245 75 : mnstr_printf(mid->to, "%s\n", line ? line : "");
3246 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3247 : }
3248 :
3249 : #define MiB (1 << 20) /* a megabyte */
3250 :
3251 : static void
3252 85 : read_file(MapiHdl hdl, uint64_t off, char *filename, bool binary)
3253 : {
3254 85 : Mapi mid = hdl->mid;
3255 85 : size_t size = 0, flushsize = 0;
3256 85 : char *data, *line;
3257 :
3258 85 : (void) read_line(mid); /* read flush marker */
3259 85 : if (filename == NULL) {
3260 : /* malloc failure */
3261 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3262 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3263 1 : return;
3264 : }
3265 85 : if (mid->getfilecontent == NULL) {
3266 0 : free(filename);
3267 0 : mnstr_printf(mid->to, "!HY000!cannot retrieve files\n");
3268 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3269 0 : return;
3270 : }
3271 85 : data = mid->getfilecontent(mid->filecontentprivate, filename, binary,
3272 : off, &size);
3273 85 : free(filename);
3274 85 : if (data != NULL && size == 0) {
3275 0 : if (strchr(data, '\n'))
3276 0 : data = "incorrect response from application";
3277 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", data);
3278 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3279 0 : return;
3280 : }
3281 85 : mnstr_printf(mid->to, "\n");
3282 23479 : while (data != NULL && size != 0) {
3283 23395 : if (flushsize >= MiB) {
3284 : /* after every MiB give the server the
3285 : * opportunity to stop reading more data */
3286 1398 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3287 : /* at this point we expect to get a PROMPT2 if
3288 : * the server wants more data, or a PROMPT3 if
3289 : * the server had enough; anything else is a
3290 : * protocol violation */
3291 1398 : line = read_line(mid);
3292 1398 : if (line == NULL) {
3293 : /* error */
3294 0 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3295 0 : return;
3296 : }
3297 1398 : assert(line[0] == PROMPTBEG);
3298 1398 : if (line[0] != PROMPTBEG) {
3299 : /* error in protocol */
3300 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3301 : return;
3302 : }
3303 1398 : if (line[1] == PROMPT3[1]) {
3304 : /* done reading: close file */
3305 1 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3306 1 : (void) read_line(mid);
3307 1 : return;
3308 : }
3309 1397 : assert(line[1] == PROMPT2[1]);
3310 1397 : if (line[1] != PROMPT2[1]) {
3311 : /* error in protocol */
3312 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3313 : return;
3314 : }
3315 : /* clear the flush marker */
3316 1397 : (void) read_line(mid);
3317 1397 : flushsize = 0;
3318 : }
3319 23394 : if (size > MiB) {
3320 0 : if (mnstr_write(mid->to, data, 1, MiB) != MiB) {
3321 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3322 0 : return;
3323 : }
3324 0 : size -= MiB;
3325 0 : data += MiB;
3326 0 : flushsize += MiB;
3327 : } else {
3328 23394 : if (mnstr_write(mid->to, data, 1, size) != (ssize_t) size) {
3329 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3330 0 : return;
3331 : }
3332 23394 : flushsize += size;
3333 23394 : data = mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, &size);
3334 : }
3335 : }
3336 84 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3337 84 : line = read_line(mid);
3338 84 : if (line == NULL)
3339 : return;
3340 84 : assert(line[0] == PROMPTBEG);
3341 84 : if (line[0] != PROMPTBEG)
3342 : return;
3343 84 : if (line[1] == PROMPT3[1]) {
3344 0 : (void) read_line(mid);
3345 0 : return;
3346 : }
3347 84 : assert(line[1] == PROMPT2[1]);
3348 84 : if (line[1] != PROMPT2[1])
3349 : return;
3350 84 : (void) read_line(mid);
3351 84 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3352 84 : line = read_line(mid);
3353 84 : if (line == NULL)
3354 : return;
3355 84 : assert(line[0] == PROMPTBEG);
3356 84 : assert(line[1] == PROMPT3[1]);
3357 84 : (void) read_line(mid);
3358 : }
3359 :
3360 : /* Read ahead and cache data read. Depending on the second argument,
3361 : reading may stop at the first non-header and non-error line, or at
3362 : a prompt.
3363 : This function is called either after a command has been sent to the
3364 : server (in which case the second argument is 1), when the
3365 : application asks for a result tuple that hadn't been cached yet (in
3366 : which case the second argument is also 1), or whenever all pending
3367 : data needs to be read in order to send a new command to the server
3368 : (in which case the second argument is 0).
3369 : Header lines result tuples are stored in the cache. Certain header
3370 : lines may cause a new result set to be created in which case all
3371 : subsequent lines are added to that result set.
3372 : */
3373 : MapiMsg
3374 1304876 : read_into_cache(MapiHdl hdl, int lookahead)
3375 : {
3376 1304876 : char *line;
3377 1304876 : Mapi mid;
3378 1304876 : struct MapiResultSet *result;
3379 :
3380 1304876 : mid = hdl->mid;
3381 1304876 : assert(mid->active == hdl);
3382 1304876 : if (hdl->needmore) {
3383 0 : hdl->needmore = false;
3384 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3385 0 : check_stream(mid, mid->to, "write error on stream", mid->error);
3386 : }
3387 1304876 : if ((result = hdl->active) == NULL)
3388 139871 : result = hdl->result; /* may also be NULL */
3389 1448887 : for (;;) {
3390 1448887 : line = read_line(mid);
3391 1448887 : if (line == NULL) {
3392 0 : if (mid->from && mnstr_eof(mid->from)) {
3393 0 : return mapi_setError(mid, "unexpected end of file", __func__, MTIMEOUT);
3394 : }
3395 0 : return mid->error;
3396 : }
3397 1448887 : switch (*line) {
3398 140050 : case PROMPTBEG: /* \001 */
3399 140050 : mid->active = NULL;
3400 140050 : hdl->active = NULL;
3401 : /* set needmore flag if line equals PROMPT2 up
3402 : to newline */
3403 140050 : if (line[1] == PROMPT2[1] && line[2] == '\0') {
3404 : /* skip end of block */
3405 103082 : mid->active = hdl;
3406 103082 : (void) read_line(mid);
3407 103082 : hdl->needmore = true;
3408 103082 : mid->active = hdl;
3409 36968 : } else if (line[1] == PROMPT3[1] && line[2] == '\0') {
3410 160 : mid->active = hdl;
3411 160 : line = read_line(mid);
3412 160 : bool binary = false;
3413 : /* rb FILE
3414 : * r OFF FILE
3415 : * w FILE
3416 : * wb FILE
3417 : */
3418 160 : switch (*line++) {
3419 85 : case 'r': {
3420 85 : uint64_t off = 0;
3421 85 : if (*line == 'b') {
3422 85 : line++;
3423 85 : binary = true;
3424 : } else {
3425 0 : off = strtoul(line, &line, 10);
3426 : }
3427 85 : if (*line++ != ' ') {
3428 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3429 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3430 0 : break;
3431 : }
3432 85 : read_file(hdl, off, strdup(line), binary);
3433 85 : break;
3434 : }
3435 75 : case 'w':
3436 75 : if (*line == 'b') {
3437 75 : line++;
3438 75 : binary = true;
3439 : }
3440 75 : if (*line++ != ' ') {
3441 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3442 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3443 0 : break;
3444 : }
3445 75 : write_file(hdl, strdup(line), binary);
3446 75 : break;
3447 : }
3448 160 : continue;
3449 : }
3450 139890 : return mid->error;
3451 80 : case '!':
3452 : /* start a new result set if we don't have one
3453 : yet (duh!), or if we've already seen
3454 : normal output for the current one */
3455 80 : if (result == NULL ||
3456 2 : result->cache.writer > 0 ||
3457 2 : result->querytype > 0) {
3458 78 : result = new_result(hdl);
3459 78 : result->commentonly = false;
3460 78 : hdl->active = result;
3461 : }
3462 80 : add_error(result, line + 1 /* skip ! */ );
3463 80 : if (!mid->error)
3464 78 : mid->error = MSERVER;
3465 : break;
3466 141543 : case '%':
3467 : case '#':
3468 : case '&':
3469 141543 : if (lookahead < 0)
3470 0 : lookahead = 1;
3471 141543 : result = parse_header_line(hdl, line, result);
3472 141543 : hdl->active = result;
3473 141543 : if (result && *line != '&')
3474 110912 : add_cache(result, strdup(line), !lookahead);
3475 : break;
3476 1167214 : default:
3477 1167214 : if (result == NULL) {
3478 195 : result = new_result(hdl);
3479 195 : hdl->active = result;
3480 : }
3481 1167214 : add_cache(result, strdup(line), !lookahead);
3482 1167214 : if (lookahead > 0 &&
3483 1165592 : (result->querytype == -1 /* unknown (not SQL) */ ||
3484 606 : result->querytype == Q_TABLE ||
3485 : result->querytype == Q_UPDATE))
3486 1164986 : return mid->error;
3487 : break;
3488 : }
3489 : }
3490 : }
3491 :
3492 : static MapiMsg
3493 31698 : mapi_execute_internal(MapiHdl hdl)
3494 : {
3495 31698 : size_t size;
3496 31698 : char *cmd;
3497 31698 : Mapi mid;
3498 :
3499 31698 : mid = hdl->mid;
3500 31698 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3501 : return MERROR;
3502 31698 : assert(mid->active == NULL);
3503 31698 : finish_handle(hdl);
3504 31698 : mapi_param_store(hdl);
3505 31698 : cmd = hdl->query;
3506 31698 : if (cmd == NULL)
3507 : return MERROR;
3508 31698 : size = strlen(cmd);
3509 :
3510 31698 : bool is_sql = msettings_lang_is_sql(mid->settings);
3511 31698 : char *prefix = is_sql ? "s" : "";
3512 4198 : char *suffix = is_sql ? "\n;" : "";
3513 31698 : mapi_log_record(mid, "SEND", "%s%s%s", prefix, cmd, suffix);
3514 :
3515 31698 : if (is_sql) {
3516 : /* indicate to server this is a SQL command */
3517 27500 : mnstr_write(mid->to, "s", 1, 1);
3518 27500 : check_stream(mid, mid->to, "write error on stream", mid->error);
3519 : }
3520 31698 : mnstr_write(mid->to, cmd, 1, size);
3521 31698 : check_stream(mid, mid->to, "write error on stream", mid->error);
3522 : /* all SQL statements should end with a semicolon */
3523 : /* for the other languages it is assumed that the statements are correct */
3524 31698 : if (is_sql) {
3525 27500 : mnstr_write(mid->to, "\n;", 2, 1);
3526 27500 : check_stream(mid, mid->to, "write error on stream", mid->error);
3527 : }
3528 31698 : mnstr_write(mid->to, "\n", 1, 1);
3529 31698 : check_stream(mid, mid->to, "write error on stream", mid->error);
3530 31698 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3531 31698 : check_stream(mid, mid->to, "write error on stream", mid->error);
3532 31698 : mid->active = hdl;
3533 31698 : return MOK;
3534 : }
3535 :
3536 : MapiMsg
3537 0 : mapi_execute(MapiHdl hdl)
3538 : {
3539 0 : int ret;
3540 :
3541 0 : mapi_hdl_check(hdl);
3542 0 : if ((ret = mapi_execute_internal(hdl)) == MOK)
3543 0 : return read_into_cache(hdl, 1);
3544 :
3545 : return ret;
3546 : }
3547 :
3548 : /*
3549 : * The routine mapi_query is one of the most heavily used ones.
3550 : * It sends a complete statement for execution
3551 : * (i.e., ending in a newline; possibly including additional newlines).
3552 : * Interaction with the server is sped up using block based interaction.
3553 : * The query is retained in the Mapi structure to repeat shipping.
3554 : */
3555 : MapiHdl
3556 29588 : mapi_query(Mapi mid, const char *cmd)
3557 : {
3558 29588 : int ret;
3559 29588 : MapiHdl hdl;
3560 :
3561 29588 : mapi_check0(mid);
3562 29588 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3563 29588 : ret = mid->error;
3564 29588 : if (ret == MOK)
3565 29588 : ret = mapi_execute_internal(hdl);
3566 29588 : if (ret == MOK)
3567 29588 : ret = read_into_cache(hdl, 1);
3568 : return hdl;
3569 : }
3570 :
3571 : /* version of mapi_query that does not wait for a response */
3572 : MapiHdl
3573 0 : mapi_send(Mapi mid, const char *cmd)
3574 : {
3575 0 : int ret;
3576 0 : MapiHdl hdl;
3577 :
3578 0 : mapi_check0(mid);
3579 0 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3580 0 : ret = mid->error;
3581 0 : if (ret == MOK)
3582 0 : ret = mapi_execute_internal(hdl);
3583 : return hdl;
3584 : }
3585 :
3586 : MapiMsg
3587 0 : mapi_read_response(MapiHdl hdl)
3588 : {
3589 0 : return read_into_cache(hdl, 1);
3590 : }
3591 :
3592 : MapiMsg
3593 2110 : mapi_query_handle(MapiHdl hdl, const char *cmd)
3594 : {
3595 2110 : int ret;
3596 :
3597 2110 : mapi_hdl_check(hdl);
3598 2110 : if (finish_handle(hdl) != MOK)
3599 : return MERROR;
3600 2110 : prepareQuery(hdl, cmd);
3601 2110 : ret = hdl->mid->error;
3602 2110 : if (ret == MOK)
3603 2110 : ret = mapi_execute_internal(hdl);
3604 2110 : if (ret == MOK)
3605 2110 : ret = read_into_cache(hdl, 1);
3606 : return ret;
3607 : }
3608 :
3609 : MapiHdl
3610 3648 : mapi_query_prep(Mapi mid)
3611 : {
3612 3648 : mapi_check0(mid);
3613 3648 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3614 : return NULL;
3615 3648 : assert(mid->active == NULL);
3616 3648 : if (msettings_lang_is_sql(mid->settings)) {
3617 : /* indicate to server this is a SQL command */
3618 3648 : mnstr_write(mid->to, "S", 1, 1);
3619 3648 : mapi_log_data(mid, "SEND", "S", 1);
3620 : }
3621 3648 : return (mid->active = mapi_new_handle(mid));
3622 : }
3623 :
3624 : MapiMsg
3625 106391 : mapi_query_part(MapiHdl hdl, const char *query, size_t size)
3626 : {
3627 106391 : Mapi mid;
3628 :
3629 106391 : mapi_hdl_check(hdl);
3630 106391 : mid = hdl->mid;
3631 106391 : assert(mid->active == NULL || mid->active == hdl);
3632 106391 : mid->active = hdl;
3633 : /* remember the query just for the error messages */
3634 106391 : if (hdl->query == NULL) {
3635 3316 : hdl->query = malloc(size + 1);
3636 3316 : if (hdl->query) {
3637 3316 : strcpy_len(hdl->query, query, size + 1);
3638 : }
3639 : } else {
3640 103075 : size_t sz = strlen(hdl->query);
3641 103075 : char *q;
3642 :
3643 103075 : if (sz < 512 &&
3644 1506 : (q = realloc(hdl->query, sz + size + 1)) != NULL) {
3645 1506 : strcpy_len(q + sz, query, size + 1);
3646 1506 : hdl->query = q;
3647 : }
3648 : }
3649 :
3650 106391 : if (mid->trace) {
3651 0 : printf("mapi_query_part:%zu:%.*s\n", size, (int) size, query);
3652 : }
3653 106391 : hdl->needmore = false;
3654 106391 : mnstr_write(mid->to, query, 1, size);
3655 106391 : if (mid->tracelog) {
3656 0 : mnstr_write(mid->tracelog, query, 1, size);
3657 0 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
3658 : }
3659 106391 : check_stream(mid, mid->to, "write error on stream", mid->error);
3660 106391 : return mid->error;
3661 : }
3662 :
3663 : MapiMsg
3664 106730 : mapi_query_done(MapiHdl hdl)
3665 : {
3666 106730 : int ret;
3667 106730 : Mapi mid;
3668 :
3669 106730 : mapi_hdl_check(hdl);
3670 106730 : mid = hdl->mid;
3671 106730 : assert(mid->active == NULL || mid->active == hdl);
3672 106730 : mid->active = hdl;
3673 106730 : hdl->needmore = false;
3674 106730 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3675 106730 : check_stream(mid, mid->to, "write error on stream", mid->error);
3676 106730 : ret = mid->error;
3677 106730 : if (ret == MOK)
3678 106730 : ret = read_into_cache(hdl, 1);
3679 106730 : return ret == MOK && hdl->needmore ? MMORE : ret;
3680 : }
3681 :
3682 : MapiMsg
3683 204 : mapi_cache_limit(Mapi mid, int limit)
3684 : {
3685 : /* clean out superflous space TODO */
3686 204 : msettings_error err = msetting_set_long(mid->settings, MP_REPLYSIZE, limit);
3687 204 : if (err)
3688 0 : return mapi_setError(mid, err, __func__, MERROR);
3689 204 : if (!mid->connected)
3690 : return MOK;
3691 39 : mapi_check(mid);
3692 : /* if (hdl->cache.rowlimit < hdl->cache.limit) { */
3693 : /* TODO: decide what to do here */
3694 : /* hdl->cache.limit = hdl->cache.rowlimit; *//* arbitrarily throw away cache lines */
3695 : /* if (hdl->cache.writer > hdl->cache.limit) { */
3696 : /* hdl->cache.writer = hdl->cache.limit; */
3697 : /* if (hdl->cache.reader > hdl->cache.writer) */
3698 : /* hdl->cache.reader = hdl->cache.writer; */
3699 : /* } */
3700 : /* } */
3701 39 : if (msettings_lang_is_sql(mid->settings)) {
3702 39 : MapiHdl hdl;
3703 :
3704 39 : if (mid->active)
3705 1 : read_into_cache(mid->active, 0);
3706 :
3707 39 : mapi_log_record(mid, "X", "X" "reply_size %d\n", limit);
3708 78 : if (mnstr_printf(mid->to, "X" "reply_size %d\n", limit) < 0 ||
3709 39 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
3710 0 : close_connection(mid);
3711 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
3712 0 : return MERROR;
3713 : }
3714 39 : hdl = prepareQuery(mapi_new_handle(mid), "reply_size");
3715 39 : if (hdl == NULL)
3716 : return MERROR;
3717 39 : mid->active = hdl;
3718 39 : read_into_cache(hdl, 0);
3719 39 : mapi_close_handle(hdl); /* reads away any output */
3720 : }
3721 : return MOK;
3722 : }
3723 :
3724 : MapiMsg
3725 0 : mapi_fetch_reset(MapiHdl hdl)
3726 : {
3727 0 : mapi_hdl_check(hdl);
3728 0 : if (hdl->result)
3729 0 : hdl->result->cache.reader = -1;
3730 : return MOK;
3731 : }
3732 :
3733 : MapiMsg
3734 2655 : mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
3735 : {
3736 2655 : struct MapiResultSet *result;
3737 :
3738 2655 : mapi_hdl_check(hdl);
3739 2655 : result = hdl->result;
3740 2655 : switch (whence) {
3741 : case MAPI_SEEK_SET:
3742 : break;
3743 0 : case MAPI_SEEK_CUR:
3744 0 : rownr += result->cache.line[result->cache.reader + 1].tuplerev;
3745 0 : break;
3746 0 : case MAPI_SEEK_END:
3747 0 : if (hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3748 : return MERROR;
3749 0 : rownr += result->row_count;
3750 0 : break;
3751 0 : default:
3752 0 : return mapi_setError(hdl->mid, "Illegal whence value", __func__, MERROR);
3753 : }
3754 2655 : if (rownr > result->row_count && hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3755 : return MERROR;
3756 2655 : if (rownr < 0 || rownr > result->row_count)
3757 0 : return mapi_setError(hdl->mid, "Illegal row number", __func__, MERROR);
3758 2655 : if (result->cache.first <= rownr && rownr < result->cache.first + result->cache.tuplecount) {
3759 : /* we've got the requested tuple in the cache */
3760 1085 : result->cache.reader = result->cache.line[rownr - result->cache.first].tupleindex - 1;
3761 : } else {
3762 : /* we don't have the requested tuple in the cache
3763 : reset the cache and at the next fetch we'll get the data */
3764 1570 : if (mapi_cache_freeup(hdl, 100) == MOK) {
3765 1570 : result->cache.first = rownr;
3766 : }
3767 : }
3768 2655 : return hdl->mid->error;
3769 : }
3770 :
3771 : /* Make space in the cache for new tuples, ignore the read pointer */
3772 : MapiMsg
3773 1570 : mapi_cache_freeup(MapiHdl hdl, int percentage)
3774 : {
3775 1570 : struct MapiResultSet *result;
3776 1570 : int k; /* # of cache lines to be deleted from front */
3777 :
3778 1570 : mapi_hdl_check(hdl);
3779 1570 : result = hdl->result;
3780 1570 : if (result == NULL || (result->cache.writer == 0 && result->cache.reader == -1))
3781 : return MOK;
3782 1570 : if (percentage < 0 || percentage > 100)
3783 0 : percentage = 100;
3784 1570 : k = (result->cache.writer * percentage) / 100;
3785 1570 : if (k < 1)
3786 0 : k = 1;
3787 1570 : return mapi_cache_freeup_internal(result, k);
3788 : }
3789 :
3790 : static char *
3791 1304734 : mapi_fetch_line_internal(MapiHdl hdl)
3792 : {
3793 1304734 : Mapi mid;
3794 1304734 : struct MapiResultSet *result;
3795 1304734 : char *reply;
3796 :
3797 : /* try to read a line from the cache */
3798 1304734 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer) {
3799 1168792 : mid = hdl->mid;
3800 1168792 : if (mid->active != hdl || hdl->needmore)
3801 : return NULL;
3802 :
3803 1164608 : if (read_into_cache(hdl, 1) != MOK)
3804 : return NULL;
3805 1164608 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer)
3806 : return NULL;
3807 : }
3808 1277176 : reply = result->cache.line[++result->cache.reader].rows;
3809 1277176 : if (hdl->bindings && (*reply == '[' || *reply == '=')) {
3810 0 : mapi_slice_row(result, result->cache.reader);
3811 0 : mapi_store_bind(result, result->cache.reader);
3812 : }
3813 : return reply;
3814 : }
3815 :
3816 : /*
3817 : * The routine mapi_fetch_line forms the basic interaction with the server.
3818 : * It simply retrieves the next line and stores it in the row cache.
3819 : * The field anchor structure is prepared for subsequent use by
3820 : * mapi_fetch_row.
3821 : * The content received is analyzed further by mapi_getRow()
3822 : */
3823 : char *
3824 1304715 : mapi_fetch_line(MapiHdl hdl)
3825 : {
3826 1304715 : char *reply;
3827 1304715 : struct MapiResultSet *result;
3828 :
3829 1304715 : mapi_hdl_check0(hdl);
3830 1304715 : reply = mapi_fetch_line_internal(hdl);
3831 1304715 : if (reply == NULL &&
3832 55116 : (result = hdl->result) != NULL &&
3833 27558 : msettings_lang_is_sql(hdl->mid->settings) &&
3834 27546 : result->querytype == Q_TABLE &&
3835 27428 : result->row_count > 0 &&
3836 23479 : result->cache.first + result->cache.tuplecount < result->row_count) {
3837 19 : if (hdl->needmore) /* escalate */
3838 : return NULL;
3839 19 : if (hdl->mid->active != NULL)
3840 0 : read_into_cache(hdl->mid->active, 0);
3841 19 : hdl->mid->active = hdl;
3842 19 : hdl->active = result;
3843 19 : mapi_log_record(hdl->mid, "W", "X" "export %d %" PRId64 "\n",
3844 : result->tableid,
3845 : result->cache.first + result->cache.tuplecount);
3846 19 : if (mnstr_printf(hdl->mid->to, "X" "export %d %" PRId64 "\n",
3847 : result->tableid,
3848 38 : result->cache.first + result->cache.tuplecount) < 0 ||
3849 19 : mnstr_flush(hdl->mid->to, MNSTR_FLUSH_DATA))
3850 0 : check_stream(hdl->mid, hdl->mid->to, "sending export command", NULL);
3851 19 : reply = mapi_fetch_line_internal(hdl);
3852 : }
3853 : return reply;
3854 : }
3855 :
3856 : /*
3857 : * To synchronize on a prompt, the low level routine mapi_finish can be used.
3858 : * It discards all output received.
3859 : */
3860 : MapiMsg
3861 0 : mapi_finish(MapiHdl hdl)
3862 : {
3863 0 : mapi_hdl_check(hdl);
3864 0 : return finish_handle(hdl);
3865 : }
3866 :
3867 : /* msg is a string consisting comma-separated values. The list of
3868 : values is terminated by endchar or by the end-of-string NULL byte.
3869 : Values can be quoted strings or unquoted values. Upon return,
3870 : *start points to the start of the first value which is stripped of
3871 : leading and trailing white space, and if it was a quoted string,
3872 : also of the quotes. Also, backslash-escaped characters in the
3873 : quoted string are replaced by the values the escapes represent.
3874 : *next points to either the start of the next value (i.e. after the
3875 : separating comma, possibly to the leading white space of the next
3876 : value), or to the trailing ] or NULL byte if this was the last
3877 : value. *lenp is the number of bytes occupied by the (possibly
3878 : converted) value, excluding final NULL byte.
3879 : msg is *not* a const string: it is altered by this function.
3880 : The function returns true if the string was quoted.
3881 : */
3882 : static int
3883 4383872 : unquote(const char *msg, char **str, const char **next, int endchar, size_t *lenp)
3884 : {
3885 4383872 : const char *p = msg;
3886 4383872 : char quote;
3887 :
3888 : /* first skip over leading white space */
3889 5640777 : while (*p && isspace((unsigned char) *p))
3890 1256905 : p++;
3891 4383872 : quote = *p;
3892 4383872 : if (quote == '\'' || quote == '"') {
3893 3564516 : size_t len = 0;
3894 3564516 : char *s, *start;
3895 :
3896 : /* get quoted string and remove trailing bracket first */
3897 3564516 : p++;
3898 : /* first count how much space we need */
3899 3564516 : msg = p; /* save for later */
3900 148317244 : while (*p && *p != quote) {
3901 144752728 : if (*p == '\\') {
3902 16844 : p++;
3903 16844 : switch (*p) {
3904 0 : case '0':
3905 : case '1':
3906 : case '2':
3907 : case '3':
3908 : /* this could be the start of
3909 : an octal sequence, check it
3910 : out */
3911 0 : if (p[1] && p[2] &&
3912 0 : p[1] >= '0' && p[1] <= '7' &&
3913 0 : p[2] >= '0' && p[2] <= '7') {
3914 0 : p += 2;
3915 0 : break;
3916 : }
3917 : /* fall through */
3918 : default:
3919 : break;
3920 : }
3921 : }
3922 144752728 : p++;
3923 144752728 : len++;
3924 : }
3925 : /* now allocate space and copy string into new space */
3926 3564516 : p = msg; /* start over */
3927 3564516 : start = s = malloc(len + 1);
3928 148317244 : while (*p && *p != quote) {
3929 144752728 : if (*p == '\\') {
3930 16844 : p++;
3931 16844 : switch (*p) {
3932 : /* later
3933 : case '0': case '1': case '2': case '3': case '4':
3934 : case '5': case '6': case '7': case '8': case '9':
3935 : */
3936 1607 : case 'n':
3937 1607 : *s = '\n';
3938 1607 : break;
3939 15 : case 't':
3940 15 : *s = '\t';
3941 15 : break;
3942 0 : case 'r':
3943 0 : *s = '\r';
3944 0 : break;
3945 0 : case 'f':
3946 0 : *s = '\f';
3947 0 : break;
3948 0 : case '0':
3949 : case '1':
3950 : case '2':
3951 : case '3':
3952 : /* this could be the start of
3953 : an octal sequence, check it
3954 : out */
3955 0 : if (p[1] && p[2] &&
3956 0 : p[1] >= '0' && p[1] <= '7' &&
3957 0 : p[2] >= '0' && p[2] <= '7') {
3958 0 : *s = ((p[0] - '0') << 6) | ((p[1] - '0') << 3) | (p[2] - '0');
3959 0 : p += 2;
3960 0 : break;
3961 : }
3962 : /* fall through */
3963 : default:
3964 15222 : *s = *p;
3965 15222 : break;
3966 : }
3967 16844 : p++;
3968 : } else {
3969 144735884 : *s = *p++;
3970 : }
3971 144752728 : s++;
3972 : }
3973 3564516 : *s = 0; /* close string */
3974 3564516 : p++; /* skip over end-of-string quote */
3975 : /* skip over trailing junk (presumably white space) */
3976 4687462 : while (*p && *p != ',' && *p != endchar)
3977 1122946 : p++;
3978 3564516 : if (next)
3979 3564516 : *next = p;
3980 3564516 : *str = start;
3981 3564516 : if (lenp)
3982 3564516 : *lenp = len;
3983 :
3984 3564516 : return 1;
3985 : } else {
3986 : const char *s;
3987 : size_t len;
3988 :
3989 : /* p points at first non-white space character */
3990 47257864 : msg = p; /* record start of value */
3991 : /* find separator or terminator */
3992 47257864 : while (*p && *p != ',' && *p != '\t' && *p != endchar)
3993 46438508 : p++;
3994 : /* search back over trailing white space */
3995 930467 : for (s = p - 1; s > msg && isspace((unsigned char) *s); s--)
3996 : ;
3997 819356 : if (s < msg || !isspace((unsigned char) *s)) /* gone one too far */
3998 819356 : s++;
3999 819356 : if (*p == '\t') {
4000 22866 : p++;
4001 : }
4002 819356 : len = s - msg;
4003 819356 : *str = malloc(len + 1);
4004 819356 : strcpy_len(*str, msg, len + 1);
4005 :
4006 819356 : if (next)
4007 819356 : *next = p;
4008 819356 : if (lenp)
4009 819356 : *lenp = len;
4010 819356 : return 0;
4011 : }
4012 : }
4013 :
4014 : char *
4015 0 : mapi_unquote(char *msg)
4016 : {
4017 0 : char *start;
4018 :
4019 0 : unquote(msg, &start, NULL, ']', NULL);
4020 0 : return start;
4021 : }
4022 :
4023 : char *
4024 0 : mapi_quote(const char *msg, int size)
4025 : {
4026 : /* we absolutely don't need more than this (until we start
4027 : producing octal escapes */
4028 0 : char *s = malloc((size < 0 ? strlen(msg) : (size_t) size) * 2 + 1);
4029 0 : char *t = s;
4030 :
4031 : /* the condition is tricky: if initially size < 0, we must
4032 : continue until a NULL byte, else, size gives the number of
4033 : bytes to be copied */
4034 0 : while (size < 0 ? *msg : size > 0) {
4035 0 : if (size > 0)
4036 0 : size--;
4037 0 : switch (*msg) {
4038 0 : case '\n':
4039 0 : *t++ = '\\';
4040 0 : *t++ = 'n';
4041 0 : break;
4042 0 : case '\t':
4043 0 : *t++ = '\\';
4044 0 : *t++ = 't';
4045 0 : break;
4046 0 : case PLACEHOLDER:
4047 0 : *t++ = '\\';
4048 0 : *t++ = PLACEHOLDER;
4049 0 : break;
4050 0 : case '\\':
4051 0 : *t++ = '\\';
4052 0 : *t++ = '\\';
4053 0 : break;
4054 0 : case '\'':
4055 0 : *t++ = '\\';
4056 0 : *t++ = '\'';
4057 0 : break;
4058 0 : case '"':
4059 0 : *t++ = '\\';
4060 0 : *t++ = '"';
4061 0 : break;
4062 0 : case '\0':
4063 0 : *t++ = '\\';
4064 0 : *t++ = '0';
4065 0 : break;
4066 0 : default:
4067 0 : *t++ = *msg;
4068 0 : break;
4069 : }
4070 0 : msg++;
4071 : /* also deal with binaries */
4072 : }
4073 0 : *t = 0;
4074 0 : return s;
4075 : }
4076 :
4077 : static int
4078 0 : mapi_extend_bindings(MapiHdl hdl, int minbindings)
4079 : {
4080 : /* extend the bindings table */
4081 0 : int nm = hdl->maxbindings + 32;
4082 :
4083 0 : if (nm <= minbindings)
4084 0 : nm = minbindings + 32;
4085 0 : REALLOC(hdl->bindings, nm);
4086 0 : assert(hdl->bindings);
4087 : /* clear new entries */
4088 0 : memset(hdl->bindings + hdl->maxbindings, 0, (nm - hdl->maxbindings) * sizeof(*hdl->bindings));
4089 0 : hdl->maxbindings = nm;
4090 0 : return MOK;
4091 : }
4092 :
4093 : static int
4094 0 : mapi_extend_params(MapiHdl hdl, int minparams)
4095 : {
4096 : /* extend the params table */
4097 0 : int nm = hdl->maxparams + 32;
4098 :
4099 0 : if (nm <= minparams)
4100 0 : nm = minparams + 32;
4101 0 : REALLOC(hdl->params, nm);
4102 0 : assert(hdl->params);
4103 : /* clear new entries */
4104 0 : memset(hdl->params + hdl->maxparams, 0, (nm - hdl->maxparams) * sizeof(*hdl->params));
4105 0 : hdl->maxparams = nm;
4106 0 : return MOK;
4107 : }
4108 :
4109 : static MapiMsg
4110 0 : store_field(struct MapiResultSet *result, int cr, int fnr, int outtype, void *dst)
4111 : {
4112 0 : char *val;
4113 :
4114 0 : val = result->cache.line[cr].anchors[fnr];
4115 :
4116 0 : if (val == 0) {
4117 0 : return mapi_setError(result->hdl->mid, "Field value undefined or nil", __func__, MERROR);
4118 : }
4119 :
4120 : /* auto convert to C-type */
4121 0 : switch (outtype) {
4122 0 : case MAPI_TINY:
4123 0 : *(signed char *) dst = (signed char) strtol(val, NULL, 0);
4124 0 : break;
4125 0 : case MAPI_UTINY:
4126 0 : *(unsigned char *) dst = (unsigned char) strtoul(val, NULL, 0);
4127 0 : break;
4128 0 : case MAPI_SHORT:
4129 0 : *(short *) dst = (short) strtol(val, NULL, 0);
4130 0 : break;
4131 0 : case MAPI_USHORT:
4132 0 : *(unsigned short *) dst = (unsigned short) strtoul(val, NULL, 0);
4133 0 : break;
4134 0 : case MAPI_NUMERIC:
4135 : case MAPI_INT:
4136 0 : *(int *) dst = (int) strtol(val, NULL, 0);
4137 0 : break;
4138 0 : case MAPI_UINT:
4139 0 : *(unsigned int *) dst = (unsigned int) strtoul(val, NULL, 0);
4140 0 : break;
4141 0 : case MAPI_LONG:
4142 0 : *(long *) dst = strtol(val, NULL, 0);
4143 0 : break;
4144 0 : case MAPI_ULONG:
4145 0 : *(unsigned long *) dst = strtoul(val, NULL, 0);
4146 0 : break;
4147 0 : case MAPI_LONGLONG:
4148 0 : *(int64_t *) dst = strtoll(val, NULL, 0);
4149 0 : break;
4150 0 : case MAPI_ULONGLONG:
4151 0 : *(uint64_t *) dst = strtoull(val, NULL, 0);
4152 0 : break;
4153 0 : case MAPI_CHAR:
4154 0 : *(char *) dst = *val;
4155 0 : break;
4156 0 : case MAPI_FLOAT:
4157 0 : *(float *) dst = strtof(val, NULL);
4158 0 : break;
4159 0 : case MAPI_DOUBLE:
4160 0 : *(double *) dst = strtod(val, NULL);
4161 0 : break;
4162 0 : case MAPI_DATE:
4163 0 : sscanf(val, "%hd-%hu-%hu",
4164 : &((MapiDate *) dst)->year,
4165 : &((MapiDate *) dst)->month,
4166 : &((MapiDate *) dst)->day);
4167 0 : break;
4168 0 : case MAPI_TIME:
4169 0 : sscanf(val, "%hu:%hu:%hu",
4170 : &((MapiTime *) dst)->hour,
4171 : &((MapiTime *) dst)->minute,
4172 : &((MapiTime *) dst)->second);
4173 0 : break;
4174 0 : case MAPI_DATETIME:{
4175 0 : int n;
4176 :
4177 0 : ((MapiDateTime *) dst)->fraction = 0;
4178 0 : sscanf(val, "%hd-%hu-%hu %hu:%hu:%hu%n",
4179 : &((MapiDateTime *) dst)->year,
4180 : &((MapiDateTime *) dst)->month,
4181 : &((MapiDateTime *) dst)->day,
4182 : &((MapiDateTime *) dst)->hour,
4183 : &((MapiDateTime *) dst)->minute,
4184 : &((MapiDateTime *) dst)->second,
4185 : &n);
4186 0 : if (val[n] == '.') {
4187 0 : unsigned int fac = 1000000000;
4188 0 : unsigned int nsec = 0;
4189 :
4190 0 : for (n++; isdigit((unsigned char) val[n]); n++) {
4191 0 : fac /= 10;
4192 0 : nsec += (val[n] - '0') * fac;
4193 : }
4194 0 : ((MapiDateTime *) dst)->fraction = nsec;
4195 : }
4196 0 : break;
4197 : }
4198 0 : case MAPI_AUTO:
4199 : case MAPI_VARCHAR:
4200 : default:
4201 0 : *(char **) dst = val;
4202 : }
4203 : return MOK;
4204 : }
4205 :
4206 : MapiMsg
4207 0 : mapi_store_field(MapiHdl hdl, int fnr, int outtype, void *dst)
4208 : {
4209 0 : struct MapiResultSet *result;
4210 :
4211 0 : mapi_hdl_check(hdl);
4212 :
4213 0 : if ((result = hdl->result) == NULL) {
4214 0 : return mapi_setError(hdl->mid, "No data read", __func__, MERROR);
4215 : }
4216 :
4217 0 : if (fnr < 0 || fnr >= result->fieldcnt) {
4218 0 : return mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4219 : }
4220 :
4221 0 : return store_field(result, result->cache.reader, fnr, outtype, dst);
4222 : }
4223 :
4224 : static void
4225 0 : mapi_store_bind(struct MapiResultSet *result, int cr)
4226 : {
4227 0 : int i;
4228 0 : MapiHdl hdl = result->hdl;
4229 :
4230 0 : for (i = 0; i < hdl->maxbindings; i++)
4231 0 : if (hdl->bindings[i].outparam)
4232 0 : store_field(result, cr, i, hdl->bindings[i].outtype, hdl->bindings[i].outparam);
4233 0 : }
4234 :
4235 : /*
4236 : * The low level routine mapi_slice_row breaks the last row received
4237 : * into pieces and binds the field descriptors with their location. All
4238 : * escaped characters are immediately replaced, such that we end with a
4239 : * list of C-strings. It overwrites the contents of the row buffer,
4240 : * because de-escaping only reduces the size. It also silently extends
4241 : * the field descriptor table.
4242 : */
4243 : static int
4244 1146050 : mapi_slice_row(struct MapiResultSet *result, int cr)
4245 : {
4246 1146050 : char *p;
4247 1146050 : int i = 0;
4248 :
4249 1146050 : p = result->cache.line[cr].rows;
4250 1146050 : if (p == NULL)
4251 0 : return mapi_setError(result->hdl->mid, "Current row missing", __func__, MERROR);
4252 1146050 : if (result->cache.line[cr].fldcnt)
4253 : return result->cache.line[cr].fldcnt; /* already sliced */
4254 :
4255 1146050 : if (*p != '[') {
4256 : /* nothing to slice */
4257 49 : i = 1;
4258 49 : REALLOC(result->cache.line[cr].anchors, 1);
4259 49 : REALLOC(result->cache.line[cr].lens, 1);
4260 : /* skip initial '=' if present */
4261 49 : if (*p == '=')
4262 49 : p++;
4263 49 : result->cache.line[cr].anchors[0] = strdup(p);
4264 49 : result->cache.line[cr].lens[0] = strlen(p);
4265 : } else {
4266 : /* work on a copy to preserve the original */
4267 1146001 : p = strdup(p);
4268 1146206 : i = slice_row(p,
4269 1146001 : msettings_lang_is_sql(result->hdl->mid->settings) ? "NULL" : "nil",
4270 : &result->cache.line[cr].anchors,
4271 : &result->cache.line[cr].lens,
4272 : result->fieldcnt, ']');
4273 1146001 : free(p);
4274 : }
4275 1146050 : if (i != result->fieldcnt) {
4276 : int j;
4277 194 : for (j = 0; j < result->fieldcnt; j++) {
4278 0 : if (result->fields[j].columnname)
4279 0 : free(result->fields[j].columnname);
4280 0 : result->fields[j].columnname = NULL;
4281 0 : if (result->fields[j].columntype)
4282 0 : free(result->fields[j].columntype);
4283 0 : result->fields[j].columntype = NULL;
4284 0 : if (result->fields[j].tablename)
4285 0 : free(result->fields[j].tablename);
4286 0 : result->fields[j].tablename = NULL;
4287 0 : result->fields[j].columnlength = 0;
4288 : }
4289 : }
4290 1146050 : if (i > result->fieldcnt) {
4291 194 : result->fieldcnt = i;
4292 194 : if (i > result->maxfields) {
4293 194 : REALLOC(result->fields, i);
4294 194 : memset(result->fields + result->maxfields, 0, (i - result->maxfields) * sizeof(*result->fields));
4295 194 : result->maxfields = i;
4296 : }
4297 : }
4298 1146050 : result->cache.line[cr].fldcnt = i;
4299 1146050 : return i;
4300 : }
4301 :
4302 : /*
4303 : * The rows presented are broken down into pieces to
4304 : * simplify access later on. However, mclient may
4305 : * first want to check the content of the line for
4306 : * useful information (e.g. #EOD)
4307 : */
4308 : int
4309 26672 : mapi_split_line(MapiHdl hdl)
4310 : {
4311 26672 : int n;
4312 26672 : struct MapiResultSet *result;
4313 :
4314 26672 : result = hdl->result;
4315 26672 : assert(result != NULL);
4316 26672 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4317 26672 : n = mapi_slice_row(result, result->cache.reader);
4318 : /* no need to call mapi_store_bind since
4319 : mapi_fetch_line would have done that if needed */
4320 : }
4321 26672 : return n;
4322 : }
4323 :
4324 : int
4325 1123629 : mapi_fetch_row(MapiHdl hdl)
4326 : {
4327 1123629 : char *reply;
4328 1123629 : int n;
4329 1123629 : struct MapiResultSet *result;
4330 :
4331 1123629 : mapi_hdl_check(hdl);
4332 1140598 : do {
4333 1140598 : if ((reply = mapi_fetch_line(hdl)) == NULL)
4334 : return 0;
4335 1136347 : } while (*reply != '[' && *reply != '=');
4336 1119378 : result = hdl->result;
4337 1119378 : assert(result != NULL);
4338 1119378 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4339 1119378 : n = mapi_slice_row(result, result->cache.reader);
4340 : /* no need to call mapi_store_bind since
4341 : mapi_fetch_line would have done that if needed */
4342 : }
4343 : return n;
4344 : }
4345 :
4346 : /*
4347 : * All rows can be cached first as well.
4348 : */
4349 : int64_t
4350 1 : mapi_fetch_all_rows(MapiHdl hdl)
4351 : {
4352 1 : Mapi mid;
4353 1 : struct MapiResultSet *result;
4354 :
4355 1 : mapi_hdl_check(hdl);
4356 :
4357 1 : mid = hdl->mid;
4358 3 : for (;;) {
4359 4 : if ((result = hdl->result) != NULL &&
4360 2 : msettings_lang_is_sql(mid->settings) &&
4361 2 : mid->active == NULL &&
4362 1 : result->row_count > 0 &&
4363 1 : result->cache.first + result->cache.tuplecount < result->row_count) {
4364 0 : mid->active = hdl;
4365 0 : hdl->active = result;
4366 0 : mapi_log_record(mid, "SEND", "X" "export %d %" PRId64 "\n",
4367 : result->tableid, result->cache.first + result->cache.tuplecount);
4368 0 : if (mnstr_printf(mid->to, "X" "export %d %" PRId64 "\n",
4369 0 : result->tableid, result->cache.first + result->cache.tuplecount) < 0 ||
4370 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA))
4371 0 : check_stream(mid, mid->to, "sending export command", 0);
4372 : }
4373 2 : if (mid->active)
4374 1 : read_into_cache(mid->active, 0);
4375 : else
4376 : break;
4377 : }
4378 1 : return result ? result->cache.tuplecount : 0;
4379 : }
4380 :
4381 : char *
4382 4149569 : mapi_fetch_field(MapiHdl hdl, int fnr)
4383 : {
4384 4149569 : int cr;
4385 4149569 : struct MapiResultSet *result;
4386 :
4387 4149569 : mapi_hdl_check0(hdl);
4388 :
4389 4149569 : if ((result = hdl->result) == NULL ||
4390 4149569 : (cr = result->cache.reader) < 0 ||
4391 4149569 : (result->cache.line[cr].rows[0] != '[' &&
4392 : result->cache.line[cr].rows[0] != '=')) {
4393 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4394 0 : return 0;
4395 : }
4396 4149569 : assert(result->cache.line != NULL);
4397 4149569 : if (fnr >= 0) {
4398 : /* slice if needed */
4399 4149569 : if (result->cache.line[cr].fldcnt == 0)
4400 0 : mapi_slice_row(result, cr);
4401 4149569 : if (fnr < result->cache.line[cr].fldcnt)
4402 4149568 : return result->cache.line[cr].anchors[fnr];
4403 : }
4404 1 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4405 1 : return 0;
4406 : }
4407 :
4408 : size_t
4409 792264 : mapi_fetch_field_len(MapiHdl hdl, int fnr)
4410 : {
4411 792264 : int cr;
4412 792264 : struct MapiResultSet *result;
4413 :
4414 792264 : mapi_hdl_check0(hdl);
4415 :
4416 792264 : if ((result = hdl->result) == NULL ||
4417 792264 : (cr = result->cache.reader) < 0 ||
4418 792264 : (result->cache.line[cr].rows[0] != '[' &&
4419 : result->cache.line[cr].rows[0] != '=')) {
4420 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4421 0 : return 0;
4422 : }
4423 792264 : assert(result->cache.line != NULL);
4424 792264 : if (fnr >= 0) {
4425 : /* slice if needed */
4426 792264 : if (result->cache.line[cr].fldcnt == 0)
4427 0 : mapi_slice_row(result, cr);
4428 792264 : if (fnr < result->cache.line[cr].fldcnt)
4429 792264 : return result->cache.line[cr].lens[fnr];
4430 : }
4431 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4432 0 : return 0;
4433 : }
4434 :
4435 : int
4436 2373 : mapi_get_field_count(MapiHdl hdl)
4437 : {
4438 2373 : mapi_hdl_check(hdl);
4439 2373 : if (hdl->result && hdl->result->fieldcnt == 0) {
4440 : /* no rows have been sliced yet, and there was no
4441 : header, so try to figure out how many columns there
4442 : are for ourselves */
4443 : int i;
4444 :
4445 2064 : for (i = 0; i < hdl->result->cache.writer; i++)
4446 0 : if (hdl->result->cache.line[i].rows[0] == '[' ||
4447 : hdl->result->cache.line[i].rows[0] == '=')
4448 0 : mapi_slice_row(hdl->result, i);
4449 : }
4450 2373 : return hdl->result ? hdl->result->fieldcnt : 0;
4451 : }
4452 :
4453 : int64_t
4454 382 : mapi_get_row_count(MapiHdl hdl)
4455 : {
4456 382 : mapi_hdl_check(hdl);
4457 382 : return hdl->result ? hdl->result->row_count : 0;
4458 : }
4459 :
4460 : int64_t
4461 0 : mapi_get_last_id(MapiHdl hdl)
4462 : {
4463 0 : mapi_hdl_check(hdl);
4464 0 : return hdl->result ? hdl->result->last_id : -1;
4465 : }
4466 :
4467 : char *
4468 928 : mapi_get_name(MapiHdl hdl, int fnr)
4469 : {
4470 928 : struct MapiResultSet *result;
4471 :
4472 928 : mapi_hdl_check0(hdl);
4473 928 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4474 928 : return result->fields[fnr].columnname;
4475 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4476 0 : return 0;
4477 : }
4478 :
4479 : char *
4480 4116531 : mapi_get_type(MapiHdl hdl, int fnr)
4481 : {
4482 4116531 : struct MapiResultSet *result;
4483 :
4484 4116531 : mapi_hdl_check0(hdl);
4485 4116531 : if ((result = hdl->result) != 0 &&
4486 4116531 : fnr >= 0 && fnr < result->fieldcnt) {
4487 4116531 : if (result->fields[fnr].columntype == NULL)
4488 : return "unknown";
4489 4116530 : return result->fields[fnr].columntype;
4490 : }
4491 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4492 0 : return 0;
4493 : }
4494 :
4495 : char *
4496 918 : mapi_get_table(MapiHdl hdl, int fnr)
4497 : {
4498 918 : struct MapiResultSet *result;
4499 :
4500 918 : mapi_hdl_check0(hdl);
4501 918 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4502 918 : return result->fields[fnr].tablename;
4503 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4504 0 : return 0;
4505 : }
4506 :
4507 : int
4508 864 : mapi_get_len(MapiHdl hdl, int fnr)
4509 : {
4510 864 : struct MapiResultSet *result;
4511 :
4512 864 : mapi_hdl_check0(hdl);
4513 864 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4514 864 : return result->fields[fnr].columnlength;
4515 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4516 0 : return 0;
4517 : }
4518 :
4519 : int
4520 921 : mapi_get_digits(MapiHdl hdl, int fnr)
4521 : {
4522 921 : struct MapiResultSet *result;
4523 :
4524 921 : mapi_hdl_check0(hdl);
4525 921 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4526 921 : return result->fields[fnr].digits;
4527 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4528 0 : return 0;
4529 : }
4530 :
4531 : int
4532 4 : mapi_get_scale(MapiHdl hdl, int fnr)
4533 : {
4534 4 : struct MapiResultSet *result;
4535 :
4536 4 : mapi_hdl_check0(hdl);
4537 4 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4538 4 : return result->fields[fnr].scale;
4539 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4540 0 : return 0;
4541 : }
4542 :
4543 : char *
4544 1053 : mapi_get_query(MapiHdl hdl)
4545 : {
4546 1053 : mapi_hdl_check0(hdl);
4547 1053 : if (hdl->query != NULL) {
4548 1053 : return strdup(hdl->query);
4549 : } else {
4550 : return NULL;
4551 : }
4552 : }
4553 :
4554 :
4555 : int
4556 9409 : mapi_get_querytype(MapiHdl hdl)
4557 : {
4558 9409 : struct MapiResultSet *result;
4559 :
4560 9409 : mapi_hdl_check0(hdl);
4561 9409 : if ((result = hdl->result) != 0)
4562 8349 : return result->querytype;
4563 1060 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4564 1060 : return 0; /* Q_PARSE! */
4565 : }
4566 :
4567 : int
4568 122 : mapi_get_tableid(MapiHdl hdl)
4569 : {
4570 122 : struct MapiResultSet *result;
4571 :
4572 122 : mapi_hdl_check0(hdl);
4573 122 : if ((result = hdl->result) != 0)
4574 122 : return result->tableid;
4575 0 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4576 0 : return 0;
4577 : }
4578 :
4579 : int64_t
4580 2826 : mapi_rows_affected(MapiHdl hdl)
4581 : {
4582 2826 : struct MapiResultSet *result;
4583 :
4584 2826 : mapi_hdl_check(hdl);
4585 2826 : if ((result = hdl->result) == NULL)
4586 : return 0;
4587 2826 : return result->row_count;
4588 : }
4589 :
4590 : int64_t
4591 3663 : mapi_get_querytime(MapiHdl hdl)
4592 : {
4593 3663 : struct MapiResultSet *result;
4594 :
4595 3663 : mapi_hdl_check(hdl);
4596 3663 : if ((result = hdl->result) == NULL)
4597 : return 0;
4598 3133 : return result->querytime;
4599 : }
4600 :
4601 : int64_t
4602 3663 : mapi_get_maloptimizertime(MapiHdl hdl)
4603 : {
4604 3663 : struct MapiResultSet *result;
4605 :
4606 3663 : mapi_hdl_check(hdl);
4607 3663 : if ((result = hdl->result) == NULL)
4608 : return 0;
4609 3133 : return result->maloptimizertime;
4610 : }
4611 :
4612 : int64_t
4613 3663 : mapi_get_sqloptimizertime(MapiHdl hdl)
4614 : {
4615 3663 : struct MapiResultSet *result;
4616 :
4617 3663 : mapi_hdl_check(hdl);
4618 3663 : if ((result = hdl->result) == NULL)
4619 : return 0;
4620 3133 : return result->sqloptimizertime;
4621 : }
4622 :
4623 : const char *
4624 198 : mapi_get_dbname(Mapi mid)
4625 : {
4626 198 : return msetting_string(mid->settings, MP_DATABASE);
4627 : }
4628 :
4629 : const char *
4630 8 : mapi_get_host(Mapi mid)
4631 : {
4632 8 : return msetting_string(mid->settings, MP_HOST);
4633 : }
4634 :
4635 : const char *
4636 8 : mapi_get_user(Mapi mid)
4637 : {
4638 8 : return msetting_string(mid->settings, MP_USER);;
4639 : }
4640 :
4641 : const char *
4642 0 : mapi_get_lang(Mapi mid)
4643 : {
4644 0 : return msetting_string(mid->settings, MP_LANGUAGE);;
4645 : }
4646 :
4647 : const char *
4648 0 : mapi_get_uri(Mapi mid)
4649 : {
4650 0 : return mid->uri;
4651 : }
4652 :
4653 : const char *
4654 1 : mapi_get_mapi_version(void)
4655 : {
4656 1 : return MAPI_VERSION;
4657 : }
4658 :
4659 : const char *
4660 0 : mapi_get_monet_version(Mapi mid)
4661 : {
4662 0 : mapi_check0(mid);
4663 0 : return mid->server ? mid->server : "";
4664 : }
4665 :
4666 : const char *
4667 0 : mapi_get_motd(Mapi mid)
4668 : {
4669 0 : mapi_check0(mid);
4670 0 : return mid->motd;
4671 : }
4672 :
4673 : bool
4674 0 : mapi_is_connected(Mapi mid)
4675 : {
4676 0 : return mid->connected;
4677 : }
4678 :
4679 : MapiHdl
4680 157 : mapi_get_active(Mapi mid)
4681 : {
4682 157 : return mid->active;
4683 : }
4684 :
4685 : msettings*
4686 0 : mapi_get_settings(Mapi mid)
4687 : {
4688 0 : return mid->settings;
4689 : }
4690 :
4691 :
4692 : MapiMsg
4693 1384 : mapi_wrap_streams(Mapi mid, stream *rstream, stream *wstream)
4694 : {
4695 : // do not use check_stream here yet because the socket is not yet in 'mid'
4696 1384 : const char *error_message;
4697 1384 : stream *error_stream;
4698 :
4699 1384 : assert(!isa_block_stream(rstream));
4700 1384 : assert(!isa_block_stream(wstream));
4701 :
4702 : // First send some NUL bytes. If we're accidentally connecting to the wrong
4703 : // port or protocol this may cause the remote server to close the
4704 : // connection. If we don't do this, the connection will often hang
4705 : // because the server expects us to speak first and we expect the server
4706 : // to speak first.
4707 : //
4708 : // Note that a pair of NUL bytes is a no-op message in MAPI.
4709 : //
4710 : // Surprisingly, it seems sending these NUL bytes makes non-TLS
4711 : // connection setup a little faster rather than slower!
4712 : static const char zeroes[8] = { 0 };
4713 : ssize_t to_write = sizeof(zeroes);
4714 2768 : while (to_write > 0) {
4715 1384 : ssize_t n = mnstr_write(wstream, zeroes, 1, to_write);
4716 1384 : if (n < 0) {
4717 0 : close_connection(mid);
4718 0 : return mapi_printError(mid, __func__, MERROR, "could not send leader block: %s", mnstr_peek_error(wstream));
4719 : }
4720 1384 : to_write -= (size_t)n;
4721 : }
4722 1384 : if (mnstr_flush(wstream, MNSTR_FLUSH_DATA) != 0) {
4723 0 : close_connection(mid);
4724 0 : return mapi_printError(mid, __func__, MERROR, "could not flush leader block: %s", mnstr_peek_error(wstream));
4725 : }
4726 :
4727 :
4728 1384 : stream *brstream = NULL;
4729 1384 : stream *bwstream = NULL;
4730 :
4731 1384 : bwstream = block_stream(wstream);
4732 1384 : if (bwstream == NULL || mnstr_errnr(bwstream) != MNSTR_NO__ERROR) {
4733 0 : error_stream = bwstream;
4734 0 : error_message = "block_stream wstream";
4735 0 : goto bailout;
4736 : }
4737 1384 : brstream = block_stream(rstream);
4738 1384 : if (brstream == NULL || mnstr_errnr(brstream) != MNSTR_NO__ERROR) {
4739 0 : error_stream = brstream;
4740 0 : error_message = "block_stream rstream";
4741 0 : goto bailout;
4742 : }
4743 :
4744 1384 : mid->to = bwstream;
4745 1384 : mid->from = brstream;
4746 1384 : return MOK;
4747 0 : bailout:
4748 : // adapted from the check_stream macro
4749 0 : if (brstream)
4750 0 : mnstr_destroy(brstream);
4751 0 : if (bwstream)
4752 0 : mnstr_destroy(bwstream);
4753 : // malloc failure is the only way these calls could have failed
4754 0 : return mapi_printError(mid, __func__, MERROR, "%s: %s", error_message, mnstr_peek_error(error_stream));
4755 : }
|