LCOV - code coverage report
Current view: top level - clients/mapilib - mapi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1271 2211 57.5 %
Date: 2024-11-13 19:37:10 Functions: 89 139 64.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 insecure 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_field_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             : /*
     798             :  * Blocking
     799             :  * --------
     800             :  *
     801             :  * The server side code works with a common/stream package, a fast
     802             :  * buffered IO scheme.  Nowadays this should be the only protocol used,
     803             :  * while historical uses were line-based instead.
     804             :  *
     805             :  *
     806             :  * Error Handling
     807             :  * --------------
     808             :  *
     809             :  * All externally visible functions should first call mapi_clrError (usually
     810             :  * though a call to one of the check macros above) to clear the error flag.
     811             :  * When an error is detected, the library calls mapi_setError to set the error
     812             :  * flag.  The application can call mapi_error or mapi_error_str to check for
     813             :  * errors, and mapi_explain or mapi_explain_query to print a formatted error
     814             :  * report.
     815             :  */
     816             : char mapi_nomem[] = "Memory allocation failed";
     817             : 
     818             : void
     819    15171050 : mapi_clrError(Mapi mid)
     820             : {
     821    15171050 :         assert(mid);
     822    15171050 :         if (mid->errorstr && mid->errorstr != mapi_nomem)
     823        1356 :                 free(mid->errorstr);
     824    15171050 :         mid->action = 0;     /* contains references to constants */
     825    15171050 :         mid->error = 0;
     826    15171050 :         mid->errorstr = 0;
     827    15171050 : }
     828             : 
     829             : MapiMsg
     830        1348 : mapi_setError(Mapi mid, const char *msg, const char *action, MapiMsg error)
     831             : {
     832        1348 :         assert(msg);
     833        1348 :         REALLOC(mid->errorstr, strlen(msg) + 1);
     834        1348 :         if (mid->errorstr == NULL)
     835           0 :                 mid->errorstr = mapi_nomem;
     836             :         else
     837        1348 :                 strcpy(mid->errorstr, msg);
     838        1348 :         mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
     839        1348 :         mid->error = error;
     840        1348 :         mid->action = action;
     841        1348 :         return mid->error;
     842             : }
     843             : 
     844          33 : MapiMsg mapi_printError(Mapi mid, const char *action, MapiMsg error, const char *fmt, ...)
     845             : {
     846          33 :         size_t size = 81; // just a guess
     847             : 
     848          59 :         while (1) {
     849          46 :                 REALLOC(mid->errorstr, size);
     850          46 :                 if (mid->errorstr == NULL) {
     851           0 :                         mid->errorstr = mapi_nomem;
     852           0 :                         break;
     853             :                 }
     854          46 :                 va_list ap;
     855          46 :                 va_start(ap, fmt);
     856          46 :                 int n = vsnprintf(mid->errorstr, size, fmt, ap);
     857          46 :                 va_end(ap);
     858          46 :                 if (n < 0) {
     859             :                         // is it even possible for vsnprintf to fail?
     860             :                         break;
     861             :                 }
     862          46 :                 if ((size_t)n < size) {
     863             :                         // it fit
     864             :                         break;
     865             :                 } else {
     866             :                         // try again larger
     867          13 :                         size = (size_t)n + 1;
     868             :                 }
     869             :         }
     870          33 :         mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
     871          33 :         mid->error = error;
     872          33 :         mid->action = action;
     873          33 :         return mid->error;
     874             : }
     875             : 
     876             : MapiMsg
     877      198356 : mapi_error(Mapi mid)
     878             : {
     879      198356 :         assert(mid);
     880      198356 :         return mid->error;
     881             : }
     882             : 
     883             : const char *
     884          27 : mapi_error_str(Mapi mid)
     885             : {
     886          27 :         assert(mid);
     887          27 :         return mid->errorstr;
     888             : }
     889             : 
     890             : #ifdef _MSC_VER
     891             : static const struct {
     892             :         int e;
     893             :         const char *m;
     894             : } wsaerrlist[] = {
     895             :         { WSA_INVALID_HANDLE, "Specified event object handle is invalid" },
     896             :         { WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available" },
     897             :         { WSA_INVALID_PARAMETER, "One or more parameters are invalid" },
     898             :         { WSA_OPERATION_ABORTED, "Overlapped operation aborted" },
     899             :         { WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state" },
     900             :         { WSA_IO_PENDING, "Overlapped operations will complete later" },
     901             :         { WSAEINTR, "Interrupted function call" },
     902             :         { WSAEBADF, "File handle is not valid" },
     903             :         { WSAEACCES, "Permission denied" },
     904             :         { WSAEFAULT, "Bad address" },
     905             :         { WSAEINVAL, "Invalid argument" },
     906             :         { WSAEMFILE, "Too many open files" },
     907             :         { WSAEWOULDBLOCK, "Resource temporarily unavailable" },
     908             :         { WSAEINPROGRESS, "Operation now in progress" },
     909             :         { WSAEALREADY, "Operation already in progress" },
     910             :         { WSAENOTSOCK, "Socket operation on nonsocket" },
     911             :         { WSAEDESTADDRREQ, "Destination address required" },
     912             :         { WSAEMSGSIZE, "Message too long" },
     913             :         { WSAEPROTOTYPE, "Protocol wrong type for socket" },
     914             :         { WSAENOPROTOOPT, "Bad protocol option" },
     915             :         { WSAEPROTONOSUPPORT, "Protocol not supported" },
     916             :         { WSAESOCKTNOSUPPORT, "Socket type not supported" },
     917             :         { WSAEOPNOTSUPP, "Operation not supported" },
     918             :         { WSAEPFNOSUPPORT, "Protocol family not supported" },
     919             :         { WSAEAFNOSUPPORT, "Address family not supported by protocol family" },
     920             :         { WSAEADDRINUSE, "Address already in use" },
     921             :         { WSAEADDRNOTAVAIL, "Cannot assign requested address" },
     922             :         { WSAENETDOWN, "Network is down" },
     923             :         { WSAENETUNREACH, "Network is unreachable" },
     924             :         { WSAENETRESET, "Network dropped connection on reset" },
     925             :         { WSAECONNABORTED, "Software caused connection abort" },
     926             :         { WSAECONNRESET, "Connection reset by peer" },
     927             :         { WSAENOBUFS, "No buffer space available" },
     928             :         { WSAEISCONN, "Socket is already connected" },
     929             :         { WSAENOTCONN, "Socket is not connected" },
     930             :         { WSAESHUTDOWN, "Cannot send after socket shutdown" },
     931             :         { WSAETOOMANYREFS, "Too many references" },
     932             :         { WSAETIMEDOUT, "Connection timed out" },
     933             :         { WSAECONNREFUSED, "Connection refused" },
     934             :         { WSAELOOP, "Cannot translate name" },
     935             :         { WSAENAMETOOLONG, "Name too long" },
     936             :         { WSAEHOSTDOWN, "Host is down" },
     937             :         { WSAEHOSTUNREACH, "No route to host" },
     938             :         { WSAENOTEMPTY, "Directory not empty" },
     939             :         { WSAEPROCLIM, "Too many processes" },
     940             :         { WSAEUSERS, "User quota exceeded" },
     941             :         { WSAEDQUOT, "Disk quota exceeded" },
     942             :         { WSAESTALE, "Stale file handle reference" },
     943             :         { WSAEREMOTE, "Item is remote" },
     944             :         { WSASYSNOTREADY, "Network subsystem is unavailable" },
     945             :         { WSAVERNOTSUPPORTED, "Winsock.dll version out of range" },
     946             :         { WSANOTINITIALISED, "Successful WSAStartup not yet performed" },
     947             :         { WSAEDISCON, "Graceful shutdown in progress" },
     948             :         { WSAENOMORE, "No more results" },
     949             :         { WSAECANCELLED, "Call has been canceled" },
     950             :         { WSAEINVALIDPROCTABLE, "Procedure call table is invalid" },
     951             :         { WSAEINVALIDPROVIDER, "Service provider is invalid" },
     952             :         { WSAEPROVIDERFAILEDINIT, "Service provider failed to initialize" },
     953             :         { WSASYSCALLFAILURE, "System call failure" },
     954             :         { WSASERVICE_NOT_FOUND, "Service not found" },
     955             :         { WSATYPE_NOT_FOUND, "Class type not found" },
     956             :         { WSA_E_NO_MORE, "No more results" },
     957             :         { WSA_E_CANCELLED, "Call was canceled" },
     958             :         { WSAEREFUSED, "Database query was refused" },
     959             :         { WSAHOST_NOT_FOUND, "Host not found" },
     960             :         { WSATRY_AGAIN, "Nonauthoritative host not found" },
     961             :         { WSANO_RECOVERY, "This is a nonrecoverable error" },
     962             :         { WSANO_DATA, "Valid name, no data record of requested type" },
     963             :         { WSA_QOS_RECEIVERS, "QOS receivers" },
     964             :         { WSA_QOS_SENDERS, "QOS senders" },
     965             :         { WSA_QOS_NO_SENDERS, "No QOS senders" },
     966             :         { WSA_QOS_NO_RECEIVERS, "QOS no receivers" },
     967             :         { WSA_QOS_REQUEST_CONFIRMED, "QOS request confirmed" },
     968             :         { WSA_QOS_ADMISSION_FAILURE, "QOS admission error" },
     969             :         { WSA_QOS_POLICY_FAILURE, "QOS policy failure" },
     970             :         { WSA_QOS_BAD_STYLE, "QOS bad style" },
     971             :         { WSA_QOS_BAD_OBJECT, "QOS bad object" },
     972             :         { WSA_QOS_TRAFFIC_CTRL_ERROR, "QOS traffic control error" },
     973             :         { WSA_QOS_GENERIC_ERROR, "QOS generic error" },
     974             :         { WSA_QOS_ESERVICETYPE, "QOS service type error" },
     975             :         { WSA_QOS_EFLOWSPEC, "QOS flowspec error" },
     976             :         { WSA_QOS_EPROVSPECBUF, "Invalid QOS provider buffer" },
     977             :         { WSA_QOS_EFILTERSTYLE, "Invalid QOS filter style" },
     978             :         { WSA_QOS_EFILTERTYPE, "Invalid QOS filter type" },
     979             :         { WSA_QOS_EFILTERCOUNT, "Incorrect QOS filter count" },
     980             :         { WSA_QOS_EOBJLENGTH, "Invalid QOS object length" },
     981             :         { WSA_QOS_EFLOWCOUNT, "Incorrect QOS flow count" },
     982             :         { WSA_QOS_EUNKOWNPSOBJ, "Unrecognized QOS object" },
     983             :         { WSA_QOS_EPOLICYOBJ, "Invalid QOS policy object" },
     984             :         { WSA_QOS_EFLOWDESC, "Invalid QOS flow descriptor" },
     985             :         { WSA_QOS_EPSFLOWSPEC, "Invalid QOS provider-specific flowspec" },
     986             :         { WSA_QOS_EPSFILTERSPEC, "Invalid QOS provider-specific filterspec" },
     987             :         { WSA_QOS_ESDMODEOBJ, "Invalid QOS shape discard mode object" },
     988             :         { WSA_QOS_ESHAPERATEOBJ, "Invalid QOS shaping rate object" },
     989             :         { WSA_QOS_RESERVED_PETYPE, "Reserved policy QOS element type" },
     990             : };
     991             : const char *
     992             : wsaerror(int err)
     993             : {
     994             :         int i;
     995             : 
     996             :         for (i = 0; i < NELEM(wsaerrlist); i++)
     997             :                 if (wsaerrlist[i].e == err)
     998             :                         return wsaerrlist[i].m;
     999             :         return "Unknown error";
    1000             : }
    1001             : #endif
    1002             : 
    1003             : static void
    1004           0 : clean_print(char *msg, const char *prefix, FILE *fd)
    1005             : {
    1006           0 :         size_t len = strlen(prefix);
    1007             : 
    1008           0 :         while (msg && *msg) {
    1009             :                 /* cut by line */
    1010           0 :                 char *p = strchr(msg, '\n');
    1011             : 
    1012           0 :                 if (p)
    1013           0 :                         *p++ = 0;
    1014             : 
    1015             :                 /* skip over prefix */
    1016           0 :                 if (strncmp(msg, prefix, len) == 0)
    1017           0 :                         msg += len;
    1018             : 
    1019             :                 /* output line */
    1020           0 :                 fputs(msg, fd);
    1021           0 :                 fputc('\n', fd);
    1022           0 :                 msg = p;
    1023             :         }
    1024           0 : }
    1025             : 
    1026             : static void
    1027         176 : indented_print(const char *msg, const char *prefix, FILE *fd)
    1028             : {
    1029             :         /* for multiline error messages, indent all subsequent
    1030             :            lines with the space it takes to print "ERROR = " */
    1031         176 :         const char *s = prefix, *p = msg, *q;
    1032         176 :         const int len = (int) strlen(s);
    1033         176 :         const char t = s[len - 1];
    1034             : 
    1035         298 :         while (p && *p) {
    1036         187 :                 fprintf(fd, "%*.*s%c", len - 1, len - 1, s, t);
    1037         187 :                 s = "";
    1038             : 
    1039         187 :                 q = strchr(p, '\n');
    1040         187 :                 if (q) {
    1041         122 :                         q++;    /* also print the newline */
    1042         122 :                         fprintf(fd, "%.*s", (int) (q - p), p);
    1043             :                 } else {
    1044             :                         /* print bit after last newline,
    1045             :                            adding one ourselves */
    1046          65 :                         fprintf(fd, "%s\n", p);
    1047          65 :                         break;  /* nothing more to do */
    1048             :                 }
    1049         122 :                 p = q;
    1050             :         }
    1051         176 : }
    1052             : 
    1053             : void
    1054          59 : mapi_noexplain(Mapi mid, const char *errorprefix)
    1055             : {
    1056          59 :         assert(mid);
    1057          59 :         if (mid->noexplain)
    1058           0 :                 free(mid->noexplain);
    1059          59 :         mid->noexplain = errorprefix ? strdup(errorprefix) : NULL;
    1060          59 : }
    1061             : 
    1062             : void
    1063           0 : mapi_explain(Mapi mid, FILE *fd)
    1064             : {
    1065           0 :         assert(mid);
    1066           0 :         if (mid->noexplain == NULL) {
    1067           0 :                 const char *host = msetting_string(mid->settings, MP_HOST);
    1068           0 :                 const char *user = msetting_string(mid->settings, MP_USER);
    1069           0 :                 int port = msetting_long(mid->settings, MP_PORT);
    1070           0 :                 if (host[0] == '/')
    1071           0 :                         fprintf(fd, "MAPI  = (%s) %s\n", user, host);
    1072             :                 else
    1073           0 :                         fprintf(fd, "MAPI  = %s@%s:%d\n",
    1074             :                                 user, host, port);
    1075           0 :                 if (mid->action)
    1076           0 :                         fprintf(fd, "ACTION= %s\n", mid->action);
    1077           0 :                 if (mid->errorstr)
    1078           0 :                         indented_print(mid->errorstr, "ERROR = !", fd);
    1079           0 :         } else if (mid->errorstr) {
    1080           0 :                 clean_print(mid->errorstr, mid->noexplain, fd);
    1081             :         }
    1082           0 :         fflush(fd);
    1083           0 :         mapi_clrError(mid);
    1084           0 : }
    1085             : 
    1086             : void
    1087           0 : mapi_explain_query(MapiHdl hdl, FILE *fd)
    1088             : {
    1089           0 :         Mapi mid;
    1090             : 
    1091           0 :         assert(hdl);
    1092           0 :         mid = hdl->mid;
    1093           0 :         assert(mid);
    1094           0 :         if (mid->noexplain == NULL) {
    1095           0 :                 const char *host = msetting_string(mid->settings, MP_HOST);
    1096           0 :                 const char *user = msetting_string(mid->settings, MP_USER);
    1097           0 :                 int port = msetting_long(mid->settings, MP_PORT);
    1098           0 :                 if (host[0] == '/')
    1099           0 :                         fprintf(fd, "MAPI  = (%s) %s\n", user, host);
    1100             :                 else
    1101           0 :                         fprintf(fd, "MAPI  = %s@%s:%d\n",
    1102             :                                 user, host, port);
    1103           0 :                 if (mid->action)
    1104           0 :                         fprintf(fd, "ACTION= %s\n", mid->action);
    1105           0 :                 if (hdl->query)
    1106           0 :                         indented_print(hdl->query, "QUERY = ", fd);
    1107           0 :                 if (mid->errorstr)
    1108           0 :                         indented_print(mid->errorstr, "ERROR = !", fd);
    1109           0 :         } else if (mid->errorstr) {
    1110           0 :                 clean_print(mid->errorstr, mid->noexplain, fd);
    1111             :         }
    1112           0 :         fflush(fd);
    1113           0 :         mapi_clrError(mid);
    1114           0 : }
    1115             : 
    1116             : void
    1117          59 : mapi_explain_result(MapiHdl hdl, FILE *fd)
    1118             : {
    1119          59 :         Mapi mid;
    1120             : 
    1121          59 :         if (hdl == NULL ||
    1122          59 :             hdl->result == NULL ||
    1123          59 :             hdl->result->errorstr == NULL)
    1124             :                 return;
    1125          59 :         assert(hdl);
    1126          59 :         assert(hdl->result);
    1127          59 :         assert(hdl->result->errorstr);
    1128          59 :         mid = hdl->mid;
    1129          59 :         assert(mid);
    1130          59 :         if (mid->noexplain == NULL) {
    1131          59 :                 const char *host = msetting_string(mid->settings, MP_HOST);
    1132          59 :                 const char *user = msetting_string(mid->settings, MP_USER);
    1133          59 :                 int port = msetting_long(mid->settings, MP_PORT);
    1134          59 :                 if (host[0] == '/')
    1135           0 :                         fprintf(fd, "MAPI  = (%s) %s\n", user, host);
    1136             :                 else
    1137          59 :                         fprintf(fd, "MAPI  = %s@%s:%d\n",
    1138             :                                 user, host, port);
    1139          59 :                 if (mid->action)
    1140           0 :                         fprintf(fd, "ACTION= %s\n", mid->action);
    1141          59 :                 if (hdl->query)
    1142          59 :                         indented_print(hdl->query, "QUERY = ", fd);
    1143          59 :                 indented_print(hdl->result->errorstr, "ERROR = !", fd);
    1144          59 :                 if (msettings_lang_is_sql(mid->settings) && hdl->result->sqlstate[0])
    1145          58 :                         indented_print(hdl->result->sqlstate, "CODE  = ", fd);
    1146             :         } else {
    1147           0 :                 clean_print(hdl->result->errorstr, mid->noexplain, fd);
    1148             :         }
    1149          59 :         fflush(fd);
    1150             : }
    1151             : 
    1152             : stream *
    1153        2800 : mapi_get_to(Mapi mid)
    1154             : {
    1155        2800 :         mapi_check0(mid);
    1156        2801 :         return mid->to;
    1157             : }
    1158             : 
    1159             : stream *
    1160        1401 : mapi_get_from(Mapi mid)
    1161             : {
    1162        1401 :         mapi_check0(mid);
    1163        1401 :         return mid->from;
    1164             : }
    1165             : 
    1166             : bool
    1167           0 : mapi_get_trace(Mapi mid)
    1168             : {
    1169           0 :         mapi_check0(mid);
    1170           0 :         return mid->trace;
    1171             : }
    1172             : 
    1173             : bool
    1174           0 : mapi_get_autocommit(Mapi mid)
    1175             : {
    1176           0 :         mapi_check0(mid);
    1177           0 :         return msetting_bool(mid->settings, MP_AUTOCOMMIT);
    1178             : }
    1179             : 
    1180             : bool
    1181           0 : mapi_get_columnar_protocol(Mapi mid)
    1182             : {
    1183           0 :         mapi_check0(mid);
    1184           0 :         return mid->columnar_protocol;
    1185             : }
    1186             : 
    1187             : int
    1188           0 : mapi_get_time_zone(Mapi mid)
    1189             : {
    1190           0 :         mapi_check0(mid);
    1191           0 :         return msetting_long(mid->settings, MP_TIMEZONE);
    1192             : }
    1193             : 
    1194             : static int64_t
    1195           0 : usec(void)
    1196             : {
    1197             : #ifdef HAVE_GETTIMEOFDAY
    1198           0 :         struct timeval tp;
    1199             : 
    1200           0 :         gettimeofday(&tp, NULL);
    1201           0 :         return ((int64_t) tp.tv_sec) * 1000000 + (int64_t) tp.tv_usec;
    1202             : #else
    1203             : #ifdef HAVE_FTIME
    1204             :         struct timeb tb;
    1205             : 
    1206             :         ftime(&tb);
    1207             :         return ((int64_t) tb.time) * 1000000 + ((int64_t) tb.millitm) * 1000;
    1208             : #endif
    1209             : #endif
    1210             : }
    1211             : 
    1212             : static void
    1213           0 : mapi_log_header(Mapi mid, const char *funcname, long line, const char *mark1, const char *mark2)
    1214             : {
    1215           0 :         int64_t now = usec();
    1216           0 :         static int64_t firstcall = 0;
    1217           0 :         if (firstcall == 0)
    1218           0 :                 firstcall = now;
    1219           0 :         double seconds = (double)(now - firstcall) / 1e6;
    1220           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 */
    1221           0 : }
    1222             : 
    1223             : void
    1224           0 : mapi_impl_log_record(Mapi mid, const char *funcname, long line, const char *mark, const char *fmt, ...)
    1225             : {
    1226           0 :         va_list ap;
    1227             : 
    1228           0 :         if (mid->tracelog == NULL)
    1229           0 :                 return;
    1230             : 
    1231             :         size_t needed = 128;
    1232           0 :         size_t to_print;
    1233           0 :         while (1) {
    1234           0 :                 if (mid->tracebuffersize < needed) {
    1235           0 :                         free(mid->tracebuffer);
    1236           0 :                         mid->tracebuffer = malloc(needed);
    1237           0 :                         if (mid->tracebuffer) {
    1238           0 :                                 mid->tracebuffersize = needed;
    1239             :                         } else {
    1240           0 :                                 mid->tracebuffersize = 0;
    1241           0 :                                 to_print = 0;
    1242           0 :                                 break;
    1243             :                         }
    1244             :                 }
    1245           0 :                 va_start(ap, fmt);
    1246           0 :                 int n = vsnprintf(mid->tracebuffer, mid->tracebuffersize, fmt, ap);
    1247           0 :                 va_end(ap);
    1248           0 :                 if (n < 0) {
    1249             :                         to_print = 0;
    1250             :                         break;
    1251             :                 }
    1252           0 :                 if ((size_t)n < mid->tracebuffersize) {
    1253             :                         to_print = n;
    1254             :                         break;
    1255             :                 }
    1256             :                 // need to reallocate
    1257           0 :                 needed = n + 1;
    1258             :         }
    1259             : 
    1260           0 :         mapi_log_header(mid, funcname, line, mark, "");
    1261           0 :         if (to_print > 0) {
    1262           0 :                 mnstr_write(mid->tracelog, mid->tracebuffer, to_print, 1);
    1263           0 :                 mnstr_writeChr(mid->tracelog, '\n');
    1264             :         }
    1265           0 :         mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
    1266             : }
    1267             : 
    1268             : #define MAPILOG_OPEN "\tb'"
    1269             : #define MAPILOG_CLOSE "'"
    1270             : 
    1271             : void
    1272           0 : mapi_impl_log_data(Mapi mid, const char *filename, long line, const char *mark, const char *start, size_t len)
    1273             : {
    1274           0 :         const char hexdigits[] = "0123456789abcdef";
    1275           0 :         if (mid->tracelog == NULL)
    1276           0 :                 return;
    1277             : 
    1278           0 :         mapi_log_header(mid, filename, line, mark, " (DATA)");
    1279             : 
    1280           0 :         const size_t margin = strlen("\\xNN" MAPILOG_CLOSE "\n," MAPILOG_OPEN);
    1281           0 :         char buffer[128] = { 0 };
    1282           0 :         char *pos = buffer;
    1283           0 :         mnstr_writeStr(mid->tracelog, MAPILOG_OPEN);
    1284           0 :         bool inside = true;
    1285           0 :         for (unsigned char *p = (unsigned char*)start; (char*)p < start + len; p++) {
    1286           0 :                 unsigned char c = *p;
    1287           0 :                 if (!inside) {
    1288           0 :                         for (char *text = "\n" MAPILOG_OPEN; *text; text++)
    1289           0 :                                 *pos++ = *text;
    1290             :                         inside = true;
    1291             :                 }
    1292           0 :                 if (pos >= buffer + sizeof(buffer) - margin) {
    1293           0 :                         mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
    1294           0 :                         pos = buffer;
    1295             :                 }
    1296           0 :                 switch (c) {
    1297           0 :                         case '\\':
    1298             :                         case '\'':
    1299           0 :                                 *pos++ = '\\';
    1300           0 :                                 *pos++ = c;
    1301           0 :                                 break;
    1302           0 :                         case '\t':
    1303           0 :                                 *pos++ = '\\';
    1304           0 :                                 *pos++ = 't';
    1305           0 :                                 break;
    1306             :                         case '\n':
    1307           0 :                                 for (char *text = "\\n" MAPILOG_CLOSE; *text; text++)
    1308           0 :                                         *pos++ = *text;
    1309             :                                 inside = false;
    1310             :                                 break;
    1311           0 :                         default:
    1312           0 :                                 if (c >= 32 && c < 127) {
    1313           0 :                                         *pos++ = c;
    1314             :                                 } else {
    1315           0 :                                         *pos++ = '\\';
    1316           0 :                                         *pos++ = 'x';
    1317           0 :                                         *pos++ = hexdigits[c / 16];
    1318           0 :                                         *pos++ = hexdigits[c % 16];
    1319             :                                 }
    1320             :                         break;
    1321             :                 }
    1322             :         }
    1323           0 :         if (inside) {
    1324           0 :                 for (char *text = MAPILOG_CLOSE; *text; text++)
    1325           0 :                         *pos++ = *text;
    1326             :         }
    1327           0 :         *pos++ = ',';
    1328           0 :         *pos++ = '\n';
    1329           0 :         mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
    1330             : 
    1331           0 :         mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
    1332             : }
    1333             : 
    1334             : MapiMsg
    1335           0 : mapi_log(Mapi mid, const char *nme)
    1336             : {
    1337           0 :         mapi_clrError(mid);
    1338           0 :         if (mid->tracelog) {
    1339           0 :                 close_stream(mid->tracelog);
    1340           0 :                 mid->tracelog = NULL;
    1341             :         }
    1342           0 :         if (nme == NULL)
    1343             :                 return MOK;
    1344           0 :         if (nme[0] == '-' && nme[1] == '\0')
    1345           0 :                 mid->tracelog = stderr_wastream();
    1346             :         else
    1347           0 :                 mid->tracelog = open_wastream(nme);
    1348           0 :         if (mid->tracelog == NULL || mnstr_errnr(mid->tracelog) != MNSTR_NO__ERROR) {
    1349           0 :                 if (mid->tracelog)
    1350           0 :                         close_stream(mid->tracelog);
    1351           0 :                 mid->tracelog = NULL;
    1352           0 :                 return mapi_setError(mid, "Could not create log file", __func__, MERROR);
    1353             :         }
    1354             :         return MOK;
    1355             : }
    1356             : 
    1357             : /* send a dummy request to the server to see whether the connection is
    1358             :    still alive */
    1359             : MapiMsg
    1360           0 : mapi_ping(Mapi mid)
    1361             : {
    1362           0 :         mapi_check(mid);
    1363             : 
    1364           0 :         MapiHdl hdl;
    1365           0 :         if (msettings_lang_is_sql(mid->settings))
    1366           0 :                 hdl = mapi_query(mid, "select true;");
    1367           0 :         else if (msettings_lang_is_mal(mid->settings))
    1368           0 :                 hdl = mapi_query(mid, "io.print(1);");
    1369             :         else
    1370             :                 hdl = NULL;
    1371             : 
    1372           0 :         if (hdl)
    1373           0 :                 mapi_close_handle(hdl);
    1374           0 :         return mid->error;
    1375             : }
    1376             : 
    1377             : /* allocate a new structure to represent a result set */
    1378             : static struct MapiResultSet *
    1379       33825 : new_result(MapiHdl hdl)
    1380             : {
    1381       33825 :         struct MapiResultSet *result;
    1382             : 
    1383       33825 :         assert((hdl->lastresult == NULL && hdl->result == NULL) ||
    1384             :                (hdl->result != NULL && hdl->lastresult != NULL && hdl->lastresult->next == NULL));
    1385             : 
    1386       33825 :         if (hdl->mid->trace)
    1387           0 :                 printf("allocating new result set\n");
    1388             :         /* append a newly allocated struct to the end of the linked list */
    1389       33825 :         result = malloc(sizeof(*result));
    1390       33825 :         if (result == NULL)
    1391             :                 return NULL;
    1392       67650 :         *result = (struct MapiResultSet) {
    1393             :                 .hdl = hdl,
    1394             :                 .tableid = -1,
    1395             :                 .querytype = -1,
    1396             :                 .last_id = -1,
    1397       33825 :                 .cache.rowlimit = msetting_long(hdl->mid->settings, MP_REPLYSIZE),
    1398             :                 .cache.reader = -1,
    1399             :                 .commentonly = true,
    1400             :         };
    1401       33825 :         if (hdl->lastresult == NULL)
    1402       33770 :                 hdl->result = hdl->lastresult = result;
    1403             :         else {
    1404          55 :                 hdl->lastresult->next = result;
    1405          55 :                 hdl->lastresult = result;
    1406             :         }
    1407             : 
    1408             :         return result;
    1409             : }
    1410             : 
    1411             : /* close a result set, discarding any unread results */
    1412             : static MapiMsg
    1413       33825 : close_result(MapiHdl hdl)
    1414             : {
    1415       33825 :         struct MapiResultSet *result;
    1416       33825 :         Mapi mid;
    1417       33825 :         int i;
    1418             : 
    1419       33825 :         result = hdl->result;
    1420       33825 :         if (result == NULL)
    1421             :                 return MERROR;
    1422       33825 :         mid = hdl->mid;
    1423       33825 :         assert(mid != NULL);
    1424       33825 :         if (mid->trace)
    1425           0 :                 printf("closing result set\n");
    1426       33825 :         if (result->tableid >= 0 && result->querytype != Q_PREPARE) {
    1427       30369 :                 if (mid->active &&
    1428           2 :                     result->next == NULL &&
    1429           2 :                     !mid->active->needmore &&
    1430           1 :                     read_into_cache(mid->active, -1) != MOK)
    1431             :                         return MERROR;
    1432       30369 :                 assert(hdl->npending_close == 0 ||
    1433             :                        (hdl->npending_close > 0 && hdl->pending_close != NULL));
    1434       30369 :                 if (mid->active &&
    1435           1 :                     (mid->active->active != result ||
    1436           0 :                      result->cache.tuplecount < result->row_count)) {
    1437             :                         /* results for which we got all tuples at the initial
    1438             :                          * response, need not to be closed as the server already
    1439             :                          * did that immediately */
    1440           1 :                         if (result->row_count > result->tuple_count) {
    1441             :                                 /* can't write "X" commands now, so save for later */
    1442           0 :                                 REALLOC(hdl->pending_close, hdl->npending_close + 1);
    1443           0 :                                 hdl->pending_close[hdl->npending_close] = result->tableid;
    1444           0 :                                 hdl->npending_close++;
    1445             :                         }
    1446       30368 :                 } else if (mid->to != NULL) {
    1447             :                         /* first close saved up to-be-closed tables */
    1448       30368 :                         for (i = 0; i < hdl->npending_close; i++) {
    1449           0 :                                 char msg[256];
    1450             : 
    1451           0 :                                 snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
    1452           0 :                                 mapi_log_record(mid, "CMD", "%s", msg);
    1453           0 :                                 mid->active = hdl;
    1454           0 :                                 if (mnstr_printf(mid->to, "%s", msg) < 0 ||
    1455           0 :                                     mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    1456           0 :                                         close_connection(mid);
    1457           0 :                                         mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    1458           0 :                                         break;
    1459             :                                 }
    1460           0 :                                 read_into_cache(hdl, 0);
    1461             :                         }
    1462       30368 :                         hdl->npending_close = 0;
    1463       30368 :                         if (hdl->pending_close)
    1464           0 :                                 free(hdl->pending_close);
    1465       30368 :                         hdl->pending_close = NULL;
    1466       30368 :                         if (mid->to != NULL && result->tuple_count < result->row_count) {
    1467          19 :                                 char msg[256];
    1468             : 
    1469          19 :                                 snprintf(msg, sizeof(msg), "Xclose %d\n", result->tableid);
    1470          19 :                                 mapi_log_record(mid, "CMD", "%s", msg);
    1471          19 :                                 mid->active = hdl;
    1472          38 :                                 if (mnstr_printf(mid->to, "%s", msg) < 0 ||
    1473          19 :                                     mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    1474           0 :                                         close_connection(mid);
    1475           0 :                                         mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    1476             :                                 } else
    1477          19 :                                         read_into_cache(hdl, 0);
    1478             :                         }
    1479             :                 }
    1480       30369 :                 result->tableid = -1;
    1481             :         }
    1482       33825 :         if (mid->active == hdl &&
    1483           3 :             hdl->active == result &&
    1484           0 :             read_into_cache(hdl, -1) != MOK)
    1485             :                 return MERROR;
    1486       33825 :         if( hdl->active == result)
    1487             :                 return MERROR;
    1488             :         //assert(hdl->active != result);
    1489       33825 :         if (result->fields) {
    1490      107731 :                 for (i = 0; i < result->maxfields; i++) {
    1491       77040 :                         if (result->fields[i].tablename)
    1492       76836 :                                 free(result->fields[i].tablename);
    1493       77040 :                         if (result->fields[i].columnname)
    1494       76836 :                                 free(result->fields[i].columnname);
    1495       77040 :                         if (result->fields[i].columntype)
    1496       76844 :                                 free(result->fields[i].columntype);
    1497             :                 }
    1498       30691 :                 free(result->fields);
    1499             :         }
    1500       33825 :         result->fields = NULL;
    1501       33825 :         result->maxfields = result->fieldcnt = 0;
    1502       33825 :         if (result->cache.line) {
    1503      188113 :                 for (i = 0; i < result->cache.writer; i++) {
    1504      157418 :                         if (result->cache.line[i].rows)
    1505      157418 :                                 free(result->cache.line[i].rows);
    1506      157418 :                         if (result->cache.line[i].anchors) {
    1507             :                                 int j;
    1508             : 
    1509      131046 :                                 for (j = 0; j < result->cache.line[i].fldcnt; j++)
    1510      116064 :                                         if (result->cache.line[i].anchors[j]) {
    1511       79522 :                                                 free(result->cache.line[i].anchors[j]);
    1512       79522 :                                                 result->cache.line[i].anchors[j] = NULL;
    1513             :                                         }
    1514       14982 :                                 free(result->cache.line[i].anchors);
    1515             :                         }
    1516      157418 :                         if (result->cache.line[i].lens)
    1517       14982 :                                 free(result->cache.line[i].lens);
    1518             :                 }
    1519       30695 :                 free(result->cache.line);
    1520       30695 :                 result->cache.line = NULL;
    1521       30695 :                 result->cache.tuplecount = 0;
    1522             :         }
    1523       33825 :         if (result->errorstr && result->errorstr != mapi_nomem)
    1524          68 :                 free(result->errorstr);
    1525       33825 :         result->errorstr = NULL;
    1526       33825 :         memset(result->sqlstate, 0, sizeof(result->sqlstate));
    1527       33825 :         result->hdl = NULL;
    1528       33825 :         hdl->result = result->next;
    1529       33825 :         if (hdl->result == NULL)
    1530       33770 :                 hdl->lastresult = NULL;
    1531       33825 :         result->next = NULL;
    1532       33825 :         free(result);
    1533       33825 :         return MOK;
    1534             : }
    1535             : 
    1536             : static void
    1537          81 : add_error(struct MapiResultSet *result, char *error)
    1538             : {
    1539             :         /* concatenate the error messages */
    1540          81 :         size_t size = result->errorstr ? strlen(result->errorstr) : 0;
    1541             : 
    1542          81 :         if (strlen(error) > 6 && error[5] == '!' &&
    1543          63 :             (isdigit((unsigned char) error[0]) ||
    1544           0 :              (error[0] >= 'A' && error[0] <= 'Z')) &&
    1545          63 :             (isdigit((unsigned char) error[1]) ||
    1546           3 :              (error[1] >= 'A' && error[1] <= 'Z')) &&
    1547          63 :             (isdigit((unsigned char) error[2]) ||
    1548           3 :              (error[2] >= 'A' && error[2] <= 'Z')) &&
    1549          63 :             (isdigit((unsigned char) error[3]) ||
    1550           0 :              (error[3] >= 'A' && error[3] <= 'Z')) &&
    1551          63 :             (isdigit((unsigned char) error[4]) ||
    1552           0 :              (error[4] >= 'A' && error[4] <= 'Z'))) {
    1553          63 :                 if (result->errorstr == NULL) {
    1554             :                         /* remember SQLSTATE for first error */
    1555          61 :                         strcpy_len(result->sqlstate, error,
    1556             :                                    sizeof(result->sqlstate));
    1557             :                 }
    1558             :                 /* skip SQLSTATE */
    1559          63 :                 error += 6;
    1560             :         }
    1561          81 :         REALLOC(result->errorstr, size + strlen(error) + 2);
    1562          81 :         if (result->errorstr == NULL)
    1563           0 :                 result->errorstr = mapi_nomem;
    1564             :         else {
    1565          81 :                 strcpy(result->errorstr + size, error);
    1566          81 :                 strcat(result->errorstr + size, "\n");
    1567             :         }
    1568          81 : }
    1569             : 
    1570             : const char *
    1571       11951 : mapi_result_error(MapiHdl hdl)
    1572             : {
    1573       11951 :         return hdl && hdl->result ? hdl->result->errorstr : NULL;
    1574             : }
    1575             : 
    1576             : const char *
    1577           3 : mapi_result_errorcode(MapiHdl hdl)
    1578             : {
    1579           3 :         return hdl && hdl->result && hdl->result->sqlstate[0] ? hdl->result->sqlstate : NULL;
    1580             : }
    1581             : 
    1582             : /* Go to the next result set, if any, and close the current result
    1583             :    set.  This function returns 1 if there are more result sets after
    1584             :    the one that was closed, otherwise, if more input is needed, return
    1585             :    MMORE, else, return MOK */
    1586             : MapiMsg
    1587        5608 : mapi_next_result(MapiHdl hdl)
    1588             : {
    1589        5608 :         mapi_hdl_check(hdl);
    1590             : 
    1591       10493 :         while (hdl->result != NULL) {
    1592        4940 :                 if (close_result(hdl) != MOK)
    1593             :                         return MERROR;
    1594        4940 :                 if (hdl->result &&
    1595          55 :                     (hdl->result->querytype == -1 ||
    1596             :                      /* basically exclude Q_PARSE and Q_BLOCK */
    1597             :                      (hdl->result->querytype >= Q_TABLE &&
    1598           0 :                       hdl->result->querytype <= Q_PREPARE) ||
    1599           0 :                      hdl->result->errorstr != NULL))
    1600             :                         return 1;
    1601             :         }
    1602        5553 :         return hdl->needmore ? MMORE : MOK;
    1603             : }
    1604             : 
    1605             : MapiMsg
    1606          17 : mapi_needmore(MapiHdl hdl)
    1607             : {
    1608          17 :         return hdl->needmore ? MMORE : MOK;
    1609             : }
    1610             : 
    1611             : bool
    1612        2008 : mapi_more_results(MapiHdl hdl)
    1613             : {
    1614        2008 :         struct MapiResultSet *result;
    1615             : 
    1616        2008 :         mapi_hdl_check(hdl);
    1617             : 
    1618        2008 :         if ((result = hdl->result) == 0) {
    1619             :                 /* there are no results at all */
    1620             :                 return false;
    1621             :         }
    1622        2000 :         if (result->querytype == Q_TABLE && hdl->mid->active == hdl) {
    1623             :                 /* read until next result (if any) */
    1624           0 :                 read_into_cache(hdl, -1);
    1625             :         }
    1626        2000 :         if (hdl->needmore) {
    1627             :                 /* assume the application will provide more data and
    1628             :                    that we will then have a result */
    1629             :                 return true;
    1630             :         }
    1631        2000 :         while (result->next) {
    1632           0 :                 result = result->next;
    1633           0 :                 if (result->querytype == -1 ||
    1634             :                     /* basically exclude Q_PARSE and Q_BLOCK */
    1635           0 :                     (hdl->result->querytype >= Q_TABLE &&
    1636           0 :                      hdl->result->querytype <= Q_PREPARE) ||
    1637           0 :                     result->errorstr != NULL)
    1638             :                         return true;
    1639             :         }
    1640             :         /* no more results */
    1641             :         return false;
    1642             : }
    1643             : 
    1644             : MapiHdl
    1645       39039 : mapi_new_handle(Mapi mid)
    1646             : {
    1647       39039 :         MapiHdl hdl;
    1648             : 
    1649       39039 :         mapi_check0(mid);
    1650             : 
    1651       39041 :         hdl = malloc(sizeof(*hdl));
    1652       39041 :         if (hdl == NULL) {
    1653           0 :                 mapi_setError(mid, "Memory allocation failure", __func__, MERROR);
    1654           0 :                 return NULL;
    1655             :         }
    1656             :         /* initialize and add to doubly-linked list */
    1657       39041 :         *hdl = (struct MapiStatement) {
    1658             :                 .mid = mid,
    1659       39041 :                 .next = mid->first,
    1660             :         };
    1661       39041 :         mid->first = hdl;
    1662       39041 :         if (hdl->next)
    1663        4800 :                 hdl->next->prev = hdl;
    1664             :         return hdl;
    1665             : }
    1666             : 
    1667             : /* close all result sets on the handle but don't close the handle itself */
    1668             : static MapiMsg
    1669       74062 : finish_handle(MapiHdl hdl)
    1670             : {
    1671       74062 :         Mapi mid;
    1672       74062 :         int i;
    1673             : 
    1674       74062 :         if (hdl == NULL)
    1675             :                 return MERROR;
    1676       74062 :         mid = hdl->mid;
    1677       74361 :         if (mid->active == hdl && !hdl->needmore && !mnstr_eof(mid->from) &&
    1678         299 :             read_into_cache(hdl, 0) != MOK)
    1679             :                 return MERROR;
    1680       74062 :         if (mid->to) {
    1681       74061 :                 if (hdl->needmore) {
    1682           0 :                         assert(mid->active == NULL || mid->active == hdl);
    1683           0 :                         hdl->needmore = false;
    1684           0 :                         mid->active = hdl;
    1685           0 :                         int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    1686           0 :                         check_stream(mid, mid->to, f, "write error on stream", mid->error);
    1687           0 :                         read_into_cache(hdl, 0);
    1688             :                 }
    1689       74061 :                 for (i = 0; i < hdl->npending_close; i++) {
    1690           0 :                         char msg[256];
    1691             : 
    1692           0 :                         snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
    1693           0 :                         mapi_log_record(mid, "CMD", "%s", msg);
    1694           0 :                         mid->active = hdl;
    1695           0 :                         if (mnstr_printf(mid->to, "%s", msg) < 0 ||
    1696           0 :                             mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    1697           0 :                                 close_connection(mid);
    1698           0 :                                 mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    1699           0 :                                 break;
    1700             :                         }
    1701           0 :                         read_into_cache(hdl, 0);
    1702             :                 }
    1703             :         }
    1704       74063 :         hdl->npending_close = 0;
    1705       74063 :         if (hdl->pending_close)
    1706           0 :                 free(hdl->pending_close);
    1707       74063 :         hdl->pending_close = NULL;
    1708      177011 :         while (hdl->result) {
    1709       28884 :                 if (close_result(hdl) != MOK)
    1710             :                         return MERROR;
    1711       28885 :                 if (hdl->needmore) {
    1712           0 :                         assert(mid->active == NULL || mid->active == hdl);
    1713           0 :                         hdl->needmore = false;
    1714           0 :                         mid->active = hdl;
    1715           0 :                         int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    1716           0 :                         check_stream(mid, mid->to, f, "write error on stream", mid->error);
    1717           0 :                         read_into_cache(hdl, 0);
    1718             :                 }
    1719             :         }
    1720             :         return MOK;
    1721             : }
    1722             : 
    1723             : /* Close a statement handle, discarding any unread output. */
    1724             : MapiMsg
    1725       39040 : mapi_close_handle(MapiHdl hdl)
    1726             : {
    1727       39040 :         if (hdl == NULL)
    1728             :                 return MOK;
    1729       39040 :         debugprint("entering %s\n", "mapi_close_handle");
    1730             : 
    1731             :         /* don't use mapi_check_hdl: it's ok if we're not connected */
    1732       39040 :         mapi_clrError(hdl->mid);
    1733             : 
    1734       39037 :         (void) finish_handle(hdl);
    1735       39038 :         hdl->npending_close = 0;
    1736       39038 :         if (hdl->pending_close)
    1737           0 :                 free(hdl->pending_close);
    1738       39038 :         hdl->pending_close = NULL;
    1739       39038 :         if (hdl->bindings)
    1740           0 :                 free(hdl->bindings);
    1741       39038 :         hdl->bindings = NULL;
    1742       39038 :         hdl->maxbindings = 0;
    1743       39038 :         if (hdl->params)
    1744           0 :                 free(hdl->params);
    1745       39038 :         hdl->params = NULL;
    1746       39038 :         hdl->maxparams = 0;
    1747       39038 :         if (hdl->query)
    1748       37274 :                 free(hdl->query);
    1749       39038 :         hdl->query = NULL;
    1750       39038 :         if (hdl->template)
    1751           0 :                 free(hdl->template);
    1752       39038 :         hdl->template = NULL;
    1753             :         /* remove from doubly-linked list */
    1754       39038 :         if (hdl->prev)
    1755           1 :                 hdl->prev->next = hdl->next;
    1756       39038 :         if (hdl->next)
    1757        4797 :                 hdl->next->prev = hdl->prev;
    1758       39038 :         if (hdl->mid->first == hdl)
    1759       39040 :                 hdl->mid->first = hdl->next;
    1760       39038 :         hdl->prev = NULL;
    1761       39038 :         hdl->next = NULL;
    1762       39038 :         hdl->mid = NULL;
    1763       39038 :         free(hdl);
    1764       39038 :         return MOK;
    1765             : }
    1766             : 
    1767             : const struct MapiStruct MapiStructDefaults = {
    1768             :         .error = MOK,
    1769             :         .redirmax = 10,
    1770             :         .blk.eos = false,
    1771             :         .blk.lim = BLOCK,
    1772             : };
    1773             : 
    1774             : /* Allocate a new connection handle. */
    1775             : Mapi
    1776         441 : mapi_new(msettings *settings)
    1777             : {
    1778         441 :         Mapi mid;
    1779         441 :         static ATOMIC_TYPE index = ATOMIC_VAR_INIT(0);
    1780             : 
    1781         441 :         if (!ATOMIC_TAS(&mapi_initialized)) {
    1782         269 :                 if (mnstr_init() < 0)
    1783             :                         return NULL;
    1784             :         }
    1785             : 
    1786         441 :         mid = malloc(sizeof(*mid));
    1787         441 :         if (mid == NULL)
    1788             :                 return NULL;
    1789             : 
    1790             :         /* then fill in some details */
    1791         441 :         *mid = MapiStructDefaults;
    1792         441 :         mid->index =  (uint32_t) ATOMIC_ADD(&index, 1); /* for distinctions in log records */
    1793         441 :         if ((mid->blk.buf = malloc(mid->blk.lim + 1)) == NULL) {
    1794           0 :                 mapi_destroy(mid);
    1795           0 :                 return NULL;
    1796             :         }
    1797         441 :         if (settings == NULL) {
    1798         401 :                 settings = msettings_create();
    1799         399 :                 if (settings == NULL) {
    1800           0 :                         mapi_destroy(mid);
    1801           0 :                         return NULL;
    1802             :                 }
    1803             :         }
    1804         439 :         mid->settings = settings;
    1805         439 :         mid->blk.buf[0] = 0;
    1806         439 :         mid->blk.buf[mid->blk.lim] = 0;
    1807             : 
    1808             :         /* also the current timezone, seconds EAST of UTC */
    1809         439 :         long time_zone;
    1810             : #if defined(_MSC_VER)
    1811             :         DYNAMIC_TIME_ZONE_INFORMATION tzinf;
    1812             : 
    1813             :         /* documentation says: UTC = localtime + Bias (in minutes),
    1814             :          * but experimentation during DST period says, UTC = localtime
    1815             :          * + Bias + DaylightBias, and presumably during non DST
    1816             :          * period, UTC = localtime + Bias */
    1817             :         switch (GetDynamicTimeZoneInformation(&tzinf)) {
    1818             :         case TIME_ZONE_ID_STANDARD:     /* using standard time */
    1819             :         case TIME_ZONE_ID_UNKNOWN:      /* no daylight saving time in this zone */
    1820             :                 time_zone = -(int) tzinf.Bias * 60;
    1821             :                 break;
    1822             :         case TIME_ZONE_ID_DAYLIGHT:     /* using daylight saving time */
    1823             :                 time_zone = -(int) (tzinf.Bias + tzinf.DaylightBias) * 60;
    1824             :                 break;
    1825             :         default:                                        /* aka TIME_ZONE_ID_INVALID */
    1826             :                 /* call failed, we don't know the time zone */
    1827             :                 time_zone = 0;
    1828             :                 break;
    1829             :         }
    1830             : #else
    1831         439 :         time_t t = time(NULL);
    1832         440 :         struct tm *local_tm = localtime_r(&t, &(struct tm){0});
    1833             : #ifdef HAVE_TM_GMTOFF
    1834         441 :         time_zone = local_tm->tm_gmtoff;
    1835             : #else
    1836             :         struct tm *gm_tm = gmtime_r(&t, &(struct tm){0});
    1837             :         time_t gt = mktime(gm_tm);
    1838             :         local_tm->tm_isdst=0; /* We need the difference without dst */
    1839             :         time_t lt = mktime(local_tm);
    1840             :         assert((int64_t) gt - (int64_t) lt >= (int64_t) INT_MIN && (int64_t) gt - (int64_t) lt <= (int64_t) INT_MAX);
    1841             :         time_zone = (long) (lt - gt);
    1842             : #endif
    1843             : #endif
    1844         441 :         msettings_error err =  msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
    1845         440 :         if (err)
    1846           0 :                 mapi_setError(mid, err, __func__, MERROR);
    1847             : 
    1848             :         return mid;
    1849             : }
    1850             : 
    1851             : /* construct the uri field of a Mapi struct */
    1852             : void
    1853         249 : set_uri(Mapi mid)
    1854             : {
    1855         249 :         const char *host = msetting_string(mid->settings, MP_HOST);
    1856         248 :         const char *database = msetting_string(mid->settings, MP_DATABASE);
    1857         250 :         int port = msetting_long(mid->settings, MP_PORT);
    1858         248 :         size_t urilen = strlen(host) + (database ? strlen(database) : 0) + 32;
    1859         248 :         char *uri = malloc(urilen);
    1860             : 
    1861             :         /* uri looks as follows:
    1862             :          *  mapi:monetdb://host:port/database
    1863             :          * or
    1864             :          *  mapi:monetdb:///some/path/to?database=database
    1865             :          */
    1866             : 
    1867         248 :         if (database != NULL) {
    1868         248 :                 if (host[0] == '/') {
    1869           0 :                         snprintf(uri, urilen, "mapi:monetdb://%s?database=%s",
    1870             :                                  host, database);
    1871             :                 } else {
    1872         248 :                         snprintf(uri, urilen, "mapi:monetdb://%s:%d/%s",
    1873             :                                  host, port, database);
    1874             :                 }
    1875             :         } else {
    1876           0 :                 if (host[0] == '/') {
    1877           0 :                         snprintf(uri, urilen, "mapi:monetdb://%s",
    1878             :                                  host);
    1879             :                 } else {
    1880           0 :                         snprintf(uri, urilen, "mapi:monetdb://%s:%d",
    1881             :                                  host, port);
    1882             :                 }
    1883             :         }
    1884             : 
    1885         248 :         if (mid->uri != NULL)
    1886           0 :                 free(mid->uri);
    1887         248 :         mid->uri = uri;
    1888         248 : }
    1889             : 
    1890             : Mapi
    1891         206 : mapi_mapiuri(const char *url, const char *user, const char *pass, const char *lang)
    1892             : {
    1893         206 :         Mapi mid;
    1894             : 
    1895         206 :         mid = mapi_new(NULL);
    1896         210 :         if (mid == NULL)
    1897             :                 return NULL;
    1898             : 
    1899         210 :         if (url == NULL) {
    1900           0 :                 mapi_setError(mid, "url is null", __func__, MERROR);
    1901           0 :                 return mid;
    1902             :         }
    1903         210 :         if (user == NULL) {
    1904           0 :                 mapi_setError(mid, "user is null", __func__, MERROR);
    1905           0 :                 return mid;
    1906             :         }
    1907         210 :         if (pass == NULL) {
    1908           0 :                 mapi_setError(mid, "pass is null", __func__, MERROR);
    1909           0 :                 return mid;
    1910             :         }
    1911         210 :         if (lang == NULL) {
    1912           0 :                 mapi_setError(mid, "lang is null", __func__, MERROR);
    1913           0 :                 return mid;
    1914             :         }
    1915             : 
    1916         210 :         msettings_error err = NULL;
    1917         210 :         if (    (err = msetting_set_string(mid->settings, MP_USER, user))
    1918         211 :                 || (err = msetting_set_string(mid->settings, MP_PASSWORD, pass))
    1919         210 :                 || (err = msetting_set_string(mid->settings, MP_LANGUAGE, lang))
    1920             :         ) {
    1921             :                 // in the old implementation we returned NULL but that doesn't
    1922             :                 // make sense to me because above we already started returning
    1923             :                 // mid with the error set.
    1924           0 :                 mapi_setError(mid, err, __func__, MERROR);
    1925           0 :                 return mid;
    1926             :         }
    1927             : 
    1928         210 :         char *error_message = NULL;
    1929         210 :         if (!msettings_parse_url(mid->settings, url, &error_message)) {
    1930           0 :                 char *msg = error_message ? error_message : "malloc failed";
    1931           0 :                 mapi_setError(mid, msg, __func__, MERROR);
    1932           0 :                 free(error_message);
    1933           0 :                 return mid;
    1934             :         }
    1935             : 
    1936         210 :         set_uri(mid);
    1937             : 
    1938         210 :         return mid;
    1939             : }
    1940             : 
    1941             : /* Allocate a new connection handle and fill in the information needed
    1942             :    to connect to a server, but don't connect yet. */
    1943             : Mapi
    1944         190 : mapi_mapi(const char *host, int port, const char *username,
    1945             :           const char *password, const char *lang, const char *dbname)
    1946             : {
    1947         190 :         Mapi mid;
    1948             : 
    1949         190 :         mid = mapi_new(NULL);
    1950         190 :         if (mid == NULL)
    1951             :                 return NULL;
    1952         190 :         msettings *settings = mid->settings;
    1953             : 
    1954         190 :         if (lang == NULL)
    1955           0 :                 lang = "sql";
    1956             : 
    1957         190 :         const char *sockdir = NULL;
    1958         190 :         if (host && host[0] == '/') {
    1959             :                 sockdir = host;
    1960             :                 host = NULL;
    1961             :         }
    1962             : 
    1963         120 :         msettings_error err = NULL;
    1964         310 :         do {
    1965         120 :                 if (host && (err = msetting_set_string(settings, MP_HOST, host)))
    1966             :                         break;
    1967         190 :                 if (sockdir && (err = msetting_set_string(settings, MP_SOCKDIR, sockdir)))
    1968             :                         break;
    1969         190 :                 if (username && (err = msetting_set_string(settings, MP_USER, username)))
    1970             :                         break;
    1971         190 :                 if (password && (err = msetting_set_string(settings, MP_PASSWORD, password)))
    1972             :                         break;
    1973         190 :                 if (lang && (err = msetting_set_string(settings, MP_LANGUAGE, lang)))
    1974             :                         break;
    1975         190 :                 if (dbname && (err = msetting_set_string(settings, MP_DATABASE, dbname)))
    1976             :                         break;
    1977         190 :                 if (port > 0 && (err = msetting_set_long(settings, MP_PORT, port)))
    1978             :                         break;
    1979             :         } while (0);
    1980         190 :         if (err) {
    1981           0 :                 mapi_setError(mid, err, __func__, MERROR);
    1982           0 :                 return mid;
    1983             :         }
    1984             : 
    1985             :         return mid;
    1986             : }
    1987             : 
    1988             : Mapi
    1989          40 : mapi_settings(msettings *settings)
    1990             : {
    1991          40 :         assert(settings);
    1992          40 :         Mapi mid = mapi_new(settings);
    1993          40 :         if (mid == NULL)
    1994             :                 return mid;
    1995             : 
    1996          40 :         set_uri(mid);
    1997          40 :         return mid;
    1998             : }
    1999             : 
    2000             : 
    2001             : /* Close a connection and free all memory associated with the
    2002             :    connection handle. */
    2003             : MapiMsg
    2004         420 : mapi_destroy(Mapi mid)
    2005             : {
    2006         420 :         char **r;
    2007             : 
    2008         420 :         mapi_clrError(mid);
    2009             : 
    2010         421 :         while (mid->first)
    2011           1 :                 mapi_close_handle(mid->first);
    2012         420 :         if (mid->connected)
    2013         182 :                 (void) mapi_disconnect(mid);
    2014         420 :         if (mid->tracelog)
    2015           0 :                 close_stream(mid->tracelog);
    2016             : 
    2017         420 :         free(mid->blk.buf);
    2018         420 :         free(mid->motd);
    2019         420 :         free(mid->server);
    2020         420 :         free(mid->uri);
    2021         420 :         free(mid->tracebuffer);
    2022         420 :         free(mid->noexplain);
    2023         420 :         if (mid->errorstr && mid->errorstr != mapi_nomem)
    2024           0 :                 free(mid->errorstr);
    2025         420 :         free(mid->clientprefix);
    2026             : 
    2027         420 :         msettings_destroy(mid->settings);
    2028             : 
    2029         420 :         r = mid->redirects;
    2030         420 :         while (*r) {
    2031           0 :                 free(*r);
    2032           0 :                 r++;
    2033             :         }
    2034             : 
    2035         420 :         free(mid);
    2036         420 :         return MOK;
    2037             : }
    2038             : 
    2039             : /* Create a connection handle and connect to the server using the
    2040             :    specified parameters. */
    2041             : Mapi
    2042          11 : mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
    2043             : {
    2044          11 :         Mapi mid;
    2045             : 
    2046          11 :         mid = mapi_mapi(host, port, username, password, lang, dbname);
    2047          11 :         if (mid && mid->error == MOK)
    2048          11 :                 mapi_reconnect(mid);    /* actually, initial connect */
    2049          11 :         return mid;
    2050             : }
    2051             : 
    2052             : /* Returns an malloced NULL-terminated array with redirects */
    2053             : char **
    2054           0 : mapi_resolve(const char *host, int port, const char *pattern)
    2055             : {
    2056           0 :         int rmax;
    2057           0 :         Mapi mid;
    2058             : 
    2059             :         /* if it doesn't make sense, don't try to crash */
    2060           0 :         if (pattern == NULL)
    2061             :                 return NULL;
    2062             : 
    2063           0 :         mid = mapi_mapi(host, port, "mero", "mero", "resolve", pattern);
    2064           0 :         if (mid) {
    2065           0 :                 if (mid->error == MOK) {
    2066           0 :                         rmax = mid->redirmax;
    2067           0 :                         mid->redirmax = 0;
    2068           0 :                         mapi_reconnect(mid);    /* real connect, don't follow redirects */
    2069           0 :                         mid->redirmax = rmax;
    2070           0 :                         if (mid->error == MOK) {
    2071           0 :                                 close_connection(mid);  /* we didn't expect a connection actually */
    2072             :                         } else {
    2073           0 :                                 char **ret = malloc(sizeof(char *) * MAXREDIR);
    2074           0 :                                 memcpy(ret, mid->redirects, sizeof(char *) * MAXREDIR);
    2075           0 :                                 mid->redirects[0] = NULL;    /* make sure the members aren't freed */
    2076           0 :                                 mapi_destroy(mid);
    2077           0 :                                 return ret;
    2078             :                         }
    2079             :                 }
    2080           0 :                 mapi_destroy(mid);
    2081             :         }
    2082             :         return NULL;
    2083             : }
    2084             : 
    2085             : void
    2086        1419 : close_connection(Mapi mid)
    2087             : {
    2088        1419 :         MapiHdl hdl;
    2089        1419 :         struct MapiResultSet *result;
    2090             : 
    2091        1419 :         mid->connected = false;
    2092        1419 :         mid->active = NULL;
    2093        1420 :         for (hdl = mid->first; hdl; hdl = hdl->next) {
    2094           1 :                 hdl->active = NULL;
    2095           1 :                 for (result = hdl->result; result; result = result->next)
    2096           0 :                         result->tableid = -1;
    2097             :         }
    2098             :         /* finish channels */
    2099             :         /* Make sure that the write- (to-) stream is closed first,
    2100             :          * as the related read- (from-) stream closes the shared
    2101             :          * socket; see also src/common/stream.c:socket_close .
    2102             :          */
    2103        1419 :         if (mid->to) {
    2104        1419 :                 close_stream(mid->to);
    2105        1419 :                 mid->to = 0;
    2106             :         }
    2107        1419 :         if (mid->from) {
    2108        1419 :                 close_stream(mid->from);
    2109        1419 :                 mid->from = 0;
    2110             :         }
    2111        1419 :         mid->redircnt = 0;
    2112        1419 :         mapi_log_record(mid, "C", "Connection closed");
    2113        1419 : }
    2114             : 
    2115             : MapiMsg
    2116        1406 : mapi_disconnect(Mapi mid)
    2117             : {
    2118        1406 :         mapi_check(mid);
    2119             : 
    2120        1406 :         close_connection(mid);
    2121        1406 :         return MOK;
    2122             : }
    2123             : 
    2124             : /* Set callback function to retrieve or send file content for COPY
    2125             :  * INTO queries.
    2126             :  *
    2127             :  * char *getfile(void *private, const char *filename, bool binary,
    2128             :  *               uint64_6 offset, size_t *size);
    2129             :  * Retrieve data from a file.
    2130             :  *
    2131             :  * The arguments are:
    2132             :  * private - the value of the filecontentprivate argument to
    2133             :  *           mapi_setfilecallback;
    2134             :  * filename - the file to read (the application is free to interpret
    2135             :  *            this any way it wants, including getting data over the
    2136             :  *            Internet);
    2137             :  * binary - if set, the file is expected to contain binary data and
    2138             :  *          should therefore be opened in binary mode, otherwise the
    2139             :  *          file is expected to contain data in the UTF-8 encoding (of
    2140             :  *          course, the application is free to transparently convert
    2141             :  *          from the actual encoding to UTF-8);
    2142             :  * offset - the line number of the first line to be retrieved (this is
    2143             :  *          one-based, i.e. the start of the file has line number one;
    2144             :  *          lines are terminated by '\n');
    2145             :  * size - pointer in which to return the size of the chunk that is
    2146             :  *        being returned.
    2147             :  *
    2148             :  * The callback function is expected to return data in chunks until it
    2149             :  * indicates to the caller that there is no more data or an error has
    2150             :  * occurred.  Chunks can be any size.  The caller does not modify or
    2151             :  * free the data returned.  The size of the chunk being returned is
    2152             :  * stored in the size argument.  Errors are indicated by returning a
    2153             :  * string containing an error message and setting *size to zero.  The
    2154             :  * error message should not contain any newlines.  Any call to the
    2155             :  * callback function is allowed to return an error.
    2156             :  *
    2157             :  * The first call to the callback function contains values for
    2158             :  * filename, binary, and offset.  These parameters are all 0 for all
    2159             :  * subsequent calls for continuation data from the same file.
    2160             :  *
    2161             :  * If the caller has retrieved enough data before the file is
    2162             :  * exhausted, it calls the callback function one more time with a NULL
    2163             :  * pointer for the size argument.  This gives the callback function
    2164             :  * the opportunity to free its resources (e.g. close the file).
    2165             :  *
    2166             :  * If there is no more data to be returned, the callback function
    2167             :  * returns a NULL pointer and sets *size to zero.  No more calls for
    2168             :  * the current file will be made.
    2169             :  *
    2170             :  * Note that if the file to be read is empty, or contains fewer lines
    2171             :  * than the requested offset, the first call to the callback function
    2172             :  * may return NULL.
    2173             :  *
    2174             :  * char *putfile(void *private, const char *filename, bool binary,
    2175             :  *               const void *data, size_t size);
    2176             :  * Send data to a file.
    2177             :  *
    2178             :  * The arguments are:
    2179             :  * private - the value of the filecontentprivate argument to
    2180             :  *           mapi_setfilecallback;
    2181             :  * filename - the file to be written;
    2182             :  * binary - if set, the data to be written is binary and the file
    2183             :  *          should therefore be opened in binary mode, otherwise the
    2184             :  *          data is UTF-8 encoded text;
    2185             :  * data - the data to be written;
    2186             :  * size - the size of the data to be written.
    2187             :  *
    2188             :  * The callback is called multiple time to write a single file.  The
    2189             :  * first time, a filename is specified, all subsequent times, the
    2190             :  * filename argument is NULL.  When all data has been written, the
    2191             :  * callback function is called one last time with NULL pointer for the
    2192             :  * data argument so that the callback function can free any resources.
    2193             :  *
    2194             :  * When an error occurs, the callback function returns a string
    2195             :  * containing an error message after which the callback will not be
    2196             :  * called again for the same file.  Otherwise, the callback function
    2197             :  * returns NULL.
    2198             :  *
    2199             :  * Note also that multibyte sequences may be split over two calls.
    2200             :  */
    2201             : void
    2202         154 : mapi_setfilecallback2(Mapi mid,
    2203             :                      char *(*getfilecontent)(void *,
    2204             :                                              const char *, bool,
    2205             :                                              uint64_t, size_t *),
    2206             :                      char *(*putfilecontent)(void *,
    2207             :                                              const char *, bool,
    2208             :                                              const void *, size_t),
    2209             :                      void *filecontentprivate)
    2210             : {
    2211         154 :         mid->getfilecontent = getfilecontent;
    2212         154 :         mid->putfilecontent = putfilecontent;
    2213         154 :         mid->filecontentprivate = filecontentprivate;
    2214         154 :         mid->putfilecontent_old = NULL;
    2215         154 :         mid->filecontentprivate_old = NULL;
    2216         154 : }
    2217             : 
    2218             : static char *
    2219           0 : putfilecontent_wrap(void *priv, const char *filename, bool binary, const void *data, size_t size)
    2220             : {
    2221           0 :         Mapi mid = priv;
    2222           0 :         void *priv_old = mid->filecontentprivate_old;
    2223           0 :         if (filename && binary)
    2224             :                 return "Client does not support writing binary files";
    2225           0 :         return mid->putfilecontent_old(priv_old, filename, data, size);
    2226             : }
    2227             : 
    2228             : /* DEPRECATED. Set callback function to retrieve or send file content for COPY
    2229             :  * INTO queries.
    2230             :  *
    2231             :  * Deprecated because it does not support binary downloads.
    2232             :  * Use mapi_setfilecallback2 instead.
    2233             :  */
    2234             : void
    2235           0 : mapi_setfilecallback(Mapi mid,
    2236             :                      char *(*getfilecontent)(void *,
    2237             :                                              const char *, bool,
    2238             :                                              uint64_t, size_t *),
    2239             :                      char *(*putfilecontent)(void *,
    2240             :                                              const char *,
    2241             :                                              const void *, size_t),
    2242             :                      void *filecontentprivate)
    2243             : {
    2244           0 :         mid->getfilecontent = getfilecontent;
    2245           0 :         mid->putfilecontent = putfilecontent_wrap;
    2246           0 :         mid->filecontentprivate = mid;
    2247           0 :         mid->putfilecontent_old = putfilecontent;
    2248           0 :         mid->filecontentprivate_old = filecontentprivate;
    2249           0 : }
    2250             : 
    2251             : void
    2252          40 : mapi_setclientprefix(Mapi mid, const char *prefix)
    2253             : {
    2254          40 :         free(mid->clientprefix);
    2255          40 :         if (prefix == NULL)
    2256           0 :                 mid->clientprefix = NULL;
    2257             :         else
    2258          40 :                 mid->clientprefix = strdup(prefix);
    2259             : 
    2260          40 : }
    2261             : 
    2262             : #define testBinding(hdl,fnr)                                            \
    2263             :         do {                                                            \
    2264             :                 mapi_hdl_check(hdl);                                    \
    2265             :                 if (fnr < 0) {                                               \
    2266             :                         return mapi_setError(hdl->mid,                       \
    2267             :                                              "Illegal field number",  \
    2268             :                                              __func__, MERROR);         \
    2269             :                 }                                                       \
    2270             :                 /* make sure there is enough space */                   \
    2271             :                 if (fnr >= hdl->maxbindings)                              \
    2272             :                         mapi_extend_bindings(hdl, fnr);                 \
    2273             :         } while (0)
    2274             : 
    2275             : #define testParam(hdl, fnr)                                             \
    2276             :         do {                                                            \
    2277             :                 mapi_hdl_check(hdl);                                    \
    2278             :                 if (fnr < 0) {                                               \
    2279             :                         return mapi_setError(hdl->mid,                       \
    2280             :                                              "Illegal param number",  \
    2281             :                                              __func__, MERROR);         \
    2282             :                 }                                                       \
    2283             :                 if (fnr >= hdl->maxparams)                                \
    2284             :                         mapi_extend_params(hdl, fnr);                   \
    2285             :         } while (0)
    2286             : 
    2287             : MapiMsg
    2288           0 : mapi_bind(MapiHdl hdl, int fnr, char **ptr)
    2289             : {
    2290           0 :         testBinding(hdl, fnr);
    2291           0 :         hdl->bindings[fnr].outparam = ptr;
    2292             : 
    2293           0 :         hdl->bindings[fnr].outtype = MAPI_AUTO;
    2294           0 :         return MOK;
    2295             : }
    2296             : 
    2297             : MapiMsg
    2298           0 : mapi_bind_var(MapiHdl hdl, int fnr, int type, void *ptr)
    2299             : {
    2300           0 :         testBinding(hdl, fnr);
    2301           0 :         hdl->bindings[fnr].outparam = ptr;
    2302             : 
    2303           0 :         if (type >= 0 && type < MAPI_NUMERIC)
    2304           0 :                 hdl->bindings[fnr].outtype = type;
    2305             :         else
    2306           0 :                 return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
    2307           0 :         return MOK;
    2308             : }
    2309             : 
    2310             : MapiMsg
    2311           0 : mapi_bind_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
    2312             : {
    2313           0 :         if (mapi_bind_var(hdl, fnr, MAPI_NUMERIC, ptr))
    2314           0 :                 return hdl->mid->error;
    2315             : 
    2316           0 :         hdl->bindings[fnr].scale = scale;
    2317           0 :         hdl->bindings[fnr].precision = prec;
    2318           0 :         return MOK;
    2319             : }
    2320             : 
    2321             : MapiMsg
    2322           0 : mapi_clear_bindings(MapiHdl hdl)
    2323             : {
    2324           0 :         mapi_hdl_check(hdl);
    2325           0 :         if (hdl->bindings)
    2326           0 :                 memset(hdl->bindings, 0, hdl->maxbindings * sizeof(*hdl->bindings));
    2327             :         return MOK;
    2328             : }
    2329             : 
    2330             : MapiMsg
    2331           0 : mapi_param_type(MapiHdl hdl, int fnr, int ctype, int sqltype, void *ptr)
    2332             : {
    2333           0 :         testParam(hdl, fnr);
    2334           0 :         hdl->params[fnr].inparam = ptr;
    2335             : 
    2336           0 :         if (ctype >= 0 && ctype < MAPI_NUMERIC)
    2337           0 :                 hdl->params[fnr].intype = ctype;
    2338             :         else
    2339           0 :                 return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
    2340           0 :         hdl->params[fnr].sizeptr = NULL;
    2341           0 :         hdl->params[fnr].outtype = sqltype;
    2342           0 :         hdl->params[fnr].scale = 0;
    2343           0 :         hdl->params[fnr].precision = 0;
    2344           0 :         return MOK;
    2345             : }
    2346             : 
    2347             : MapiMsg
    2348           0 : mapi_param_string(MapiHdl hdl, int fnr, int sqltype, char *ptr, int *sizeptr)
    2349             : {
    2350           0 :         testParam(hdl, fnr);
    2351           0 :         hdl->params[fnr].inparam = (void *) ptr;
    2352             : 
    2353           0 :         hdl->params[fnr].intype = MAPI_VARCHAR;
    2354           0 :         hdl->params[fnr].sizeptr = sizeptr;
    2355           0 :         hdl->params[fnr].outtype = sqltype;
    2356           0 :         hdl->params[fnr].scale = 0;
    2357           0 :         hdl->params[fnr].precision = 0;
    2358           0 :         return MOK;
    2359             : }
    2360             : 
    2361             : MapiMsg
    2362           0 : mapi_param(MapiHdl hdl, int fnr, char **ptr)
    2363             : {
    2364           0 :         return mapi_param_type(hdl, fnr, MAPI_AUTO, MAPI_AUTO, ptr);
    2365             : }
    2366             : 
    2367             : MapiMsg
    2368           0 : mapi_param_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
    2369             : {
    2370           0 :         if (mapi_param_type(hdl, fnr, MAPI_NUMERIC, MAPI_NUMERIC, ptr))
    2371           0 :                 return hdl->mid->error;
    2372             : 
    2373           0 :         hdl->params[fnr].scale = scale;
    2374           0 :         hdl->params[fnr].precision = prec;
    2375           0 :         return MOK;
    2376             : }
    2377             : 
    2378             : MapiMsg
    2379           2 : mapi_clear_params(MapiHdl hdl)
    2380             : {
    2381           2 :         mapi_hdl_check(hdl);
    2382           2 :         if (hdl->params)
    2383           0 :                 memset(hdl->params, 0, hdl->maxparams * sizeof(*hdl->params));
    2384             :         return MOK;
    2385             : }
    2386             : 
    2387             : static MapiHdl
    2388       34188 : prepareQuery(MapiHdl hdl, const char *cmd)
    2389             : {
    2390       34188 :         if (hdl && cmd) {
    2391       34196 :                 if (hdl->query)
    2392        2098 :                         free(hdl->query);
    2393       34196 :                 hdl->query = strdup(cmd);
    2394       34196 :                 assert(hdl->query);
    2395       34196 :                 if (hdl->template) {
    2396           0 :                         free(hdl->template);
    2397           0 :                         hdl->template = NULL;
    2398             :                 }
    2399             :         }
    2400       34188 :         return hdl;
    2401             : }
    2402             : 
    2403             : 
    2404             : MapiMsg
    2405           0 : mapi_set_rtimeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
    2406             : {
    2407           0 :         mapi_check(mid);
    2408           0 :         if (mid->trace)
    2409           0 :                 printf("Set timeout to %u\n", timeout);
    2410           0 :         mnstr_settimeout(mid->from, timeout, callback, callback_data);
    2411           0 :         return MOK;
    2412             : }
    2413             : 
    2414             : MapiMsg
    2415           0 : mapi_set_timeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
    2416             : {
    2417           0 :         mapi_check(mid);
    2418           0 :         if (mid->trace)
    2419           0 :                 printf("Set timeout to %u\n", timeout);
    2420           0 :         mnstr_settimeout(mid->to, timeout, callback, callback_data);
    2421           0 :         mnstr_settimeout(mid->from, timeout, callback, callback_data);
    2422           0 :         return MOK;
    2423             : }
    2424             : 
    2425             : MapiMsg
    2426           0 : mapi_timeout(Mapi mid, unsigned int timeout)
    2427             : {
    2428           0 :         return mapi_set_timeout(mid, timeout, NULL, NULL);
    2429             : }
    2430             : 
    2431             : MapiMsg
    2432        1229 : mapi_Xcommand(Mapi mid, const char *cmdname, const char *cmdvalue)
    2433             : {
    2434        1229 :         MapiHdl hdl;
    2435             : 
    2436        1229 :         mapi_check(mid);
    2437        1229 :         if (mid->active && read_into_cache(mid->active, 0) != MOK)
    2438             :                 return MERROR;
    2439        2458 :         if (mnstr_printf(mid->to, "X" "%s %s\n", cmdname, cmdvalue) < 0 ||
    2440        1229 :             mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    2441           0 :                 close_connection(mid);
    2442           0 :                 mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    2443           0 :                 return MERROR;
    2444             :         }
    2445        1229 :         mapi_log_record(mid, "X", "X" "%s %s\n", cmdname, cmdvalue);
    2446        1229 :         hdl = prepareQuery(mapi_new_handle(mid), "Xcommand");
    2447        1229 :         if (hdl == NULL)
    2448             :                 return MERROR;
    2449        1229 :         mid->active = hdl;
    2450        1229 :         read_into_cache(hdl, 0);
    2451        1229 :         mapi_close_handle(hdl); /* reads away any output */
    2452        1229 :         return MOK;
    2453             : }
    2454             : 
    2455             : MapiMsg
    2456           0 : mapi_prepare_handle(MapiHdl hdl, const char *cmd)
    2457             : {
    2458           0 :         mapi_hdl_check(hdl);
    2459           0 :         if (finish_handle(hdl) != MOK)
    2460             :                 return MERROR;
    2461           0 :         prepareQuery(hdl, cmd);
    2462           0 :         hdl->template = strdup(hdl->query);
    2463           0 :         assert(hdl->template);
    2464           0 :         return hdl->mid->error;
    2465             : }
    2466             : 
    2467             : MapiHdl
    2468           0 : mapi_prepare(Mapi mid, const char *cmd)
    2469             : {
    2470           0 :         MapiHdl hdl;
    2471             : 
    2472           0 :         mapi_check0(mid);
    2473           0 :         hdl = mapi_new_handle(mid);
    2474           0 :         if (hdl == NULL)
    2475             :                 return NULL;
    2476           0 :         mapi_prepare_handle(hdl, cmd);
    2477           0 :         return hdl;
    2478             : }
    2479             : 
    2480             : /*
    2481             :  * Building the query string using replacement of values requires
    2482             :  * some care to not overflow the space allocated.
    2483             :  */
    2484             : #define checkSpace(len)                                         \
    2485             :         do {                                                    \
    2486             :                 /* note: k==strlen(hdl->query) */            \
    2487             :                 if (k+len >= lim) {                          \
    2488             :                         lim = k + len + MAPIBLKSIZE;            \
    2489             :                         char *q = realloc(hdl->query, lim);  \
    2490             :                         if (q == NULL) {                        \
    2491             :                                 free(hdl->query);            \
    2492             :                                 hdl->query = NULL;           \
    2493             :                                 return;                         \
    2494             :                         }                                       \
    2495             :                         hdl->query = q;                              \
    2496             :                 }                                               \
    2497             :         } while (0)
    2498             : 
    2499             : static void
    2500       32910 : mapi_param_store(MapiHdl hdl)
    2501             : {
    2502       32910 :         char *val, buf[MAPIBLKSIZE];
    2503       32910 :         char *p = hdl->template, *q;
    2504       32910 :         int i;
    2505       32910 :         size_t k;
    2506       32910 :         size_t lim;
    2507             : 
    2508       32910 :         if (hdl->template == 0)
    2509             :                 return;
    2510             : 
    2511           0 :         lim = strlen(hdl->template) + MAPIBLKSIZE;
    2512           0 :         REALLOC(hdl->query, lim);
    2513           0 :         if (hdl->query == NULL)
    2514             :                 return;
    2515           0 :         hdl->query[0] = 0;
    2516           0 :         k = 0;
    2517             : 
    2518           0 :         q = strchr(hdl->template, PLACEHOLDER);
    2519           0 :         i = 0;
    2520             :         /* loop invariant: k == strlen(hdl->query) */
    2521           0 :         while (q && i < hdl->maxparams) {
    2522           0 :                 if (q > p && *(q - 1) == '\\') {
    2523           0 :                         q = strchr(q + 1, PLACEHOLDER);
    2524           0 :                         continue;
    2525             :                 }
    2526             : 
    2527           0 :                 if (k + (q - p) >= lim) {
    2528           0 :                         lim += MAPIBLKSIZE;
    2529           0 :                         REALLOC(hdl->query, lim);
    2530           0 :                         if (hdl->query == NULL)
    2531             :                                 return;
    2532             :                 }
    2533           0 :                 memcpy(hdl->query + k, p, q - p);
    2534           0 :                 k += q - p;
    2535           0 :                 hdl->query[k] = 0;
    2536             : 
    2537           0 :                 if (hdl->params[i].inparam == 0) {
    2538           0 :                         char *nullstr = "NULL";
    2539           0 :                         checkSpace(5);
    2540           0 :                         if (msettings_lang_is_mal(hdl->mid->settings))
    2541           0 :                                 nullstr = "nil";
    2542           0 :                         strcpy(hdl->query + k, nullstr);
    2543             :                 } else {
    2544           0 :                         void *src = hdl->params[i].inparam;  /* abbrev */
    2545             : 
    2546           0 :                         switch (hdl->params[i].intype) {
    2547           0 :                         case MAPI_TINY:
    2548           0 :                                 checkSpace(5);
    2549           0 :                                 snprintf(hdl->query + k, lim - k, "%hhd", *(signed char *) src);
    2550           0 :                                 break;
    2551           0 :                         case MAPI_UTINY:
    2552           0 :                                 checkSpace(5);
    2553           0 :                                 snprintf(hdl->query + k, lim - k, "%hhu", *(unsigned char *) src);
    2554           0 :                                 break;
    2555           0 :                         case MAPI_SHORT:
    2556           0 :                                 checkSpace(10);
    2557           0 :                                 snprintf(hdl->query + k, lim - k, "%hd", *(short *) src);
    2558           0 :                                 break;
    2559           0 :                         case MAPI_USHORT:
    2560           0 :                                 checkSpace(10);
    2561           0 :                                 snprintf(hdl->query + k, lim - k, "%hu", *(unsigned short *) src);
    2562           0 :                                 break;
    2563           0 :                         case MAPI_INT:
    2564           0 :                                 checkSpace(20);
    2565           0 :                                 snprintf(hdl->query + k, lim - k, "%d", *(int *) src);
    2566           0 :                                 break;
    2567           0 :                         case MAPI_UINT:
    2568           0 :                                 checkSpace(20);
    2569           0 :                                 snprintf(hdl->query + k, lim - k, "%u", *(unsigned int *) src);
    2570           0 :                                 break;
    2571           0 :                         case MAPI_LONG:
    2572           0 :                                 checkSpace(20);
    2573           0 :                                 snprintf(hdl->query + k, lim - k, "%ld", *(long *) src);
    2574           0 :                                 break;
    2575           0 :                         case MAPI_ULONG:
    2576           0 :                                 checkSpace(20);
    2577           0 :                                 snprintf(hdl->query + k, lim - k, "%lu", *(unsigned long *) src);
    2578           0 :                                 break;
    2579           0 :                         case MAPI_LONGLONG:
    2580           0 :                                 checkSpace(30);
    2581           0 :                                 snprintf(hdl->query + k, lim - k, "%"PRId64, *(int64_t *) src);
    2582           0 :                                 break;
    2583           0 :                         case MAPI_ULONGLONG:
    2584           0 :                                 checkSpace(30);
    2585           0 :                                 snprintf(hdl->query + k, lim - k, "%"PRIu64, *(uint64_t *) src);
    2586           0 :                                 break;
    2587           0 :                         case MAPI_FLOAT:
    2588           0 :                                 checkSpace(30);
    2589           0 :                                 snprintf(hdl->query + k, lim - k, "%.9g", *(float *) src);
    2590           0 :                                 break;
    2591           0 :                         case MAPI_DOUBLE:
    2592           0 :                                 checkSpace(30);
    2593           0 :                                 snprintf(hdl->query + k, lim - k, "%.17g", *(double *) src);
    2594           0 :                                 break;
    2595           0 :                         case MAPI_DATE:
    2596           0 :                                 checkSpace(50);
    2597           0 :                                 snprintf(hdl->query + k, lim - k,
    2598             :                                          "DATE '%04hd-%02hu-%02hu'",
    2599           0 :                                          ((MapiDate *) src)->year,
    2600           0 :                                          ((MapiDate *) src)->month,
    2601           0 :                                          ((MapiDate *) src)->day);
    2602           0 :                                 break;
    2603           0 :                         case MAPI_TIME:
    2604           0 :                                 checkSpace(60);
    2605           0 :                                 snprintf(hdl->query + k, lim - k,
    2606             :                                          "TIME '%02hu:%02hu:%02hu'",
    2607           0 :                                          ((MapiTime *) src)->hour,
    2608           0 :                                          ((MapiTime *) src)->minute,
    2609           0 :                                          ((MapiTime *) src)->second);
    2610           0 :                                 break;
    2611           0 :                         case MAPI_DATETIME:
    2612           0 :                                 checkSpace(110);
    2613           0 :                                 snprintf(hdl->query + k, lim - k,
    2614             :                                          "TIMESTAMP '%04hd-%02hu-%02hu %02hu:%02hu:%02hu.%09u'",
    2615           0 :                                          ((MapiDateTime *) src)->year,
    2616           0 :                                          ((MapiDateTime *) src)->month,
    2617           0 :                                          ((MapiDateTime *) src)->day,
    2618           0 :                                          ((MapiDateTime *) src)->hour,
    2619           0 :                                          ((MapiDateTime *) src)->minute,
    2620           0 :                                          ((MapiDateTime *) src)->second,
    2621             :                                          ((MapiDateTime *) src)->fraction);
    2622           0 :                                 break;
    2623           0 :                         case MAPI_CHAR:
    2624           0 :                                 buf[0] = *(char *) src;
    2625           0 :                                 buf[1] = 0;
    2626           0 :                                 val = mapi_quote(buf, 1);
    2627             :                                 /* note: k==strlen(hdl->query) */
    2628           0 :                                 if (k + strlen(val) + 3 >= lim) {
    2629           0 :                                         lim = k + strlen(val) + 3 + MAPIBLKSIZE;
    2630           0 :                                         char *q = realloc(hdl->query, lim);
    2631           0 :                                         if (q == NULL) {
    2632           0 :                                                 free(hdl->query);
    2633           0 :                                                 hdl->query = NULL;
    2634           0 :                                                 free(val);
    2635           0 :                                                 return;
    2636             :                                         }
    2637           0 :                                         hdl->query = q;
    2638             :                                 }
    2639           0 :                                 snprintf(hdl->query + k, lim - k, "'%s'", val);
    2640           0 :                                 free(val);
    2641           0 :                                 break;
    2642           0 :                         case MAPI_VARCHAR:
    2643           0 :                                 val = mapi_quote((char *) src, hdl->params[i].sizeptr ? *hdl->params[i].sizeptr : -1);
    2644             :                                 /* note: k==strlen(hdl->query) */
    2645           0 :                                 if (k + strlen(val) + 3 >= lim) {
    2646           0 :                                         lim = k + strlen(val) + 3 + MAPIBLKSIZE;
    2647           0 :                                         char *q = realloc(hdl->query, lim);
    2648           0 :                                         if (q == NULL) {
    2649           0 :                                                 free(hdl->query);
    2650           0 :                                                 hdl->query = NULL;
    2651           0 :                                                 free(val);
    2652           0 :                                                 return;
    2653             :                                         }
    2654           0 :                                         hdl->query = q;
    2655             :                                 }
    2656           0 :                                 snprintf(hdl->query + k, lim - k, "'%s'", val);
    2657           0 :                                 free(val);
    2658           0 :                                 break;
    2659           0 :                         default:
    2660           0 :                                 strcpy_len(hdl->query + k, src, lim - k);
    2661           0 :                                 break;
    2662             :                         }
    2663             :                 }
    2664           0 :                 k += strlen(hdl->query + k);
    2665             : 
    2666           0 :                 i++;
    2667           0 :                 p = q + 1;
    2668           0 :                 q = strchr(p, PLACEHOLDER);
    2669             :         }
    2670           0 :         checkSpace(strlen(p) + 1);
    2671           0 :         strcpy(hdl->query + k, p);
    2672           0 :         if (hdl->mid->trace)
    2673           0 :                 printf("param_store: result=%s\n", hdl->query);
    2674             :         return;
    2675             : }
    2676             : 
    2677             : /* Read one more line from the input stream and return it.  This
    2678             :    returns a pointer into the input buffer, so the data needs to be
    2679             :    copied if it is to be retained. */
    2680             : static char *
    2681     1798355 : read_line(Mapi mid)
    2682             : {
    2683     1798355 :         char *reply;
    2684     1798355 :         char *nl;
    2685     1798355 :         char *s;                /* from where to search for newline */
    2686             : 
    2687     1798355 :         if (mid->active == NULL)
    2688             :                 return NULL;
    2689             : 
    2690             :         /* check if we need to read more blocks to get a new line */
    2691     1798355 :         mid->blk.eos = false;
    2692     1798355 :         s = mid->blk.buf + mid->blk.nxt;
    2693     2114000 :         while ((nl = strchr(s, '\n')) == NULL && !mid->blk.eos) {
    2694      315631 :                 ssize_t len;
    2695             : 
    2696      315631 :                 if (mid->blk.lim - mid->blk.end < BLOCK) {
    2697       16407 :                         int len;
    2698             : 
    2699       16407 :                         len = mid->blk.lim;
    2700       16407 :                         if (mid->blk.nxt <= BLOCK) {
    2701             :                                 /* extend space */
    2702        3815 :                                 len += BLOCK;
    2703             :                         }
    2704       16407 :                         REALLOC(mid->blk.buf, len + 1);
    2705       16407 :                         if (mid->blk.nxt > 0) {
    2706       13040 :                                 memmove(mid->blk.buf, mid->blk.buf + mid->blk.nxt, mid->blk.end - mid->blk.nxt + 1);
    2707       13040 :                                 mid->blk.end -= mid->blk.nxt;
    2708       13040 :                                 mid->blk.nxt = 0;
    2709             :                         }
    2710       16407 :                         mid->blk.lim = len;
    2711             :                 }
    2712             : 
    2713      315631 :                 s = mid->blk.buf + mid->blk.end;
    2714             : 
    2715             :                 /* fetch one more block */
    2716      315631 :                 if (mid->trace)
    2717           0 :                         printf("fetch next block: start at:%d\n", mid->blk.end);
    2718      315631 :                 for (;;) {
    2719      315631 :                         len = mnstr_read(mid->from, mid->blk.buf + mid->blk.end, 1, BLOCK);
    2720      315646 :                         if (len == -1 && mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
    2721           0 :                                 mnstr_clearerr(mid->from);
    2722           0 :                                 if (mid->oobintr && !mid->active->aborted) {
    2723           0 :                                         mid->active->aborted = true;
    2724           0 :                                         mnstr_putoob(mid->to, 1);
    2725             :                                 }
    2726             :                         } else
    2727             :                                 break;
    2728             :                 }
    2729      315646 :                 check_stream(mid, mid->from, len, "Connection terminated during read line", (mid->blk.eos = true, (char *) 0));
    2730      315647 :                 mapi_log_data(mid, "RECV", mid->blk.buf + mid->blk.end, len);
    2731      315645 :                 mid->blk.buf[mid->blk.end + len] = 0;
    2732      315645 :                 if (mid->trace) {
    2733           0 :                         printf("got next block: length:%zd\n", len);
    2734           0 :                         printf("text:%s\n", mid->blk.buf + mid->blk.end);
    2735             :                 }
    2736      315645 :                 if (len == 0) { /* add prompt */
    2737      146297 :                         if (mnstr_eof(mid->from))
    2738             :                                 return NULL;
    2739      146297 :                         if (mid->blk.end > mid->blk.nxt) {
    2740             :                                 /* add fake newline since newline was
    2741             :                                  * missing from server */
    2742           5 :                                 nl = mid->blk.buf + mid->blk.end;
    2743           5 :                                 *nl = '\n';
    2744           5 :                                 mid->blk.end++;
    2745             :                         }
    2746      146297 :                         len = 2;
    2747      146297 :                         mid->blk.buf[mid->blk.end] = PROMPTBEG;
    2748      146297 :                         mid->blk.buf[mid->blk.end + 1] = '\n';
    2749      146297 :                         mid->blk.buf[mid->blk.end + 2] = 0;
    2750             :                 }
    2751      315645 :                 mid->blk.end += (int) len;
    2752             :         }
    2753     1798369 :         if (mid->trace) {
    2754           0 :                 printf("got complete block: \n");
    2755           0 :                 printf("text:%s\n", mid->blk.buf + mid->blk.nxt);
    2756             :         }
    2757             : 
    2758             :         /* we have a complete line in the buffer */
    2759     1798369 :         assert(nl);
    2760     1798369 :         *nl++ = 0;
    2761     1798369 :         reply = mid->blk.buf + mid->blk.nxt;
    2762     1798369 :         mid->blk.nxt = (int) (nl - mid->blk.buf);
    2763             : 
    2764     1798369 :         if (mid->trace)
    2765           0 :                 printf("read_line:%s\n", reply);
    2766             :         return reply;
    2767             : }
    2768             : 
    2769             : /* set or unset the autocommit flag in the server */
    2770             : MapiMsg
    2771        1182 : mapi_setAutocommit(Mapi mid, bool autocommit)
    2772             : {
    2773        1182 :         if (msetting_bool(mid->settings, MP_AUTOCOMMIT) == autocommit)
    2774             :                 return MOK;
    2775           8 :         if (!msettings_lang_is_sql(mid->settings)) {
    2776           0 :                 mapi_setError(mid, "autocommit only supported in SQL", __func__, MERROR);
    2777           0 :                 return MERROR;
    2778             :         }
    2779           8 :         msettings_error err = msetting_set_bool(mid->settings, MP_AUTOCOMMIT, autocommit);
    2780           8 :         if (err)
    2781           0 :                 return mapi_setError(mid, err, __func__, MERROR);
    2782           8 :         if (!mid->connected)
    2783             :                 return MOK;
    2784           8 :         if (autocommit)
    2785           1 :                 return mapi_Xcommand(mid, "auto_commit", "1");
    2786             :         else
    2787           7 :                 return mapi_Xcommand(mid, "auto_commit", "0");
    2788             : }
    2789             : 
    2790             : MapiMsg
    2791          24 : mapi_set_time_zone(Mapi mid, int time_zone)
    2792             : {
    2793          24 :         msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
    2794          24 :         if (err)
    2795           0 :                 return mapi_setError(mid, err, __func__, MERROR);
    2796          24 :         if (!mid->connected)
    2797             :                 return MOK;
    2798             : 
    2799           0 :         char buf[100];
    2800           0 :         if (time_zone < 0)
    2801           0 :                 snprintf(buf, sizeof(buf),
    2802             :                          "SET TIME ZONE INTERVAL '-%02d:%02d' HOUR TO MINUTE",
    2803           0 :                          -time_zone / 3600, (-time_zone % 3600) / 60);
    2804             :         else
    2805           0 :                 snprintf(buf, sizeof(buf),
    2806             :                          "SET TIME ZONE INTERVAL '+%02d:%02d' HOUR TO MINUTE",
    2807           0 :                          time_zone / 3600, (time_zone % 3600) / 60);
    2808             : 
    2809           0 :         MapiHdl hdl = mapi_query(mid, buf);
    2810           0 :         if (hdl == NULL)
    2811           0 :                 return mid->error;
    2812           0 :         mapi_close_handle(hdl);
    2813             : 
    2814           0 :         return MOK;
    2815             : }
    2816             : 
    2817             : MapiMsg
    2818           0 : mapi_set_columnar_protocol(Mapi mid, bool columnar_protocol)
    2819             : {
    2820           0 :         if (mid->columnar_protocol == columnar_protocol)
    2821             :                 return MOK;
    2822           0 :         mid->columnar_protocol = columnar_protocol;
    2823           0 :         if (!mid->connected)
    2824             :                 return MOK;
    2825           0 :         if (columnar_protocol)
    2826           0 :                 return mapi_Xcommand(mid, "columnar_protocol", "1");
    2827             :         else
    2828           0 :                 return mapi_Xcommand(mid, "columnar_protocol", "0");
    2829             : }
    2830             : 
    2831             : MapiMsg
    2832         215 : mapi_set_size_header(Mapi mid, bool value)
    2833             : {
    2834         215 :         if (!msettings_lang_is_sql(mid->settings)) {
    2835           0 :                 mapi_setError(mid, "size header only supported in SQL", __func__, MERROR);
    2836           0 :                 return MERROR;
    2837             :         }
    2838         215 :         if (mid->sizeheader == value)
    2839             :                 return MOK;
    2840          49 :         mid->sizeheader = value;
    2841          49 :         if (!mid->connected)
    2842             :                 return MOK;
    2843           0 :         if (value)
    2844           0 :                 return mapi_Xcommand(mid, "sizeheader", "1");
    2845             :         else
    2846           0 :                 return mapi_Xcommand(mid, "sizeheader", "0");
    2847             : }
    2848             : 
    2849             : MapiMsg
    2850           2 : mapi_release_id(Mapi mid, int id)
    2851             : {
    2852           2 :         char buf[10];
    2853             : 
    2854           2 :         if (!msettings_lang_is_sql(mid->settings)) {
    2855           0 :                 mapi_setError(mid, "release only supported in SQL", __func__, MERROR);
    2856           0 :                 return MERROR;
    2857             :         }
    2858           2 :         snprintf(buf, sizeof(buf), "%d", id);
    2859           2 :         return mapi_Xcommand(mid, "release", buf);
    2860             : }
    2861             : 
    2862             : void
    2863         178 : mapi_trace(Mapi mid, bool flag)
    2864             : {
    2865         178 :         mapi_clrError(mid);
    2866         178 :         mid->trace = flag;
    2867         178 : }
    2868             : 
    2869             : 
    2870             : static int
    2871     1491911 : slice_row(const char *reply, char *null, char ***anchorsp, size_t **lensp, int length, int endchar)
    2872             : {
    2873             :         /* This function does the actual work for splicing a real,
    2874             :            multi-column row into columns.  It skips over the first
    2875             :            character and ends at the end of the string or at endchar,
    2876             :            whichever comes first. */
    2877     1491911 :         char *start;
    2878     1491911 :         char **anchors;
    2879     1491911 :         int i;
    2880     1491911 :         size_t len;
    2881     1491911 :         size_t *lens;
    2882             : 
    2883     1491911 :         reply++;                /* skip over initial char (usually '[') */
    2884     1491911 :         i = 0;
    2885     1491911 :         anchors = length == 0 ? NULL : malloc(length * sizeof(*anchors));
    2886     1491716 :         lens = length == 0 ? NULL : malloc(length * sizeof(*lens));
    2887     5616218 :         for (;;) {
    2888     5616218 :                 if (i >= length) {
    2889       28717 :                         length = i + 1;
    2890       28717 :                         REALLOC(anchors, length);
    2891       28717 :                         REALLOC(lens, length);
    2892             :                 }
    2893     5616218 :                 if (!unquote(reply, &start, &reply, endchar, &len) && null && strcmp(start, null) == 0) {
    2894             :                         /* indicate NULL/nil with NULL pointer */
    2895      791712 :                         free(start);
    2896      791712 :                         start = NULL;
    2897      791712 :                         len = 0;
    2898             :                 }
    2899     5616218 :                 lens[i] = len;
    2900     5616218 :                 anchors[i++] = start;
    2901     5616218 :                 if (reply == NULL)
    2902             :                         break;
    2903     5616218 :                 while (*reply && isspace((unsigned char) *reply))
    2904           0 :                         reply++;
    2905     5616218 :                 if (*reply == ',') {
    2906     4124303 :                         reply++;
    2907     8248606 :                         while (*reply && isspace((unsigned char) *reply))
    2908     4124303 :                                 reply++;
    2909     1491915 :                 } else if (*reply == 0 || *reply == endchar)
    2910             :                         break;
    2911             :         }
    2912     1491911 :         *anchorsp = anchors;
    2913     1491911 :         *lensp = lens;
    2914     1491911 :         return i;
    2915             : }
    2916             : 
    2917             : static MapiMsg
    2918       15103 : mapi_cache_freeup_internal(struct MapiResultSet *result, int k)
    2919             : {
    2920       15103 :         int i;                  /* just a counter */
    2921       15103 :         int64_t n = 0;  /* # of tuples being deleted from front */
    2922             : 
    2923       15103 :         result->cache.tuplecount = 0;
    2924       15103 :         for (i = 0; i < result->cache.writer - k; i++) {
    2925           0 :                 if (result->cache.line[i].rows) {
    2926           0 :                         if (result->cache.line[i].rows[0] == '[' ||
    2927             :                             result->cache.line[i].rows[0] == '=')
    2928           0 :                                 n++;
    2929           0 :                         free(result->cache.line[i].rows);
    2930             :                 }
    2931           0 :                 result->cache.line[i].rows = result->cache.line[i + k].rows;
    2932           0 :                 result->cache.line[i + k].rows = 0;
    2933           0 :                 if (result->cache.line[i].anchors) {
    2934             :                         int j = 0;
    2935             : 
    2936           0 :                         for (j = 0; j < result->cache.line[i].fldcnt; j++)
    2937           0 :                                 free(result->cache.line[i].anchors[j]);
    2938           0 :                         free(result->cache.line[i].anchors);
    2939             :                 }
    2940           0 :                 if (result->cache.line[i].lens)
    2941           0 :                         free(result->cache.line[i].lens);
    2942           0 :                 result->cache.line[i].anchors = result->cache.line[i + k].anchors;
    2943           0 :                 result->cache.line[i + k].anchors = 0;
    2944           0 :                 result->cache.line[i].lens = result->cache.line[i + k].lens;
    2945           0 :                 result->cache.line[i + k].lens = 0;
    2946           0 :                 result->cache.line[i].fldcnt = result->cache.line[i + k].fldcnt;
    2947           0 :                 if (result->cache.line[i].rows &&
    2948           0 :                     (result->cache.line[i].rows[0] == '[' ||
    2949             :                      result->cache.line[i].rows[0] == '=')) {
    2950           0 :                         result->cache.line[i].tuplerev = result->cache.tuplecount;
    2951           0 :                         result->cache.line[result->cache.tuplecount++].tupleindex = i;
    2952             :                 }
    2953             :         }
    2954             :         /* after the previous loop, i == result->cache.writer - k, and
    2955             :            the last (result->cache.writer - k) cache entries have been
    2956             :            cleared already , so we don't need to go the Full Monty
    2957             :            here */
    2958     1370817 :         for ( /*i = result->cache.writer - k */ ; i < k /*result->cache.writer */ ; i++) {
    2959     1355714 :                 if (result->cache.line[i].rows) {
    2960     1355714 :                         if (result->cache.line[i].rows[0] == '[' ||
    2961             :                             result->cache.line[i].rows[0] == '=')
    2962     1354829 :                                 n++;
    2963     1355714 :                         free(result->cache.line[i].rows);
    2964             :                 }
    2965     1355714 :                 result->cache.line[i].rows = 0;
    2966     1355714 :                 if (result->cache.line[i].anchors) {
    2967             :                         int j = 0;
    2968             : 
    2969     6546626 :                         for (j = 0; j < result->cache.line[i].fldcnt; j++)
    2970     5191797 :                                 free(result->cache.line[i].anchors[j]);
    2971     1354829 :                         free(result->cache.line[i].anchors);
    2972             :                 }
    2973     1355714 :                 if (result->cache.line[i].lens)
    2974     1354829 :                         free(result->cache.line[i].lens);
    2975     1355714 :                 result->cache.line[i].anchors = 0;
    2976     1355714 :                 result->cache.line[i].lens = 0;
    2977     1355714 :                 result->cache.line[i].fldcnt = 0;
    2978             :         }
    2979       15103 :         result->cache.reader -= k;
    2980       15103 :         if (result->cache.reader < 0)
    2981       15103 :                 result->cache.reader = -1;
    2982       15103 :         result->cache.writer -= k;
    2983       15103 :         if (result->cache.writer < 0)     /* "cannot happen" */
    2984           0 :                 result->cache.writer = 0;
    2985       15103 :         result->cache.first += n;
    2986             : 
    2987       15103 :         return MOK;
    2988             : }
    2989             : 
    2990             : static void
    2991       44229 : mapi_extend_cache(struct MapiResultSet *result, int cacheall)
    2992             : {
    2993       44229 :         int incr, newsize, oldsize = result->cache.limit, i;
    2994             : 
    2995             :         /* if there are read entries, delete them */
    2996       44229 :         if (result->cache.reader >= 0) {
    2997       13527 :                 mapi_cache_freeup_internal(result, result->cache.reader + 1);
    2998             :                 /* since we've made space, we can return */
    2999       13527 :                 return;
    3000             :         }
    3001             : 
    3002             :         /* extend row cache */
    3003       30702 :   retry:;
    3004       30704 :         if (oldsize == 0)
    3005             :                 incr = 100;
    3006             :         else
    3007           9 :                 incr = oldsize * 2;
    3008           9 :         if (incr > 200000)
    3009           0 :                 incr = 20000;
    3010       30704 :         newsize = oldsize + incr;
    3011       30704 :         if (result->cache.rowlimit > 0 &&
    3012           4 :             newsize > result->cache.rowlimit &&
    3013             :             !cacheall) {
    3014           4 :                 newsize = result->cache.rowlimit;
    3015           4 :                 incr = newsize - oldsize;
    3016           4 :                 if (incr <= 0) {
    3017             :                         /* not enough space, so increase limit and try again */
    3018           2 :                         result->cache.rowlimit += 100;
    3019           2 :                         goto retry;
    3020             :                 }
    3021             :         }
    3022             : 
    3023       30702 :         REALLOC(result->cache.line, newsize + 1);
    3024       30702 :         assert(result->cache.line);
    3025     3133716 :         for (i = oldsize; i <= newsize; i++) {
    3026     3103014 :                 result->cache.line[i].fldcnt = 0;
    3027     3103014 :                 result->cache.line[i].rows = NULL;
    3028     3103014 :                 result->cache.line[i].tupleindex = -1;
    3029     3103014 :                 result->cache.line[i].tuplerev = -1;
    3030     3103014 :                 result->cache.line[i].anchors = NULL;
    3031     3103014 :                 result->cache.line[i].lens = NULL;
    3032             :         }
    3033       30702 :         result->cache.limit = newsize;
    3034             : }
    3035             : 
    3036             : /* store a line in the cache */
    3037             : static void
    3038     1513132 : add_cache(struct MapiResultSet *result, char *line, int cacheall)
    3039             : {
    3040             :         /* manage the row cache space first */
    3041     1513132 :         if (result->cache.writer >= result->cache.limit)
    3042       44229 :                 mapi_extend_cache(result, cacheall);
    3043             : 
    3044     1513132 :         result->cache.line[result->cache.writer].rows = line;
    3045     1513132 :         result->cache.line[result->cache.writer].tuplerev = result->cache.tuplecount;
    3046     1513132 :         result->cache.line[result->cache.writer + 1].tuplerev = result->cache.tuplecount + 1;
    3047     1513132 :         if (*line == '[' || *line == '=') {
    3048     1390980 :                 result->cache.line[result->cache.tuplecount++].tupleindex = result->cache.writer;
    3049     1390980 :                 if (result->row_count < result->cache.first + result->cache.tuplecount)
    3050         207 :                         result->row_count = result->cache.first + result->cache.tuplecount;
    3051             :         }
    3052     1513132 :         result->cache.writer++;
    3053     1513132 : }
    3054             : 
    3055             : static struct MapiResultSet *
    3056      155722 : parse_header_line(MapiHdl hdl, char *line, struct MapiResultSet *result)
    3057             : {
    3058      155722 :         char *tag, *etag;
    3059      155722 :         int i, n;
    3060      155722 :         char **anchors;
    3061      155722 :         size_t *lens;
    3062             : 
    3063      155722 :         if (line[0] == '&') {
    3064       33573 :                 char *nline = line;
    3065       33573 :                 int qt;
    3066       33573 :                 uint64_t queryid;
    3067             : 
    3068             :                 /* handle fields &qt */
    3069             : 
    3070       33573 :                 nline++;        /* query type */
    3071       33573 :                 qt = (int) strtol(nline, &nline, 0);
    3072             : 
    3073       33573 :                 if (result == NULL || (qt != Q_BLOCK && !result->commentonly))
    3074       33546 :                         result = new_result(hdl);
    3075       33573 :                 result->querytype = qt;
    3076       33573 :                 result->commentonly = false;
    3077       33573 :                 result->querytime = 0;
    3078       33573 :                 result->maloptimizertime = 0;
    3079       33573 :                 result->sqloptimizertime = 0;
    3080             : 
    3081       33573 :                 nline++;        /* skip space */
    3082       33573 :                 switch (qt) {
    3083         598 :                 case Q_SCHEMA:
    3084         598 :                         result->querytime = strtoll(nline, &nline, 10);
    3085         598 :                         result->maloptimizertime = strtoll(nline, &nline, 10);
    3086         598 :                         result->sqloptimizertime = strtoll(nline, &nline, 10);
    3087         598 :                         break;
    3088         227 :                 case Q_TRANS:
    3089         227 :                         msetting_set_bool(hdl->mid->settings, MP_AUTOCOMMIT, *nline != 'f');
    3090         227 :                         break;
    3091        2229 :                 case Q_UPDATE:
    3092        2229 :                         result->row_count = strtoll(nline, &nline, 10);
    3093        2229 :                         result->last_id = strtoll(nline, &nline, 10);
    3094        2229 :                         queryid = strtoll(nline, &nline, 10);
    3095        2229 :                         result->querytime = strtoll(nline, &nline, 10);
    3096        2229 :                         result->maloptimizertime = strtoll(nline, &nline, 10);
    3097        2229 :                         result->sqloptimizertime = strtoll(nline, &nline, 10);
    3098        2229 :                         break;
    3099       30369 :                 case Q_TABLE:
    3100       30369 :                         if (sscanf(nline,
    3101             :                                    "%d %" SCNd64 " %d %" SCNd64 " %" SCNu64
    3102             :                                    " %" SCNd64 " %" SCNd64 " %" SCNd64,
    3103             :                                    &result->tableid, &result->row_count,
    3104             :                                    &result->fieldcnt, &result->tuple_count,
    3105             :                                    &queryid, &result->querytime,
    3106             :                                    &result->maloptimizertime,
    3107             :                                    &result->sqloptimizertime) < 8){
    3108           2 :                                 result->querytime = 0;
    3109           2 :                                 result->maloptimizertime = 0;
    3110           2 :                                 result->sqloptimizertime = 0;
    3111             :                         }
    3112             :                         (void) queryid; /* ignored for now */
    3113             :                         break;
    3114         123 :                 case Q_PREPARE:
    3115         123 :                         sscanf(nline, "%d %" SCNd64 " %d %" SCNd64,
    3116             :                                &result->tableid, &result->row_count,
    3117             :                                &result->fieldcnt, &result->tuple_count);
    3118         123 :                         break;
    3119          27 :                 case Q_BLOCK:
    3120             :                         /* Mapi ignores the Q_BLOCK header, so spoof
    3121             :                          * the querytype back to a Q_TABLE to let it
    3122             :                          * go unnoticed */
    3123          27 :                         result->querytype = Q_TABLE;
    3124          27 :                         break;
    3125             :                 }
    3126             : 
    3127             : 
    3128       33573 :                 if (result->fieldcnt > result->maxfields) {
    3129       30492 :                         REALLOC(result->fields, result->fieldcnt);
    3130       30492 :                         memset(result->fields + result->maxfields, 0, (result->fieldcnt - result->maxfields) * sizeof(*result->fields));
    3131       30492 :                         result->maxfields = result->fieldcnt;
    3132             :                 }
    3133             : 
    3134             :                 /* start of new SQL result */
    3135       33573 :                 return result;
    3136             :         }
    3137      122149 :         if (result == NULL)
    3138           4 :                 result = new_result(hdl);
    3139             : 
    3140      122149 :         if (line[0] == '#' && !msettings_lang_is_mal(hdl->mid->settings)) {
    3141             :                 /* comment */
    3142             :                 return result;
    3143             :         }
    3144             : 
    3145      122149 :         line = strdup(line);    /* make copy we can play with */
    3146      122149 :         etag = strrchr(line, '#');
    3147      122149 :         if (etag == 0 || etag == line) {
    3148             :                 /* not a useful header line */
    3149           0 :                 free(line);
    3150           0 :                 return result;
    3151             :         }
    3152             : 
    3153      122149 :         n = slice_row(line, NULL, &anchors, &lens, 10, '#');
    3154             : 
    3155      122149 :         result->commentonly = false;
    3156             : 
    3157      122149 :         tag = etag + 1;
    3158      244290 :         while (*tag && isspace((unsigned char) *tag))
    3159      122141 :                 tag++;
    3160             : 
    3161      122149 :         if (n > result->fieldcnt) {
    3162           8 :                 result->fieldcnt = n;
    3163           8 :                 if (n > result->maxfields) {
    3164           8 :                         REALLOC(result->fields, n);
    3165           8 :                         memset(result->fields + result->maxfields, 0, (n - result->maxfields) * sizeof(*result->fields));
    3166           8 :                         result->maxfields = n;
    3167             :                 }
    3168             :         }
    3169             : 
    3170      122149 :         if (strcmp(tag, "name") == 0) {
    3171       30492 :                 result->fieldcnt = n;
    3172      107328 :                 for (i = 0; i < n; i++) {
    3173       76836 :                         if (anchors[i]) {
    3174       76836 :                                 if (result->fields[i].columnname)
    3175           0 :                                         free(result->fields[i].columnname);
    3176       76836 :                                 result->fields[i].columnname = anchors[i];
    3177       76836 :                                 anchors[i] = NULL;
    3178             :                         }
    3179             :                 }
    3180       91657 :         } else if (strcmp(tag, "type") == 0) {
    3181       30496 :                 result->fieldcnt = n;
    3182      107340 :                 for (i = 0; i < n; i++) {
    3183       76844 :                         if (anchors[i]) {
    3184       76844 :                                 if (result->fields[i].columntype)
    3185           0 :                                         free(result->fields[i].columntype);
    3186       76844 :                                 result->fields[i].columntype = anchors[i];
    3187       76844 :                                 anchors[i] = NULL;
    3188             :                         }
    3189             :                 }
    3190       61161 :         } else if (strcmp(tag, "length") == 0) {
    3191       30492 :                 result->fieldcnt = n;
    3192      107328 :                 for (i = 0; i < n; i++) {
    3193       76836 :                         if (anchors[i])
    3194       76836 :                                 result->fields[i].columnlength = atoi(anchors[i]);
    3195             :                 }
    3196       30669 :         } else if (strcmp(tag, "table_name") == 0) {
    3197       30492 :                 result->fieldcnt = n;
    3198      107328 :                 for (i = 0; i < n; i++) {
    3199       76836 :                         if (anchors[i]) {
    3200       76836 :                                 if (result->fields[i].tablename)
    3201           0 :                                         free(result->fields[i].tablename);
    3202       76836 :                                 result->fields[i].tablename = anchors[i];
    3203       76836 :                                 anchors[i] = NULL;
    3204             :                         }
    3205             :                 }
    3206         177 :         } else if (strcmp(tag, "typesizes") == 0) {
    3207         169 :                 result->fieldcnt = n;
    3208        1215 :                 for (i = 0; i < n; i++) {
    3209        1046 :                         if (anchors[i]) {
    3210        1046 :                                 char *p;
    3211        1046 :                                 result->fields[i].digits = atoi(anchors[i]);
    3212        1046 :                                 p = strchr(anchors[i], ' ');
    3213        1046 :                                 if (p)
    3214        1046 :                                         result->fields[i].scale = atoi(p + 1);
    3215             :                         }
    3216             :                 }
    3217             :         }
    3218             : 
    3219             :         /* clean up */
    3220      122149 :         free(line);
    3221      430555 :         for (i = 0; i < n; i++)
    3222      308406 :                 if (anchors[i])
    3223       77890 :                         free(anchors[i]);
    3224      122149 :         free(anchors);
    3225      122149 :         free(lens);
    3226             : 
    3227      122149 :         return result;
    3228             : }
    3229             : 
    3230             : static void
    3231          75 : write_file(MapiHdl hdl, char *filename, bool binary)
    3232             : {
    3233          75 :         Mapi mid = hdl->mid;
    3234          75 :         char *line;
    3235          75 :         char data[BLOCK];
    3236          75 :         ssize_t len;
    3237             : 
    3238          75 :         (void) read_line(mid);  /* read flush marker */
    3239          75 :         if (filename == NULL) {
    3240             :                 /* malloc failure */
    3241           0 :                 mnstr_printf(mid->to, "!HY001!allocation failure\n");
    3242           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3243           0 :                 return;
    3244             :         }
    3245          75 :         if (mid->putfilecontent == NULL) {
    3246           0 :                 free(filename);
    3247           0 :                 mnstr_printf(mid->to, "!HY000!cannot send files\n");
    3248           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3249           0 :                 return;
    3250             :         }
    3251          75 :         line = mid->putfilecontent(mid->filecontentprivate, filename, binary, NULL, 0);
    3252          75 :         free(filename);
    3253          75 :         if (line != NULL) {
    3254           0 :                 if (strchr(line, '\n'))
    3255           0 :                         line = "incorrect response from application";
    3256           0 :                 mnstr_printf(mid->to, "!HY000!%.64s\n", line);
    3257           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3258           0 :                 return;
    3259             :         }
    3260          75 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3261      180811 :         for (;;) {
    3262      180811 :                 len = mnstr_read(mid->from, data, 1, sizeof(data));
    3263      180811 :                 if (len == -1) {
    3264           0 :                         if (mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
    3265           0 :                                 mnstr_clearerr(mid->from);
    3266           0 :                                 if (mid->oobintr && !hdl->aborted) {
    3267           0 :                                         hdl->aborted = true;
    3268           0 :                                         mnstr_putoob(mid->to, 1);
    3269             :                                 }
    3270             :                         } else {
    3271             :                                 break;
    3272             :                         }
    3273      180811 :                 } else if (len == 0) {
    3274             :                         break;
    3275      180736 :                 } else if (line == NULL) {
    3276      180736 :                         line = mid->putfilecontent(mid->filecontentprivate,
    3277             :                                                    NULL, binary, data, len);
    3278             :                 }
    3279             :         }
    3280          75 :         if (line == NULL)
    3281          75 :                 line = mid->putfilecontent(mid->filecontentprivate,
    3282             :                                            NULL, binary, NULL, 0);
    3283          75 :         if (line && strchr(line, '\n'))
    3284             :                 line = "incorrect response from application";
    3285          75 :         mnstr_printf(mid->to, "%s\n", line ? line : "");
    3286          75 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3287             : }
    3288             : 
    3289             : #define MiB     (1 << 20) /* a megabyte */
    3290             : 
    3291             : static void
    3292         104 : read_file(MapiHdl hdl, uint64_t off, char *filename, bool binary)
    3293             : {
    3294         104 :         Mapi mid = hdl->mid;
    3295         104 :         size_t size = 0, flushsize = 0;
    3296         104 :         char *data, *line;
    3297             : 
    3298         104 :         (void) read_line(mid);  /* read flush marker */
    3299         104 :         if (filename == NULL) {
    3300             :                 /* malloc failure */
    3301           0 :                 mnstr_printf(mid->to, "!HY001!allocation failure\n");
    3302           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3303          20 :                 return;
    3304             :         }
    3305         104 :         if (mid->getfilecontent == NULL) {
    3306           0 :                 free(filename);
    3307           0 :                 mnstr_printf(mid->to, "!HY000!cannot retrieve files\n");
    3308           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3309           0 :                 return;
    3310             :         }
    3311         104 :         data = mid->getfilecontent(mid->filecontentprivate, filename, binary,
    3312             :                                    off, &size);
    3313         104 :         free(filename);
    3314         104 :         if (data != NULL && size == 0) {
    3315           0 :                 if (strchr(data, '\n'))
    3316           0 :                         data = "incorrect response from application";
    3317           0 :                 mnstr_printf(mid->to, "!HY000!%.64s\n", data);
    3318           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3319           0 :                 return;
    3320             :         }
    3321         104 :         mnstr_printf(mid->to, "\n");
    3322       23786 :         while (data != NULL && size != 0) {
    3323       23683 :                 if (flushsize >= MiB) {
    3324             :                         /* after every MiB give the server the
    3325             :                          * opportunity to stop reading more data */
    3326        1414 :                         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3327             :                         /* at this point we expect to get a PROMPT2 if
    3328             :                          * the server wants more data, or a PROMPT3 if
    3329             :                          * the server had enough; anything else is a
    3330             :                          * protocol violation */
    3331        1414 :                         line = read_line(mid);
    3332        1414 :                         if (line == NULL) {
    3333             :                                 /* error */
    3334           0 :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3335           0 :                                 return;
    3336             :                         }
    3337        1414 :                         assert(line[0] == PROMPTBEG);
    3338        1414 :                         if (line[0] != PROMPTBEG) {
    3339             :                                 /* error in protocol */
    3340             :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3341             :                                 return;
    3342             :                         }
    3343        1414 :                         if (line[1] == PROMPT3[1]) {
    3344             :                                 /* done reading: close file */
    3345           1 :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3346           1 :                                 (void) read_line(mid);
    3347           1 :                                 return;
    3348             :                         }
    3349        1413 :                         assert(line[1] == PROMPT2[1]);
    3350        1413 :                         if (line[1] != PROMPT2[1]) {
    3351             :                                 /* error  in protocol */
    3352             :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3353             :                                 return;
    3354             :                         }
    3355             :                         /* clear the flush marker */
    3356        1413 :                         (void) read_line(mid);
    3357        1413 :                         flushsize = 0;
    3358             :                 }
    3359       23682 :                 if (size > MiB) {
    3360           0 :                         if (mnstr_write(mid->to, data, 1, MiB) != MiB) {
    3361           0 :                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3362           0 :                                 return;
    3363             :                         }
    3364           0 :                         size -= MiB;
    3365           0 :                         data += MiB;
    3366           0 :                         flushsize += MiB;
    3367             :                 } else {
    3368       23682 :                         if (mnstr_write(mid->to, data, 1, size) != (ssize_t) size) {
    3369           0 :                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3370           0 :                                 return;
    3371             :                         }
    3372       23682 :                         flushsize += size;
    3373       23682 :                         data = mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, &size);
    3374             :                 }
    3375             :         }
    3376         103 :         if (data != NULL && size == 0) {
    3377             :                 /* some error occurred */
    3378           0 :                 mnstr_clearerr(mid->from);
    3379           0 :                 if (mid->oobintr && !hdl->aborted) {
    3380           0 :                         hdl->aborted = true;
    3381           0 :                         mnstr_putoob(mid->to, 1);
    3382             :                 }
    3383             :         }
    3384         103 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3385         103 :         line = read_line(mid);
    3386         103 :         if (line == NULL)
    3387             :                 return;
    3388         103 :         assert(line[0] == PROMPTBEG);
    3389         103 :         if (line[0] != PROMPTBEG)
    3390             :                 return;
    3391         103 :         if (line[1] == PROMPT3[1]) {
    3392          19 :                 (void) read_line(mid);
    3393          19 :                 return;
    3394             :         }
    3395          84 :         assert(line[1] == PROMPT2[1]);
    3396          84 :         if (line[1] != PROMPT2[1])
    3397             :                 return;
    3398          84 :         (void) read_line(mid);
    3399          84 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3400          84 :         line = read_line(mid);
    3401          84 :         if (line == NULL)
    3402             :                 return;
    3403          84 :         assert(line[0] == PROMPTBEG);
    3404          84 :         assert(line[1] == PROMPT3[1]);
    3405          84 :         (void) read_line(mid);
    3406             : }
    3407             : 
    3408             : 
    3409             : /* Read ahead and cache data read.  Depending on the second argument,
    3410             :    reading may stop at the first non-header and non-error line, or at
    3411             :    a prompt.
    3412             :    This function is called either after a command has been sent to the
    3413             :    server (in which case the second argument is 1), when the
    3414             :    application asks for a result tuple that hadn't been cached yet (in
    3415             :    which case the second argument is also 1), or whenever all pending
    3416             :    data needs to be read in order to send a new command to the server
    3417             :    (in which case the second argument is 0).
    3418             :    Header lines result tuples are stored in the cache.  Certain header
    3419             :    lines may cause a new result set to be created in which case all
    3420             :    subsequent lines are added to that result set.
    3421             : */
    3422             : MapiMsg
    3423     1533104 : read_into_cache(MapiHdl hdl, int lookahead)
    3424             : {
    3425     1533104 :         char *line;
    3426     1533104 :         Mapi mid;
    3427     1533104 :         struct MapiResultSet *result;
    3428             : 
    3429     1533104 :         mid = hdl->mid;
    3430     1533104 :         assert(mid->active == hdl);
    3431     1533104 :         if (hdl->needmore) {
    3432           0 :                 hdl->needmore = false;
    3433           0 :                 int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3434           0 :                 check_stream(mid, mid->to, f, "write error on stream", mid->error);
    3435             :         }
    3436     1533104 :         if ((result = hdl->active) == NULL)
    3437      144478 :                 result = hdl->result;        /* may also be NULL */
    3438             : 
    3439     1691470 :         for (;;) {
    3440     1691470 :                 line = read_line(mid);
    3441     1691478 :                 if (line == NULL) {
    3442           0 :                         if (mid->from && mnstr_eof(mid->from)) {
    3443           0 :                                 return mapi_setError(mid, "unexpected end of file", __func__, MTIMEOUT);
    3444             :                         }
    3445             : 
    3446           0 :                         return mid->error;
    3447             :                 }
    3448     1691478 :                 switch (*line) {
    3449      144692 :                 case PROMPTBEG: /* \001 */
    3450      144692 :                         mid->active = NULL;
    3451      144692 :                         hdl->active = NULL;
    3452             :                         /* set needmore flag if line equals PROMPT2 up
    3453             :                            to newline */
    3454      144692 :                         if (line[1] == PROMPT2[1] && line[2] == '\0') {
    3455             :                                 /* skip end of block */
    3456      103328 :                                 mid->active = hdl;
    3457      103328 :                                 (void) read_line(mid);
    3458      103328 :                                 hdl->needmore = true;
    3459      103328 :                                 mid->active = hdl;
    3460       41364 :                         } else if (line[1] == PROMPT3[1] && line[2] == '\0') {
    3461         179 :                                 mid->active = hdl;
    3462         179 :                                 line = read_line(mid);
    3463         179 :                                 bool binary = false;
    3464             :                                 /* rb FILE
    3465             :                                  * r OFF FILE
    3466             :                                  * w FILE
    3467             :                                  * wb FILE
    3468             :                                  */
    3469         179 :                                 switch (*line++) {
    3470         104 :                                 case 'r': {
    3471         104 :                                         uint64_t off = 0;
    3472         104 :                                         if (*line == 'b') {
    3473          85 :                                                 line++;
    3474          85 :                                                 binary = true;
    3475             :                                         } else {
    3476          19 :                                                 off = strtoul(line, &line, 10);
    3477             :                                         }
    3478         104 :                                         if (*line++ != ' ') {
    3479           0 :                                                 mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
    3480           0 :                                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3481           0 :                                                 break;
    3482             :                                         }
    3483         104 :                                         read_file(hdl, off, strdup(line), binary);
    3484         104 :                                         break;
    3485             :                                 }
    3486          75 :                                 case 'w':
    3487          75 :                                         if (*line == 'b') {
    3488          75 :                                                 line++;
    3489          75 :                                                 binary = true;
    3490             :                                         }
    3491          75 :                                         if (*line++ != ' ') {
    3492           0 :                                                 mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
    3493           0 :                                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3494           0 :                                                 break;
    3495             :                                         }
    3496          75 :                                         write_file(hdl, strdup(line), binary);
    3497          75 :                                         break;
    3498             :                                 }
    3499         179 :                                 continue;
    3500             :                         }
    3501      144513 :                         return mid->error;
    3502          81 :                 case '!':
    3503             :                         /* start a new result set if we don't have one
    3504             :                            yet (duh!), or if we've already seen
    3505             :                            normal output for the current one */
    3506          81 :                         if (result == NULL ||
    3507           2 :                             result->cache.writer > 0 ||
    3508           2 :                             result->querytype > 0) {
    3509          79 :                                 result = new_result(hdl);
    3510          79 :                                 result->commentonly = false;
    3511          79 :                                 hdl->active = result;
    3512             :                         }
    3513          81 :                         add_error(result, line + 1 /* skip ! */ );
    3514          81 :                         if (!mid->error)
    3515          79 :                                 mid->error = MSERVER;
    3516             :                         break;
    3517      155722 :                 case '%':
    3518             :                 case '#':
    3519             :                 case '&':
    3520      155722 :                         if (lookahead < 0)
    3521           0 :                                 lookahead = 1;
    3522      155722 :                         result = parse_header_line(hdl, line, result);
    3523      155722 :                         hdl->active = result;
    3524      155722 :                         if (result && *line != '&')
    3525      122149 :                                 add_cache(result, strdup(line), !lookahead);
    3526             :                         break;
    3527     1390983 :                 default:
    3528     1390983 :                         if (result == NULL) {
    3529         196 :                                 result = new_result(hdl);
    3530         196 :                                 hdl->active = result;
    3531             :                         }
    3532     1390983 :                         add_cache(result, strdup(line), !lookahead);
    3533     1390983 :                         if (lookahead > 0 &&
    3534     1389207 :                             (result->querytype == -1 /* unknown (not SQL) */ ||
    3535         608 :                              result->querytype == Q_TABLE ||
    3536             :                              result->querytype == Q_UPDATE)) {
    3537     1388599 :                                 return mid->error;
    3538             :                         }
    3539             :                         break;
    3540             :                 }
    3541             :         }
    3542             : }
    3543             : 
    3544             : static MapiMsg
    3545       32911 : mapi_execute_internal(MapiHdl hdl)
    3546             : {
    3547       32911 :         size_t size;
    3548       32911 :         char *cmd;
    3549       32911 :         Mapi mid;
    3550             : 
    3551       32911 :         mid = hdl->mid;
    3552       32911 :         if (mid->active && read_into_cache(mid->active, 0) != MOK)
    3553             :                 return MERROR;
    3554       32911 :         assert(mid->active == NULL);
    3555       32911 :         finish_handle(hdl);
    3556       32915 :         mapi_param_store(hdl);
    3557       32910 :         cmd = hdl->query;
    3558       32910 :         if (cmd == NULL)
    3559             :                 return MERROR;
    3560       32910 :         size = strlen(cmd);
    3561             : 
    3562       32910 :         bool is_sql = msettings_lang_is_sql(mid->settings);
    3563       32917 :         char *prefix = is_sql ? "s" : "";
    3564        4209 :         char *suffix = is_sql ? "\n;" : "";
    3565       32917 :         mapi_log_record(mid, "SEND", "%s%s%s", prefix, cmd, suffix);
    3566             : 
    3567       32919 :         if (is_sql) {
    3568             :                 /* indicate to server this is a SQL command */
    3569       28700 :                 ssize_t w = mnstr_write(mid->to, "s", 1, 1);
    3570       28700 :                 check_stream(mid, mid->to, w, "write error on stream", mid->error);
    3571             :         }
    3572       32919 :         ssize_t w = mnstr_write(mid->to, cmd, 1, size);
    3573       32908 :         check_stream(mid, mid->to, w, "write error on stream", mid->error);
    3574             :         /* all SQL statements should end with a semicolon */
    3575             :         /* for the other languages it is assumed that the statements are correct */
    3576       32909 :         if (is_sql) {
    3577       28700 :                 w = mnstr_write(mid->to, "\n;", 2, 1);
    3578       28700 :                 check_stream(mid, mid->to, w, "write error on stream", mid->error);
    3579             :         }
    3580       32909 :         w = mnstr_write(mid->to, "\n", 1, 1);
    3581       32918 :         check_stream(mid, mid->to, w, "write error on stream", mid->error);
    3582       32921 :         w = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3583       32907 :         check_stream(mid, mid->to, w, "write error on stream", mid->error);
    3584       32909 :         mid->active = hdl;
    3585             : 
    3586       32909 :         return MOK;
    3587             : }
    3588             : 
    3589             : MapiMsg
    3590           0 : mapi_execute(MapiHdl hdl)
    3591             : {
    3592           0 :         int ret;
    3593             : 
    3594           0 :         mapi_hdl_check(hdl);
    3595           0 :         if ((ret = mapi_execute_internal(hdl)) == MOK)
    3596           0 :                 return read_into_cache(hdl, 1);
    3597             : 
    3598             :         return ret;
    3599             : }
    3600             : 
    3601             : /*
    3602             :  * The routine mapi_query is one of the most heavily used ones.
    3603             :  * It sends a complete statement for execution
    3604             :  * (i.e., ending in a newline; possibly including additional newlines).
    3605             :  * Interaction with the server is sped up using block based interaction.
    3606             :  * The query is retained in the Mapi structure to repeat shipping.
    3607             :  */
    3608             : MapiHdl
    3609       30797 : mapi_query(Mapi mid, const char *cmd)
    3610             : {
    3611       30797 :         int ret;
    3612       30797 :         MapiHdl hdl;
    3613             : 
    3614       30797 :         mapi_check0(mid);
    3615       30801 :         hdl = prepareQuery(mapi_new_handle(mid), cmd);
    3616       30814 :         ret = mid->error;
    3617       30814 :         if (ret == MOK)
    3618       30806 :                 ret = mapi_execute_internal(hdl);
    3619       30795 :         if (ret == MOK)
    3620       30798 :                 ret = read_into_cache(hdl, 1);
    3621             :         return hdl;
    3622             : }
    3623             : 
    3624             : /* version of mapi_query that does not wait for a response */
    3625             : MapiHdl
    3626           0 : mapi_send(Mapi mid, const char *cmd)
    3627             : {
    3628           0 :         int ret;
    3629           0 :         MapiHdl hdl;
    3630             : 
    3631           0 :         mapi_check0(mid);
    3632           0 :         hdl = prepareQuery(mapi_new_handle(mid), cmd);
    3633           0 :         ret = mid->error;
    3634           0 :         if (ret == MOK)
    3635           0 :                 ret = mapi_execute_internal(hdl);
    3636             :         return hdl;
    3637             : }
    3638             : 
    3639             : MapiMsg
    3640           0 : mapi_read_response(MapiHdl hdl)
    3641             : {
    3642           0 :         return read_into_cache(hdl, 1);
    3643             : }
    3644             : 
    3645             : MapiMsg
    3646        2116 : mapi_query_handle(MapiHdl hdl, const char *cmd)
    3647             : {
    3648        2116 :         int ret;
    3649             : 
    3650        2116 :         mapi_hdl_check(hdl);
    3651        2116 :         if (finish_handle(hdl) != MOK)
    3652             :                 return MERROR;
    3653        2116 :         prepareQuery(hdl, cmd);
    3654        2116 :         ret = hdl->mid->error;
    3655        2116 :         if (ret == MOK)
    3656        2116 :                 ret = mapi_execute_internal(hdl);
    3657        2116 :         if (ret == MOK)
    3658        2116 :                 ret = read_into_cache(hdl, 1);
    3659             :         return ret;
    3660             : }
    3661             : 
    3662             : MapiHdl
    3663        5519 : mapi_query_prep(Mapi mid)
    3664             : {
    3665        5519 :         mapi_check0(mid);
    3666        5519 :         if (mid->active && read_into_cache(mid->active, 0) != MOK)
    3667             :                 return NULL;
    3668        5519 :         assert(mid->active == NULL);
    3669        5519 :         if (msettings_lang_is_sql(mid->settings)) {
    3670             :                 /* indicate to server this is a SQL command */
    3671        5519 :                 mnstr_write(mid->to, "S", 1, 1);
    3672        5519 :                 mapi_log_data(mid, "SEND", "S", 1);
    3673             :         }
    3674        5519 :         return (mid->active = mapi_new_handle(mid));
    3675             : }
    3676             : 
    3677             : MapiMsg
    3678      108503 : mapi_query_part(MapiHdl hdl, const char *query, size_t size)
    3679             : {
    3680      108503 :         Mapi mid;
    3681             : 
    3682      108503 :         mapi_hdl_check(hdl);
    3683      108503 :         mid = hdl->mid;
    3684      108503 :         assert(mid->active == NULL || mid->active == hdl);
    3685      108503 :         mid->active = hdl;
    3686             :         /* remember the query just for the error messages */
    3687      108503 :         if (hdl->query == NULL) {
    3688        5182 :                 hdl->query = malloc(size + 1);
    3689        5182 :                 if (hdl->query) {
    3690        5182 :                         strcpy_len(hdl->query, query, size + 1);
    3691             :                 }
    3692             :         } else {
    3693      103321 :                 size_t sz = strlen(hdl->query);
    3694      103321 :                 char *q;
    3695             : 
    3696      103321 :                 if (sz < 512 &&
    3697        1723 :                     (q = realloc(hdl->query, sz + size + 1)) != NULL) {
    3698        1723 :                         strcpy_len(q + sz, query, size + 1);
    3699        1723 :                         hdl->query = q;
    3700             :                 }
    3701             :         }
    3702             : 
    3703      108503 :         if (mid->trace) {
    3704           0 :                 printf("mapi_query_part:%zu:%.*s\n", size, (int) size, query);
    3705             :         }
    3706      108503 :         hdl->needmore = false;
    3707      108503 :         size = mnstr_write(mid->to, query, 1, size);
    3708      108503 :         if (mid->tracelog) {
    3709           0 :                 mnstr_write(mid->tracelog, query, 1, size);
    3710           0 :                 mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
    3711             :         }
    3712      108503 :         check_stream(mid, mid->to, size, "write error on stream", mid->error);
    3713      108503 :         return mid->error;
    3714             : }
    3715             : 
    3716             : MapiMsg
    3717      108847 : mapi_query_done(MapiHdl hdl)
    3718             : {
    3719      108847 :         int ret;
    3720      108847 :         Mapi mid;
    3721             : 
    3722      108847 :         mapi_hdl_check(hdl);
    3723      108847 :         mid = hdl->mid;
    3724      108847 :         assert(mid->active == NULL || mid->active == hdl);
    3725      108847 :         mid->active = hdl;
    3726      108847 :         hdl->needmore = false;
    3727      108847 :         int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3728      108847 :         check_stream(mid, mid->to, f, "write error on stream", mid->error);
    3729      108847 :         ret = mid->error;
    3730      108847 :         if (ret == MOK)
    3731      108847 :                 ret = read_into_cache(hdl, 1);
    3732      108847 :         return ret == MOK && hdl->needmore ? MMORE : ret;
    3733             : }
    3734             : 
    3735             : MapiMsg
    3736           0 : mapi_query_abort(MapiHdl hdl, int reason)
    3737             : {
    3738           0 :         Mapi mid;
    3739             : 
    3740           0 :         assert(reason > 0 && reason <= 127);
    3741           0 :         mapi_hdl_check(hdl);
    3742           0 :         mid = hdl->mid;
    3743           0 :         assert(mid->active == NULL || mid->active == hdl);
    3744           0 :         if (mid->oobintr && !hdl->aborted && mnstr_putoob(mid->to, reason) == 0) {
    3745           0 :                 hdl->aborted = true;
    3746           0 :                 return MOK;
    3747             :         }
    3748             :         return MERROR;
    3749             : }
    3750             : 
    3751             : MapiMsg
    3752         223 : mapi_cache_limit(Mapi mid, int limit)
    3753             : {
    3754             :         /* clean out superfluous space TODO */
    3755         223 :         msettings_error err = msetting_set_long(mid->settings, MP_REPLYSIZE, limit);
    3756         223 :         if (err)
    3757           0 :                 return mapi_setError(mid, err, __func__, MERROR);
    3758         223 :         if (!mid->connected)
    3759             :                 return MOK;
    3760          48 :         mapi_check(mid);
    3761             : /*      if (hdl->cache.rowlimit < hdl->cache.limit) { */
    3762             :         /* TODO: decide what to do here */
    3763             :         /*              hdl->cache.limit = hdl->cache.rowlimit; *//* arbitrarily throw away cache lines */
    3764             : /*              if (hdl->cache.writer > hdl->cache.limit) { */
    3765             : /*                      hdl->cache.writer = hdl->cache.limit; */
    3766             : /*                      if (hdl->cache.reader > hdl->cache.writer) */
    3767             : /*                              hdl->cache.reader = hdl->cache.writer; */
    3768             : /*              } */
    3769             : /*      } */
    3770          48 :         if (msettings_lang_is_sql(mid->settings)) {
    3771          48 :                 MapiHdl hdl;
    3772             : 
    3773          48 :                 if (mid->active)
    3774           1 :                         read_into_cache(mid->active, 0);
    3775             : 
    3776          48 :                 mapi_log_record(mid, "X", "X" "reply_size %d\n", limit);
    3777          96 :                 if (mnstr_printf(mid->to, "X" "reply_size %d\n", limit) < 0 ||
    3778          48 :                     mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    3779           0 :                         close_connection(mid);
    3780           0 :                         mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    3781           0 :                         return MERROR;
    3782             :                 }
    3783          48 :                 hdl = prepareQuery(mapi_new_handle(mid), "reply_size");
    3784          48 :                 if (hdl == NULL)
    3785             :                         return MERROR;
    3786          48 :                 mid->active = hdl;
    3787          48 :                 read_into_cache(hdl, 0);
    3788          48 :                 mapi_close_handle(hdl); /* reads away any output */
    3789             :         }
    3790             :         return MOK;
    3791             : }
    3792             : 
    3793             : MapiMsg
    3794           0 : mapi_fetch_reset(MapiHdl hdl)
    3795             : {
    3796           0 :         mapi_hdl_check(hdl);
    3797           0 :         if (hdl->result)
    3798           0 :                 hdl->result->cache.reader = -1;
    3799             :         return MOK;
    3800             : }
    3801             : 
    3802             : MapiMsg
    3803        2668 : mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
    3804             : {
    3805        2668 :         struct MapiResultSet *result;
    3806             : 
    3807        2668 :         mapi_hdl_check(hdl);
    3808        2668 :         result = hdl->result;
    3809        2668 :         switch (whence) {
    3810             :         case MAPI_SEEK_SET:
    3811             :                 break;
    3812           0 :         case MAPI_SEEK_CUR:
    3813           0 :                 rownr += result->cache.line[result->cache.reader + 1].tuplerev;
    3814           0 :                 break;
    3815           0 :         case MAPI_SEEK_END:
    3816           0 :                 if (hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
    3817             :                         return MERROR;
    3818           0 :                 rownr += result->row_count;
    3819           0 :                 break;
    3820           0 :         default:
    3821           0 :                 return mapi_setError(hdl->mid, "Illegal whence value", __func__, MERROR);
    3822             :         }
    3823        2668 :         if (rownr > result->row_count && hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
    3824             :                 return MERROR;
    3825        2668 :         if (rownr < 0 || rownr > result->row_count)
    3826           0 :                 return mapi_setError(hdl->mid, "Illegal row number", __func__, MERROR);
    3827        2668 :         if (result->cache.first <= rownr && rownr < result->cache.first + result->cache.tuplecount) {
    3828             :                 /* we've got the requested tuple in the cache */
    3829        1092 :                 result->cache.reader = result->cache.line[rownr - result->cache.first].tupleindex - 1;
    3830             :         } else {
    3831             :                 /* we don't have the requested tuple in the cache
    3832             :                    reset the cache and at the next fetch we'll get the data */
    3833        1576 :                 if (mapi_cache_freeup(hdl, 100) == MOK) {
    3834        1576 :                         result->cache.first = rownr;
    3835             :                 }
    3836             :         }
    3837        2668 :         return hdl->mid->error;
    3838             : }
    3839             : 
    3840             : /* Make space in the cache for new tuples, ignore the read pointer */
    3841             : MapiMsg
    3842        1576 : mapi_cache_freeup(MapiHdl hdl, int percentage)
    3843             : {
    3844        1576 :         struct MapiResultSet *result;
    3845        1576 :         int k;                  /* # of cache lines to be deleted from front */
    3846             : 
    3847        1576 :         mapi_hdl_check(hdl);
    3848        1576 :         result = hdl->result;
    3849        1576 :         if (result == NULL || (result->cache.writer == 0 && result->cache.reader == -1))
    3850             :                 return MOK;
    3851        1576 :         if (percentage < 0 || percentage > 100)
    3852           0 :                 percentage = 100;
    3853        1576 :         k = (result->cache.writer * percentage) / 100;
    3854        1576 :         if (k < 1)
    3855           0 :                 k = 1;
    3856        1576 :         return mapi_cache_freeup_internal(result, k);
    3857             : }
    3858             : 
    3859             : static char *
    3860     1542362 : mapi_fetch_line_internal(MapiHdl hdl)
    3861             : {
    3862     1542362 :         Mapi mid;
    3863     1542362 :         struct MapiResultSet *result;
    3864     1542362 :         char *reply;
    3865             : 
    3866             :         /* try to read a line from the cache */
    3867     1542362 :         if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer) {
    3868     1394364 :                 mid = hdl->mid;
    3869     1394364 :                 if (mid->active != hdl || hdl->needmore)
    3870             :                         return NULL;
    3871             : 
    3872     1388184 :                 if (read_into_cache(hdl, 1) != MOK)
    3873             :                         return NULL;
    3874     1388184 :                 if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer)
    3875             :                         return NULL;
    3876             :         }
    3877     1512045 :         reply = result->cache.line[++result->cache.reader].rows;
    3878     1512045 :         if (hdl->bindings && (*reply == '[' || *reply == '=')) {
    3879           0 :                 mapi_slice_row(result, result->cache.reader);
    3880           0 :                 mapi_store_bind(result, result->cache.reader);
    3881             :         }
    3882             :         return reply;
    3883             : }
    3884             : 
    3885             : /*
    3886             :  * The routine mapi_fetch_line forms the basic interaction with the server.
    3887             :  * It simply retrieves the next line and stores it in the row cache.
    3888             :  * The field anchor structure is prepared for subsequent use by
    3889             :  * mapi_fetch_row.
    3890             :  * The content received is analyzed further by mapi_getRow()
    3891             :  */
    3892             : char *
    3893     1542335 : mapi_fetch_line(MapiHdl hdl)
    3894             : {
    3895     1542335 :         char *reply;
    3896     1542335 :         struct MapiResultSet *result;
    3897             : 
    3898     1542335 :         mapi_hdl_check0(hdl);
    3899     1542335 :         reply = mapi_fetch_line_internal(hdl);
    3900     1542335 :         if (reply == NULL &&
    3901       60634 :             (result = hdl->result) != NULL &&
    3902       30317 :             msettings_lang_is_sql(hdl->mid->settings) &&
    3903       30305 :             result->querytype == Q_TABLE &&
    3904       30186 :             result->row_count > 0 &&
    3905       24266 :             result->cache.first + result->cache.tuplecount < result->row_count) {
    3906          27 :                 if (hdl->needmore)   /* escalate */
    3907             :                         return NULL;
    3908          27 :                 if (hdl->mid->active != NULL)
    3909           0 :                         read_into_cache(hdl->mid->active, 0);
    3910          27 :                 hdl->mid->active = hdl;
    3911          27 :                 hdl->active = result;
    3912          27 :                 mapi_log_record(hdl->mid, "W", "X" "export %d %" PRId64 "\n",
    3913             :                                      result->tableid,
    3914             :                                      result->cache.first + result->cache.tuplecount);
    3915          27 :                 int e;
    3916          27 :                 if ((e = mnstr_printf(hdl->mid->to, "X" "export %d %" PRId64 "\n",
    3917             :                                  result->tableid,
    3918          27 :                                       result->cache.first + result->cache.tuplecount)) < 0 ||
    3919          27 :                     (e = mnstr_flush(hdl->mid->to, MNSTR_FLUSH_DATA)) < 0)
    3920           0 :                         check_stream(hdl->mid, hdl->mid->to, e, "sending export command", NULL);
    3921          27 :                 reply = mapi_fetch_line_internal(hdl);
    3922             :         }
    3923             :         return reply;
    3924             : }
    3925             : 
    3926             : /*
    3927             :  * To synchronize on a prompt, the low level routine mapi_finish can be used.
    3928             :  * It discards all output received.
    3929             :  */
    3930             : MapiMsg
    3931           0 : mapi_finish(MapiHdl hdl)
    3932             : {
    3933           0 :         mapi_hdl_check(hdl);
    3934           0 :         return finish_handle(hdl);
    3935             : }
    3936             : 
    3937             : /* msg is a string consisting comma-separated values.  The list of
    3938             :    values is terminated by endchar or by the end-of-string NULL byte.
    3939             :    Values can be quoted strings or unquoted values.  Upon return,
    3940             :    *start points to the start of the first value which is stripped of
    3941             :    leading and trailing white space, and if it was a quoted string,
    3942             :    also of the quotes.  Also, backslash-escaped characters in the
    3943             :    quoted string are replaced by the values the escapes represent.
    3944             :    *next points to either the start of the next value (i.e. after the
    3945             :    separating comma, possibly to the leading white space of the next
    3946             :    value), or to the trailing ] or NULL byte if this was the last
    3947             :    value.  *lenp is the number of bytes occupied by the (possibly
    3948             :    converted) value, excluding final NULL byte.
    3949             :    msg is *not* a const string: it is altered by this function.
    3950             :    The function returns true if the string was quoted.
    3951             : */
    3952             : static int
    3953     5616218 : unquote(const char *msg, char **str, const char **next, int endchar, size_t *lenp)
    3954             : {
    3955     5616218 :         const char *p = msg;
    3956     5616218 :         char quote;
    3957             : 
    3958             :         /* first skip over leading white space */
    3959     7108121 :         while (*p && isspace((unsigned char) *p))
    3960     1491903 :                 p++;
    3961     5616218 :         quote = *p;
    3962     5616218 :         if (quote == '\'' || quote == '"') {
    3963     4334080 :                 size_t len = 0;
    3964     4334080 :                 char *s, *start;
    3965             : 
    3966             :                 /* get quoted string and remove trailing bracket first */
    3967     4334080 :                 p++;
    3968             :                 /* first count how much space we need */
    3969     4334080 :                 msg = p;        /* save for later */
    3970   176774080 :                 while (*p && *p != quote) {
    3971   172440000 :                         if (*p == '\\') {
    3972       29473 :                                 p++;
    3973       29473 :                                 switch (*p) {
    3974           0 :                                 case '0':
    3975             :                                 case '1':
    3976             :                                 case '2':
    3977             :                                 case '3':
    3978             :                                         /* this could be the start of
    3979             :                                            an octal sequence, check it
    3980             :                                            out */
    3981           0 :                                         if (p[1] && p[2] &&
    3982           0 :                                             p[1] >= '0' && p[1] <= '7' &&
    3983           0 :                                             p[2] >= '0' && p[2] <= '7') {
    3984           0 :                                                 p += 2;
    3985           0 :                                                 break;
    3986             :                                         }
    3987             :                                         /* fall through */
    3988             :                                 default:
    3989             :                                         break;
    3990             :                                 }
    3991             :                         }
    3992   172440000 :                         p++;
    3993   172440000 :                         len++;
    3994             :                 }
    3995             :                 /* now allocate space and copy string into new space */
    3996     4334080 :                 p = msg;        /* start over */
    3997     4334080 :                 start = s = malloc(len + 1);
    3998   176774080 :                 while (*p && *p != quote) {
    3999   172440000 :                         if (*p == '\\') {
    4000       29473 :                                 p++;
    4001       29473 :                                 switch (*p) {
    4002             :                                 /* later
    4003             :                                    case '0': case '1': case '2': case '3': case '4':
    4004             :                                    case '5': case '6': case '7': case '8': case '9':
    4005             :                                 */
    4006        1724 :                                 case 'n':
    4007        1724 :                                         *s = '\n';
    4008        1724 :                                         break;
    4009          15 :                                 case 't':
    4010          15 :                                         *s = '\t';
    4011          15 :                                         break;
    4012           0 :                                 case 'r':
    4013           0 :                                         *s = '\r';
    4014           0 :                                         break;
    4015           0 :                                 case 'f':
    4016           0 :                                         *s = '\f';
    4017           0 :                                         break;
    4018           0 :                                 case '0':
    4019             :                                 case '1':
    4020             :                                 case '2':
    4021             :                                 case '3':
    4022             :                                         /* this could be the start of
    4023             :                                            an octal sequence, check it
    4024             :                                            out */
    4025           0 :                                         if (p[1] && p[2] &&
    4026           0 :                                             p[1] >= '0' && p[1] <= '7' &&
    4027           0 :                                             p[2] >= '0' && p[2] <= '7') {
    4028           0 :                                                 *s = ((p[0] - '0') << 6) | ((p[1] - '0') << 3) | (p[2] - '0');
    4029           0 :                                                 p += 2;
    4030           0 :                                                 break;
    4031             :                                         }
    4032             :                                         /* fall through */
    4033             :                                 default:
    4034       27734 :                                         *s = *p;
    4035       27734 :                                         break;
    4036             :                                 }
    4037       29473 :                                 p++;
    4038             :                         } else {
    4039   172410527 :                                 *s = *p++;
    4040             :                         }
    4041   172440000 :                         s++;
    4042             :                 }
    4043     4334080 :                 *s = 0;         /* close string */
    4044     4334080 :                 p++;            /* skip over end-of-string quote */
    4045             :                 /* skip over trailing junk (presumably white space) */
    4046     5667559 :                 while (*p && *p != ',' && *p != endchar)
    4047     1333479 :                         p++;
    4048     4334080 :                 if (next)
    4049     4334080 :                         *next = p;
    4050     4334080 :                 *str = start;
    4051     4334080 :                 if (lenp)
    4052     4334080 :                         *lenp = len;
    4053             : 
    4054     4334080 :                 return 1;
    4055             :         } else {
    4056             :                 const char *s;
    4057             :                 size_t len;
    4058             : 
    4059             :                 /* p points at first non-white space character */
    4060    57409235 :                 msg = p;        /* record start of value */
    4061             :                 /* find separator or terminator */
    4062    57409235 :                 while (*p && *p != ',' && *p != '\t' && *p != endchar)
    4063    56127097 :                         p++;
    4064             :                 /* search back over trailing white space */
    4065     1404484 :                 for (s = p - 1; s > msg && isspace((unsigned char) *s); s--)
    4066             :                         ;
    4067     1282138 :                 if (s < msg || !isspace((unsigned char) *s)) /* gone one too far */
    4068     1282138 :                         s++;
    4069     1282138 :                 if (*p == '\t') {
    4070       36096 :                         p++;
    4071             :                 }
    4072     1282138 :                 len = s - msg;
    4073     1282138 :                 *str = malloc(len + 1);
    4074     1282138 :                 strcpy_len(*str, msg, len + 1);
    4075             : 
    4076     1282138 :                 if (next)
    4077     1282138 :                         *next = p;
    4078     1282138 :                 if (lenp)
    4079     1282138 :                         *lenp = len;
    4080     1282138 :                 return 0;
    4081             :         }
    4082             : }
    4083             : 
    4084             : char *
    4085           0 : mapi_unquote(char *msg)
    4086             : {
    4087           0 :         char *start;
    4088             : 
    4089           0 :         unquote(msg, &start, NULL, ']', NULL);
    4090           0 :         return start;
    4091             : }
    4092             : 
    4093             : char *
    4094           0 : mapi_quote(const char *msg, int size)
    4095             : {
    4096             :         /* we absolutely don't need more than this (until we start
    4097             :            producing octal escapes */
    4098           0 :         char *s = malloc((size < 0 ? strlen(msg) : (size_t) size) * 2 + 1);
    4099           0 :         char *t = s;
    4100             : 
    4101             :         /* the condition is tricky: if initially size < 0, we must
    4102             :            continue until a NULL byte, else, size gives the number of
    4103             :            bytes to be copied */
    4104           0 :         while (size < 0 ? *msg : size > 0) {
    4105           0 :                 if (size > 0)
    4106           0 :                         size--;
    4107           0 :                 switch (*msg) {
    4108           0 :                 case '\n':
    4109           0 :                         *t++ = '\\';
    4110           0 :                         *t++ = 'n';
    4111           0 :                         break;
    4112           0 :                 case '\t':
    4113           0 :                         *t++ = '\\';
    4114           0 :                         *t++ = 't';
    4115           0 :                         break;
    4116           0 :                 case PLACEHOLDER:
    4117           0 :                         *t++ = '\\';
    4118           0 :                         *t++ = PLACEHOLDER;
    4119           0 :                         break;
    4120           0 :                 case '\\':
    4121           0 :                         *t++ = '\\';
    4122           0 :                         *t++ = '\\';
    4123           0 :                         break;
    4124           0 :                 case '\'':
    4125           0 :                         *t++ = '\\';
    4126           0 :                         *t++ = '\'';
    4127           0 :                         break;
    4128           0 :                 case '"':
    4129           0 :                         *t++ = '\\';
    4130           0 :                         *t++ = '"';
    4131           0 :                         break;
    4132           0 :                 case '\0':
    4133           0 :                         *t++ = '\\';
    4134           0 :                         *t++ = '0';
    4135           0 :                         break;
    4136           0 :                 default:
    4137           0 :                         *t++ = *msg;
    4138           0 :                         break;
    4139             :                 }
    4140           0 :                 msg++;
    4141             :                 /* also deal with binaries */
    4142             :         }
    4143           0 :         *t = 0;
    4144           0 :         return s;
    4145             : }
    4146             : 
    4147             : static int
    4148           0 : mapi_extend_bindings(MapiHdl hdl, int minbindings)
    4149             : {
    4150             :         /* extend the bindings table */
    4151           0 :         int nm = hdl->maxbindings + 32;
    4152             : 
    4153           0 :         if (nm <= minbindings)
    4154           0 :                 nm = minbindings + 32;
    4155           0 :         REALLOC(hdl->bindings, nm);
    4156           0 :         assert(hdl->bindings);
    4157             :         /* clear new entries */
    4158           0 :         memset(hdl->bindings + hdl->maxbindings, 0, (nm - hdl->maxbindings) * sizeof(*hdl->bindings));
    4159           0 :         hdl->maxbindings = nm;
    4160           0 :         return MOK;
    4161             : }
    4162             : 
    4163             : static int
    4164           0 : mapi_extend_params(MapiHdl hdl, int minparams)
    4165             : {
    4166             :         /* extend the params table */
    4167           0 :         int nm = hdl->maxparams + 32;
    4168             : 
    4169           0 :         if (nm <= minparams)
    4170           0 :                 nm = minparams + 32;
    4171           0 :         REALLOC(hdl->params, nm);
    4172           0 :         assert(hdl->params);
    4173             :         /* clear new entries */
    4174           0 :         memset(hdl->params + hdl->maxparams, 0, (nm - hdl->maxparams) * sizeof(*hdl->params));
    4175           0 :         hdl->maxparams = nm;
    4176           0 :         return MOK;
    4177             : }
    4178             : 
    4179             : static MapiMsg
    4180           0 : store_field(struct MapiResultSet *result, int cr, int fnr, int outtype, void *dst)
    4181             : {
    4182           0 :         char *val;
    4183             : 
    4184           0 :         val = result->cache.line[cr].anchors[fnr];
    4185             : 
    4186           0 :         if (val == 0) {
    4187           0 :                 return mapi_setError(result->hdl->mid, "Field value undefined or nil", __func__, MERROR);
    4188             :         }
    4189             : 
    4190             :         /* auto convert to C-type */
    4191           0 :         switch (outtype) {
    4192           0 :         case MAPI_TINY:
    4193           0 :                 *(signed char *) dst = (signed char) strtol(val, NULL, 0);
    4194           0 :                 break;
    4195           0 :         case MAPI_UTINY:
    4196           0 :                 *(unsigned char *) dst = (unsigned char) strtoul(val, NULL, 0);
    4197           0 :                 break;
    4198           0 :         case MAPI_SHORT:
    4199           0 :                 *(short *) dst = (short) strtol(val, NULL, 0);
    4200           0 :                 break;
    4201           0 :         case MAPI_USHORT:
    4202           0 :                 *(unsigned short *) dst = (unsigned short) strtoul(val, NULL, 0);
    4203           0 :                 break;
    4204           0 :         case MAPI_NUMERIC:
    4205             :         case MAPI_INT:
    4206           0 :                 *(int *) dst = (int) strtol(val, NULL, 0);
    4207           0 :                 break;
    4208           0 :         case MAPI_UINT:
    4209           0 :                 *(unsigned int *) dst = (unsigned int) strtoul(val, NULL, 0);
    4210           0 :                 break;
    4211           0 :         case MAPI_LONG:
    4212           0 :                 *(long *) dst = strtol(val, NULL, 0);
    4213           0 :                 break;
    4214           0 :         case MAPI_ULONG:
    4215           0 :                 *(unsigned long *) dst = strtoul(val, NULL, 0);
    4216           0 :                 break;
    4217           0 :         case MAPI_LONGLONG:
    4218           0 :                 *(int64_t *) dst = strtoll(val, NULL, 0);
    4219           0 :                 break;
    4220           0 :         case MAPI_ULONGLONG:
    4221           0 :                 *(uint64_t *) dst = strtoull(val, NULL, 0);
    4222           0 :                 break;
    4223           0 :         case MAPI_CHAR:
    4224           0 :                 *(char *) dst = *val;
    4225           0 :                 break;
    4226           0 :         case MAPI_FLOAT:
    4227           0 :                 *(float *) dst = strtof(val, NULL);
    4228           0 :                 break;
    4229           0 :         case MAPI_DOUBLE:
    4230           0 :                 *(double *) dst = strtod(val, NULL);
    4231           0 :                 break;
    4232           0 :         case MAPI_DATE:
    4233           0 :                 sscanf(val, "%hd-%hu-%hu",
    4234             :                        &((MapiDate *) dst)->year,
    4235             :                        &((MapiDate *) dst)->month,
    4236             :                        &((MapiDate *) dst)->day);
    4237           0 :                 break;
    4238           0 :         case MAPI_TIME:
    4239           0 :                 sscanf(val, "%hu:%hu:%hu",
    4240             :                        &((MapiTime *) dst)->hour,
    4241             :                        &((MapiTime *) dst)->minute,
    4242             :                        &((MapiTime *) dst)->second);
    4243           0 :                 break;
    4244           0 :         case MAPI_DATETIME:{
    4245           0 :                 int n;
    4246             : 
    4247           0 :                 ((MapiDateTime *) dst)->fraction = 0;
    4248           0 :                 sscanf(val, "%hd-%hu-%hu %hu:%hu:%hu%n",
    4249             :                        &((MapiDateTime *) dst)->year,
    4250             :                        &((MapiDateTime *) dst)->month,
    4251             :                        &((MapiDateTime *) dst)->day,
    4252             :                        &((MapiDateTime *) dst)->hour,
    4253             :                        &((MapiDateTime *) dst)->minute,
    4254             :                        &((MapiDateTime *) dst)->second,
    4255             :                        &n);
    4256           0 :                 if (val[n] == '.') {
    4257           0 :                         unsigned int fac = 1000000000;
    4258           0 :                         unsigned int nsec = 0;
    4259             : 
    4260           0 :                         for (n++; isdigit((unsigned char) val[n]); n++) {
    4261           0 :                                 fac /= 10;
    4262           0 :                                 nsec += (val[n] - '0') * fac;
    4263             :                         }
    4264           0 :                         ((MapiDateTime *) dst)->fraction = nsec;
    4265             :                 }
    4266           0 :                 break;
    4267             :         }
    4268           0 :         case MAPI_AUTO:
    4269             :         case MAPI_VARCHAR:
    4270             :         default:
    4271           0 :                 *(char **) dst = val;
    4272             :         }
    4273             :         return MOK;
    4274             : }
    4275             : 
    4276             : MapiMsg
    4277           0 : mapi_store_field(MapiHdl hdl, int fnr, int outtype, void *dst)
    4278             : {
    4279           0 :         struct MapiResultSet *result;
    4280             : 
    4281           0 :         mapi_hdl_check(hdl);
    4282             : 
    4283           0 :         if ((result = hdl->result) == NULL) {
    4284           0 :                 return mapi_setError(hdl->mid, "No data read", __func__, MERROR);
    4285             :         }
    4286             : 
    4287           0 :         if (fnr < 0 || fnr >= result->fieldcnt) {
    4288           0 :                 return mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4289             :         }
    4290             : 
    4291           0 :         return store_field(result, result->cache.reader, fnr, outtype, dst);
    4292             : }
    4293             : 
    4294             : static void
    4295           0 : mapi_store_bind(struct MapiResultSet *result, int cr)
    4296             : {
    4297           0 :         int i;
    4298           0 :         MapiHdl hdl = result->hdl;
    4299             : 
    4300           0 :         for (i = 0; i < hdl->maxbindings; i++)
    4301           0 :                 if (hdl->bindings[i].outparam)
    4302           0 :                         store_field(result, cr, i, hdl->bindings[i].outtype, hdl->bindings[i].outparam);
    4303           0 : }
    4304             : 
    4305             : /*
    4306             :  * The low level routine mapi_slice_row breaks the last row received
    4307             :  * into pieces and binds the field descriptors with their location. All
    4308             :  * escaped characters are immediately replaced, such that we end with a
    4309             :  * list of C-strings.  It overwrites the contents of the row buffer,
    4310             :  * because de-escaping only reduces the size.  It also silently extends
    4311             :  * the field descriptor table.
    4312             :  */
    4313             : static int
    4314     1369811 : mapi_slice_row(struct MapiResultSet *result, int cr)
    4315             : {
    4316     1369811 :         char *p;
    4317     1369811 :         int i = 0;
    4318             : 
    4319     1369811 :         p = result->cache.line[cr].rows;
    4320     1369811 :         if (p == NULL)
    4321           0 :                 return mapi_setError(result->hdl->mid, "Current row missing", __func__, MERROR);
    4322     1369811 :         if (result->cache.line[cr].fldcnt)
    4323             :                 return result->cache.line[cr].fldcnt;        /* already sliced */
    4324             : 
    4325     1369811 :         if (*p != '[') {
    4326             :                 /* nothing to slice */
    4327          49 :                 i = 1;
    4328          49 :                 REALLOC(result->cache.line[cr].anchors, 1);
    4329          49 :                 REALLOC(result->cache.line[cr].lens, 1);
    4330             :                 /* skip initial '=' if present */
    4331          49 :                 if (*p == '=')
    4332          49 :                         p++;
    4333          49 :                 result->cache.line[cr].anchors[0] = strdup(p);
    4334          49 :                 result->cache.line[cr].lens[0] = strlen(p);
    4335             :         } else {
    4336             :                 /* work on a copy to preserve the original */
    4337     1369762 :                 p = strdup(p);
    4338     1369968 :                 i = slice_row(p,
    4339     1369762 :                               msettings_lang_is_sql(result->hdl->mid->settings) ? "NULL" : "nil",
    4340             :                               &result->cache.line[cr].anchors,
    4341             :                               &result->cache.line[cr].lens,
    4342             :                               result->fieldcnt, ']');
    4343     1369762 :                 free(p);
    4344             :         }
    4345     1369811 :         if (i != result->fieldcnt) {
    4346             :                 int j;
    4347         195 :                 for (j = 0; j < result->fieldcnt; j++) {
    4348           0 :                         if (result->fields[j].columnname)
    4349           0 :                                 free(result->fields[j].columnname);
    4350           0 :                         result->fields[j].columnname = NULL;
    4351           0 :                         if (result->fields[j].columntype)
    4352           0 :                                 free(result->fields[j].columntype);
    4353           0 :                         result->fields[j].columntype = NULL;
    4354           0 :                         if (result->fields[j].tablename)
    4355           0 :                                 free(result->fields[j].tablename);
    4356           0 :                         result->fields[j].tablename = NULL;
    4357           0 :                         result->fields[j].columnlength = 0;
    4358             :                 }
    4359             :         }
    4360     1369811 :         if (i > result->fieldcnt) {
    4361         195 :                 result->fieldcnt = i;
    4362         195 :                 if (i > result->maxfields) {
    4363         195 :                         REALLOC(result->fields, i);
    4364         195 :                         memset(result->fields + result->maxfields, 0, (i - result->maxfields) * sizeof(*result->fields));
    4365         195 :                         result->maxfields = i;
    4366             :                 }
    4367             :         }
    4368     1369811 :         result->cache.line[cr].fldcnt = i;
    4369     1369811 :         return i;
    4370             : }
    4371             : 
    4372             : /*
    4373             :  * The rows presented are broken down into pieces to
    4374             :  * simplify access later on. However, mclient may
    4375             :  * first want to check the content of the line for
    4376             :  * useful information (e.g. #EOD)
    4377             :  */
    4378             : int
    4379       47035 : mapi_split_line(MapiHdl hdl)
    4380             : {
    4381       47035 :         int n;
    4382       47035 :         struct MapiResultSet *result;
    4383             : 
    4384       47035 :         result = hdl->result;
    4385       47035 :         assert(result != NULL);
    4386       47035 :         if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
    4387       47035 :                 n = mapi_slice_row(result, result->cache.reader);
    4388             :                 /* no need to call mapi_store_bind since
    4389             :                    mapi_fetch_line would have done that if needed */
    4390             :         }
    4391       47035 :         return n;
    4392             : }
    4393             : 
    4394             : int
    4395     1328174 : mapi_fetch_row(MapiHdl hdl)
    4396             : {
    4397     1328174 :         char *reply;
    4398     1328174 :         int n;
    4399     1328174 :         struct MapiResultSet *result;
    4400             : 
    4401     1328174 :         mapi_hdl_check(hdl);
    4402     1349835 :         do {
    4403     1349835 :                 if ((reply = mapi_fetch_line(hdl)) == NULL)
    4404             :                         return 0;
    4405     1344437 :         } while (*reply != '[' && *reply != '=');
    4406     1322776 :         result = hdl->result;
    4407     1322776 :         assert(result != NULL);
    4408     1322776 :         if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
    4409     1322776 :                 n = mapi_slice_row(result, result->cache.reader);
    4410             :                 /* no need to call mapi_store_bind since
    4411             :                    mapi_fetch_line would have done that if needed */
    4412             :         }
    4413             :         return n;
    4414             : }
    4415             : 
    4416             : /*
    4417             :  * All rows can be cached first as well.
    4418             :  */
    4419             : int64_t
    4420           1 : mapi_fetch_all_rows(MapiHdl hdl)
    4421             : {
    4422           1 :         Mapi mid;
    4423           1 :         struct MapiResultSet *result;
    4424             : 
    4425           1 :         mapi_hdl_check(hdl);
    4426             : 
    4427           1 :         mid = hdl->mid;
    4428           3 :         for (;;) {
    4429           4 :                 if ((result = hdl->result) != NULL &&
    4430           2 :                     msettings_lang_is_sql(mid->settings) &&
    4431           2 :                     mid->active == NULL &&
    4432           1 :                     result->row_count > 0 &&
    4433           1 :                     result->cache.first + result->cache.tuplecount < result->row_count) {
    4434           0 :                         mid->active = hdl;
    4435           0 :                         hdl->active = result;
    4436           0 :                         mapi_log_record(mid, "SEND", "X" "export %d %" PRId64 "\n",
    4437             :                                              result->tableid, result->cache.first + result->cache.tuplecount);
    4438           0 :                         int e;
    4439           0 :                         if ((e = mnstr_printf(mid->to, "X" "export %d %" PRId64 "\n",
    4440           0 :                                               result->tableid, result->cache.first + result->cache.tuplecount)) < 0 ||
    4441           0 :                             (e = mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) < 0)
    4442           0 :                                 check_stream(mid, mid->to, e, "sending export command", 0);
    4443             :                 }
    4444           2 :                 if (mid->active)
    4445           1 :                         read_into_cache(mid->active, 0);
    4446             :                 else
    4447             :                         break;
    4448             :         }
    4449           1 :         return result ? result->cache.tuplecount : 0;
    4450             : }
    4451             : 
    4452             : char *
    4453     5304070 : mapi_fetch_field(MapiHdl hdl, int fnr)
    4454             : {
    4455     5304070 :         int cr;
    4456     5304070 :         struct MapiResultSet *result;
    4457             : 
    4458     5304070 :         mapi_hdl_check0(hdl);
    4459             : 
    4460     5304070 :         if ((result = hdl->result) == NULL ||
    4461     5304070 :             (cr = result->cache.reader) < 0 ||
    4462     5304070 :             (result->cache.line[cr].rows[0] != '[' &&
    4463             :              result->cache.line[cr].rows[0] != '=')) {
    4464           0 :                 mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
    4465           0 :                 return 0;
    4466             :         }
    4467     5304070 :         assert(result->cache.line != NULL);
    4468     5304070 :         if (fnr >= 0) {
    4469             :                 /* slice if needed */
    4470     5304070 :                 if (result->cache.line[cr].fldcnt == 0)
    4471           0 :                         mapi_slice_row(result, cr);
    4472     5304070 :                 if (fnr < result->cache.line[cr].fldcnt)
    4473     5304069 :                         return result->cache.line[cr].anchors[fnr];
    4474             :         }
    4475           1 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4476           1 :         return 0;
    4477             : }
    4478             : 
    4479             : size_t
    4480     1335619 : mapi_fetch_field_len(MapiHdl hdl, int fnr)
    4481             : {
    4482     1335619 :         int cr;
    4483     1335619 :         struct MapiResultSet *result;
    4484             : 
    4485     1335619 :         mapi_hdl_check0(hdl);
    4486             : 
    4487     1335619 :         if ((result = hdl->result) == NULL ||
    4488     1335619 :             (cr = result->cache.reader) < 0 ||
    4489     1335619 :             (result->cache.line[cr].rows[0] != '[' &&
    4490             :              result->cache.line[cr].rows[0] != '=')) {
    4491           0 :                 mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
    4492           0 :                 return 0;
    4493             :         }
    4494     1335619 :         assert(result->cache.line != NULL);
    4495     1335619 :         if (fnr >= 0) {
    4496             :                 /* slice if needed */
    4497     1335619 :                 if (result->cache.line[cr].fldcnt == 0)
    4498           0 :                         mapi_slice_row(result, cr);
    4499     1335619 :                 if (fnr < result->cache.line[cr].fldcnt)
    4500     1335619 :                         return result->cache.line[cr].lens[fnr];
    4501             :         }
    4502           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4503           0 :         return 0;
    4504             : }
    4505             : 
    4506             : int
    4507        2429 : mapi_get_field_count(MapiHdl hdl)
    4508             : {
    4509        2429 :         mapi_hdl_check(hdl);
    4510        2429 :         if (hdl->result && hdl->result->fieldcnt == 0) {
    4511             :                 /* no rows have been sliced yet, and there was no
    4512             :                    header, so try to figure out how many columns there
    4513             :                    are for ourselves */
    4514             :                 int i;
    4515             : 
    4516        2063 :                 for (i = 0; i < hdl->result->cache.writer; i++)
    4517           0 :                         if (hdl->result->cache.line[i].rows[0] == '[' ||
    4518             :                             hdl->result->cache.line[i].rows[0] == '=')
    4519           0 :                                 mapi_slice_row(hdl->result, i);
    4520             :         }
    4521        2429 :         return hdl->result ? hdl->result->fieldcnt : 0;
    4522             : }
    4523             : 
    4524             : int64_t
    4525         482 : mapi_get_row_count(MapiHdl hdl)
    4526             : {
    4527         482 :         mapi_hdl_check(hdl);
    4528         482 :         return hdl->result ? hdl->result->row_count : 0;
    4529             : }
    4530             : 
    4531             : int64_t
    4532           0 : mapi_get_last_id(MapiHdl hdl)
    4533             : {
    4534           0 :         mapi_hdl_check(hdl);
    4535           0 :         return hdl->result ? hdl->result->last_id : -1;
    4536             : }
    4537             : 
    4538             : char *
    4539        1066 : mapi_get_name(MapiHdl hdl, int fnr)
    4540             : {
    4541        1066 :         struct MapiResultSet *result;
    4542             : 
    4543        1066 :         mapi_hdl_check0(hdl);
    4544        1066 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4545        1066 :                 return result->fields[fnr].columnname;
    4546           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4547           0 :         return 0;
    4548             : }
    4549             : 
    4550             : char *
    4551     5266909 : mapi_get_type(MapiHdl hdl, int fnr)
    4552             : {
    4553     5266909 :         struct MapiResultSet *result;
    4554             : 
    4555     5266909 :         mapi_hdl_check0(hdl);
    4556     5266909 :         if ((result = hdl->result) != 0 &&
    4557     5266909 :             fnr >= 0 && fnr < result->fieldcnt) {
    4558     5266909 :                 if (result->fields[fnr].columntype == NULL)
    4559             :                         return "unknown";
    4560     5266908 :                 return result->fields[fnr].columntype;
    4561             :         }
    4562           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4563           0 :         return 0;
    4564             : }
    4565             : 
    4566             : char *
    4567         925 : mapi_get_table(MapiHdl hdl, int fnr)
    4568             : {
    4569         925 :         struct MapiResultSet *result;
    4570             : 
    4571         925 :         mapi_hdl_check0(hdl);
    4572         925 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4573         925 :                 return result->fields[fnr].tablename;
    4574           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4575           0 :         return 0;
    4576             : }
    4577             : 
    4578             : int
    4579         965 : mapi_get_len(MapiHdl hdl, int fnr)
    4580             : {
    4581         965 :         struct MapiResultSet *result;
    4582             : 
    4583         965 :         mapi_hdl_check0(hdl);
    4584         965 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4585         965 :                 return result->fields[fnr].columnlength;
    4586           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4587           0 :         return 0;
    4588             : }
    4589             : 
    4590             : int
    4591         928 : mapi_get_digits(MapiHdl hdl, int fnr)
    4592             : {
    4593         928 :         struct MapiResultSet *result;
    4594             : 
    4595         928 :         mapi_hdl_check0(hdl);
    4596         928 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4597         928 :                 return result->fields[fnr].digits;
    4598           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4599           0 :         return 0;
    4600             : }
    4601             : 
    4602             : int
    4603           4 : mapi_get_scale(MapiHdl hdl, int fnr)
    4604             : {
    4605           4 :         struct MapiResultSet *result;
    4606             : 
    4607           4 :         mapi_hdl_check0(hdl);
    4608           4 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4609           4 :                 return result->fields[fnr].scale;
    4610           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4611           0 :         return 0;
    4612             : }
    4613             : 
    4614             : char *
    4615        1056 : mapi_get_query(MapiHdl hdl)
    4616             : {
    4617        1056 :         mapi_hdl_check0(hdl);
    4618        1056 :         if (hdl->query != NULL) {
    4619        1056 :                 return strdup(hdl->query);
    4620             :         } else {
    4621             :                 return NULL;
    4622             :         }
    4623             : }
    4624             : 
    4625             : 
    4626             : int
    4627       13161 : mapi_get_querytype(MapiHdl hdl)
    4628             : {
    4629       13161 :         struct MapiResultSet *result;
    4630             : 
    4631       13161 :         mapi_hdl_check0(hdl);
    4632       13161 :         if ((result = hdl->result) != 0)
    4633       11827 :                 return result->querytype;
    4634        1334 :         mapi_setError(hdl->mid, "No query result", __func__, MERROR);
    4635        1334 :         return 0; /* Q_PARSE! */
    4636             : }
    4637             : 
    4638             : int
    4639         123 : mapi_get_tableid(MapiHdl hdl)
    4640             : {
    4641         123 :         struct MapiResultSet *result;
    4642             : 
    4643         123 :         mapi_hdl_check0(hdl);
    4644         123 :         if ((result = hdl->result) != 0)
    4645         123 :                 return result->tableid;
    4646           0 :         mapi_setError(hdl->mid, "No query result", __func__, MERROR);
    4647           0 :         return 0;
    4648             : }
    4649             : 
    4650             : int64_t
    4651        2987 : mapi_rows_affected(MapiHdl hdl)
    4652             : {
    4653        2987 :         struct MapiResultSet *result;
    4654             : 
    4655        2987 :         mapi_hdl_check(hdl);
    4656        2987 :         if ((result = hdl->result) == NULL)
    4657             :                 return 0;
    4658        2987 :         return result->row_count;
    4659             : }
    4660             : 
    4661             : int64_t
    4662        5538 : mapi_get_querytime(MapiHdl hdl)
    4663             : {
    4664        5538 :         struct MapiResultSet *result;
    4665             : 
    4666        5538 :         mapi_hdl_check(hdl);
    4667        5538 :         if ((result = hdl->result) == NULL)
    4668             :                 return 0;
    4669        4871 :         return result->querytime;
    4670             : }
    4671             : 
    4672             : int64_t
    4673        5538 : mapi_get_maloptimizertime(MapiHdl hdl)
    4674             : {
    4675        5538 :         struct MapiResultSet *result;
    4676             : 
    4677        5538 :         mapi_hdl_check(hdl);
    4678        5538 :         if ((result = hdl->result) == NULL)
    4679             :                 return 0;
    4680        4871 :         return result->maloptimizertime;
    4681             : }
    4682             : 
    4683             : int64_t
    4684        5538 : mapi_get_sqloptimizertime(MapiHdl hdl)
    4685             : {
    4686        5538 :         struct MapiResultSet *result;
    4687             : 
    4688        5538 :         mapi_hdl_check(hdl);
    4689        5538 :         if ((result = hdl->result) == NULL)
    4690             :                 return 0;
    4691        4871 :         return result->sqloptimizertime;
    4692             : }
    4693             : 
    4694             : const char *
    4695         197 : mapi_get_dbname(Mapi mid)
    4696             : {
    4697         197 :         return msetting_string(mid->settings, MP_DATABASE);
    4698             : }
    4699             : 
    4700             : const char *
    4701           6 : mapi_get_host(Mapi mid)
    4702             : {
    4703           6 :         return msetting_string(mid->settings, MP_HOST);
    4704             : }
    4705             : 
    4706             : const char *
    4707           6 : mapi_get_user(Mapi mid)
    4708             : {
    4709           6 :         return msetting_string(mid->settings, MP_USER);;
    4710             : }
    4711             : 
    4712             : const char *
    4713           0 : mapi_get_lang(Mapi mid)
    4714             : {
    4715           0 :         return msetting_string(mid->settings, MP_LANGUAGE);;
    4716             : }
    4717             : 
    4718             : const char *
    4719           0 : mapi_get_uri(Mapi mid)
    4720             : {
    4721           0 :         return mid->uri;
    4722             : }
    4723             : 
    4724             : const char *
    4725           1 : mapi_get_mapi_version(void)
    4726             : {
    4727           1 :         return MAPI_VERSION;
    4728             : }
    4729             : 
    4730             : const char *
    4731           0 : mapi_get_monet_version(Mapi mid)
    4732             : {
    4733           0 :         mapi_check0(mid);
    4734           0 :         return mid->server ? mid->server : "";
    4735             : }
    4736             : 
    4737             : const char *
    4738           0 : mapi_get_motd(Mapi mid)
    4739             : {
    4740           0 :         mapi_check0(mid);
    4741           0 :         return mid->motd;
    4742             : }
    4743             : 
    4744             : bool
    4745           0 : mapi_is_connected(Mapi mid)
    4746             : {
    4747           0 :         return mid->connected;
    4748             : }
    4749             : 
    4750             : MapiHdl
    4751         171 : mapi_get_active(Mapi mid)
    4752             : {
    4753         171 :         return mid->active;
    4754             : }
    4755             : 
    4756             : msettings*
    4757           0 : mapi_get_settings(Mapi mid)
    4758             : {
    4759           0 :         return mid->settings;
    4760             : }
    4761             : 
    4762             : 
    4763             : MapiMsg
    4764        1426 : mapi_wrap_streams(Mapi mid, stream *rstream, stream *wstream)
    4765             : {
    4766             :         // do not use check_stream here yet because the socket is not yet in 'mid'
    4767        1426 :         const char *error_message;
    4768        1426 :         stream *error_stream;
    4769             : 
    4770        1426 :         assert(!isa_block_stream(rstream));
    4771        1426 :         assert(!isa_block_stream(wstream));
    4772             : 
    4773             :         // First send some NUL bytes. If we're accidentally connecting to the wrong
    4774             :         // port or protocol this may cause the remote server to close the
    4775             :         // connection. If we don't do this, the connection will often hang
    4776             :         // because the server expects us to speak first and we expect the server
    4777             :         // to speak first.
    4778             :         //
    4779             :         // Note that a pair of NUL bytes is a no-op message in MAPI.
    4780             :         //
    4781             :         // Surprisingly, it seems sending these NUL bytes makes non-TLS
    4782             :         // connection setup a little faster rather than slower!
    4783             :         static const char zeroes[8] = { 0 };
    4784             :         ssize_t to_write = sizeof(zeroes);
    4785        2852 :         while (to_write > 0) {
    4786        1426 :                 ssize_t n = mnstr_write(wstream, zeroes, 1, to_write);
    4787        1426 :                 if (n < 0) {
    4788           0 :                         close_connection(mid);
    4789           0 :                         return mapi_printError(mid, __func__, MERROR, "could not send leader block: %s", mnstr_peek_error(wstream));
    4790             :                 }
    4791        1426 :                 to_write -= (size_t)n;
    4792             :         }
    4793        1426 :         if (mnstr_flush(wstream, MNSTR_FLUSH_DATA) != 0) {
    4794           0 :                 close_connection(mid);
    4795           0 :                 return mapi_printError(mid, __func__, MERROR, "could not flush leader block: %s", mnstr_peek_error(wstream));
    4796             :         }
    4797             : 
    4798             : 
    4799        1426 :         stream *brstream = NULL;
    4800        1426 :         stream *bwstream = NULL;
    4801             : 
    4802        1426 :         bwstream = block_stream(wstream);
    4803        1426 :         if (bwstream == NULL || mnstr_errnr(bwstream) != MNSTR_NO__ERROR) {
    4804           0 :                 error_stream = bwstream;
    4805           0 :                 error_message = "block_stream wstream";
    4806           0 :                 goto bailout;
    4807             :         }
    4808        1426 :         brstream = block_stream(rstream);
    4809        1426 :         if (brstream == NULL || mnstr_errnr(brstream) != MNSTR_NO__ERROR) {
    4810           0 :                 error_stream = brstream;
    4811           0 :                 error_message = "block_stream rstream";
    4812           0 :                 goto bailout;
    4813             :         }
    4814             : 
    4815        1426 :         mid->to = bwstream;
    4816        1426 :         mid->from = brstream;
    4817        1426 :         return MOK;
    4818           0 : bailout:
    4819             :         // adapted from the check_stream macro
    4820           0 :         if (brstream)
    4821           0 :                 mnstr_destroy(brstream);
    4822           0 :         if (bwstream)
    4823           0 :                 mnstr_destroy(bwstream);
    4824             :         // malloc failure is the only way these calls could have failed
    4825           0 :         return mapi_printError(mid, __func__, MERROR, "%s: %s", error_message, mnstr_peek_error(error_stream));
    4826             : }

Generated by: LCOV version 1.14