LCOV - code coverage report
Current view: top level - clients/mapilib - mapi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1342 2213 60.6 %
Date: 2025-03-25 21:27:32 Functions: 94 139 67.6 %

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

Generated by: LCOV version 1.14