@@ -109,6 +109,7 @@ static int g_nOutput = 0;
109109static int g_clearOnExec = 1 ; /* Clear results before each execution */
110110static int g_viewMode = 0 ; /* 0 = query, 1 = results */
111111static int g_showingHint = 0 ; /* 1 = showing startup hint in query pane */
112+ static int g_suppressLineCount = 0 ; /* 1 = suppress UpdateLineCount temporarily */
112113static wchar_t g_lastResultStatus [64 ] = L"" ; /* Saved result status */
113114static wchar_t g_findText [128 ] = L"" ; /* Last search text */
114115static 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
501506static 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
593611static 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