Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 76 additions & 24 deletions QCSRC/DB2JSON.CPP
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ struct json_symbols_t {
const char* POSITION;
const char* ERROR;
const char* SQLSTATE;
const char* SQLCODE;
const char* MSGTEXT;
const char* DB2JSON;
};
Expand Down Expand Up @@ -219,6 +220,7 @@ const json_symbols_t json_ascii = {
"\"position\"",
"\"error\"",
"\"sqlstate\"",
"\"sqlcode\"",
"\"msgtext\"",
"\"DB2JSON\""
};
Expand Down Expand Up @@ -260,6 +262,7 @@ const json_symbols_t json_job = {
"\"position\"",
"\"error\"",
"\"sqlstate\"",
"\"sqlcode\"",
"\"msgtext\"",
"\"DB2JSON\""
};
Expand Down Expand Up @@ -423,13 +426,15 @@ int checkError(std::ostream& output, SQLRETURN rc, SQLHANDLE handle, SQLSMALLINT
{
return 0;
}

char buff[256];
SQLCHAR sqlstate[11] = {0};
SQLCHAR message[SQL_MAX_MESSAGE_LENGTH+1] = {0};
SQLINTEGER nativeError = 0;
SQLINTEGER sqlcode = 0;
SQLSMALLINT textLen = 0;
if (handle != SQL_NULL_HANDLE)
{
SQLGetDiagRec(type, handle, 1, sqlstate, &nativeError, message, sizeof(message)-1, &textLen);
SQLGetDiagRec(type, handle, 1, sqlstate, &sqlcode, message, sizeof(message)-1, &textLen);
}
else if (msg != NULL && strlen(msg) > 0) {
textLen = strlen(msg);
Expand All @@ -439,26 +444,66 @@ int checkError(std::ostream& output, SQLRETURN rc, SQLHANDLE handle, SQLSMALLINT
}
}
message[textLen] = 0x00;
logSQLMsg("Error: %5s, %s",sqlstate, message);
logSQLMsg("SQLSTATE: %5s SQLCODE: %d - %s",sqlstate, sqlcode, message);

std::string sqlstateStr = cvtToASCII(reinterpret_cast<const char*>(sqlstate));
std::string msgtextStr = cvtToASCII(reinterpret_cast<const char*>(message));
std::string sqlcodeStr;
char sqlcodeBuf[128];

switch (sqlcode) {
case -901:
strcpy(sqlcodeBuf, "SQL system error (-901)");
break;
case -902:
strcpy(sqlcodeBuf, "System error (-902)");
break;
case -204:
strcpy(sqlcodeBuf, "Object not found (-204)");
break;
case -443:
strcpy(sqlcodeBuf, "Routine/function error (-443)");
break;
case -7008:
strcpy(sqlcodeBuf, "Object not valid for operation (-7008)");
break;
case -30020:
strcpy(sqlcodeBuf, "Communication error with database host server (-30020)");
break;
default:
// fallback: just put the numeric code in text form
if (sqlcode != 0) {
sprintf(sqlcodeBuf, "non-zero SQLCODE detected", (int)sqlcode);
}
break;
}

// Convert to ASCII for JSON
if (sqlcode != 0)
{
sprintf(buff," SQLCODE: %d - %s", sqlcode, sqlcodeBuf);
sqlcodeStr = cvtToASCII(reinterpret_cast<const char*>(buff));
}


// Then include sqlcodeStr in your JSON output

bool rootWasOpen = closeJsonNode(output);

if (!rootWasOpen) {
output << json->LCURLY; // open root object if not already open
g_rootOpen = true;
}
char buf[16];
sprintf(buf,"%d", errPos);
std::string errPosStr = cvtToASCII(reinterpret_cast<const char*>(buf));
sprintf(buff,"%d", errPos);
std::string errPosStr = cvtToASCII(reinterpret_cast<const char*>(buff));

output << json->LF
<< json->ERROR << json->COLON << json->LCURLY
<< json->SQLSTATE << json->COLON
<< json->QUOTE << jsonEscape(sqlstateStr) << json->QUOTE << json->COMMA
<< json->POSITION << json->COLON << errPosStr << json->COMMA
<< json->MSGTEXT << json->COLON
<< json->QUOTE << jsonEscape(msgtextStr) << json->QUOTE
<< json->QUOTE << jsonEscape(msgtextStr) << jsonEscape(sqlcodeStr) << json->QUOTE
<< json->RCURLY;
if (g_rootOpen) {
output << json->LF << json->RCURLY << json->LF;
Expand Down Expand Up @@ -1229,6 +1274,10 @@ int main(int argc, char *argv[])
rc = SQLSetConnectAttr(hDbc,SQL_ATTR_EXTENDED_COL_INFO, (SQLPOINTER)&attr,0);
ifCheckError(output, rc, hDbc, SQL_HANDLE_DBC, "SetConnAttr");

attr = SQL_AUTOCOMMIT_ON;
SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&attr, 0);


#ifndef TOASCII
attr = SQL_TRUE; // Return UTF-8 data
rc = SQLSetConnectAttr(hDbc, SQL_ATTR_UTF8, (SQLPOINTER)&attr,0);
Expand Down Expand Up @@ -1564,8 +1613,10 @@ int main(int argc, char *argv[])
g_rootOpen = false;

// Cleanup
if (hStmt)
if (hStmt) {
SQLCloseCursor(hStmt);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
if (hDbc)
SQLDisconnect(hDbc);
if (hDbc)
Expand Down Expand Up @@ -1651,34 +1702,35 @@ const char* sqlTypeToJSONText(int sqlType) {

int SQLSyntaxCheck( std::string& sqlStmt, std::string& rtnMsg )
{
Qus_EC_t ec;
int recProc = 0;
int restrictedVerb = 0;
char langID[10];
char msgid[8];
char msgfile[11];
char msgflib[11];
char msgf[21];
char subValues[11];
char fmtCtrl[11];
char buffer[4096];
char* msgInfo = buffer;
Qus_EC_t ec;
int recProc = 0;
int restrictedVerb = 0;
char langID[10];
char msgid[8];
char msgfile[11];
char msgflib[11];
char msgf[21];
char subValues[11];
char fmtCtrl[11];
char buffer[4096];
char* msgInfo = buffer;


typedef _Packed struct tag_Statement_Info
{
{
char Message_File_Name[10];
char Message_File_Library_Name[10];
int Number_of_Statements_Processed;
Qsq_Statement_I_t Statement;
char msgdata[1920];
} sqlStmtInfo_t;
} sqlStmtInfo_t;

typedef _Packed struct tagsqlOptions {
typedef _Packed struct tagsqlOptions
{
int key;
int length;
char data[10];
} sqlOption_t;
} sqlOption_t;

struct {
int keys;
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 0.0.10 - 15-SEPT-2025
- We now close the SQL Cursor before freeing the statement handle. We also now use AUTOCOMMIT for users who insist on using the DB2JSON.PGM to run non-query statements, such as INSERT/UPDATE/DELETE/MERGE. Also the SQL CODE is returned with the SQLSTATE when an error is detected.

## 0.0.9 - 12-SEPT-2025 -
- The SQL Statement History in the Db2 Query HTML App now avoids adding duplicate entries. Once a statement has been archived in the history log, running that statement in the future avoids adding it a second time. Previously it would avoid adding duplicate statements only when they were run consecutively. Now if the SQL statement being run has been previously logged, it is not logged.
- A new "Remove Duplicates" button has been added to the SQL Statement History dialog. Note this takes effect immediately.
Expand Down
47 changes: 44 additions & 3 deletions css/db2json.css → css/db2query.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ body {
box-shadow: 0 8px 16px -4px #bbb;
vertical-align: middle;
background-clip: padding-box;
white-space: normal; /* headers can wrap (you already inject <br>) */
word-break: normal;
overflow-wrap: normal;
}

/* Add minimal top/bottom breathing room for header cells without using padding, to avoid sticky bleed */
Expand Down Expand Up @@ -88,6 +91,10 @@ td {
font-size: var(--table-font-size, 1em);
font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Menlo', monospace;
white-space: pre-wrap;
white-space: nowrap; /* prevent 2-line rows */
word-break: normal;
overflow: hidden; /* clip inside the cell */
text-overflow: ellipsis; /* … when clipped */
}

.scroll-table th.right {
Expand Down Expand Up @@ -696,7 +703,41 @@ td {
gap: 8px;
}

/* Optional: allow wrapping on narrow screens */
@media (max-width: 520px) {
.modal .modal-actions { flex-wrap: wrap; }
/* Simple-DataTables inside our scroll wrapper: our wrapper scrolls, not the plugin */
#results .scroll-table-wrapper .dataTable-container {
overflow: visible !important;
max-height: none !important;
padding-bottom: 44px; /* avoid last rows hiding under sticky pager */
}

/* Keep pager/top visible inside the wrapper */
#results .scroll-table-wrapper .dataTable-bottom {
position: sticky;
bottom: 0;
background: #fff;
border-top: 1px solid #ddd;
z-index: 3;
}
#results .scroll-table-wrapper .dataTable-top {
position: sticky;
top: 0;
background: #fff;
z-index: 3;
}

/* If Simple-DataTables forces wrapping, neutralize it here */
#results .scroll-table-wrapper .dataTable-table td {
white-space: nowrap;
word-break: normal;
overflow: hidden;
text-overflow: ellipsis;
}

#results .scroll-table tbody td.wrap {
white-space: normal;
max-width: 250ch;
overflow-wrap: anywhere;
word-break: break-word;
overflow: visible;
text-overflow: clip;
}
Loading
Loading