/* MariaDB Connector/ODBC: after a failed parameter-array execute, a later
   single-row execute of the same SQL text (new statement handle) crashes */
#include <stdio.h>
#include <string.h>
#include <sql.h>
#include <sqlext.h>

#define INSERT "INSERT INTO odbc_ps_crash (id, name) VALUES (?, ?)"

static void check(SQLSMALLINT type, SQLHANDLE h, const char *what, SQLRETURN rc) {
    SQLCHAR state[6], msg[512]; SQLINTEGER native; SQLSMALLINT len;
    printf("%s: rc=%d", what, rc);
    if (rc != SQL_SUCCESS && SQLGetDiagRec(type, h, 1, state, &native, msg, sizeof msg, &len) == SQL_SUCCESS)
        printf(" [%s] %s", state, msg);
    printf("\n");
    fflush(stdout);
}

int main(int argc, char **argv) {
    SQLHENV env; SQLHDBC dbc; SQLHSTMT stmt; SQLRETURN rc;

    if (argc < 4) { fprintf(stderr, "usage: %s DSN user password\n", argv[0]); return 2; }
    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
    rc = SQLConnect(dbc, (SQLCHAR*)argv[1], SQL_NTS, (SQLCHAR*)argv[2], SQL_NTS, (SQLCHAR*)argv[3], SQL_NTS);
    check(SQL_HANDLE_DBC, dbc, "SQLConnect", rc);

    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    SQLExecDirect(stmt, (SQLCHAR*)"DROP TABLE IF EXISTS odbc_ps_crash", SQL_NTS);
    rc = SQLExecDirect(stmt, (SQLCHAR*)"CREATE TABLE odbc_ps_crash (id INT PRIMARY KEY, name VARCHAR(20))", SQL_NTS);
    check(SQL_HANDLE_STMT, stmt, "CREATE TABLE", rc);
    rc = SQLExecDirect(stmt, (SQLCHAR*)"INSERT INTO odbc_ps_crash VALUES (1, 'x')", SQL_NTS);
    check(SQL_HANDLE_STMT, stmt, "INSERT id 1", rc);
    SQLFreeHandle(SQL_HANDLE_STMT, stmt);

    /* 1. parameter array of 3 rows; row 2 has the duplicate key 1 -> error */
    {
        SQLINTEGER ids[3] = { 2, 1, 3 };
        char names[3][8] = { "a", "b", "c" };
        SQLLEN idInd[3] = { 0, 0, 0 }, nameInd[3] = { 1, 1, 1 };

        SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
        rc = SQLPrepare(stmt, (SQLCHAR*)INSERT, SQL_NTS);
        check(SQL_HANDLE_STMT, stmt, "SQLPrepare (array)", rc);
        SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)SQL_PARAM_BIND_BY_COLUMN, 0);
        SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)3, 0);
        SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, ids, 0, idInd);
        SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 8, 0, names, 8, nameInd);
        rc = SQLExecute(stmt);
        check(SQL_HANDLE_STMT, stmt, "SQLExecute (array, duplicate key expected)", rc);
        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
    }

    /* 2. new statement handle, same SQL text, single row -> SIGSEGV */
    {
        SQLINTEGER id = 10;
        char name[20] = "single";
        SQLLEN idInd = 0, nameInd = 6;

        SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
        rc = SQLPrepare(stmt, (SQLCHAR*)INSERT, SQL_NTS);
        check(SQL_HANDLE_STMT, stmt, "SQLPrepare (single row)", rc);
        SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &id, 0, &idInd);
        SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 20, 0, name, sizeof name, &nameInd);
        rc = SQLExecute(stmt);
        check(SQL_HANDLE_STMT, stmt, "SQLExecute (single row)", rc);
        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
    }
    SQLDisconnect(dbc);
    printf("done\n");
    return 0;
}
