@@ -606,6 +606,221 @@ static int test_join(void) {
606606 return ok ;
607607}
608608
609+ /*============================================================================
610+ ** Test Cases - Export/Import (0.2.0)
611+ **============================================================================*/
612+
613+ static int test_sql_quote_escape (void ) {
614+ int ok ;
615+ char * * result ;
616+ int nRow , nCol ;
617+ ExecOK ("CREATE TABLE q(s TEXT)" );
618+ ExecOK ("INSERT INTO q VALUES('it''s a test')" );
619+ ExecOK ("INSERT INTO q VALUES('say \"hello\"')" );
620+ ok = (CountRows ("SELECT * FROM q" ) == 2 );
621+ /* Verify content */
622+ if (sqlite_get_table (g_db , "SELECT s FROM q WHERE s LIKE '%it''s%'" , & result , & nRow , & nCol , NULL ) == SQLITE_OK ) {
623+ ok = ok && (nRow == 1 );
624+ sqlite_free_table (result );
625+ }
626+ ExecOK ("DROP TABLE q" );
627+ return ok ;
628+ }
629+
630+ static int test_sqlite_master_tables (void ) {
631+ int ok ;
632+ ExecOK ("CREATE TABLE sm_t1(a INTEGER)" );
633+ ExecOK ("CREATE TABLE sm_t2(b TEXT)" );
634+ ExecOK ("CREATE TABLE sm_t3(c REAL)" );
635+ ok = (CountRows ("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'sm_t%'" ) == 3 );
636+ ExecOK ("DROP TABLE sm_t1" );
637+ ExecOK ("DROP TABLE sm_t2" );
638+ ExecOK ("DROP TABLE sm_t3" );
639+ return ok ;
640+ }
641+
642+ static int test_sqlite_master_indexes (void ) {
643+ int ok ;
644+ char * * result ;
645+ int nRow , nCol ;
646+ ExecOK ("CREATE TABLE idx_t(a INTEGER, b TEXT)" );
647+ ExecOK ("CREATE INDEX idx_a ON idx_t(a)" );
648+ ok = (CountRows ("SELECT name FROM sqlite_master WHERE type='index' AND name='idx_a'" ) == 1 );
649+ /* Verify SQL is stored */
650+ if (sqlite_get_table (g_db , "SELECT sql FROM sqlite_master WHERE name='idx_a'" , & result , & nRow , & nCol , NULL ) == SQLITE_OK ) {
651+ ok = ok && (nRow == 1 ) && (result [1 ] != NULL );
652+ sqlite_free_table (result );
653+ }
654+ ExecOK ("DROP TABLE idx_t" );
655+ return ok ;
656+ }
657+
658+ static int test_export_db_schema (void ) {
659+ const char * srcPath = "\\Temp\\exp_src.db" ;
660+ const char * dstPath = "\\Temp\\exp_dst.db" ;
661+ sqlite * srcDb , * dstDb ;
662+ char * * result ;
663+ int nRow , nCol , ok = 0 ;
664+
665+ DeleteFileW (L"\\Temp\\exp_src.db" );
666+ DeleteFileW (L"\\Temp\\exp_dst.db" );
667+
668+ /* Create source with schema */
669+ srcDb = sqlite_open (srcPath , 0 , NULL );
670+ if (!srcDb ) return 0 ;
671+ sqlite_exec (srcDb , "CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)" , NULL , NULL , NULL );
672+ sqlite_exec (srcDb , "CREATE TABLE t2(x REAL, y REAL)" , NULL , NULL , NULL );
673+
674+ /* "Export" by copying schema */
675+ dstDb = sqlite_open (dstPath , 0 , NULL );
676+ if (!dstDb ) { sqlite_close (srcDb ); return 0 ; }
677+
678+ if (sqlite_get_table (srcDb , "SELECT sql FROM sqlite_master WHERE type='table'" , & result , & nRow , & nCol , NULL ) == SQLITE_OK ) {
679+ int i ;
680+ for (i = 1 ; i <= nRow ; i ++ ) {
681+ if (result [i ]) sqlite_exec (dstDb , result [i ], NULL , NULL , NULL );
682+ }
683+ sqlite_free_table (result );
684+ }
685+ sqlite_close (srcDb );
686+ sqlite_close (dstDb );
687+
688+ /* Verify destination has both tables */
689+ dstDb = sqlite_open (dstPath , 0 , NULL );
690+ if (dstDb ) {
691+ ok = (CountRows ("SELECT name FROM sqlite_master WHERE type='table'" ) >= 2 );
692+ /* Temporarily use dstDb for count */
693+ {
694+ sqlite * saved = g_db ;
695+ g_db = dstDb ;
696+ ok = (CountRows ("SELECT name FROM sqlite_master WHERE type='table'" ) == 2 );
697+ g_db = saved ;
698+ }
699+ sqlite_close (dstDb );
700+ }
701+
702+ DeleteFileW (L"\\Temp\\exp_src.db" );
703+ DeleteFileW (L"\\Temp\\exp_dst.db" );
704+ return ok ;
705+ }
706+
707+ static int test_export_db_data (void ) {
708+ const char * srcPath = "\\Temp\\expd_src.db" ;
709+ const char * dstPath = "\\Temp\\expd_dst.db" ;
710+ sqlite * srcDb , * dstDb ;
711+ char * * result ;
712+ int nRow , nCol , ok = 0 ;
713+
714+ DeleteFileW (L"\\Temp\\expd_src.db" );
715+ DeleteFileW (L"\\Temp\\expd_dst.db" );
716+
717+ /* Create source with data */
718+ srcDb = sqlite_open (srcPath , 0 , NULL );
719+ if (!srcDb ) return 0 ;
720+ sqlite_exec (srcDb , "CREATE TABLE t(id INTEGER, val TEXT)" , NULL , NULL , NULL );
721+ sqlite_exec (srcDb , "INSERT INTO t VALUES(1, 'one')" , NULL , NULL , NULL );
722+ sqlite_exec (srcDb , "INSERT INTO t VALUES(2, 'two')" , NULL , NULL , NULL );
723+ sqlite_exec (srcDb , "INSERT INTO t VALUES(3, 'three')" , NULL , NULL , NULL );
724+
725+ /* Export schema */
726+ dstDb = sqlite_open (dstPath , 0 , NULL );
727+ if (!dstDb ) { sqlite_close (srcDb ); return 0 ; }
728+ sqlite_exec (dstDb , "CREATE TABLE t(id INTEGER, val TEXT)" , NULL , NULL , NULL );
729+
730+ /* Export data */
731+ if (sqlite_get_table (srcDb , "SELECT * FROM t" , & result , & nRow , & nCol , NULL ) == SQLITE_OK ) {
732+ int i , j ;
733+ for (i = 1 ; i <= nRow ; i ++ ) {
734+ char sql [256 ];
735+ char * p = sql ;
736+ char * s ;
737+ for (s = "INSERT INTO t VALUES(" ; * s ; ) * p ++ = * s ++ ;
738+ for (s = result [i * nCol ]; * s ; ) * p ++ = * s ++ ; /* id */
739+ * p ++ = ',' ; * p ++ = '\'' ;
740+ for (s = result [i * nCol + 1 ]; * s ; ) * p ++ = * s ++ ; /* val */
741+ * p ++ = '\'' ; * p ++ = ')' ; * p = '\0' ;
742+ sqlite_exec (dstDb , sql , NULL , NULL , NULL );
743+ }
744+ sqlite_free_table (result );
745+ }
746+ sqlite_close (srcDb );
747+ sqlite_close (dstDb );
748+
749+ /* Verify destination has 3 rows */
750+ dstDb = sqlite_open (dstPath , 0 , NULL );
751+ if (dstDb ) {
752+ sqlite * saved = g_db ;
753+ g_db = dstDb ;
754+ ok = (CountRows ("SELECT * FROM t" ) == 3 );
755+ ok = ok && (GetInt ("SELECT id FROM t WHERE val='two'" ) == 2 );
756+ g_db = saved ;
757+ sqlite_close (dstDb );
758+ }
759+
760+ DeleteFileW (L"\\Temp\\expd_src.db" );
761+ DeleteFileW (L"\\Temp\\expd_dst.db" );
762+ return ok ;
763+ }
764+
765+ static int test_invalid_sql (void ) {
766+ int rc ;
767+ char * errmsg = NULL ;
768+ rc = sqlite_exec (g_db , "SELEKT * FORM nowhere" , NULL , NULL , & errmsg );
769+ if (errmsg ) sqlite_freemem (errmsg );
770+ return (rc != SQLITE_OK ); /* Should fail */
771+ }
772+
773+ static int test_missing_table (void ) {
774+ int rc ;
775+ char * errmsg = NULL ;
776+ rc = sqlite_exec (g_db , "SELECT * FROM nonexistent_table_xyz" , NULL , NULL , & errmsg );
777+ if (errmsg ) sqlite_freemem (errmsg );
778+ return (rc != SQLITE_OK ); /* Should fail */
779+ }
780+
781+ static int test_constraint_violation (void ) {
782+ int rc ;
783+ char * errmsg = NULL ;
784+ ExecOK ("CREATE TABLE cv(id INTEGER PRIMARY KEY)" );
785+ ExecOK ("INSERT INTO cv VALUES(1)" );
786+ rc = sqlite_exec (g_db , "INSERT INTO cv VALUES(1)" , NULL , NULL , & errmsg ); /* Duplicate */
787+ if (errmsg ) sqlite_freemem (errmsg );
788+ ExecOK ("DROP TABLE cv" );
789+ return (rc != SQLITE_OK ); /* Should fail */
790+ }
791+
792+ static int test_memory_isolation (void ) {
793+ sqlite * db1 , * db2 ;
794+ int ok = 0 ;
795+
796+ db1 = sqlite_open (":memory:" , 0 , NULL );
797+ db2 = sqlite_open (":memory:" , 0 , NULL );
798+ if (!db1 || !db2 ) {
799+ if (db1 ) sqlite_close (db1 );
800+ if (db2 ) sqlite_close (db2 );
801+ return 0 ;
802+ }
803+
804+ sqlite_exec (db1 , "CREATE TABLE t(x INTEGER)" , NULL , NULL , NULL );
805+ sqlite_exec (db1 , "INSERT INTO t VALUES(42)" , NULL , NULL , NULL );
806+
807+ /* db2 should NOT see db1's table */
808+ {
809+ char * * result ;
810+ int nRow , nCol ;
811+ if (sqlite_get_table (db2 , "SELECT * FROM t" , & result , & nRow , & nCol , NULL ) != SQLITE_OK ) {
812+ ok = 1 ; /* Expected: table doesn't exist in db2 */
813+ } else {
814+ sqlite_free_table (result );
815+ ok = 0 ; /* Unexpected: table exists in db2 */
816+ }
817+ }
818+
819+ sqlite_close (db1 );
820+ sqlite_close (db2 );
821+ return ok ;
822+ }
823+
609824/*============================================================================
610825** Test Registry
611826**============================================================================*/
@@ -649,6 +864,21 @@ static TestCase g_tests[] = {
649864 { "MIN/MAX aggregate" , test_min_max },
650865 { "JOIN" , test_join },
651866
867+ /* Export/Import (0.2.0) */
868+ { "SQL quote escaping" , test_sql_quote_escape },
869+ { "sqlite_master tables" , test_sqlite_master_tables },
870+ { "sqlite_master indexes" , test_sqlite_master_indexes },
871+ { "Export DB schema" , test_export_db_schema },
872+ { "Export DB data" , test_export_db_data },
873+
874+ /* Error handling */
875+ { "Invalid SQL error" , test_invalid_sql },
876+ { "Missing table error" , test_missing_table },
877+ { "Constraint violation" , test_constraint_violation },
878+
879+ /* Memory databases */
880+ { "Memory DB isolation" , test_memory_isolation },
881+
652882 { NULL , NULL }
653883};
654884
0 commit comments