LCOV - code coverage report
Current view: top level - clients/mapilib - mapi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1273 2199 57.9 %
Date: 2024-04-26 00:35:57 Functions: 89 137 65.0 %

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

Generated by: LCOV version 1.14