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