Skip to content

Commit 1d3f3ef

Browse files
committed
Implemented statement-level SQL execution to enable distinct rowset returns for multiple queries with like columns. Splits on semicolons with mitigations for comments and quotes with escapes. Error feedback implemented that redirects the cursor to the first problem statement with a status line indication as well as more detail in the results pane.
1 parent d427f41 commit 1d3f3ef

1 file changed

Lines changed: 173 additions & 55 deletions

File tree

src/sqlite-ce-edit/main.c

Lines changed: 173 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ static int g_nOutput = 0;
109109
static int g_clearOnExec = 1; /* Clear results before each execution */
110110
static int g_viewMode = 0; /* 0 = query, 1 = results */
111111
static int g_showingHint = 0; /* 1 = showing startup hint in query pane */
112+
static int g_suppressLineCount = 0; /* 1 = suppress UpdateLineCount temporarily */
112113
static wchar_t g_lastResultStatus[64] = L""; /* Saved result status */
113114
static wchar_t g_findText[128] = L""; /* Last search text */
114115
static int g_searchMode = 0; /* 1 = Enter triggers Find Next */
@@ -118,6 +119,10 @@ static void UpdateLineCount(void) {
118119
wchar_t buf[32];
119120
DWORD sel;
120121
int cur, total;
122+
if (g_suppressLineCount) {
123+
g_suppressLineCount--;
124+
return;
125+
}
121126
SendMessage(g_hwndQuery, EM_GETSEL, (WPARAM)&sel, 0);
122127
cur = (int)SendMessage(g_hwndQuery, EM_LINEFROMCHAR, sel, 0) + 1;
123128
total = (int)SendMessage(g_hwndQuery, EM_GETLINECOUNT, 0, 0);
@@ -500,13 +505,26 @@ static void FlushResultSet(void) {
500505

501506
static int QueryCallback(void *arg, int argc, char **argv, char **cols) {
502507
int i, len;
508+
int colsChanged = 0;
503509
(void)arg;
504510

505511
if (argc > MAX_RESULT_COLS) argc = MAX_RESULT_COLS;
506512

507-
/* New query detected - flush previous results first */
508-
if (g_nRows > 0 && argc != g_nCols) {
509-
FlushResultSet();
513+
/* Check if columns changed (new statement) */
514+
if (g_nRows > 0) {
515+
if (argc != g_nCols) {
516+
colsChanged = 1;
517+
} else {
518+
/* Same count - check if names differ */
519+
for (i = 0; i < argc; i++) {
520+
const char *newCol = cols[i] ? cols[i] : "";
521+
const char *oldCol = g_results[0][i] ? g_results[0][i] : "";
522+
const char *a = newCol, *b = oldCol;
523+
while (*a && *b && *a == *b) { a++; b++; }
524+
if (*a != *b) { colsChanged = 1; break; }
525+
}
526+
}
527+
if (colsChanged) FlushResultSet();
510528
}
511529

512530
/* Store column headers on first row */
@@ -591,29 +609,60 @@ static void OutputResults(void) {
591609
}
592610

593611
static void ExecuteQuery(void) {
594-
int len, rc;
612+
int len, rc, hadError = 0;
595613
char *sql;
596614
char *errmsg = NULL;
597615
wchar_t *wsql;
616+
DWORD selStart, selEnd;
598617

599618
if (!g_db) {
600619
SetWindowTextW(g_hwndResult, L"No database open.");
601620
return;
602621
}
603622

604-
/* Get query text */
605-
len = GetWindowTextLengthW(g_hwndQuery);
606-
if (len == 0) return;
623+
/* Check for selection */
624+
SendMessage(g_hwndQuery, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
607625

608-
wsql = (wchar_t *)LocalAlloc(LMEM_FIXED, (len + 1) * sizeof(wchar_t));
609-
sql = (char *)LocalAlloc(LMEM_FIXED, (len + 1) * 3); /* UTF-8 worst case */
610-
if (!wsql || !sql) {
611-
if (wsql) LocalFree(wsql);
612-
if (sql) LocalFree(sql);
613-
return;
626+
if (selStart != selEnd) {
627+
/* Execute selected text only */
628+
len = selEnd - selStart;
629+
wsql = (wchar_t *)LocalAlloc(LMEM_FIXED, (len + 1) * sizeof(wchar_t));
630+
sql = (char *)LocalAlloc(LMEM_FIXED, (len + 1) * 3);
631+
if (!wsql || !sql) {
632+
if (wsql) LocalFree(wsql);
633+
if (sql) LocalFree(sql);
634+
return;
635+
}
636+
SendMessage(g_hwndQuery, EM_SETSEL, selStart, selEnd);
637+
SendMessage(g_hwndQuery, WM_COPY, 0, 0);
638+
/* Get selected text via buffer */
639+
{
640+
int fullLen = GetWindowTextLengthW(g_hwndQuery);
641+
wchar_t *full = (wchar_t *)LocalAlloc(LMEM_FIXED, (fullLen + 1) * sizeof(wchar_t));
642+
if (full) {
643+
int i;
644+
GetWindowTextW(g_hwndQuery, full, fullLen + 1);
645+
for (i = 0; i < len && (selStart + i) <= (DWORD)fullLen; i++)
646+
wsql[i] = full[selStart + i];
647+
wsql[i] = 0;
648+
LocalFree(full);
649+
}
650+
}
651+
} else {
652+
/* Execute entire buffer */
653+
len = GetWindowTextLengthW(g_hwndQuery);
654+
if (len == 0) return;
655+
656+
wsql = (wchar_t *)LocalAlloc(LMEM_FIXED, (len + 1) * sizeof(wchar_t));
657+
sql = (char *)LocalAlloc(LMEM_FIXED, (len + 1) * 3);
658+
if (!wsql || !sql) {
659+
if (wsql) LocalFree(wsql);
660+
if (sql) LocalFree(sql);
661+
return;
662+
}
663+
GetWindowTextW(g_hwndQuery, wsql, len + 1);
614664
}
615665

616-
GetWindowTextW(g_hwndQuery, wsql, len + 1);
617666
WideCharToMultiByte(CP_ACP, 0, wsql, -1, sql, (len + 1) * 3, NULL, NULL);
618667
LocalFree(wsql);
619668

@@ -631,52 +680,117 @@ static void ExecuteQuery(void) {
631680
{
632681
DWORD startTick = GetTickCount();
633682
DWORD elapsed;
634-
rc = sqlite_exec(g_db, sql, QueryCallback, NULL, &errmsg);
635-
elapsed = GetTickCount() - startTick;
683+
char *stmt = sql;
684+
char *p = sql;
685+
int inString = 0;
686+
int inComment = 0;
687+
int stmtOffset = 0; /* Character offset of current statement */
688+
int errorOffset = 0;
689+
690+
/* Split on semicolons and execute each statement */
691+
while (*p && !hadError) {
692+
if (inComment) {
693+
if (inComment == 1 && *p == '\n') inComment = 0;
694+
else if (inComment == 2 && *p == '*' && p[1] == '/') { inComment = 0; p++; }
695+
} else if (inString) {
696+
if (*p == '\'' && p[1] == '\'') p++; /* escaped quote */
697+
else if (*p == '\'') inString = 0;
698+
} else {
699+
if (*p == '\'') inString = 1;
700+
else if (*p == '-' && p[1] == '-') inComment = 1;
701+
else if (*p == '/' && p[1] == '*') { inComment = 2; p++; }
702+
else if (*p == ';') {
703+
char saved = p[1];
704+
p[1] = '\0';
705+
/* Execute this statement */
706+
rc = sqlite_exec(g_db, stmt, QueryCallback, NULL, &errmsg);
707+
p[1] = saved;
708+
if (rc != SQLITE_OK) {
709+
int ln = 1, i;
710+
char lb[16]; char *lp = lb + 14;
711+
for (i = 0; i < stmtOffset; i++) if (sql[i] == '\n') ln++;
712+
lb[15] = '\0'; *lp = '\0';
713+
while (ln > 0) { *--lp = '0' + (ln % 10); ln /= 10; }
714+
Output("Line "); Output(lp); Output(": ");
715+
OutputLine(errmsg ? errmsg : sqlite_error_string(rc));
716+
if (errmsg) sqlite_freemem(errmsg);
717+
errorOffset = stmtOffset;
718+
hadError = 1;
719+
} else if (g_nRows > 0) {
720+
FlushResultSet();
721+
} else {
722+
int changes = sqlite_changes(g_db);
723+
if (changes > 0) {
724+
char buf[32]; char *bp = buf + 30; int c = changes;
725+
buf[31] = '\0'; *bp = '\0';
726+
while (c > 0) { *--bp = '0' + (c % 10); c /= 10; }
727+
Output(bp); OutputLine(" row(s) affected.");
728+
}
729+
}
730+
stmt = p + 1;
731+
stmtOffset = (int)(stmt - sql);
732+
while (*stmt == ' ' || *stmt == '\t' || *stmt == '\r' || *stmt == '\n') { stmt++; stmtOffset++; }
733+
}
734+
}
735+
p++;
736+
}
636737

637-
if (rc != SQLITE_OK) {
638-
Output("Error: ");
639-
OutputLine(errmsg ? errmsg : sqlite_error_string(rc));
640-
if (errmsg) sqlite_freemem(errmsg);
641-
SetStatusResult(L"Error");
642-
} else if (g_nRows == 0) {
643-
/* Check if it was a non-SELECT statement */
644-
int changes = sqlite_changes(g_db);
645-
wchar_t wbuf[64];
646-
if (changes > 0) {
647-
char buf[32];
648-
char *p = buf + 30;
649-
buf[31] = '\0';
650-
*p = '\0';
651-
while (changes > 0) { *--p = '0' + (changes % 10); changes /= 10; }
652-
Output(p);
653-
OutputLine(" row(s) affected.");
654-
Output("Query executed in ");
655-
{ char tb[16]; char *tp = tb + 14; long e = (long)elapsed; tb[15] = '\0'; *tp = '\0';
656-
if (e == 0) *--tp = '0'; else while (e > 0) { *--tp = (char)('0' + (e % 10)); e /= 10; }
657-
Output(tp); }
658-
OutputLine("ms.");
659-
wsprintfW(wbuf, L"%hs row(s) affected (%lums)", p, elapsed);
738+
/* Execute any remaining statement (no trailing semicolon) */
739+
if (!hadError && *stmt) {
740+
rc = sqlite_exec(g_db, stmt, QueryCallback, NULL, &errmsg);
741+
if (rc != SQLITE_OK) {
742+
int ln = 1, i;
743+
char lb[16]; char *lp = lb + 14;
744+
for (i = 0; i < stmtOffset; i++) if (sql[i] == '\n') ln++;
745+
lb[15] = '\0'; *lp = '\0';
746+
while (ln > 0) { *--lp = '0' + (ln % 10); ln /= 10; }
747+
errorOffset = stmtOffset;
748+
Output("Line "); Output(lp); Output(": ");
749+
OutputLine(errmsg ? errmsg : sqlite_error_string(rc));
750+
if (errmsg) sqlite_freemem(errmsg);
751+
hadError = 1;
752+
} else if (g_nRows > 0) {
753+
FlushResultSet();
660754
} else {
661-
OutputLine("OK");
662-
Output("Query executed in ");
663-
{ char tb[16]; char *tp = tb + 14; long e = (long)elapsed; tb[15] = '\0'; *tp = '\0';
664-
if (e == 0) *--tp = '0'; else while (e > 0) { *--tp = (char)('0' + (e % 10)); e /= 10; }
665-
Output(tp); }
666-
OutputLine("ms.");
667-
wsprintfW(wbuf, L"OK (%lums)", elapsed);
755+
int changes = sqlite_changes(g_db);
756+
if (changes > 0) {
757+
char buf[32]; char *bp = buf + 30; int c = changes;
758+
buf[31] = '\0'; *bp = '\0';
759+
while (c > 0) { *--bp = '0' + (c % 10); c /= 10; }
760+
Output(bp); OutputLine(" row(s) affected.");
761+
}
668762
}
669-
SetStatusResult(wbuf);
763+
}
764+
765+
elapsed = GetTickCount() - startTick;
766+
767+
if (hadError) {
768+
/* Position cursor at the errored statement */
769+
int adjOffset = errorOffset;
770+
int lineNum = 1;
771+
int i;
772+
wchar_t wbuf[48];
773+
for (i = 0; i < errorOffset; i++) {
774+
if (sql[i] == '\n') lineNum++;
775+
}
776+
if (selStart != selEnd) adjOffset += selStart; /* Adjust for selection offset */
777+
SendMessage(g_hwndQuery, EM_SETSEL, adjOffset, adjOffset);
778+
SendMessage(g_hwndQuery, EM_SCROLLCARET, 0, 0);
779+
wsprintfW(wbuf, L"Error at line %d", lineNum);
780+
SendMessageW(g_hwndStatus, SB_SETTEXTW, 1, (LPARAM)wbuf);
781+
g_suppressLineCount = 3; /* Skip next few UpdateLineCount calls from pending messages */
782+
MessageBeep(MB_ICONEXCLAMATION);
670783
} else {
671784
wchar_t wbuf[64];
672-
/* Flush final result set (adds to g_totalRows) */
673-
FlushResultSet();
674785
Output("Query executed in ");
675786
{ char tb[16]; char *tp = tb + 14; long e = (long)elapsed; tb[15] = '\0'; *tp = '\0';
676787
if (e == 0) *--tp = '0'; else while (e > 0) { *--tp = (char)('0' + (e % 10)); e /= 10; }
677788
Output(tp); }
678789
OutputLine("ms.");
679-
wsprintfW(wbuf, L"%d row(s) returned (%lums)", g_totalRows, elapsed);
790+
if (g_totalRows > 0)
791+
wsprintfW(wbuf, L"%d row(s) returned (%lums)", g_totalRows, elapsed);
792+
else
793+
wsprintfW(wbuf, L"OK (%lums)", elapsed);
680794
SetStatusResult(wbuf);
681795
}
682796
}
@@ -685,10 +799,12 @@ static void ExecuteQuery(void) {
685799
FlushOutput();
686800
UpdateDbSize();
687801

688-
/* Switch to results view */
689-
SwitchView(1);
690-
SendMessage(g_hwndCB, TB_CHECKBUTTON, IDM_VIEWQUERY, FALSE);
691-
SendMessage(g_hwndCB, TB_CHECKBUTTON, IDM_VIEWRESULT, TRUE);
802+
/* Switch to results view (unless error - stay in query to show cursor) */
803+
if (!hadError) {
804+
SwitchView(1);
805+
SendMessage(g_hwndCB, TB_CHECKBUTTON, IDM_VIEWQUERY, FALSE);
806+
SendMessage(g_hwndCB, TB_CHECKBUTTON, IDM_VIEWRESULT, TRUE);
807+
}
692808
}
693809

694810
/* Helper to extract filename from path */
@@ -1946,7 +2062,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
19462062
case IDM_NEW: DoFileNew(); break;
19472063
case IDM_OPEN: DoFileOpen(); break;
19482064
case IDM_CLOSE: CloseDatabase(); break;
1949-
case IDM_OPENQUERY: DoOpenQuery(); break;
2065+
case IDM_OPENQUERY:
2066+
if (g_showingHint) DoFileOpen(); else DoOpenQuery();
2067+
break;
19502068
case IDM_SAVEQUERY: DoSaveQuery(); break;
19512069
case IDM_EXPORTCSV: DoExportCSV(); break;
19522070
case IDM_EXPORTDB: DoExportDb(); break;

0 commit comments

Comments
 (0)