blob: 87d6875ddd88a090d851c5923ef57ecd29c47341 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Tests.h"
#include <vector>
#include "ColorPrint.h"
#include "fstream"
// ----- whether to ask user for DSN and connection parameters OR specify -----
//#define _CONNECT_WITH_PROMPT 1
// ------------- maximum length to be displayed for a column ------------------
#define _DISPLAY_MAX 128
// -------------------- macro to handle error situations ----------------------
#define ODBC_CHK_ERROR(hType,hValue,iStatus,szMsg) \
{\
if ( status != SQL_SUCCESS ) {\
ShowDiagMessages ( hType, hValue, iStatus, szMsg );\
}\
if ( status == SQL_ERROR ) {\
goto Cleanup;\
}\
}
// ---------------------------------- structure -------------------------------
typedef struct BindColInfo
{
SQLSMALLINT iColTitleSize; // size of column title
wchar_t* szColTitle; // column title
SQLLEN iColDisplaySize; // size to display
void* szColData; // display buffer
int iType;
bool isSigned;
SQLLEN indPtr; // size or null indicator
BOOL fChar; // character col flag
struct BindColInfo* next; // linked list
} BIND_COL_INFO;
// -------------------------- function prototypes -----------------------------
void ShowDiagMessages ( SQLSMALLINT hType, SQLHANDLE hValue, SQLRETURN iStatus, char* szMsg );
SQLRETURN CheckResults ( HSTMT hStmt, wchar_t* sql );
void FreeBindings ( BIND_COL_INFO* pBindColInfo );
int totalCount;
int successCount;
int failCount;
std::vector <wstring> failedQueries;
void validateOneQuery ( wchar_t* sql )
{
Sleep ( 1000 );
SQLRETURN status;
SQLHANDLE hEnv = 0;
SQLHANDLE hConn = 0;
SQLHANDLE hStmt = 0;
wchar_t szConnStrOut[1024];
SQLSMALLINT x;
// show query to be executed
wprintf ( L"The query being validated: %ls \n", sql );
// BEFORE U CONNECT
// allocate ENVIRONMENT
status = SQLAllocHandle ( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_ENV, hEnv, status, "" );
// set the ODBC version for behaviour expected
status = SQLSetEnvAttr ( hEnv, SQL_ATTR_ODBC_VERSION, ( SQLPOINTER ) SQL_OV_ODBC3, 0 );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_ENV, hEnv, status, "" );
// allocate CONNECTION
status = SQLAllocHandle ( SQL_HANDLE_DBC, hEnv, &hConn );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_ENV, hEnv, status, "" );
#ifdef _WIN64
// ----------- real connection takes place at this point
// ----------- option 1: user is prompted for DSN & options
status = SQLDriverConnect ( hConn, GetDesktopWindow(),
( unsigned char* ) "",
SQL_NTS, szConnStrOut, 1024, &x,
SQL_DRIVER_COMPLETE );
#else
status = SQLDriverConnectW ( hConn, GetDesktopWindow (),
//L"DSN=testDSN;",
L"DRIVER={KylinODBCDriver};PROJECT=default;UID=ADMIN;SERVER=http://localhost;PORT=80;",
SQL_NTS, szConnStrOut, 1024, &x,
SQL_DRIVER_COMPLETE );
#endif
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_DBC, hConn, status, "" );
// CONGRATUALTIONS ---- u r connected to a DBMS via an ODBC driver
// allocate STATEMENT
status = SQLAllocHandle ( SQL_HANDLE_STMT, hConn, &hStmt );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_DBC, hConn, status, "" );
// execute the statement
//status = SQLExecDirect ( hStmt, ( unsigned char* )argv[1], SQL_NTS );
status = SQLExecDirectW ( hStmt, ( SQLWCHAR* ) sql, SQL_NTS );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_STMT, hConn, status, "" );
// RESULTS READY
// show the full results row by row
status = CheckResults ( hStmt, sql );
totalCount++;
if ( status == SQL_ERROR )
{
setPrintColorRED ();
fputs ( "[FAIL]\n", stdout );
resetPrintColor ();
failCount++;
failedQueries . push_back ( sql );
}
else if ( status == SQL_SUCCESS )
{
setPrintColorGreen ();
fputs ( "[SUCCESS]\n", stdout );
resetPrintColor ();
successCount++;
}
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_STMT, hStmt, status, "" );
Cleanup:
if ( hStmt )
{
SQLFreeHandle ( SQL_HANDLE_STMT, hStmt );
}
if ( hConn )
{
SQLFreeHandle ( SQL_HANDLE_DBC, hConn );
}
if ( hEnv )
{
SQLFreeHandle ( SQL_HANDLE_ENV, hEnv );
}
return;
}
void validateQueries ( char* file )
{
std::string line;
std::ifstream infile ( file );
while ( std::getline ( infile, line ) )
{
if ( line . size () < 5 )
{
continue;
}
unique_ptr <wchar_t[]> p ( char2wchar ( line . c_str () ) );
validateOneQuery ( p . get () );
}
infile . close ();
}
bool isValueConsistent ( void* data, wstring& valueJ, int pSrcDataType, bool isSigned )
{
fwprintf ( stdout, L"The value from the JDBC is : %s \n", valueJ . c_str () );
switch ( pSrcDataType )
{
case SQL_BIT :
{
wstring tempW;
if ( * ( char* ) data == 0 )
{
tempW = L"false";
}
else if ( * ( char* ) data == 1 )
{
tempW = L"true";
}
else
{
return false;
}
return tempW . compare ( valueJ ) == 0;
}
case SQL_CHAR :
case SQL_VARCHAR :
{
string temp ( ( char* ) data );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
case SQL_WCHAR :
case SQL_WVARCHAR :
{
wstring tempW ( ( wchar_t* ) data );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
case SQL_DECIMAL :
{
string temp ( ( char* ) data );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
case SQL_TINYINT :
{
int v = 0;
if ( isSigned )
{
v = * ( char* ) data;
}
else
{
v = * ( unsigned char* ) data;
}
char buffer[100];
_itoa ( v, buffer, 10 );
string temp ( buffer );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
case SQL_SMALLINT :
{
int v = 0;
if ( isSigned )
{
v = * ( short* ) data;
}
else
{
v = * ( unsigned short* ) data;
}
char buffer[100];
_itoa ( v, buffer, 10 );
string temp ( buffer );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
case SQL_INTEGER :
{
__int64 v = 0;
if ( isSigned )
{
v = * ( int* ) data;
}
else
{
v = * ( unsigned int* ) data;
}
char buffer[100];
_i64toa ( v, buffer, 10 );
string temp ( buffer );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
case SQL_BIGINT :
{
if ( isSigned )
{
__int64 v = 0;
v = * ( __int64* ) data;
char buffer[100];
_i64toa ( v, buffer, 10 );
string temp ( buffer );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
else
{
unsigned __int64 v = 0;
v = * ( unsigned __int64* ) data;
char buffer[100];
_ui64toa ( v, buffer, 10 );
string temp ( buffer );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
}
case SQL_FLOAT :
{
float v = 0;
v = * ( float* ) data;
fwprintf ( stdout, L"The value from the ODBC is (float) : %9.9f \n", v );
double x = ( v - _wtof ( valueJ . c_str () ) );
return ( x > -0.0000001 ) && ( x < 0.0000001 ); // In Kylin float is treated like double, so it might be more accurate
}
case SQL_DOUBLE :
{
double v = * ( double* ) data;
fwprintf ( stdout, L"The value from the ODBC is (double) : %9.9f\n ", v );
return v == wcstod ( valueJ . c_str (), NULL );
}
case SQL_TYPE_DATE :
{
string temp ( ( char* ) data );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
case SQL_TYPE_TIMESTAMP :
{
string temp ( ( char* ) data );
wstring tempW = string2wstring ( temp );
fwprintf ( stdout, L"The value from the ODBC is : %s \n", tempW . c_str () );
return tempW . compare ( valueJ ) == 0;
}
default :
return false;
}
}
// ----------------------------------------------------------------------------
// to validate the full results row by row
// ----------------------------------------------------------------------------
SQLRETURN CheckResults ( HSTMT hStmt, wchar_t* sql )
{
//First directly call REST to get a JDBC version result to compare against
std::unique_ptr <SQLResponse> response = restQuery ( sql, "http://localhost", 80, "ADMIN", "KADMIN", "default" );
//Go with hStmt now
int i, iCol;
BIND_COL_INFO* head;
BIND_COL_INFO* last;
BIND_COL_INFO* curr;
SQLRETURN status;
SQLLEN cType;
SQLSMALLINT iColCount;
// initializations
head = NULL;
// ALLOCATE SPACE TO FETCH A COMPLETE ROW
// get number of cols
if ( ( status = SQLNumResultCols ( hStmt, &iColCount ) ) != SQL_SUCCESS )
{
return status;
}
// loop to allocate binding info structure
for ( iCol = 1; iCol <= iColCount; iCol ++ )
{
// alloc binding structure
curr = ( BIND_COL_INFO* ) calloc ( 1, sizeof ( BIND_COL_INFO) );
if ( curr == NULL )
{
fprintf ( stderr, "Out of memory!\n" );
return SQL_ERROR; // its not an ODBC error so no diags r required
}
memset ( curr, 0, sizeof ( BIND_COL_INFO) );
// maintain link list
if ( iCol == 1 )
{
head = curr;
} // first col, therefore head of list
else
{
last -> next = curr;
} // attach
last = curr; // tail
// get column title size
if ( ( status = SQLColAttributeW ( hStmt, iCol, SQL_DESC_NAME, NULL, 0, & ( curr -> iColTitleSize ),
NULL ) ) != SQL_SUCCESS )
{
FreeBindings ( head );
return status;
}
else
{
++ curr -> iColTitleSize; // allow space for null char
}
// allocate buffer for title
curr -> szColTitle = ( wchar_t* ) calloc ( 1, curr -> iColTitleSize * sizeof ( wchar_t) );
if ( curr -> szColTitle == NULL )
{
FreeBindings ( head );
fprintf ( stderr, "Out of memory!\n" );
return SQL_ERROR; // its not an ODBC error so no diags r required
}
// get column title
if ( ( status = SQLColAttributeW ( hStmt, iCol, SQL_DESC_NAME, curr -> szColTitle, curr -> iColTitleSize,
& ( curr -> iColTitleSize ), NULL ) ) != SQL_SUCCESS )
{
FreeBindings ( head );
return status;
}
//xxx
// get col length
if ( ( status = SQLColAttributeW ( hStmt, iCol, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL,
& ( curr -> iColDisplaySize ) ) ) != SQL_SUCCESS )
{
FreeBindings ( head );
return status;
}
// arbitrary limit on display size
if ( curr -> iColDisplaySize > _DISPLAY_MAX )
{
curr -> iColDisplaySize = _DISPLAY_MAX;
}
// allocate buffer for col data + NULL terminator
curr -> szColData = ( void* ) calloc ( 1, 2 * ( curr -> iColDisplaySize + 1 ) * sizeof ( char) );
if ( curr -> szColData == NULL )
{
FreeBindings ( head );
fprintf ( stderr, "Out of memory!\n" );
return SQL_ERROR; // its not an ODBC error so no diags r required
}
//xxx
// get col type, not used now but can be checked to print value right aligned etcc
if ( ( status = SQLColAttributeW ( hStmt, iCol, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &cType ) ) != SQL_SUCCESS )
{
FreeBindings ( head );
return status;
}
curr -> iType = cType;
fprintf ( stdout, "The type for column %d is %d\n", iCol, cType );
//xxx
// get col type, not used now but can be checked to print value right aligned etcc
SQLLEN unsignedV = 0;
if ( ( status = SQLColAttributeW ( hStmt, iCol, SQL_DESC_UNSIGNED, NULL, 0, NULL, &unsignedV ) ) != SQL_SUCCESS )
{
FreeBindings ( head );
return status;
}
curr -> isSigned = ( unsignedV == 1 ) ? false : true;
fprintf ( stdout, "The column %d is signed ? %d\n", iCol, curr -> isSigned );
// set col type indicator in struct
curr -> fChar = ( cType == SQL_CHAR || cType == SQL_VARCHAR || cType == SQL_LONGVARCHAR ||
cType == SQL_WCHAR || cType == SQL_WVARCHAR || cType == SQL_WLONGVARCHAR );
fprintf ( stdout, "char flag is set to %d\n", curr -> fChar );
fputs ( "\n", stdout );
//xxx
// bind the col buffer so that the driver feeds it with col value on every fetch and use generic char binding for very column
if ( ( status = SQLBindCol ( hStmt, iCol, SQL_C_DEFAULT, ( SQLPOINTER ) curr -> szColData,
2 * ( curr -> iColDisplaySize + 1 ) * sizeof ( char), & ( curr -> indPtr ) ) ) != SQL_SUCCESS )
{
FreeBindings ( head );
return status;
}
}
// loop to print all the rows one by one
for ( i = 1; TRUE; i ++ )
{
// fetch the next row
if ( ( status = SQLFetch ( hStmt ) ) == SQL_NO_DATA_FOUND )
{
break;
} // no more rows so break
// check for error
else if ( status == SQL_ERROR )
{ // fetch failed
FreeBindings ( head );
return status;
}
for ( curr = head , iCol = 0; iCol < iColCount; iCol ++ , curr = curr -> next )
{
fprintf ( stdout, "Row Index: %d, Column Cardinal : %d\n", i - 1, iCol );
if ( !isValueConsistent ( curr -> szColData, response -> results[i - 1] -> contents[iCol], curr -> iType, curr -> isSigned ) )
{
FreeBindings ( head );
return SQL_ERROR;
}
fputs ( "\n", stdout );
}
}
// free the allocated bindings
FreeBindings ( head );
return SQL_SUCCESS;
}
// ----------------------------------------------------------------------------
// to free the col info allocated by ShowFullResults
// ----------------------------------------------------------------------------
void FreeBindings ( BIND_COL_INFO* pBindColInfo )
{
BIND_COL_INFO* next;
// precaution
if ( pBindColInfo )
{
do
{
// get the next col binding
next = pBindColInfo -> next;
// free any buffer for col title
if ( pBindColInfo -> szColTitle )
{
free ( pBindColInfo -> szColTitle );
pBindColInfo -> szColTitle = NULL;
}
// free any col data
if ( pBindColInfo -> szColData )
{
free ( pBindColInfo -> szColData );
pBindColInfo -> szColData = NULL;
}
// free the current binding
free ( pBindColInfo );
// make next the current
pBindColInfo = next;
}
while ( pBindColInfo );
}
}
// ----------------------------------------------------------------------------
// to show the ODBC diagnostic messages
// ----------------------------------------------------------------------------
void ShowDiagMessages ( SQLSMALLINT hType, SQLHANDLE hValue, SQLRETURN iStatus, char* szMsg )
{
SQLSMALLINT iRec = 0;
SQLINTEGER iError;
SQLTCHAR szMessage[1024];
SQLTCHAR szState[1024];
// header
fputs ( "\nDiagnostics:\n", stdout );
// in case of an invalid handle, no message can be extracted
if ( iStatus == SQL_INVALID_HANDLE )
{
fprintf ( stderr, "ODBC Error: Invalid handle!\n" );
return;
}
// loop to get all diag messages from driver/driver manager
while ( SQLGetDiagRec ( hType, hValue, ++ iRec, szState, &iError, szMessage,
( SQLSMALLINT ) ( sizeof ( szMessage ) / sizeof ( SQLTCHAR) ), ( SQLSMALLINT* ) NULL ) == SQL_SUCCESS )
{
_ftprintf ( stderr, TEXT ( "[%5.5s] %s (%d)\n" ), szState, szMessage, iError );
}
// gap
fputs ( "\n", stdout );
}
void crossValidate ()
{
char* queryFile = "testqueries.txt";
//char* queryFile = "c:\\foo.txt";
fprintf ( stdout, "The test queries file location is: %s\n", queryFile );
//validateOneQuery(L"SELECT * FROM classicmodels.new_table;");
validateQueries ( queryFile );
fprintf ( stdout, "The verify process is done.\n", queryFile );
fprintf ( stdout, "Total queries: %d, Successful queries: %d, Failed queries: %d.\n", totalCount, successCount,
failCount );
for ( vector <wstring>::iterator iter = failedQueries . begin (); iter != failedQueries . end (); ++iter )
{
fprintf ( stdout, wstring2string ( *iter ) . c_str () );
fprintf ( stdout, "\n\n" );
}
}
void validateSQLGetTypeInfo ()
{
Sleep ( 1000 );
SQLRETURN status;
SQLHANDLE hEnv = 0;
SQLHANDLE hConn = 0;
SQLHANDLE hStmt = 0;
wchar_t szConnStrOut[1024];
SQLSMALLINT x;
// BEFORE U CONNECT
// allocate ENVIRONMENT
status = SQLAllocHandle ( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_ENV, hEnv, status, "" );
// set the ODBC version for behaviour expected
status = SQLSetEnvAttr ( hEnv, SQL_ATTR_ODBC_VERSION, ( SQLPOINTER ) SQL_OV_ODBC3, 0 );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_ENV, hEnv, status, "" );
// allocate CONNECTION
status = SQLAllocHandle ( SQL_HANDLE_DBC, hEnv, &hConn );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_ENV, hEnv, status, "" );
#ifdef _WIN64
// ----------- real connection takes place at this point
// ----------- option 1: user is prompted for DSN & options
status = SQLDriverConnect ( hConn, GetDesktopWindow(),
( unsigned char* ) "",
SQL_NTS, szConnStrOut, 1024, &x,
SQL_DRIVER_COMPLETE );
#else
status = SQLDriverConnectW ( hConn, GetDesktopWindow (),
//L"DSN=testDSN;",
L"DRIVER={KylinODBCDriver};PROJECT=default;UID=ADMIN;SERVER=http://localhost;PORT=80;",
SQL_NTS, szConnStrOut, 1024, &x,
SQL_DRIVER_COMPLETE );
#endif
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_DBC, hConn, status, "" );
// CONGRATUALTIONS ---- u r connected to a DBMS via an ODBC driver
// allocate STATEMENT
status = SQLAllocHandle ( SQL_HANDLE_STMT, hConn, &hStmt );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_DBC, hConn, status, "" );
// execute the statement
//status = SQLExecDirect ( hStmt, ( unsigned char* )argv[1], SQL_NTS );
status = SQLGetTypeInfoW ( hStmt, SQL_ALL_TYPES );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_STMT, hConn, status, "" );
// check for error
ODBC_CHK_ERROR ( SQL_HANDLE_STMT, hStmt, status, "" );
Cleanup:
if ( hStmt )
{
SQLFreeHandle ( SQL_HANDLE_STMT, hStmt );
}
if ( hConn )
{
SQLFreeHandle ( SQL_HANDLE_DBC, hConn );
}
if ( hEnv )
{
SQLFreeHandle ( SQL_HANDLE_ENV, hEnv );
}
return;
}