/*
vim:ts=4:sw=4:et:nowrap:filetype=c
** ODBC C template/demo
** Makes use of ODBC 3 functions, see
**  http://msdn.microsoft.com/en-us/library/ms716309(VS.85).aspx )
** and so NEED to request ODBC v3 at init time
**
**  Windows MVSC (using what ever is in the path, 32 or 64 bit):
**
**      cl odbc_grant.c odbc32.lib
**
**  Windows gcc (MingW32):
**
**      gcc -o odbc_grant odbc_grant.c -lodbc32
**
**  Linux/Unix gcc (assuming UnixODBC in default system location):
**
**      gcc -o odbc_grant odbc_grant.c -lodbc
**
**  Linux/Unix with Ingres CLI
**
**      sh odbccc.sh odbc_grant.c
**
*/

#ifdef _WIN32
    /* Windows check - NOTE not tested under Windows 64 bit compiler (has been tested under win64 with 32 bit compiler) */
    #include <windows.h>
#endif /* _WIN32 */

#include        <stdio.h>
#include        <stdlib.h>
#include        <sql.h>
#include        <sqlext.h>
#define TRUE 1
#define FALSE 0

void dumb_unicode_size_check()
{
   printf("wchar_t is %d bytes\n", sizeof(wchar_t));
   if (sizeof(wchar_t) != 2)
   {
       printf("native wchar_t is not UCS2/UTF16\n");
   }
   
   printf("SQLWCHAR is %d bytes\n", sizeof(SQLWCHAR));
   if (sizeof(SQLWCHAR) != 2)
   {
       printf("ODBC SQLWCHAR is not UCS2/UTF16\n");
   }
   
   if (sizeof(SQLWCHAR) != sizeof(wchar_t))
   {
       printf("sizeof(SQLWCHAR) != sizeof(wchar_t)\n");
   }
}

/*
**  Read and display all errors, optionally exit
*/
void ProcessErrors(SQLHANDLE hHandle, SQLSMALLINT hType, RETCODE RetCode, int exit_once_done) 
{
    SQLSMALLINT     iRec = 0;
    SQLINTEGER      iError;
    SQLCHAR         szMessage[1000];
    SQLCHAR         szState[SQL_SQLSTATE_SIZE];
    SQLRETURN       rc;

    fprintf(stderr, "Error number is %d\n", RetCode);
    if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO &&
         rc != SQL_NO_DATA )

    fprintf(stderr, "\tSQL_SUCCESS=%d\n", SQL_SUCCESS);
    fprintf(stderr, "\tSQL_SUCCESS_WITH_INFO=%d\n", SQL_SUCCESS_WITH_INFO);
    fprintf(stderr, "\tSQL_NO_DATA=%d\n", SQL_NO_DATA);
    if (RetCode == SQL_INVALID_HANDLE) {
            fprintf(stderr, "Invalid handle!\n");
            goto HandleErrorExit;
    }
    fprintf(stderr, "HandleError, pre-while\n");
    do
    {
        rc = SQLGetDiagRec(  hType,
                        hHandle,
                        ++iRec,
                        (SQLCHAR *) szState,
                        &iError,
                        (SQLCHAR *) szMessage,
                        (SQLSMALLINT) (sizeof(szMessage) / sizeof(SQLCHAR)),
                        (SQLSMALLINT *) NULL);
        if (SQL_SUCCEEDED(rc))
            fprintf(stderr, "[%5.5s] %s (%d)\n", szState, szMessage, iError);
    } while( rc == SQL_SUCCESS );
HandleErrorExit:
    if (exit_once_done)
        exit(-1);
}

#define HandleError(hHandle, hType, RetCode) ProcessErrors(hHandle, hType, RetCode, 1)

int main(int argc, char **argv)
{
    SQLHENV         henv;
    SQLHDBC         hdbc;
    SQLHSTMT        hstmt;
    SQLRETURN       nstatus;

    SQLCHAR * connectionString=NULL;

    SQLLEN          StrLen_or_Ind=SQL_NTS;
    SQLCHAR         col1[32+1] = "";

    #define     myMAX_CURSOR_NAME_LEN   30
    SQLCHAR     cursor_name[myMAX_CURSOR_NAME_LEN];
    SQLSMALLINT     cursor_name_len=0;

    //dumb_unicode_size_check();

    if (argc != 2)
    {
        printf("Usage: %s connectString\n", argv[0]);
        printf("\n");
        printf("Where connectString can be just a DSN or a connect string.\n");
        printf("Example:\n");
        printf("    dsn=dsnname\n");
        printf("    dsn=dsnname;Uid=username;Pwd=password\n");
        printf("    \"Driver={MariaDB ODBC 3.0 Driver};database=mydb_latin1;user=USER;password=PASS\"\n");
        printf("See http://www.connectionstrings.com/\n");

        /*
        example using Microsoft SQL server dsn:
            dsn=mydsnname;User Id=myuser;Password=mypass;Trusted_Connection=False
        */
        return(0); 
    }
    connectionString=(SQLCHAR *) argv[1];


//#define use_odbc_v3

    printf("SQLAllocHandle() SQL_HANDLE_ENV\n"); fflush(stdout);
    nstatus = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);

    if (nstatus != SQL_SUCCESS) HandleError(SQL_NULL_HDBC, SQL_HANDLE_ENV, nstatus);
#ifdef use_odbc_v3
    nstatus = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);
#else
    nstatus = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC2, 0);
#endif // !use_odbc_v3
    if (nstatus != SQL_SUCCESS)
    {
        // TODO improve error check here
        printf("SQLSetEnvAttr failed with %d\n", nstatus);
        // call HandleError with henv?
        exit(-1);
    }

    printf("SQLAllocHandle() SQL_HANDLE_DBC\n");
    nstatus = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    if (nstatus != SQL_SUCCESS) HandleError(hdbc, SQL_HANDLE_DBC, nstatus);

    printf("connecting to: ");
    printf("%s\n", connectionString);
    fflush(stdout);
    nstatus = SQLDriverConnect(hdbc, NULL,
                               connectionString,
                                SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT); // NOTE no prompt and no out buffer..... not sure if this is allowed, seems to work
    if (nstatus != SQL_SUCCESS && nstatus != SQL_SUCCESS_WITH_INFO) {
            HandleError(hdbc, SQL_HANDLE_DBC, nstatus);
            return(0);
    }
    
    /* Simple SELECT SQL example (could use ODBC metadata API SQLTables() but this is to demo SELECT) */
    printf("SQLAllocHandle() SQL_HANDLE_STMT\n"); fflush(stdout);
    nstatus = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    if (nstatus != SQL_SUCCESS) HandleError(hdbc, SQL_HANDLE_DBC, nstatus);

    printf("SQLExecDirect()\n"); fflush(stdout);
    nstatus = SQLExecDirect(hstmt, "grant all PRIVILEGES  on iidbconstants to public", 48);
    if (nstatus != SQL_SUCCESS) HandleError(hstmt, SQL_HANDLE_STMT, nstatus);

    nstatus = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
    
    nstatus = SQLDisconnect(hdbc);
    nstatus = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    nstatus = SQLFreeHandle(SQL_HANDLE_ENV, henv);
    
    return(0);
}
