From 7cffc8807d2258649003eb4e33aad9e602a6a7a6 Mon Sep 17 00:00:00 2001 From: Stephen Lombardo Date: Thu, 5 May 2011 22:37:34 -0400 Subject: [PATCH] track 3.7.6.2 --- Makefile.in | 10 +- VERSION | 2 +- art/2005osaward.gif | Bin 0 -> 3750 bytes art/SQLite.eps | Bin 0 -> 24155 bytes art/SQLite.gif | Bin 0 -> 3062 bytes art/SQLiteLogo3.tiff | Bin 0 -> 85156 bytes art/SQLite_big.gif | Bin 0 -> 7428 bytes art/nocopy.gif | Bin 0 -> 3449 bytes art/powered_by_sqlite.gif | Bin 0 -> 3391 bytes art/sqlite370.eps | 5333 +++++++++++++++++++++++++++++++++++++ art/sqlite370.ico | Bin 0 -> 2238 bytes art/sqlite370.jpg | Bin 0 -> 80726 bytes art/src_logo.gif | Bin 0 -> 3348 bytes configure | 18 +- contrib/sqlitecon.tcl | 679 +++++ doc/lemon.html | 892 +++++++ doc/pager-invariants.txt | 76 + doc/vfs-shm.txt | 130 + ext/fts3/fts3.c | 442 ++- ext/fts3/fts3Int.h | 46 +- ext/fts3/fts3_aux.c | 470 ++++ ext/fts3/fts3_snippet.c | 13 +- ext/fts3/fts3_write.c | 494 ++-- ext/icu/icu.c | 4 +- ext/rtree/rtree.c | 47 +- ext/rtree/rtree6.test | 47 + ext/rtree/rtreeB.test | 34 + main.mk | 14 +- manifest | 309 ++- manifest.uuid | 2 +- src/alter.c | 28 +- src/analyze.c | 71 +- src/attach.c | 16 +- src/backup.c | 29 +- src/btmutex.c | 157 +- src/btree.c | 68 +- src/btree.h | 26 +- src/btreeInt.h | 6 +- src/build.c | 105 +- src/callback.c | 11 +- src/delete.c | 19 +- src/expr.c | 32 +- src/fkey.c | 5 +- src/func.c | 9 +- src/insert.c | 12 +- src/loadext.c | 5 + src/main.c | 108 +- src/malloc.c | 2 +- src/mem5.c | 5 +- src/mutex_os2.c | 123 +- src/mutex_w32.c | 2 +- src/os_os2.c | 1211 +++++++-- src/os_unix.c | 1028 ++++--- src/os_win.c | 5 +- src/pager.c | 81 +- src/pager.h | 2 +- src/pragma.c | 35 +- src/prepare.c | 14 +- src/printf.c | 6 +- src/select.c | 61 +- src/shell.c | 169 +- src/sqlite.h.in | 201 +- src/sqliteInt.h | 45 +- src/sqliteLimit.h | 2 +- src/status.c | 2 + src/tclsqlite.c | 10 +- src/test1.c | 109 + src/test4.c | 2 +- src/test7.c | 2 +- src/test_config.c | 6 + src/test_demovfs.c | 6 +- src/test_func.c | 5 + src/test_fuzzer.c | 944 +++++++ src/test_hexio.c | 7 +- src/test_multiplex.c | 580 ++-- src/test_multiplex.h | 91 + src/test_osinst.c | 4 +- src/test_server.c | 5 +- src/test_syscall.c | 674 +++++ src/test_thread.c | 8 +- src/test_vfstrace.c | 773 ++++++ src/test_wholenumber.c | 311 +++ src/trigger.c | 24 +- src/update.c | 4 +- src/utf.c | 8 +- src/util.c | 140 +- src/vacuum.c | 5 +- src/vdbe.c | 227 +- src/vdbe.h | 2 +- src/vdbeInt.h | 18 +- src/vdbeapi.c | 8 +- src/vdbeaux.c | 161 +- src/vdbeblob.c | 1 + src/vdbemem.c | 16 +- src/vdbetrace.c | 2 +- src/vtab.c | 12 +- src/wal.c | 196 +- src/wal.h | 35 +- src/where.c | 486 +++- test/alter.test | 19 + test/analyze.test | 10 +- test/analyze2.test | 38 +- test/analyze3.test | 2 +- test/analyze5.test | 241 ++ test/analyze6.test | 122 + test/analyze7.test | 111 + test/attach.test | 37 +- test/attach4.test | 116 + test/auth.test | 13 +- test/backcompat.test | 73 + test/badutf2.test | 121 + test/capi3.test | 6 +- test/capi3e.test | 30 +- test/corrupt3.test | 5 +- test/corruptA.test | 5 + test/corruptD.test | 5 + test/e_createtable.test | 2 +- test/e_expr.test | 12 + test/e_vacuum.test | 2 + test/eqp.test | 47 +- test/exclusive2.test | 5 + test/exists.test | 198 ++ test/expr.test | 119 +- test/filefmt.test | 20 + test/fkey4.test | 55 + test/fts3aux1.test | 462 ++++ test/fts3comp1.test | 115 + test/fts3corrupt.test | 27 + test/fts3defer2.test | 15 +- test/fts3fault2.test | 85 + test/fuzzer1.test | 1382 ++++++++++ test/incrvacuum2.test | 77 +- test/index.test | 2 +- test/indexedby.test | 4 +- test/like.test | 38 +- test/malloc.test | 2 +- test/malloc_common.tcl | 4 +- test/mem5.test | 66 + test/memdb.test | 14 +- test/minmax3.test | 1 + test/multiplex.test | 134 +- test/oserror.test | 122 + test/pager1.test | 2 +- test/pagerfault3.test | 1 + test/permutations.test | 3 + test/releasetest.tcl | 8 +- test/superlock.test | 12 +- test/syscall.test | 276 ++ test/sysfault.test | 247 ++ test/tableapi.test | 12 +- test/tester.tcl | 21 + test/thread1.test | 5 +- test/thread2.test | 6 +- test/thread_common.tcl | 3 + test/threadtest3.c | 6 +- test/tkt-752e1646fc.test | 30 + test/tkt-b72787b1.test | 77 + test/tkt-f7b4edec.test | 74 + test/tkt3824.test | 2 +- test/trace2.test | 4 +- test/tt3_checkpoint.c | 150 ++ test/unixexcl.test | 83 + test/unordered.test | 65 + test/vacuum2.test | 5 + test/wal.test | 93 +- test/wal2.test | 16 +- test/wal3.test | 9 +- test/wal5.test | 346 +++ test/wal_common.tcl | 2 + test/walfault.test | 10 +- test/where3.test | 6 +- test/where9.test | 2 +- tool/mksqlite3c.tcl | 3 +- tool/omittest.tcl | 230 +- tool/rollback-test.c | 155 ++ tool/shell1.test | 2 +- tool/split-sqlite3c.tcl | 82 + 177 files changed, 21128 insertions(+), 2529 deletions(-) create mode 100644 art/2005osaward.gif create mode 100644 art/SQLite.eps create mode 100644 art/SQLite.gif create mode 100644 art/SQLiteLogo3.tiff create mode 100644 art/SQLite_big.gif create mode 100644 art/nocopy.gif create mode 100644 art/powered_by_sqlite.gif create mode 100644 art/sqlite370.eps create mode 100644 art/sqlite370.ico create mode 100644 art/sqlite370.jpg create mode 100644 art/src_logo.gif create mode 100644 contrib/sqlitecon.tcl create mode 100644 doc/lemon.html create mode 100644 doc/pager-invariants.txt create mode 100644 doc/vfs-shm.txt create mode 100644 ext/fts3/fts3_aux.c create mode 100644 ext/rtree/rtreeB.test create mode 100644 src/test_fuzzer.c create mode 100644 src/test_multiplex.h create mode 100644 src/test_syscall.c create mode 100644 src/test_vfstrace.c create mode 100644 src/test_wholenumber.c create mode 100644 test/analyze5.test create mode 100644 test/analyze6.test create mode 100644 test/analyze7.test create mode 100644 test/attach4.test create mode 100644 test/badutf2.test create mode 100644 test/exists.test create mode 100644 test/fkey4.test create mode 100644 test/fts3aux1.test create mode 100644 test/fts3comp1.test create mode 100644 test/fts3fault2.test create mode 100644 test/fuzzer1.test create mode 100644 test/mem5.test create mode 100644 test/oserror.test create mode 100644 test/syscall.test create mode 100644 test/sysfault.test create mode 100644 test/tkt-752e1646fc.test create mode 100644 test/tkt-b72787b1.test create mode 100644 test/tkt-f7b4edec.test create mode 100644 test/tt3_checkpoint.c create mode 100644 test/unixexcl.test create mode 100644 test/unordered.test create mode 100644 test/wal5.test create mode 100644 tool/rollback-test.c create mode 100644 tool/split-sqlite3c.tcl diff --git a/Makefile.in b/Makefile.in index 0e2837c..f3239f3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -165,7 +165,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ backup.lo bitvec.lo btmutex.lo btree.lo build.lo \ callback.lo complete.lo ctime.lo date.lo delete.lo \ expr.lo fault.lo fkey.lo \ - fts3.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \ + fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \ fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo fts3_write.lo \ func.lo global.lo hash.lo \ icu.lo insert.lo journal.lo legacy.lo loadext.lo \ @@ -306,6 +306,7 @@ SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ + $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_hash.c \ $(TOP)/ext/fts3/fts3_hash.h \ @@ -355,6 +356,7 @@ TESTSRC = \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ + $(TOP)/src/test_fuzzer.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ @@ -370,10 +372,12 @@ TESTSRC = \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_superlock.c \ + $(TOP)/src/test_syscall.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ + $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c # Source code to the library files needed by the test fixture @@ -414,6 +418,7 @@ TESTSRC2 = \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ + $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ @@ -811,6 +816,9 @@ fts2_tokenizer1.lo: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) fts3.lo: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c +fts3_aux.lo: $(TOP)/ext/fts3/fts3_aux.c $(HDR) $(EXTHDR) + $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_aux.c + fts3_expr.lo: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c diff --git a/VERSION b/VERSION index aaf18d2..81aca73 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7.5 +3.7.6.2 diff --git a/art/2005osaward.gif b/art/2005osaward.gif new file mode 100644 index 0000000000000000000000000000000000000000..fa6d7d7c2761f603db7d88b16eb606cc7a29ff89 GIT binary patch literal 3750 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 QI-@jNqc$1?34s6rJAm}olmGw# literal 0 HcmV?d00001 diff --git a/art/SQLite.eps b/art/SQLite.eps new file mode 100644 index 0000000000000000000000000000000000000000..1f334ecf7b49ee6e596802d5cf8e208caebb034f GIT binary patch literal 24155 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 DOHLiR literal 0 HcmV?d00001 diff --git a/art/SQLite.gif b/art/SQLite.gif new file mode 100644 index 0000000000000000000000000000000000000000..5ec05b0be2aded7c2f7aafd23ff53107d39e4362 GIT binary patch literal 3062 zcmVgd_z;l$O@&)wPR@9lo1n3A%nwadh%z_;`E z_3rcW`1<+w`1k++|NZ^_EC2ui07n5~000F4(8)=wy*TU5yZ>M)j$~<`XsWJk>%MR- zn@>R?Nv}Ja7Zi~k4Q%VaWFWa(C7gnSR}95tajUa5E9X__<2YW7~Hga%}y4Y z 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 EI|CU?v;Y7A literal 0 HcmV?d00001 diff --git a/art/SQLiteLogo3.tiff b/art/SQLiteLogo3.tiff new file mode 100644 index 0000000000000000000000000000000000000000..70b88e78057d7aa046d489e025273ba38d774c1b GIT binary patch literal 85156 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%0A2vxehzOjQ>q9f8Q0p_RnbfbgOQW+j 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 literal 0 HcmV?d00001 diff --git a/art/SQLite_big.gif b/art/SQLite_big.gif new file mode 100644 index 0000000000000000000000000000000000000000..dc9e6a0e6cf97d4a4e601dac533c8166b72c4165 GIT binary patch literal 7428 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 CE;7mzbECnVFfInwp!No1LAV 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 bmSkC$W_gxqnU-p~mTcLUZuyqv5C{M}^9D)q literal 0 HcmV?d00001 diff --git a/art/powered_by_sqlite.gif b/art/powered_by_sqlite.gif new file mode 100644 index 0000000000000000000000000000000000000000..5bfed023ee754825e304cbccadaae6c0f874e555 GIT binary patch literal 3391 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 literal 0 HcmV?d00001 diff --git a/art/sqlite370.eps b/art/sqlite370.eps new file mode 100644 index 0000000..7701756 --- /dev/null +++ b/art/sqlite370.eps @@ -0,0 +1,5333 @@ +%!PS-Adobe-3.1 EPSF-3.0 +%ADO_DSC_Encoding: MacOS Roman +%%Title: logo_color_eps.eps +%%Creator: Adobe Illustrator(R) 13.0 +%%For: Mary Diarte +%%CreationDate: 5/25/10 +%%BoundingBox: 0 0 372 212 +%%HiResBoundingBox: 0 0 372 211.3042 +%%CropBox: 0 0 372 211.3042 +%%LanguageLevel: 2 +%%DocumentData: Clean7Bit +%ADOBeginClientInjection: DocumentHeader "AI11EPS" +%%AI8_CreatorVersion: 13.0.2 %AI9_PrintingDataBegin %AI3_Cropmarks: 36.0000 36.0000 336.0000 175.3042 +%ADO_BuildNumber: Adobe Illustrator(R) 13.0.2 x434 R agm 4.4379 ct 5.1039 %ADO_ContainsXMP: MainFirst %AI7_Thumbnail: 128 76 8 %%BeginData: 6312 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFD4AFFA90C0D %0C35A8FD79FF3505060506050CA8FD5AFF84360E0E0D140E0E0D140E0E0D %140E0E0D140E0E0D140E0E0D140E0E0D0D0506050C05060535FD59FF7E0E %3685608560856085608560856085608560856085608560856085600D0506 %05060506050605FD59FF365A856085618560856185608561856085618560 %856185608561856035050605060506050605067EFD58FF0D615A615A615A %615A615A615A615A615A615A615A615A615A615A5A050605060506050605 %060584FD58FF146185608561856085618560856185608561856085618560 %856161060C0506050C0606050C05067EFD58FF0D615A615A615A615A615A %615A615A615A615A615A615A615A610D0605060506060C050605060584FD %58FF0E5A855A615A855A615A855A615A855A615A855A615A855A61360605 %060506062F050605060506A8FD58FF0D5A5A615A5A5A615A5A5A615A5A5A %615A5A5A615A5A5A615A0C05060506055905060506050605FD59FF0E5A61 %5A615A615A615A615A615A615A615A615A615A615A5A050C0506052F0C06 %050C05060535FD59FF0D5A365B5A5A365B5A5A365B5A5A365B5A5A365B5A %5A365B0C0605060506590605060506050659FD59FF145A5B5A615A5B5A61 %5A5B5A615A5B5A615A5B5A615A5B360605060506350C05060506050606FD %5AFF0D5A365A365A365A365A365A365A365A365A365A365A360D05060506 %05840506050605060535FD5AFF14365B365B365B365B365B365B365B365B %365B365B365B050C0506055A0C06050C0506050CA8FD5AFF0D5A3636365A %3636365A3636365A3636365A3636365A0D060506050C5906050605060506 %0CFD5BFF0E365A365A365A365A365A365A365A365A365A365A360D050605 %06590C05060506050605A9FD5BFF0D363036303630363036303630363036 %3036303630360506050605A90506050605060535FD5CFF0EFD14360D0C05 %06055A2F06050C0506050CAFFD5CFF0D360E3630360E3630360E3630360E %3630360E36300D0506050684060506050605062FFD5DFF1436360E363636 %0E3636360E3636360E3636360E360C0605060C5905060506050606FD5EFF %0D360E360D360E360D360E360D360E360D360E360D0C0506058406060506 %05060CA9FD5EFF140E360E360E360E360E360E360E360E360E360E0D0506 %050CA806050C062F2FA9FFFF2F0C050C06350C84FD07FF5A0C0C060D050C %2FFD06FF0D0506050C0CFD3AFF0D360E140D360E140D360E140D360E140D %360E14060605062E3505060506050CFFFF0C0659FFFFA806065AFD05FF7E %0C0535A8FFFFFF590C0559FD05FF7E05062EFD3BFF0E0E360E140E360E14 %0E360E140E360E140E360E0C0506058406060506050684FF84062FFD05FF %0584FD04FF590605A9FD06FFA80C052FFD04FF84060584FD3BFF0D0E0D14 %0D0E0D140D0E0D140D0E0D140D0E0D0D050605067E06050605062FFFFF35 %055AFD05FF2F59FFFFFF7E060559FD08FF84060535FD04FF05065AFD09FF %060CFD30FF0E0E140E140E140E140E140E140E140E140E140D06050C0C5A %050C05060CFFFFFF2F0C0CFD05FFA8FD04FF0D050CFD0AFF2F0605AFFFFF %FF0C0584FD09FF0D05AFFD05FFA9FD29FF0D0E0D140D0E0D140D0E0D140D %0E0D140D0E0D0D050605590606050605AFFFFFFF5905062FFD08FF840506 %2FFD0AFFA905062EFFFFFF05065AFD0AFFA9FD05FF5A06FD29FF140E140D %140E140D140E140D140E140D140E140D0C05065906050606A9FD04FFA80C %05060CFD07FF2F060584FD0BFF0C050CFFFFFF060584FD10FF060CFD29FF %0D0E0D0E0D0E0D0E0D0E0D0E0D0E0D0E0D0E0D0E0606055A05062FFD07FF %2F0605060559FD05FF0C0506FD0CFF060605FFFFFF050659FD07FFA85A2E %0C06FFFF350506050C0684FD04FF0C06050C59FD1DFF140E140D140E140D %140E140D140E0E0D140E0E0D0E050C2F0C84FD09FF350C0506050DAFFFFF %FF0C0605FD0CFF0D0506FFFFFF060584FD07FFA92F0C050DFFFF7E5A050C %7E84AFFFFFFF060C2F0C050CA9FD1CFF0D0E0D0E0D0E0D0E0D0E0D0E0D0E %0D0E0D0E0D0E0D060C0C59FD0BFF840C0506050CA8FFFF0C0506FD0CFF06 %0605FFFFA9050659FD09FF840606FFFFFF590606FD05FF060C84FF59060C %FD1DFF0E0D140E0E0D140E0E0D140E0E0D140E0E0D140E0E06590CFD0EFF %5A0606050DFFFF0C060584FD0BFF0C050CFFFF7E060584FD0AFF050CFFFF %FF84050DFD04FF840635FFFF2F05FD1EFF0D0E0D0E0D0E0D0E0D0E0D0E0D %0E0D0E0D0E0D0E0D0C2E84FD0FFF84050605FFFF59050659FD0BFF05060C %FFFF8405065AFD09FF590606FFFFFF5A0606FD04FF59055A590C0584FD1E %FF0E0D140E0E0D140E0E0D140E0E0D140E0E0D140E0E0684FD0AFF59FD06 %FF5A050C7EFFFF0C052FFD0AFF59060584FFFF7E0C0584FD09FF84050DFF %FFFF84050DFD04FF2F0606355AFD20FF0D0E0D0E0D0E0D0E0D0E0D0E0D0E %0D0E0D0E0D0E0D0C59FD0AFF0C59FD05FFA80605A9FFFF59060584FD09FF %0C050CFFFFFF8405065AFD09FF590606FFFFFF5A062EFD04FF350559FD05 %FFA9FD1CFF140E0E0D140E0E0D140E0E0D140E0E0D140E0E0D140684FD0A %FF0C2FFD05FF84050CFD04FF2F0606FD08FF35050CAFFFFFFF7E060584FD %05FF8459FFFF84050DFFFFFF840535FFFF84847E0606FD04FF8406FD1CFF %5A0E0D0E0D0E0D0E0D0E0D0E0D0E0D0E0D0E0D0E0D0C84FD0AFF0C0559FD %04FF060659FD05FF2E0606A8FD04FFA82F050CA8FD04FF5A05062EFD05FF %0559FFFF5A0605FFFFFF590605842F0C7EFF0506065A592F055AFD1DFF5A %0E0D140E0E0D140E0E0D140E0E0D140E0E0D140CA8FD0AFF0C0C060D2F2F %050C59FD07FF7E0D052F2F590C0C065AFD05FF350506050C060D0C0C0506 %7EFF2F0C050C0C5AFFFF0C06050C84FFFFA90C06050C0635FD1FFFA98584 %A9848584A9848584A9848584A9848584355AFD0AFF84A8FF7E5A2F5AA8FD %0AFFA80C0605062EFD07FF7E845A8459845A8459845AAFFD0AFF5A84FD06 %FFA88484FD34FF3584FD1EFF0C06050659FD5BFF842FFD1FFF0C0605067E %FD5AFFA935FD20FF2F06050C59FD5AFF06FD21FF59060506062F2F5AFD7A %FF8435592F5984FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFC %FFFDD1FFFF %%EndData +%ADOEndClientInjection: DocumentHeader "AI11EPS" +%%Pages: 1 +%%DocumentNeededResources: +%%DocumentSuppliedResources: procset Adobe_AGM_Image 1.0 0 +%%+ procset Adobe_CoolType_Utility_T42 1.0 0 +%%+ procset Adobe_CoolType_Utility_MAKEOCF 1.23 0 +%%+ procset Adobe_CoolType_Core 2.31 0 +%%+ procset Adobe_AGM_Core 2.0 0 +%%+ procset Adobe_AGM_Utils 1.0 0 +%%DocumentFonts: +%%DocumentNeededFonts: +%%DocumentNeededFeatures: +%%DocumentSuppliedFeatures: +%%DocumentProcessColors: +%%DocumentCustomColors: (PANTONE 2905 U) +%%+ (PANTONE 309 U) +%%+ (PANTONE 3005 U) +%%CMYKCustomColor: 0.4100 0.0200 0 0 (PANTONE 2905 U) +%%+ 1 0 0.0900 0.7200 (PANTONE 309 U) +%%+ 1 0.3400 0 0.0200 (PANTONE 3005 U) +%%RGBCustomColor: +%%EndComments + + + + + + +%%BeginDefaults +%%ViewingOrientation: 1 0 0 1 +%%EndDefaults +%%BeginProlog +%%BeginResource: procset Adobe_AGM_Utils 1.0 0 +%%Version: 1.0 0 +%%Copyright: Copyright(C)2000-2006 Adobe Systems, Inc. All Rights Reserved. +systemdict/setpacking known +{currentpacking true setpacking}if +userdict/Adobe_AGM_Utils 73 dict dup begin put +/bdf +{bind def}bind def +/nd{null def}bdf +/xdf +{exch def}bdf +/ldf +{load def}bdf +/ddf +{put}bdf +/xddf +{3 -1 roll put}bdf +/xpt +{exch put}bdf +/ndf +{ + exch dup where{ + pop pop pop + }{ + xdf + }ifelse +}def +/cdndf +{ + exch dup currentdict exch known{ + pop pop + }{ + exch def + }ifelse +}def +/gx +{get exec}bdf +/ps_level + /languagelevel where{ + pop systemdict/languagelevel gx + }{ + 1 + }ifelse +def +/level2 + ps_level 2 ge +def +/level3 + ps_level 3 ge +def +/ps_version + {version cvr}stopped{-1}if +def +/set_gvm +{currentglobal exch setglobal}bdf +/reset_gvm +{setglobal}bdf +/makereadonlyarray +{ + /packedarray where{pop packedarray + }{ + array astore readonly}ifelse +}bdf +/map_reserved_ink_name +{ + dup type/stringtype eq{ + dup/Red eq{ + pop(_Red_) + }{ + dup/Green eq{ + pop(_Green_) + }{ + dup/Blue eq{ + pop(_Blue_) + }{ + dup()cvn eq{ + pop(Process) + }if + }ifelse + }ifelse + }ifelse + }if +}bdf +/AGMUTIL_GSTATE 22 dict def +/get_gstate +{ + AGMUTIL_GSTATE begin + /AGMUTIL_GSTATE_clr_spc currentcolorspace def + /AGMUTIL_GSTATE_clr_indx 0 def + /AGMUTIL_GSTATE_clr_comps 12 array def + mark currentcolor counttomark + {AGMUTIL_GSTATE_clr_comps AGMUTIL_GSTATE_clr_indx 3 -1 roll put + /AGMUTIL_GSTATE_clr_indx AGMUTIL_GSTATE_clr_indx 1 add def}repeat pop + /AGMUTIL_GSTATE_fnt rootfont def + /AGMUTIL_GSTATE_lw currentlinewidth def + /AGMUTIL_GSTATE_lc currentlinecap def + /AGMUTIL_GSTATE_lj currentlinejoin def + /AGMUTIL_GSTATE_ml currentmiterlimit def + currentdash/AGMUTIL_GSTATE_do xdf/AGMUTIL_GSTATE_da xdf + /AGMUTIL_GSTATE_sa currentstrokeadjust def + /AGMUTIL_GSTATE_clr_rnd currentcolorrendering def + /AGMUTIL_GSTATE_op currentoverprint def + /AGMUTIL_GSTATE_bg currentblackgeneration cvlit def + /AGMUTIL_GSTATE_ucr currentundercolorremoval cvlit def + currentcolortransfer cvlit/AGMUTIL_GSTATE_gy_xfer xdf cvlit/AGMUTIL_GSTATE_b_xfer xdf + cvlit/AGMUTIL_GSTATE_g_xfer xdf cvlit/AGMUTIL_GSTATE_r_xfer xdf + /AGMUTIL_GSTATE_ht currenthalftone def + /AGMUTIL_GSTATE_flt currentflat def + end +}def +/set_gstate +{ + AGMUTIL_GSTATE begin + AGMUTIL_GSTATE_clr_spc setcolorspace + AGMUTIL_GSTATE_clr_indx{AGMUTIL_GSTATE_clr_comps AGMUTIL_GSTATE_clr_indx 1 sub get + /AGMUTIL_GSTATE_clr_indx AGMUTIL_GSTATE_clr_indx 1 sub def}repeat setcolor + AGMUTIL_GSTATE_fnt setfont + AGMUTIL_GSTATE_lw setlinewidth + AGMUTIL_GSTATE_lc setlinecap + AGMUTIL_GSTATE_lj setlinejoin + AGMUTIL_GSTATE_ml setmiterlimit + AGMUTIL_GSTATE_da AGMUTIL_GSTATE_do setdash + AGMUTIL_GSTATE_sa setstrokeadjust + AGMUTIL_GSTATE_clr_rnd setcolorrendering + AGMUTIL_GSTATE_op setoverprint + AGMUTIL_GSTATE_bg cvx setblackgeneration + AGMUTIL_GSTATE_ucr cvx setundercolorremoval + AGMUTIL_GSTATE_r_xfer cvx AGMUTIL_GSTATE_g_xfer cvx AGMUTIL_GSTATE_b_xfer cvx + AGMUTIL_GSTATE_gy_xfer cvx setcolortransfer + AGMUTIL_GSTATE_ht/HalftoneType get dup 9 eq exch 100 eq or + { + currenthalftone/HalftoneType get AGMUTIL_GSTATE_ht/HalftoneType get ne + { + mark AGMUTIL_GSTATE_ht{sethalftone}stopped cleartomark + }if + }{ + AGMUTIL_GSTATE_ht sethalftone + }ifelse + AGMUTIL_GSTATE_flt setflat + end +}def +/get_gstate_and_matrix +{ + AGMUTIL_GSTATE begin + /AGMUTIL_GSTATE_ctm matrix currentmatrix def + end + get_gstate +}def +/set_gstate_and_matrix +{ + set_gstate + AGMUTIL_GSTATE begin + AGMUTIL_GSTATE_ctm setmatrix + end +}def +/AGMUTIL_str256 256 string def +/AGMUTIL_src256 256 string def +/AGMUTIL_dst64 64 string def +/AGMUTIL_srcLen nd +/AGMUTIL_ndx nd +/AGMUTIL_cpd nd +/capture_cpd{ + //Adobe_AGM_Utils/AGMUTIL_cpd currentpagedevice ddf +}def +/thold_halftone +{ + level3 + {sethalftone currenthalftone} + { + dup/HalftoneType get 3 eq + { + sethalftone currenthalftone + }{ + begin + Width Height mul{ + Thresholds read{pop}if + }repeat + end + currenthalftone + }ifelse + }ifelse +}def +/rdcmntline +{ + currentfile AGMUTIL_str256 readline pop + (%)anchorsearch{pop}if +}bdf +/filter_cmyk +{ + dup type/filetype ne{ + exch()/SubFileDecode filter + }{ + exch pop + } + ifelse + [ + exch + { + AGMUTIL_src256 readstring pop + dup length/AGMUTIL_srcLen exch def + /AGMUTIL_ndx 0 def + AGMCORE_plate_ndx 4 AGMUTIL_srcLen 1 sub{ + 1 index exch get + AGMUTIL_dst64 AGMUTIL_ndx 3 -1 roll put + /AGMUTIL_ndx AGMUTIL_ndx 1 add def + }for + pop + AGMUTIL_dst64 0 AGMUTIL_ndx getinterval + } + bind + /exec cvx + ]cvx +}bdf +/filter_indexed_devn +{ + cvi Names length mul names_index add Lookup exch get +}bdf +/filter_devn +{ + 4 dict begin + /srcStr xdf + /dstStr xdf + dup type/filetype ne{ + 0()/SubFileDecode filter + }if + [ + exch + [ + /devicen_colorspace_dict/AGMCORE_gget cvx/begin cvx + currentdict/srcStr get/readstring cvx/pop cvx + /dup cvx/length cvx 0/gt cvx[ + Adobe_AGM_Utils/AGMUTIL_ndx 0/ddf cvx + names_index Names length currentdict/srcStr get length 1 sub{ + 1/index cvx/exch cvx/get cvx + currentdict/dstStr get/AGMUTIL_ndx/load cvx 3 -1/roll cvx/put cvx + Adobe_AGM_Utils/AGMUTIL_ndx/AGMUTIL_ndx/load cvx 1/add cvx/ddf cvx + }for + currentdict/dstStr get 0/AGMUTIL_ndx/load cvx/getinterval cvx + ]cvx/if cvx + /end cvx + ]cvx + bind + /exec cvx + ]cvx + end +}bdf +/AGMUTIL_imagefile nd +/read_image_file +{ + AGMUTIL_imagefile 0 setfileposition + 10 dict begin + /imageDict xdf + /imbufLen Width BitsPerComponent mul 7 add 8 idiv def + /imbufIdx 0 def + /origDataSource imageDict/DataSource get def + /origMultipleDataSources imageDict/MultipleDataSources get def + /origDecode imageDict/Decode get def + /dstDataStr imageDict/Width get colorSpaceElemCnt mul string def + imageDict/MultipleDataSources known{MultipleDataSources}{false}ifelse + { + /imbufCnt imageDict/DataSource get length def + /imbufs imbufCnt array def + 0 1 imbufCnt 1 sub{ + /imbufIdx xdf + imbufs imbufIdx imbufLen string put + imageDict/DataSource get imbufIdx[AGMUTIL_imagefile imbufs imbufIdx get/readstring cvx/pop cvx]cvx put + }for + DeviceN_PS2{ + imageDict begin + /DataSource[DataSource/devn_sep_datasource cvx]cvx def + /MultipleDataSources false def + /Decode[0 1]def + end + }if + }{ + /imbuf imbufLen string def + Indexed_DeviceN level3 not and DeviceN_NoneName or{ + /srcDataStrs[imageDict begin + currentdict/MultipleDataSources known{MultipleDataSources{DataSource length}{1}ifelse}{1}ifelse + { + Width Decode length 2 div mul cvi string + }repeat + end]def + imageDict begin + /DataSource[AGMUTIL_imagefile Decode BitsPerComponent false 1/filter_indexed_devn load dstDataStr srcDataStrs devn_alt_datasource/exec cvx]cvx def + /Decode[0 1]def + end + }{ + imageDict/DataSource[1 string dup 0 AGMUTIL_imagefile Decode length 2 idiv string/readstring cvx/pop cvx names_index/get cvx/put cvx]cvx put + imageDict/Decode[0 1]put + }ifelse + }ifelse + imageDict exch + load exec + imageDict/DataSource origDataSource put + imageDict/MultipleDataSources origMultipleDataSources put + imageDict/Decode origDecode put + end +}bdf +/write_image_file +{ + begin + {(AGMUTIL_imagefile)(w+)file}stopped{ + false + }{ + Adobe_AGM_Utils/AGMUTIL_imagefile xddf + 2 dict begin + /imbufLen Width BitsPerComponent mul 7 add 8 idiv def + MultipleDataSources{DataSource 0 get}{DataSource}ifelse type/filetype eq{ + /imbuf imbufLen string def + }if + 1 1 Height MultipleDataSources not{Decode length 2 idiv mul}if{ + pop + MultipleDataSources{ + 0 1 DataSource length 1 sub{ + DataSource type dup + /arraytype eq{ + pop DataSource exch gx + }{ + /filetype eq{ + DataSource exch get imbuf readstring pop + }{ + DataSource exch get + }ifelse + }ifelse + AGMUTIL_imagefile exch writestring + }for + }{ + DataSource type dup + /arraytype eq{ + pop DataSource exec + }{ + /filetype eq{ + DataSource imbuf readstring pop + }{ + DataSource + }ifelse + }ifelse + AGMUTIL_imagefile exch writestring + }ifelse + }for + end + true + }ifelse + end +}bdf +/close_image_file +{ + AGMUTIL_imagefile closefile(AGMUTIL_imagefile)deletefile +}def +statusdict/product known userdict/AGMP_current_show known not and{ + /pstr statusdict/product get def + pstr(HP LaserJet 2200)eq + pstr(HP LaserJet 4000 Series)eq or + pstr(HP LaserJet 4050 Series )eq or + pstr(HP LaserJet 8000 Series)eq or + pstr(HP LaserJet 8100 Series)eq or + pstr(HP LaserJet 8150 Series)eq or + pstr(HP LaserJet 5000 Series)eq or + pstr(HP LaserJet 5100 Series)eq or + pstr(HP Color LaserJet 4500)eq or + pstr(HP Color LaserJet 4600)eq or + pstr(HP LaserJet 5Si)eq or + pstr(HP LaserJet 1200 Series)eq or + pstr(HP LaserJet 1300 Series)eq or + pstr(HP LaserJet 4100 Series)eq or + { + userdict/AGMP_current_show/show load put + userdict/show{ + currentcolorspace 0 get + /Pattern eq + {false charpath f} + {AGMP_current_show}ifelse + }put + }if + currentdict/pstr undef +}if +/consumeimagedata +{ + begin + AGMIMG_init_common + currentdict/MultipleDataSources known not + {/MultipleDataSources false def}if + MultipleDataSources + { + DataSource 0 get type + dup/filetype eq + { + 1 dict begin + /flushbuffer Width cvi string def + 1 1 Height cvi + { + pop + 0 1 DataSource length 1 sub + { + DataSource exch get + flushbuffer readstring pop pop + }for + }for + end + }if + dup/arraytype eq exch/packedarraytype eq or DataSource 0 get xcheck and + { + Width Height mul cvi + { + 0 1 DataSource length 1 sub + {dup DataSource exch gx length exch 0 ne{pop}if}for + dup 0 eq + {pop exit}if + sub dup 0 le + {exit}if + }loop + pop + }if + } + { + /DataSource load type + dup/filetype eq + { + 1 dict begin + /flushbuffer Width Decode length 2 idiv mul cvi string def + 1 1 Height{pop DataSource flushbuffer readstring pop pop}for + end + }if + dup/arraytype eq exch/packedarraytype eq or/DataSource load xcheck and + { + Height Width BitsPerComponent mul 8 BitsPerComponent sub add 8 idiv Decode length 2 idiv mul mul + { + DataSource length dup 0 eq + {pop exit}if + sub dup 0 le + {exit}if + }loop + pop + }if + }ifelse + end +}bdf +/addprocs +{ + 2{/exec load}repeat + 3 1 roll + [5 1 roll]bind cvx +}def +/modify_halftone_xfer +{ + currenthalftone dup length dict copy begin + currentdict 2 index known{ + 1 index load dup length dict copy begin + currentdict/TransferFunction known{ + /TransferFunction load + }{ + currenttransfer + }ifelse + addprocs/TransferFunction xdf + currentdict end def + currentdict end sethalftone + }{ + currentdict/TransferFunction known{ + /TransferFunction load + }{ + currenttransfer + }ifelse + addprocs/TransferFunction xdf + currentdict end sethalftone + pop + }ifelse +}def +/clonearray +{ + dup xcheck exch + dup length array exch + Adobe_AGM_Core/AGMCORE_tmp -1 ddf + { + Adobe_AGM_Core/AGMCORE_tmp 2 copy get 1 add ddf + dup type/dicttype eq + { + Adobe_AGM_Core/AGMCORE_tmp get + exch + clonedict + Adobe_AGM_Core/AGMCORE_tmp 4 -1 roll ddf + }if + dup type/arraytype eq + { + Adobe_AGM_Core/AGMCORE_tmp get exch + clonearray + Adobe_AGM_Core/AGMCORE_tmp 4 -1 roll ddf + }if + exch dup + Adobe_AGM_Core/AGMCORE_tmp get 4 -1 roll put + }forall + exch{cvx}if +}bdf +/clonedict +{ + dup length dict + begin + { + dup type/dicttype eq + {clonedict}if + dup type/arraytype eq + {clonearray}if + def + }forall + currentdict + end +}bdf +/DeviceN_PS2 +{ + /currentcolorspace AGMCORE_gget 0 get/DeviceN eq level3 not and +}bdf +/Indexed_DeviceN +{ + /indexed_colorspace_dict AGMCORE_gget dup null ne{ + dup/CSDBase known{ + /CSDBase get/CSD get_res/Names known + }{ + pop false + }ifelse + }{ + pop false + }ifelse +}bdf +/DeviceN_NoneName +{ + /Names where{ + pop + false Names + { + (None)eq or + }forall + }{ + false + }ifelse +}bdf +/DeviceN_PS2_inRip_seps +{ + /AGMCORE_in_rip_sep where + { + pop dup type dup/arraytype eq exch/packedarraytype eq or + { + dup 0 get/DeviceN eq level3 not and AGMCORE_in_rip_sep and + { + /currentcolorspace exch AGMCORE_gput + false + }{ + true + }ifelse + }{ + true + }ifelse + }{ + true + }ifelse +}bdf +/base_colorspace_type +{ + dup type/arraytype eq{0 get}if +}bdf +/currentdistillerparams where{pop currentdistillerparams/CoreDistVersion get 5000 lt}{true}ifelse +{ + /pdfmark_5{cleartomark}bind def +}{ + /pdfmark_5{pdfmark}bind def +}ifelse +/ReadBypdfmark_5 +{ + currentfile exch 0 exch/SubFileDecode filter + /currentdistillerparams where + {pop currentdistillerparams/CoreDistVersion get 5000 lt}{true}ifelse + {flushfile cleartomark} + {/PUT pdfmark}ifelse +}bdf +/xpdfm +{ + { + dup 0 get/Label eq + { + aload length[exch 1 add 1 roll/PAGELABEL + }{ + aload pop + [{ThisPage}<<5 -2 roll>>/PUT + }ifelse + pdfmark_5 + }forall +}bdf +/ds{ + Adobe_AGM_Utils begin +}bdf +/dt{ + currentdict Adobe_AGM_Utils eq{ + end + }if +}bdf +systemdict/setpacking known +{setpacking}if +%%EndResource +%%BeginResource: procset Adobe_AGM_Core 2.0 0 +%%Version: 2.0 0 +%%Copyright: Copyright(C)1997-2007 Adobe Systems, Inc. All Rights Reserved. +systemdict/setpacking known +{ + currentpacking + true setpacking +}if +userdict/Adobe_AGM_Core 209 dict dup begin put +/Adobe_AGM_Core_Id/Adobe_AGM_Core_2.0_0 def +/AGMCORE_str256 256 string def +/AGMCORE_save nd +/AGMCORE_graphicsave nd +/AGMCORE_c 0 def +/AGMCORE_m 0 def +/AGMCORE_y 0 def +/AGMCORE_k 0 def +/AGMCORE_cmykbuf 4 array def +/AGMCORE_screen[currentscreen]cvx def +/AGMCORE_tmp 0 def +/AGMCORE_&setgray nd +/AGMCORE_&setcolor nd +/AGMCORE_&setcolorspace nd +/AGMCORE_&setcmykcolor nd +/AGMCORE_cyan_plate nd +/AGMCORE_magenta_plate nd +/AGMCORE_yellow_plate nd +/AGMCORE_black_plate nd +/AGMCORE_plate_ndx nd +/AGMCORE_get_ink_data nd +/AGMCORE_is_cmyk_sep nd +/AGMCORE_host_sep nd +/AGMCORE_avoid_L2_sep_space nd +/AGMCORE_distilling nd +/AGMCORE_composite_job nd +/AGMCORE_producing_seps nd +/AGMCORE_ps_level -1 def +/AGMCORE_ps_version -1 def +/AGMCORE_environ_ok nd +/AGMCORE_CSD_cache 0 dict def +/AGMCORE_currentoverprint false def +/AGMCORE_deltaX nd +/AGMCORE_deltaY nd +/AGMCORE_name nd +/AGMCORE_sep_special nd +/AGMCORE_err_strings 4 dict def +/AGMCORE_cur_err nd +/AGMCORE_current_spot_alias false def +/AGMCORE_inverting false def +/AGMCORE_feature_dictCount nd +/AGMCORE_feature_opCount nd +/AGMCORE_feature_ctm nd +/AGMCORE_ConvertToProcess false def +/AGMCORE_Default_CTM matrix def +/AGMCORE_Default_PageSize nd +/AGMCORE_Default_flatness nd +/AGMCORE_currentbg nd +/AGMCORE_currentucr nd +/AGMCORE_pattern_paint_type 0 def +/knockout_unitsq nd +currentglobal true setglobal +[/CSA/Gradient/Procedure] +{ + /Generic/Category findresource dup length dict copy/Category defineresource pop +}forall +setglobal +/AGMCORE_key_known +{ + where{ + /Adobe_AGM_Core_Id known + }{ + false + }ifelse +}ndf +/flushinput +{ + save + 2 dict begin + /CompareBuffer 3 -1 roll def + /readbuffer 256 string def + mark + { + currentfile readbuffer{readline}stopped + {cleartomark mark} + { + not + {pop exit} + if + CompareBuffer eq + {exit} + if + }ifelse + }loop + cleartomark + end + restore +}bdf +/getspotfunction +{ + AGMCORE_screen exch pop exch pop + dup type/dicttype eq{ + dup/HalftoneType get 1 eq{ + /SpotFunction get + }{ + dup/HalftoneType get 2 eq{ + /GraySpotFunction get + }{ + pop + { + abs exch abs 2 copy add 1 gt{ + 1 sub dup mul exch 1 sub dup mul add 1 sub + }{ + dup mul exch dup mul add 1 exch sub + }ifelse + }bind + }ifelse + }ifelse + }if +}def +/np +{newpath}bdf +/clp_npth +{clip np}def +/eoclp_npth +{eoclip np}def +/npth_clp +{np clip}def +/graphic_setup +{ + /AGMCORE_graphicsave save store + concat + 0 setgray + 0 setlinecap + 0 setlinejoin + 1 setlinewidth + []0 setdash + 10 setmiterlimit + np + false setoverprint + false setstrokeadjust + //Adobe_AGM_Core/spot_alias gx + /Adobe_AGM_Image where{ + pop + Adobe_AGM_Image/spot_alias 2 copy known{ + gx + }{ + pop pop + }ifelse + }if + /sep_colorspace_dict null AGMCORE_gput + 100 dict begin + /dictstackcount countdictstack def + /showpage{}def + mark +}def +/graphic_cleanup +{ + cleartomark + dictstackcount 1 countdictstack 1 sub{end}for + end + AGMCORE_graphicsave restore +}def +/compose_error_msg +{ + grestoreall initgraphics + /Helvetica findfont 10 scalefont setfont + /AGMCORE_deltaY 100 def + /AGMCORE_deltaX 310 def + clippath pathbbox np pop pop 36 add exch 36 add exch moveto + 0 AGMCORE_deltaY rlineto AGMCORE_deltaX 0 rlineto + 0 AGMCORE_deltaY neg rlineto AGMCORE_deltaX neg 0 rlineto closepath + 0 AGMCORE_&setgray + gsave 1 AGMCORE_&setgray fill grestore + 1 setlinewidth gsave stroke grestore + currentpoint AGMCORE_deltaY 15 sub add exch 8 add exch moveto + /AGMCORE_deltaY 12 def + /AGMCORE_tmp 0 def + AGMCORE_err_strings exch get + { + dup 32 eq + { + pop + AGMCORE_str256 0 AGMCORE_tmp getinterval + stringwidth pop currentpoint pop add AGMCORE_deltaX 28 add gt + { + currentpoint AGMCORE_deltaY sub exch pop + clippath pathbbox pop pop pop 44 add exch moveto + }if + AGMCORE_str256 0 AGMCORE_tmp getinterval show( )show + 0 1 AGMCORE_str256 length 1 sub + { + AGMCORE_str256 exch 0 put + }for + /AGMCORE_tmp 0 def + }{ + AGMCORE_str256 exch AGMCORE_tmp xpt + /AGMCORE_tmp AGMCORE_tmp 1 add def + }ifelse + }forall +}bdf +/AGMCORE_CMYKDeviceNColorspaces[ + [/Separation/None/DeviceCMYK{0 0 0}] + [/Separation(Black)/DeviceCMYK{0 0 0 4 -1 roll}bind] + [/Separation(Yellow)/DeviceCMYK{0 0 3 -1 roll 0}bind] + [/DeviceN[(Yellow)(Black)]/DeviceCMYK{0 0 4 2 roll}bind] + [/Separation(Magenta)/DeviceCMYK{0 exch 0 0}bind] + [/DeviceN[(Magenta)(Black)]/DeviceCMYK{0 3 1 roll 0 exch}bind] + [/DeviceN[(Magenta)(Yellow)]/DeviceCMYK{0 3 1 roll 0}bind] + [/DeviceN[(Magenta)(Yellow)(Black)]/DeviceCMYK{0 4 1 roll}bind] + [/Separation(Cyan)/DeviceCMYK{0 0 0}] + [/DeviceN[(Cyan)(Black)]/DeviceCMYK{0 0 3 -1 roll}bind] + [/DeviceN[(Cyan)(Yellow)]/DeviceCMYK{0 exch 0}bind] + [/DeviceN[(Cyan)(Yellow)(Black)]/DeviceCMYK{0 3 1 roll}bind] + [/DeviceN[(Cyan)(Magenta)]/DeviceCMYK{0 0}] + [/DeviceN[(Cyan)(Magenta)(Black)]/DeviceCMYK{0 exch}bind] + [/DeviceN[(Cyan)(Magenta)(Yellow)]/DeviceCMYK{0}] + [/DeviceCMYK] +]def +/ds{ + Adobe_AGM_Core begin + /currentdistillerparams where + { + pop currentdistillerparams/CoreDistVersion get 5000 lt + {<>setdistillerparams}if + }if + /AGMCORE_ps_version xdf + /AGMCORE_ps_level xdf + errordict/AGM_handleerror known not{ + errordict/AGM_handleerror errordict/handleerror get put + errordict/handleerror{ + Adobe_AGM_Core begin + $error/newerror get AGMCORE_cur_err null ne and{ + $error/newerror false put + AGMCORE_cur_err compose_error_msg + }if + $error/newerror true put + end + errordict/AGM_handleerror get exec + }bind put + }if + /AGMCORE_environ_ok + ps_level AGMCORE_ps_level ge + ps_version AGMCORE_ps_version ge and + AGMCORE_ps_level -1 eq or + def + AGMCORE_environ_ok not + {/AGMCORE_cur_err/AGMCORE_bad_environ def}if + /AGMCORE_&setgray systemdict/setgray get def + level2{ + /AGMCORE_&setcolor systemdict/setcolor get def + /AGMCORE_&setcolorspace systemdict/setcolorspace get def + }if + /AGMCORE_currentbg currentblackgeneration def + /AGMCORE_currentucr currentundercolorremoval def + /AGMCORE_Default_flatness currentflat def + /AGMCORE_distilling + /product where{ + pop systemdict/setdistillerparams known product(Adobe PostScript Parser)ne and + }{ + false + }ifelse + def + /AGMCORE_GSTATE AGMCORE_key_known not{ + /AGMCORE_GSTATE 21 dict def + /AGMCORE_tmpmatrix matrix def + /AGMCORE_gstack 32 array def + /AGMCORE_gstackptr 0 def + /AGMCORE_gstacksaveptr 0 def + /AGMCORE_gstackframekeys 14 def + /AGMCORE_&gsave/gsave ldf + /AGMCORE_&grestore/grestore ldf + /AGMCORE_&grestoreall/grestoreall ldf + /AGMCORE_&save/save ldf + /AGMCORE_&setoverprint/setoverprint ldf + /AGMCORE_gdictcopy{ + begin + {def}forall + end + }def + /AGMCORE_gput{ + AGMCORE_gstack AGMCORE_gstackptr get + 3 1 roll + put + }def + /AGMCORE_gget{ + AGMCORE_gstack AGMCORE_gstackptr get + exch + get + }def + /gsave{ + AGMCORE_&gsave + AGMCORE_gstack AGMCORE_gstackptr get + AGMCORE_gstackptr 1 add + dup 32 ge{limitcheck}if + /AGMCORE_gstackptr exch store + AGMCORE_gstack AGMCORE_gstackptr get + AGMCORE_gdictcopy + }def + /grestore{ + AGMCORE_&grestore + AGMCORE_gstackptr 1 sub + dup AGMCORE_gstacksaveptr lt{1 add}if + dup AGMCORE_gstack exch get dup/AGMCORE_currentoverprint known + {/AGMCORE_currentoverprint get setoverprint}{pop}ifelse + /AGMCORE_gstackptr exch store + }def + /grestoreall{ + AGMCORE_&grestoreall + /AGMCORE_gstackptr AGMCORE_gstacksaveptr store + }def + /save{ + AGMCORE_&save + AGMCORE_gstack AGMCORE_gstackptr get + AGMCORE_gstackptr 1 add + dup 32 ge{limitcheck}if + /AGMCORE_gstackptr exch store + /AGMCORE_gstacksaveptr AGMCORE_gstackptr store + AGMCORE_gstack AGMCORE_gstackptr get + AGMCORE_gdictcopy + }def + /setoverprint{ + dup/AGMCORE_currentoverprint exch AGMCORE_gput AGMCORE_&setoverprint + }def + 0 1 AGMCORE_gstack length 1 sub{ + AGMCORE_gstack exch AGMCORE_gstackframekeys dict put + }for + }if + level3/AGMCORE_&sysshfill AGMCORE_key_known not and + { + /AGMCORE_&sysshfill systemdict/shfill get def + /AGMCORE_&sysmakepattern systemdict/makepattern get def + /AGMCORE_&usrmakepattern/makepattern load def + }if + /currentcmykcolor[0 0 0 0]AGMCORE_gput + /currentstrokeadjust false AGMCORE_gput + /currentcolorspace[/DeviceGray]AGMCORE_gput + /sep_tint 0 AGMCORE_gput + /devicen_tints[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]AGMCORE_gput + /sep_colorspace_dict null AGMCORE_gput + /devicen_colorspace_dict null AGMCORE_gput + /indexed_colorspace_dict null AGMCORE_gput + /currentcolor_intent()AGMCORE_gput + /customcolor_tint 1 AGMCORE_gput + /absolute_colorimetric_crd null AGMCORE_gput + /relative_colorimetric_crd null AGMCORE_gput + /saturation_crd null AGMCORE_gput + /perceptual_crd null AGMCORE_gput + currentcolortransfer cvlit/AGMCore_gray_xfer xdf cvlit/AGMCore_b_xfer xdf + cvlit/AGMCore_g_xfer xdf cvlit/AGMCore_r_xfer xdf + << + /MaxPatternItem currentsystemparams/MaxPatternCache get + >> + setuserparams + end +}def +/ps +{ + /setcmykcolor where{ + pop + Adobe_AGM_Core/AGMCORE_&setcmykcolor/setcmykcolor load put + }if + Adobe_AGM_Core begin + /setcmykcolor + { + 4 copy AGMCORE_cmykbuf astore/currentcmykcolor exch AGMCORE_gput + 1 sub 4 1 roll + 3{ + 3 index add neg dup 0 lt{ + pop 0 + }if + 3 1 roll + }repeat + setrgbcolor pop + }ndf + /currentcmykcolor + { + /currentcmykcolor AGMCORE_gget aload pop + }ndf + /setoverprint + {pop}ndf + /currentoverprint + {false}ndf + /AGMCORE_cyan_plate 1 0 0 0 test_cmyk_color_plate def + /AGMCORE_magenta_plate 0 1 0 0 test_cmyk_color_plate def + /AGMCORE_yellow_plate 0 0 1 0 test_cmyk_color_plate def + /AGMCORE_black_plate 0 0 0 1 test_cmyk_color_plate def + /AGMCORE_plate_ndx + AGMCORE_cyan_plate{ + 0 + }{ + AGMCORE_magenta_plate{ + 1 + }{ + AGMCORE_yellow_plate{ + 2 + }{ + AGMCORE_black_plate{ + 3 + }{ + 4 + }ifelse + }ifelse + }ifelse + }ifelse + def + /AGMCORE_have_reported_unsupported_color_space false def + /AGMCORE_report_unsupported_color_space + { + AGMCORE_have_reported_unsupported_color_space false eq + { + (Warning: Job contains content that cannot be separated with on-host methods. This content appears on the black plate, and knocks out all other plates.)== + Adobe_AGM_Core/AGMCORE_have_reported_unsupported_color_space true ddf + }if + }def + /AGMCORE_composite_job + AGMCORE_cyan_plate AGMCORE_magenta_plate and AGMCORE_yellow_plate and AGMCORE_black_plate and def + /AGMCORE_in_rip_sep + /AGMCORE_in_rip_sep where{ + pop AGMCORE_in_rip_sep + }{ + AGMCORE_distilling + { + false + }{ + userdict/Adobe_AGM_OnHost_Seps known{ + false + }{ + level2{ + currentpagedevice/Separations 2 copy known{ + get + }{ + pop pop false + }ifelse + }{ + false + }ifelse + }ifelse + }ifelse + }ifelse + def + /AGMCORE_producing_seps AGMCORE_composite_job not AGMCORE_in_rip_sep or def + /AGMCORE_host_sep AGMCORE_producing_seps AGMCORE_in_rip_sep not and def + /AGM_preserve_spots + /AGM_preserve_spots where{ + pop AGM_preserve_spots + }{ + AGMCORE_distilling AGMCORE_producing_seps or + }ifelse + def + /AGM_is_distiller_preserving_spotimages + { + currentdistillerparams/PreserveOverprintSettings known + { + currentdistillerparams/PreserveOverprintSettings get + { + currentdistillerparams/ColorConversionStrategy known + { + currentdistillerparams/ColorConversionStrategy get + /sRGB ne + }{ + true + }ifelse + }{ + false + }ifelse + }{ + false + }ifelse + }def + /convert_spot_to_process where{pop}{ + /convert_spot_to_process + { + //Adobe_AGM_Core begin + dup map_alias{ + /Name get exch pop + }if + dup dup(None)eq exch(All)eq or + { + pop false + }{ + AGMCORE_host_sep + { + gsave + 1 0 0 0 setcmykcolor currentgray 1 exch sub + 0 1 0 0 setcmykcolor currentgray 1 exch sub + 0 0 1 0 setcmykcolor currentgray 1 exch sub + 0 0 0 1 setcmykcolor currentgray 1 exch sub + add add add 0 eq + { + pop false + }{ + false setoverprint + current_spot_alias false set_spot_alias + 1 1 1 1 6 -1 roll findcmykcustomcolor 1 setcustomcolor + set_spot_alias + currentgray 1 ne + }ifelse + grestore + }{ + AGMCORE_distilling + { + pop AGM_is_distiller_preserving_spotimages not + }{ + //Adobe_AGM_Core/AGMCORE_name xddf + false + //Adobe_AGM_Core/AGMCORE_pattern_paint_type get 0 eq + AGMUTIL_cpd/OverrideSeparations known and + { + AGMUTIL_cpd/OverrideSeparations get + { + /HqnSpots/ProcSet resourcestatus + { + pop pop pop true + }if + }if + }if + { + AGMCORE_name/HqnSpots/ProcSet findresource/TestSpot gx not + }{ + gsave + [/Separation AGMCORE_name/DeviceGray{}]AGMCORE_&setcolorspace + false + AGMUTIL_cpd/SeparationColorNames 2 copy known + { + get + {AGMCORE_name eq or}forall + not + }{ + pop pop pop true + }ifelse + grestore + }ifelse + }ifelse + }ifelse + }ifelse + end + }def + }ifelse + /convert_to_process where{pop}{ + /convert_to_process + { + dup length 0 eq + { + pop false + }{ + AGMCORE_host_sep + { + dup true exch + { + dup(Cyan)eq exch + dup(Magenta)eq 3 -1 roll or exch + dup(Yellow)eq 3 -1 roll or exch + dup(Black)eq 3 -1 roll or + {pop} + {convert_spot_to_process and}ifelse + } + forall + { + true exch + { + dup(Cyan)eq exch + dup(Magenta)eq 3 -1 roll or exch + dup(Yellow)eq 3 -1 roll or exch + (Black)eq or and + }forall + not + }{pop false}ifelse + }{ + false exch + { + /PhotoshopDuotoneList where{pop false}{true}ifelse + { + dup(Cyan)eq exch + dup(Magenta)eq 3 -1 roll or exch + dup(Yellow)eq 3 -1 roll or exch + dup(Black)eq 3 -1 roll or + {pop} + {convert_spot_to_process or}ifelse + } + { + convert_spot_to_process or + } + ifelse + } + forall + }ifelse + }ifelse + }def + }ifelse + /AGMCORE_avoid_L2_sep_space + version cvr 2012 lt + level2 and + AGMCORE_producing_seps not and + def + /AGMCORE_is_cmyk_sep + AGMCORE_cyan_plate AGMCORE_magenta_plate or AGMCORE_yellow_plate or AGMCORE_black_plate or + def + /AGM_avoid_0_cmyk where{ + pop AGM_avoid_0_cmyk + }{ + AGM_preserve_spots + userdict/Adobe_AGM_OnHost_Seps known + userdict/Adobe_AGM_InRip_Seps known or + not and + }ifelse + { + /setcmykcolor[ + { + 4 copy add add add 0 eq currentoverprint and{ + pop 0.0005 + }if + }/exec cvx + /AGMCORE_&setcmykcolor load dup type/operatortype ne{ + /exec cvx + }if + ]cvx def + }if + /AGMCORE_IsSeparationAProcessColor + { + dup(Cyan)eq exch dup(Magenta)eq exch dup(Yellow)eq exch(Black)eq or or or + }def + AGMCORE_host_sep{ + /setcolortransfer + { + AGMCORE_cyan_plate{ + pop pop pop + }{ + AGMCORE_magenta_plate{ + 4 3 roll pop pop pop + }{ + AGMCORE_yellow_plate{ + 4 2 roll pop pop pop + }{ + 4 1 roll pop pop pop + }ifelse + }ifelse + }ifelse + settransfer + } + def + /AGMCORE_get_ink_data + AGMCORE_cyan_plate{ + {pop pop pop} + }{ + AGMCORE_magenta_plate{ + {4 3 roll pop pop pop} + }{ + AGMCORE_yellow_plate{ + {4 2 roll pop pop pop} + }{ + {4 1 roll pop pop pop} + }ifelse + }ifelse + }ifelse + def + /AGMCORE_RemoveProcessColorNames + { + 1 dict begin + /filtername + { + dup/Cyan eq 1 index(Cyan)eq or + {pop(_cyan_)}if + dup/Magenta eq 1 index(Magenta)eq or + {pop(_magenta_)}if + dup/Yellow eq 1 index(Yellow)eq or + {pop(_yellow_)}if + dup/Black eq 1 index(Black)eq or + {pop(_black_)}if + }def + dup type/arraytype eq + {[exch{filtername}forall]} + {filtername}ifelse + end + }def + level3{ + /AGMCORE_IsCurrentColor + { + dup AGMCORE_IsSeparationAProcessColor + { + AGMCORE_plate_ndx 0 eq + {dup(Cyan)eq exch/Cyan eq or}if + AGMCORE_plate_ndx 1 eq + {dup(Magenta)eq exch/Magenta eq or}if + AGMCORE_plate_ndx 2 eq + {dup(Yellow)eq exch/Yellow eq or}if + AGMCORE_plate_ndx 3 eq + {dup(Black)eq exch/Black eq or}if + AGMCORE_plate_ndx 4 eq + {pop false}if + }{ + gsave + false setoverprint + current_spot_alias false set_spot_alias + 1 1 1 1 6 -1 roll findcmykcustomcolor 1 setcustomcolor + set_spot_alias + currentgray 1 ne + grestore + }ifelse + }def + /AGMCORE_filter_functiondatasource + { + 5 dict begin + /data_in xdf + data_in type/stringtype eq + { + /ncomp xdf + /comp xdf + /string_out data_in length ncomp idiv string def + 0 ncomp data_in length 1 sub + { + string_out exch dup ncomp idiv exch data_in exch ncomp getinterval comp get 255 exch sub put + }for + string_out + }{ + string/string_in xdf + /string_out 1 string def + /component xdf + [ + data_in string_in/readstring cvx + [component/get cvx 255/exch cvx/sub cvx string_out/exch cvx 0/exch cvx/put cvx string_out]cvx + [/pop cvx()]cvx/ifelse cvx + ]cvx/ReusableStreamDecode filter + }ifelse + end + }def + /AGMCORE_separateShadingFunction + { + 2 dict begin + /paint? xdf + /channel xdf + dup type/dicttype eq + { + begin + FunctionType 0 eq + { + /DataSource channel Range length 2 idiv DataSource AGMCORE_filter_functiondatasource def + currentdict/Decode known + {/Decode Decode channel 2 mul 2 getinterval def}if + paint? not + {/Decode[1 1]def}if + }if + FunctionType 2 eq + { + paint? + { + /C0[C0 channel get 1 exch sub]def + /C1[C1 channel get 1 exch sub]def + }{ + /C0[1]def + /C1[1]def + }ifelse + }if + FunctionType 3 eq + { + /Functions[Functions{channel paint? AGMCORE_separateShadingFunction}forall]def + }if + currentdict/Range known + {/Range[0 1]def}if + currentdict + end}{ + channel get 0 paint? AGMCORE_separateShadingFunction + }ifelse + end + }def + /AGMCORE_separateShading + { + 3 -1 roll begin + currentdict/Function known + { + currentdict/Background known + {[1 index{Background 3 index get 1 exch sub}{1}ifelse]/Background xdf}if + Function 3 1 roll AGMCORE_separateShadingFunction/Function xdf + /ColorSpace[/DeviceGray]def + }{ + ColorSpace dup type/arraytype eq{0 get}if/DeviceCMYK eq + { + /ColorSpace[/DeviceN[/_cyan_/_magenta_/_yellow_/_black_]/DeviceCMYK{}]def + }{ + ColorSpace dup 1 get AGMCORE_RemoveProcessColorNames 1 exch put + }ifelse + ColorSpace 0 get/Separation eq + { + { + [1/exch cvx/sub cvx]cvx + }{ + [/pop cvx 1]cvx + }ifelse + ColorSpace 3 3 -1 roll put + pop + }{ + { + [exch ColorSpace 1 get length 1 sub exch sub/index cvx 1/exch cvx/sub cvx ColorSpace 1 get length 1 add 1/roll cvx ColorSpace 1 get length{/pop cvx}repeat]cvx + }{ + pop[ColorSpace 1 get length{/pop cvx}repeat cvx 1]cvx + }ifelse + ColorSpace 3 3 -1 roll bind put + }ifelse + ColorSpace 2/DeviceGray put + }ifelse + end + }def + /AGMCORE_separateShadingDict + { + dup/ColorSpace get + dup type/arraytype ne + {[exch]}if + dup 0 get/DeviceCMYK eq + { + exch begin + currentdict + AGMCORE_cyan_plate + {0 true}if + AGMCORE_magenta_plate + {1 true}if + AGMCORE_yellow_plate + {2 true}if + AGMCORE_black_plate + {3 true}if + AGMCORE_plate_ndx 4 eq + {0 false}if + dup not currentoverprint and + {/AGMCORE_ignoreshade true def}if + AGMCORE_separateShading + currentdict + end exch + }if + dup 0 get/Separation eq + { + exch begin + ColorSpace 1 get dup/None ne exch/All ne and + { + ColorSpace 1 get AGMCORE_IsCurrentColor AGMCORE_plate_ndx 4 lt and ColorSpace 1 get AGMCORE_IsSeparationAProcessColor not and + { + ColorSpace 2 get dup type/arraytype eq{0 get}if/DeviceCMYK eq + { + /ColorSpace + [ + /Separation + ColorSpace 1 get + /DeviceGray + [ + ColorSpace 3 get/exec cvx + 4 AGMCORE_plate_ndx sub -1/roll cvx + 4 1/roll cvx + 3[/pop cvx]cvx/repeat cvx + 1/exch cvx/sub cvx + ]cvx + ]def + }{ + AGMCORE_report_unsupported_color_space + AGMCORE_black_plate not + { + currentdict 0 false AGMCORE_separateShading + }if + }ifelse + }{ + currentdict ColorSpace 1 get AGMCORE_IsCurrentColor + 0 exch + dup not currentoverprint and + {/AGMCORE_ignoreshade true def}if + AGMCORE_separateShading + }ifelse + }if + currentdict + end exch + }if + dup 0 get/DeviceN eq + { + exch begin + ColorSpace 1 get convert_to_process + { + ColorSpace 2 get dup type/arraytype eq{0 get}if/DeviceCMYK eq + { + /ColorSpace + [ + /DeviceN + ColorSpace 1 get + /DeviceGray + [ + ColorSpace 3 get/exec cvx + 4 AGMCORE_plate_ndx sub -1/roll cvx + 4 1/roll cvx + 3[/pop cvx]cvx/repeat cvx + 1/exch cvx/sub cvx + ]cvx + ]def + }{ + AGMCORE_report_unsupported_color_space + AGMCORE_black_plate not + { + currentdict 0 false AGMCORE_separateShading + /ColorSpace[/DeviceGray]def + }if + }ifelse + }{ + currentdict + false -1 ColorSpace 1 get + { + AGMCORE_IsCurrentColor + { + 1 add + exch pop true exch exit + }if + 1 add + }forall + exch + dup not currentoverprint and + {/AGMCORE_ignoreshade true def}if + AGMCORE_separateShading + }ifelse + currentdict + end exch + }if + dup 0 get dup/DeviceCMYK eq exch dup/Separation eq exch/DeviceN eq or or not + { + exch begin + ColorSpace dup type/arraytype eq + {0 get}if + /DeviceGray ne + { + AGMCORE_report_unsupported_color_space + AGMCORE_black_plate not + { + ColorSpace 0 get/CIEBasedA eq + { + /ColorSpace[/Separation/_ciebaseda_/DeviceGray{}]def + }if + ColorSpace 0 get dup/CIEBasedABC eq exch dup/CIEBasedDEF eq exch/DeviceRGB eq or or + { + /ColorSpace[/DeviceN[/_red_/_green_/_blue_]/DeviceRGB{}]def + }if + ColorSpace 0 get/CIEBasedDEFG eq + { + /ColorSpace[/DeviceN[/_cyan_/_magenta_/_yellow_/_black_]/DeviceCMYK{}]def + }if + currentdict 0 false AGMCORE_separateShading + }if + }if + currentdict + end exch + }if + pop + dup/AGMCORE_ignoreshade known + { + begin + /ColorSpace[/Separation(None)/DeviceGray{}]def + currentdict end + }if + }def + /shfill + { + AGMCORE_separateShadingDict + dup/AGMCORE_ignoreshade known + {pop} + {AGMCORE_&sysshfill}ifelse + }def + /makepattern + { + exch + dup/PatternType get 2 eq + { + clonedict + begin + /Shading Shading AGMCORE_separateShadingDict def + Shading/AGMCORE_ignoreshade known + currentdict end exch + {pop<>}if + exch AGMCORE_&sysmakepattern + }{ + exch AGMCORE_&usrmakepattern + }ifelse + }def + }if + }if + AGMCORE_in_rip_sep{ + /setcustomcolor + { + exch aload pop + dup 7 1 roll inRip_spot_has_ink not { + 4{4 index mul 4 1 roll} + repeat + /DeviceCMYK setcolorspace + 6 -2 roll pop pop + }{ + //Adobe_AGM_Core begin + /AGMCORE_k xdf/AGMCORE_y xdf/AGMCORE_m xdf/AGMCORE_c xdf + end + [/Separation 4 -1 roll/DeviceCMYK + {dup AGMCORE_c mul exch dup AGMCORE_m mul exch dup AGMCORE_y mul exch AGMCORE_k mul} + ] + setcolorspace + }ifelse + setcolor + }ndf + /setseparationgray + { + [/Separation(All)/DeviceGray{}]setcolorspace_opt + 1 exch sub setcolor + }ndf + }{ + /setseparationgray + { + AGMCORE_&setgray + }ndf + }ifelse + /findcmykcustomcolor + { + 5 makereadonlyarray + }ndf + /setcustomcolor + { + exch aload pop pop + 4{4 index mul 4 1 roll}repeat + setcmykcolor pop + }ndf + /has_color + /colorimage where{ + AGMCORE_producing_seps{ + pop true + }{ + systemdict eq + }ifelse + }{ + false + }ifelse + def + /map_index + { + 1 index mul exch getinterval{255 div}forall + }bdf + /map_indexed_devn + { + Lookup Names length 3 -1 roll cvi map_index + }bdf + /n_color_components + { + base_colorspace_type + dup/DeviceGray eq{ + pop 1 + }{ + /DeviceCMYK eq{ + 4 + }{ + 3 + }ifelse + }ifelse + }bdf + level2{ + /mo/moveto ldf + /li/lineto ldf + /cv/curveto ldf + /knockout_unitsq + { + 1 setgray + 0 0 1 1 rectfill + }def + level2/setcolorspace AGMCORE_key_known not and{ + /AGMCORE_&&&setcolorspace/setcolorspace ldf + /AGMCORE_ReplaceMappedColor + { + dup type dup/arraytype eq exch/packedarraytype eq or + { + /AGMCORE_SpotAliasAry2 where{ + begin + dup 0 get dup/Separation eq + { + pop + dup length array copy + dup dup 1 get + current_spot_alias + { + dup map_alias + { + false set_spot_alias + dup 1 exch setsepcolorspace + true set_spot_alias + begin + /sep_colorspace_dict currentdict AGMCORE_gput + pop pop pop + [ + /Separation Name + CSA map_csa + MappedCSA + /sep_colorspace_proc load + ] + dup Name + end + }if + }if + map_reserved_ink_name 1 xpt + }{ + /DeviceN eq + { + dup length array copy + dup dup 1 get[ + exch{ + current_spot_alias{ + dup map_alias{ + /Name get exch pop + }if + }if + map_reserved_ink_name + }forall + ]1 xpt + }if + }ifelse + end + }if + }if + }def + /setcolorspace + { + dup type dup/arraytype eq exch/packedarraytype eq or + { + dup 0 get/Indexed eq + { + AGMCORE_distilling + { + /PhotoshopDuotoneList where + { + pop false + }{ + true + }ifelse + }{ + true + }ifelse + { + aload pop 3 -1 roll + AGMCORE_ReplaceMappedColor + 3 1 roll 4 array astore + }if + }{ + AGMCORE_ReplaceMappedColor + }ifelse + }if + DeviceN_PS2_inRip_seps{AGMCORE_&&&setcolorspace}if + }def + }if + }{ + /adj + { + currentstrokeadjust{ + transform + 0.25 sub round 0.25 add exch + 0.25 sub round 0.25 add exch + itransform + }if + }def + /mo{ + adj moveto + }def + /li{ + adj lineto + }def + /cv{ + 6 2 roll adj + 6 2 roll adj + 6 2 roll adj curveto + }def + /knockout_unitsq + { + 1 setgray + 8 8 1[8 0 0 8 0 0]{}image + }def + /currentstrokeadjust{ + /currentstrokeadjust AGMCORE_gget + }def + /setstrokeadjust{ + /currentstrokeadjust exch AGMCORE_gput + }def + /setcolorspace + { + /currentcolorspace exch AGMCORE_gput + }def + /currentcolorspace + { + /currentcolorspace AGMCORE_gget + }def + /setcolor_devicecolor + { + base_colorspace_type + dup/DeviceGray eq{ + pop setgray + }{ + /DeviceCMYK eq{ + setcmykcolor + }{ + setrgbcolor + }ifelse + }ifelse + }def + /setcolor + { + currentcolorspace 0 get + dup/DeviceGray ne{ + dup/DeviceCMYK ne{ + dup/DeviceRGB ne{ + dup/Separation eq{ + pop + currentcolorspace 3 gx + currentcolorspace 2 get + }{ + dup/Indexed eq{ + pop + currentcolorspace 3 get dup type/stringtype eq{ + currentcolorspace 1 get n_color_components + 3 -1 roll map_index + }{ + exec + }ifelse + currentcolorspace 1 get + }{ + /AGMCORE_cur_err/AGMCORE_invalid_color_space def + AGMCORE_invalid_color_space + }ifelse + }ifelse + }if + }if + }if + setcolor_devicecolor + }def + }ifelse + /sop/setoverprint ldf + /lw/setlinewidth ldf + /lc/setlinecap ldf + /lj/setlinejoin ldf + /ml/setmiterlimit ldf + /dsh/setdash ldf + /sadj/setstrokeadjust ldf + /gry/setgray ldf + /rgb/setrgbcolor ldf + /cmyk[ + /currentcolorspace[/DeviceCMYK]/AGMCORE_gput cvx + /setcmykcolor load dup type/operatortype ne{/exec cvx}if + ]cvx bdf + level3 AGMCORE_host_sep not and{ + /nzopmsc{ + 6 dict begin + /kk exch def + /yy exch def + /mm exch def + /cc exch def + /sum 0 def + cc 0 ne{/sum sum 2#1000 or def cc}if + mm 0 ne{/sum sum 2#0100 or def mm}if + yy 0 ne{/sum sum 2#0010 or def yy}if + kk 0 ne{/sum sum 2#0001 or def kk}if + AGMCORE_CMYKDeviceNColorspaces sum get setcolorspace + sum 0 eq{0}if + end + setcolor + }bdf + }{ + /nzopmsc/cmyk ldf + }ifelse + /sep/setsepcolor ldf + /devn/setdevicencolor ldf + /idx/setindexedcolor ldf + /colr/setcolor ldf + /csacrd/set_csa_crd ldf + /sepcs/setsepcolorspace ldf + /devncs/setdevicencolorspace ldf + /idxcs/setindexedcolorspace ldf + /cp/closepath ldf + /clp/clp_npth ldf + /eclp/eoclp_npth ldf + /f/fill ldf + /ef/eofill ldf + /@/stroke ldf + /nclp/npth_clp ldf + /gset/graphic_setup ldf + /gcln/graphic_cleanup ldf + /ct/concat ldf + /cf/currentfile ldf + /fl/filter ldf + /rs/readstring ldf + /AGMCORE_def_ht currenthalftone def + /clonedict Adobe_AGM_Utils begin/clonedict load end def + /clonearray Adobe_AGM_Utils begin/clonearray load end def + currentdict{ + dup xcheck 1 index type dup/arraytype eq exch/packedarraytype eq or and{ + bind + }if + def + }forall + /getrampcolor + { + /indx exch def + 0 1 NumComp 1 sub + { + dup + Samples exch get + dup type/stringtype eq{indx get}if + exch + Scaling exch get aload pop + 3 1 roll + mul add + }for + ColorSpaceFamily/Separation eq + {sep} + { + ColorSpaceFamily/DeviceN eq + {devn}{setcolor}ifelse + }ifelse + }bdf + /sssetbackground{ + aload pop + ColorSpaceFamily/Separation eq + {sep} + { + ColorSpaceFamily/DeviceN eq + {devn}{setcolor}ifelse + }ifelse + }bdf + /RadialShade + { + 40 dict begin + /ColorSpaceFamily xdf + /background xdf + /ext1 xdf + /ext0 xdf + /BBox xdf + /r2 xdf + /c2y xdf + /c2x xdf + /r1 xdf + /c1y xdf + /c1x xdf + /rampdict xdf + /setinkoverprint where{pop/setinkoverprint{pop}def}if + gsave + BBox length 0 gt + { + np + BBox 0 get BBox 1 get moveto + BBox 2 get BBox 0 get sub 0 rlineto + 0 BBox 3 get BBox 1 get sub rlineto + BBox 2 get BBox 0 get sub neg 0 rlineto + closepath + clip + np + }if + c1x c2x eq + { + c1y c2y lt{/theta 90 def}{/theta 270 def}ifelse + }{ + /slope c2y c1y sub c2x c1x sub div def + /theta slope 1 atan def + c2x c1x lt c2y c1y ge and{/theta theta 180 sub def}if + c2x c1x lt c2y c1y lt and{/theta theta 180 add def}if + }ifelse + gsave + clippath + c1x c1y translate + theta rotate + -90 rotate + {pathbbox}stopped + {0 0 0 0}if + /yMax xdf + /xMax xdf + /yMin xdf + /xMin xdf + grestore + xMax xMin eq yMax yMin eq or + { + grestore + end + }{ + /max{2 copy gt{pop}{exch pop}ifelse}bdf + /min{2 copy lt{pop}{exch pop}ifelse}bdf + rampdict begin + 40 dict begin + background length 0 gt{background sssetbackground gsave clippath fill grestore}if + gsave + c1x c1y translate + theta rotate + -90 rotate + /c2y c1x c2x sub dup mul c1y c2y sub dup mul add sqrt def + /c1y 0 def + /c1x 0 def + /c2x 0 def + ext0 + { + 0 getrampcolor + c2y r2 add r1 sub 0.0001 lt + { + c1x c1y r1 360 0 arcn + pathbbox + /aymax exch def + /axmax exch def + /aymin exch def + /axmin exch def + /bxMin xMin axmin min def + /byMin yMin aymin min def + /bxMax xMax axmax max def + /byMax yMax aymax max def + bxMin byMin moveto + bxMax byMin lineto + bxMax byMax lineto + bxMin byMax lineto + bxMin byMin lineto + eofill + }{ + c2y r1 add r2 le + { + c1x c1y r1 0 360 arc + fill + } + { + c2x c2y r2 0 360 arc fill + r1 r2 eq + { + /p1x r1 neg def + /p1y c1y def + /p2x r1 def + /p2y c1y def + p1x p1y moveto p2x p2y lineto p2x yMin lineto p1x yMin lineto + fill + }{ + /AA r2 r1 sub c2y div def + AA -1 eq + {/theta 89.99 def} + {/theta AA 1 AA dup mul sub sqrt div 1 atan def} + ifelse + /SS1 90 theta add dup sin exch cos div def + /p1x r1 SS1 SS1 mul SS1 SS1 mul 1 add div sqrt mul neg def + /p1y p1x SS1 div neg def + /SS2 90 theta sub dup sin exch cos div def + /p2x r1 SS2 SS2 mul SS2 SS2 mul 1 add div sqrt mul def + /p2y p2x SS2 div neg def + r1 r2 gt + { + /L1maxX p1x yMin p1y sub SS1 div add def + /L2maxX p2x yMin p2y sub SS2 div add def + }{ + /L1maxX 0 def + /L2maxX 0 def + }ifelse + p1x p1y moveto p2x p2y lineto L2maxX L2maxX p2x sub SS2 mul p2y add lineto + L1maxX L1maxX p1x sub SS1 mul p1y add lineto + fill + }ifelse + }ifelse + }ifelse + }if + c1x c2x sub dup mul + c1y c2y sub dup mul + add 0.5 exp + 0 dtransform + dup mul exch dup mul add 0.5 exp 72 div + 0 72 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt + 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt + 1 index 1 index lt{exch}if pop + /hires xdf + hires mul + /numpix xdf + /numsteps NumSamples def + /rampIndxInc 1 def + /subsampling false def + numpix 0 ne + { + NumSamples numpix div 0.5 gt + { + /numsteps numpix 2 div round cvi dup 1 le{pop 2}if def + /rampIndxInc NumSamples 1 sub numsteps div def + /subsampling true def + }if + }if + /xInc c2x c1x sub numsteps div def + /yInc c2y c1y sub numsteps div def + /rInc r2 r1 sub numsteps div def + /cx c1x def + /cy c1y def + /radius r1 def + np + xInc 0 eq yInc 0 eq rInc 0 eq and and + { + 0 getrampcolor + cx cy radius 0 360 arc + stroke + NumSamples 1 sub getrampcolor + cx cy radius 72 hires div add 0 360 arc + 0 setlinewidth + stroke + }{ + 0 + numsteps + { + dup + subsampling{round cvi}if + getrampcolor + cx cy radius 0 360 arc + /cx cx xInc add def + /cy cy yInc add def + /radius radius rInc add def + cx cy radius 360 0 arcn + eofill + rampIndxInc add + }repeat + pop + }ifelse + ext1 + { + c2y r2 add r1 lt + { + c2x c2y r2 0 360 arc + fill + }{ + c2y r1 add r2 sub 0.0001 le + { + c2x c2y r2 360 0 arcn + pathbbox + /aymax exch def + /axmax exch def + /aymin exch def + /axmin exch def + /bxMin xMin axmin min def + /byMin yMin aymin min def + /bxMax xMax axmax max def + /byMax yMax aymax max def + bxMin byMin moveto + bxMax byMin lineto + bxMax byMax lineto + bxMin byMax lineto + bxMin byMin lineto + eofill + }{ + c2x c2y r2 0 360 arc fill + r1 r2 eq + { + /p1x r2 neg def + /p1y c2y def + /p2x r2 def + /p2y c2y def + p1x p1y moveto p2x p2y lineto p2x yMax lineto p1x yMax lineto + fill + }{ + /AA r2 r1 sub c2y div def + AA -1 eq + {/theta 89.99 def} + {/theta AA 1 AA dup mul sub sqrt div 1 atan def} + ifelse + /SS1 90 theta add dup sin exch cos div def + /p1x r2 SS1 SS1 mul SS1 SS1 mul 1 add div sqrt mul neg def + /p1y c2y p1x SS1 div sub def + /SS2 90 theta sub dup sin exch cos div def + /p2x r2 SS2 SS2 mul SS2 SS2 mul 1 add div sqrt mul def + /p2y c2y p2x SS2 div sub def + r1 r2 lt + { + /L1maxX p1x yMax p1y sub SS1 div add def + /L2maxX p2x yMax p2y sub SS2 div add def + }{ + /L1maxX 0 def + /L2maxX 0 def + }ifelse + p1x p1y moveto p2x p2y lineto L2maxX L2maxX p2x sub SS2 mul p2y add lineto + L1maxX L1maxX p1x sub SS1 mul p1y add lineto + fill + }ifelse + }ifelse + }ifelse + }if + grestore + grestore + end + end + end + }ifelse + }bdf + /GenStrips + { + 40 dict begin + /ColorSpaceFamily xdf + /background xdf + /ext1 xdf + /ext0 xdf + /BBox xdf + /y2 xdf + /x2 xdf + /y1 xdf + /x1 xdf + /rampdict xdf + /setinkoverprint where{pop/setinkoverprint{pop}def}if + gsave + BBox length 0 gt + { + np + BBox 0 get BBox 1 get moveto + BBox 2 get BBox 0 get sub 0 rlineto + 0 BBox 3 get BBox 1 get sub rlineto + BBox 2 get BBox 0 get sub neg 0 rlineto + closepath + clip + np + }if + x1 x2 eq + { + y1 y2 lt{/theta 90 def}{/theta 270 def}ifelse + }{ + /slope y2 y1 sub x2 x1 sub div def + /theta slope 1 atan def + x2 x1 lt y2 y1 ge and{/theta theta 180 sub def}if + x2 x1 lt y2 y1 lt and{/theta theta 180 add def}if + } + ifelse + gsave + clippath + x1 y1 translate + theta rotate + {pathbbox}stopped + {0 0 0 0}if + /yMax exch def + /xMax exch def + /yMin exch def + /xMin exch def + grestore + xMax xMin eq yMax yMin eq or + { + grestore + end + }{ + rampdict begin + 20 dict begin + background length 0 gt{background sssetbackground gsave clippath fill grestore}if + gsave + x1 y1 translate + theta rotate + /xStart 0 def + /xEnd x2 x1 sub dup mul y2 y1 sub dup mul add 0.5 exp def + /ySpan yMax yMin sub def + /numsteps NumSamples def + /rampIndxInc 1 def + /subsampling false def + xStart 0 transform + xEnd 0 transform + 3 -1 roll + sub dup mul + 3 1 roll + sub dup mul + add 0.5 exp 72 div + 0 72 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt + 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt + 1 index 1 index lt{exch}if pop + mul + /numpix xdf + numpix 0 ne + { + NumSamples numpix div 0.5 gt + { + /numsteps numpix 2 div round cvi dup 1 le{pop 2}if def + /rampIndxInc NumSamples 1 sub numsteps div def + /subsampling true def + }if + }if + ext0 + { + 0 getrampcolor + xMin xStart lt + { + xMin yMin xMin neg ySpan rectfill + }if + }if + /xInc xEnd xStart sub numsteps div def + /x xStart def + 0 + numsteps + { + dup + subsampling{round cvi}if + getrampcolor + x yMin xInc ySpan rectfill + /x x xInc add def + rampIndxInc add + }repeat + pop + ext1{ + xMax xEnd gt + { + xEnd yMin xMax xEnd sub ySpan rectfill + }if + }if + grestore + grestore + end + end + end + }ifelse + }bdf +}def +/pt +{ + end +}def +/dt{ +}def +/pgsv{ + //Adobe_AGM_Core/AGMCORE_save save put +}def +/pgrs{ + //Adobe_AGM_Core/AGMCORE_save get restore +}def +systemdict/findcolorrendering known{ + /findcolorrendering systemdict/findcolorrendering get def +}if +systemdict/setcolorrendering known{ + /setcolorrendering systemdict/setcolorrendering get def +}if +/test_cmyk_color_plate +{ + gsave + setcmykcolor currentgray 1 ne + grestore +}def +/inRip_spot_has_ink +{ + dup//Adobe_AGM_Core/AGMCORE_name xddf + convert_spot_to_process not +}def +/map255_to_range +{ + 1 index sub + 3 -1 roll 255 div mul add +}def +/set_csa_crd +{ + /sep_colorspace_dict null AGMCORE_gput + begin + CSA get_csa_by_name setcolorspace_opt + set_crd + end +} +def +/map_csa +{ + currentdict/MappedCSA known{MappedCSA null ne}{false}ifelse + {pop}{get_csa_by_name/MappedCSA xdf}ifelse +}def +/setsepcolor +{ + /sep_colorspace_dict AGMCORE_gget begin + dup/sep_tint exch AGMCORE_gput + TintProc + end +}def +/setdevicencolor +{ + /devicen_colorspace_dict AGMCORE_gget begin + Names length copy + Names length 1 sub -1 0 + { + /devicen_tints AGMCORE_gget 3 1 roll xpt + }for + TintProc + end +}def +/sep_colorspace_proc +{ + /AGMCORE_tmp exch store + /sep_colorspace_dict AGMCORE_gget begin + currentdict/Components known{ + Components aload pop + TintMethod/Lab eq{ + 2{AGMCORE_tmp mul NComponents 1 roll}repeat + LMax sub AGMCORE_tmp mul LMax add NComponents 1 roll + }{ + TintMethod/Subtractive eq{ + NComponents{ + AGMCORE_tmp mul NComponents 1 roll + }repeat + }{ + NComponents{ + 1 sub AGMCORE_tmp mul 1 add NComponents 1 roll + }repeat + }ifelse + }ifelse + }{ + ColorLookup AGMCORE_tmp ColorLookup length 1 sub mul round cvi get + aload pop + }ifelse + end +}def +/sep_colorspace_gray_proc +{ + /AGMCORE_tmp exch store + /sep_colorspace_dict AGMCORE_gget begin + GrayLookup AGMCORE_tmp GrayLookup length 1 sub mul round cvi get + end +}def +/sep_proc_name +{ + dup 0 get + dup/DeviceRGB eq exch/DeviceCMYK eq or level2 not and has_color not and{ + pop[/DeviceGray] + /sep_colorspace_gray_proc + }{ + /sep_colorspace_proc + }ifelse +}def +/setsepcolorspace +{ + current_spot_alias{ + dup begin + Name map_alias{ + exch pop + }if + end + }if + dup/sep_colorspace_dict exch AGMCORE_gput + begin + CSA map_csa + /AGMCORE_sep_special Name dup()eq exch(All)eq or store + AGMCORE_avoid_L2_sep_space{ + [/Indexed MappedCSA sep_proc_name 255 exch + {255 div}/exec cvx 3 -1 roll[4 1 roll load/exec cvx]cvx + ]setcolorspace_opt + /TintProc{ + 255 mul round cvi setcolor + }bdf + }{ + MappedCSA 0 get/DeviceCMYK eq + currentdict/Components known and + AGMCORE_sep_special not and{ + /TintProc[ + Components aload pop Name findcmykcustomcolor + /exch cvx/setcustomcolor cvx + ]cvx bdf + }{ + AGMCORE_host_sep Name(All)eq and{ + /TintProc{ + 1 exch sub setseparationgray + }bdf + }{ + AGMCORE_in_rip_sep MappedCSA 0 get/DeviceCMYK eq and + AGMCORE_host_sep or + Name()eq and{ + /TintProc[ + MappedCSA sep_proc_name exch 0 get/DeviceCMYK eq{ + cvx/setcmykcolor cvx + }{ + cvx/setgray cvx + }ifelse + ]cvx bdf + }{ + AGMCORE_producing_seps MappedCSA 0 get dup/DeviceCMYK eq exch/DeviceGray eq or and AGMCORE_sep_special not and{ + /TintProc[ + /dup cvx + MappedCSA sep_proc_name cvx exch + 0 get/DeviceGray eq{ + 1/exch cvx/sub cvx 0 0 0 4 -1/roll cvx + }if + /Name cvx/findcmykcustomcolor cvx/exch cvx + AGMCORE_host_sep{ + AGMCORE_is_cmyk_sep + /Name cvx + /AGMCORE_IsSeparationAProcessColor load/exec cvx + /not cvx/and cvx + }{ + Name inRip_spot_has_ink not + }ifelse + [ + /pop cvx 1 + ]cvx/if cvx + /setcustomcolor cvx + ]cvx bdf + }{ + /TintProc{setcolor}bdf + [/Separation Name MappedCSA sep_proc_name load]setcolorspace_opt + }ifelse + }ifelse + }ifelse + }ifelse + }ifelse + set_crd + setsepcolor + end +}def +/additive_blend +{ + 3 dict begin + /numarrays xdf + /numcolors xdf + 0 1 numcolors 1 sub + { + /c1 xdf + 1 + 0 1 numarrays 1 sub + { + 1 exch add/index cvx + c1/get cvx/mul cvx + }for + numarrays 1 add 1/roll cvx + }for + numarrays[/pop cvx]cvx/repeat cvx + end +}def +/subtractive_blend +{ + 3 dict begin + /numarrays xdf + /numcolors xdf + 0 1 numcolors 1 sub + { + /c1 xdf + 1 1 + 0 1 numarrays 1 sub + { + 1 3 3 -1 roll add/index cvx + c1/get cvx/sub cvx/mul cvx + }for + /sub cvx + numarrays 1 add 1/roll cvx + }for + numarrays[/pop cvx]cvx/repeat cvx + end +}def +/exec_tint_transform +{ + /TintProc[ + /TintTransform cvx/setcolor cvx + ]cvx bdf + MappedCSA setcolorspace_opt +}bdf +/devn_makecustomcolor +{ + 2 dict begin + /names_index xdf + /Names xdf + 1 1 1 1 Names names_index get findcmykcustomcolor + /devicen_tints AGMCORE_gget names_index get setcustomcolor + Names length{pop}repeat + end +}bdf +/setdevicencolorspace +{ + dup/AliasedColorants known{false}{true}ifelse + current_spot_alias and{ + 7 dict begin + /names_index 0 def + dup/names_len exch/Names get length def + /new_names names_len array def + /new_LookupTables names_len array def + /alias_cnt 0 def + dup/Names get + { + dup map_alias{ + exch pop + dup/ColorLookup known{ + dup begin + new_LookupTables names_index ColorLookup put + end + }{ + dup/Components known{ + dup begin + new_LookupTables names_index Components put + end + }{ + dup begin + new_LookupTables names_index[null null null null]put + end + }ifelse + }ifelse + new_names names_index 3 -1 roll/Name get put + /alias_cnt alias_cnt 1 add def + }{ + /name xdf + new_names names_index name put + dup/LookupTables known{ + dup begin + new_LookupTables names_index LookupTables names_index get put + end + }{ + dup begin + new_LookupTables names_index[null null null null]put + end + }ifelse + }ifelse + /names_index names_index 1 add def + }forall + alias_cnt 0 gt{ + /AliasedColorants true def + /lut_entry_len new_LookupTables 0 get dup length 256 ge{0 get length}{length}ifelse def + 0 1 names_len 1 sub{ + /names_index xdf + new_LookupTables names_index get dup length 256 ge{0 get length}{length}ifelse lut_entry_len ne{ + /AliasedColorants false def + exit + }{ + new_LookupTables names_index get 0 get null eq{ + dup/Names get names_index get/name xdf + name(Cyan)eq name(Magenta)eq name(Yellow)eq name(Black)eq + or or or not{ + /AliasedColorants false def + exit + }if + }if + }ifelse + }for + lut_entry_len 1 eq{ + /AliasedColorants false def + }if + AliasedColorants{ + dup begin + /Names new_names def + /LookupTables new_LookupTables def + /AliasedColorants true def + /NComponents lut_entry_len def + /TintMethod NComponents 4 eq{/Subtractive}{/Additive}ifelse def + /MappedCSA TintMethod/Additive eq{/DeviceRGB}{/DeviceCMYK}ifelse def + currentdict/TTTablesIdx known not{ + /TTTablesIdx -1 def + }if + end + }if + }if + end + }if + dup/devicen_colorspace_dict exch AGMCORE_gput + begin + currentdict/AliasedColorants known{ + AliasedColorants + }{ + false + }ifelse + dup not{ + CSA map_csa + }if + /TintTransform load type/nulltype eq or{ + /TintTransform[ + 0 1 Names length 1 sub + { + /TTTablesIdx TTTablesIdx 1 add def + dup LookupTables exch get dup 0 get null eq + { + 1 index + Names exch get + dup(Cyan)eq + { + pop exch + LookupTables length exch sub + /index cvx + 0 0 0 + } + { + dup(Magenta)eq + { + pop exch + LookupTables length exch sub + /index cvx + 0/exch cvx 0 0 + }{ + (Yellow)eq + { + exch + LookupTables length exch sub + /index cvx + 0 0 3 -1/roll cvx 0 + }{ + exch + LookupTables length exch sub + /index cvx + 0 0 0 4 -1/roll cvx + }ifelse + }ifelse + }ifelse + 5 -1/roll cvx/astore cvx + }{ + dup length 1 sub + LookupTables length 4 -1 roll sub 1 add + /index cvx/mul cvx/round cvx/cvi cvx/get cvx + }ifelse + Names length TTTablesIdx add 1 add 1/roll cvx + }for + Names length[/pop cvx]cvx/repeat cvx + NComponents Names length + TintMethod/Subtractive eq + { + subtractive_blend + }{ + additive_blend + }ifelse + ]cvx bdf + }if + AGMCORE_host_sep{ + Names convert_to_process{ + exec_tint_transform + } + { + currentdict/AliasedColorants known{ + AliasedColorants not + }{ + false + }ifelse + 5 dict begin + /AvoidAliasedColorants xdf + /painted? false def + /names_index 0 def + /names_len Names length def + AvoidAliasedColorants{ + /currentspotalias current_spot_alias def + false set_spot_alias + }if + Names{ + AGMCORE_is_cmyk_sep{ + dup(Cyan)eq AGMCORE_cyan_plate and exch + dup(Magenta)eq AGMCORE_magenta_plate and exch + dup(Yellow)eq AGMCORE_yellow_plate and exch + (Black)eq AGMCORE_black_plate and or or or{ + /devicen_colorspace_dict AGMCORE_gget/TintProc[ + Names names_index/devn_makecustomcolor cvx + ]cvx ddf + /painted? true def + }if + painted?{exit}if + }{ + 0 0 0 0 5 -1 roll findcmykcustomcolor 1 setcustomcolor currentgray 0 eq{ + /devicen_colorspace_dict AGMCORE_gget/TintProc[ + Names names_index/devn_makecustomcolor cvx + ]cvx ddf + /painted? true def + exit + }if + }ifelse + /names_index names_index 1 add def + }forall + AvoidAliasedColorants{ + currentspotalias set_spot_alias + }if + painted?{ + /devicen_colorspace_dict AGMCORE_gget/names_index names_index put + }{ + /devicen_colorspace_dict AGMCORE_gget/TintProc[ + names_len[/pop cvx]cvx/repeat cvx 1/setseparationgray cvx + 0 0 0 0/setcmykcolor cvx + ]cvx ddf + }ifelse + end + }ifelse + } + { + AGMCORE_in_rip_sep{ + Names convert_to_process not + }{ + level3 + }ifelse + { + [/DeviceN Names MappedCSA/TintTransform load]setcolorspace_opt + /TintProc level3 not AGMCORE_in_rip_sep and{ + [ + Names/length cvx[/pop cvx]cvx/repeat cvx + ]cvx bdf + }{ + {setcolor}bdf + }ifelse + }{ + exec_tint_transform + }ifelse + }ifelse + set_crd + /AliasedColorants false def + end +}def +/setindexedcolorspace +{ + dup/indexed_colorspace_dict exch AGMCORE_gput + begin + currentdict/CSDBase known{ + CSDBase/CSD get_res begin + currentdict/Names known{ + currentdict devncs + }{ + 1 currentdict sepcs + }ifelse + AGMCORE_host_sep{ + 4 dict begin + /compCnt/Names where{pop Names length}{1}ifelse def + /NewLookup HiVal 1 add string def + 0 1 HiVal{ + /tableIndex xdf + Lookup dup type/stringtype eq{ + compCnt tableIndex map_index + }{ + exec + }ifelse + /Names where{ + pop setdevicencolor + }{ + setsepcolor + }ifelse + currentgray + tableIndex exch + 255 mul cvi + NewLookup 3 1 roll put + }for + [/Indexed currentcolorspace HiVal NewLookup]setcolorspace_opt + end + }{ + level3 + { + currentdict/Names known{ + [/Indexed[/DeviceN Names MappedCSA/TintTransform load]HiVal Lookup]setcolorspace_opt + }{ + [/Indexed[/Separation Name MappedCSA sep_proc_name load]HiVal Lookup]setcolorspace_opt + }ifelse + }{ + [/Indexed MappedCSA HiVal + [ + currentdict/Names known{ + Lookup dup type/stringtype eq + {/exch cvx CSDBase/CSD get_res/Names get length dup/mul cvx exch/getinterval cvx{255 div}/forall cvx} + {/exec cvx}ifelse + /TintTransform load/exec cvx + }{ + Lookup dup type/stringtype eq + {/exch cvx/get cvx 255/div cvx} + {/exec cvx}ifelse + CSDBase/CSD get_res/MappedCSA get sep_proc_name exch pop/load cvx/exec cvx + }ifelse + ]cvx + ]setcolorspace_opt + }ifelse + }ifelse + end + set_crd + } + { + CSA map_csa + AGMCORE_host_sep level2 not and{ + 0 0 0 0 setcmykcolor + }{ + [/Indexed MappedCSA + level2 not has_color not and{ + dup 0 get dup/DeviceRGB eq exch/DeviceCMYK eq or{ + pop[/DeviceGray] + }if + HiVal GrayLookup + }{ + HiVal + currentdict/RangeArray known{ + { + /indexed_colorspace_dict AGMCORE_gget begin + Lookup exch + dup HiVal gt{ + pop HiVal + }if + NComponents mul NComponents getinterval{}forall + NComponents 1 sub -1 0{ + RangeArray exch 2 mul 2 getinterval aload pop map255_to_range + NComponents 1 roll + }for + end + }bind + }{ + Lookup + }ifelse + }ifelse + ]setcolorspace_opt + set_crd + }ifelse + }ifelse + end +}def +/setindexedcolor +{ + AGMCORE_host_sep{ + /indexed_colorspace_dict AGMCORE_gget + begin + currentdict/CSDBase known{ + CSDBase/CSD get_res begin + currentdict/Names known{ + map_indexed_devn + devn + } + { + Lookup 1 3 -1 roll map_index + sep + }ifelse + end + }{ + Lookup MappedCSA/DeviceCMYK eq{4}{1}ifelse 3 -1 roll + map_index + MappedCSA/DeviceCMYK eq{setcmykcolor}{setgray}ifelse + }ifelse + end + }{ + level3 not AGMCORE_in_rip_sep and/indexed_colorspace_dict AGMCORE_gget/CSDBase known and{ + /indexed_colorspace_dict AGMCORE_gget/CSDBase get/CSD get_res begin + map_indexed_devn + devn + end + } + { + setcolor + }ifelse + }ifelse +}def +/ignoreimagedata +{ + currentoverprint not{ + gsave + dup clonedict begin + 1 setgray + /Decode[0 1]def + /DataSourcedef + /MultipleDataSources false def + /BitsPerComponent 8 def + currentdict end + systemdict/image gx + grestore + }if + consumeimagedata +}def +/add_res +{ + dup/CSD eq{ + pop + //Adobe_AGM_Core begin + /AGMCORE_CSD_cache load 3 1 roll put + end + }{ + defineresource pop + }ifelse +}def +/del_res +{ + { + aload pop exch + dup/CSD eq{ + pop + {//Adobe_AGM_Core/AGMCORE_CSD_cache get exch undef}forall + }{ + exch + {1 index undefineresource}forall + pop + }ifelse + }forall +}def +/get_res +{ + dup/CSD eq{ + pop + dup type dup/nametype eq exch/stringtype eq or{ + AGMCORE_CSD_cache exch get + }if + }{ + findresource + }ifelse +}def +/get_csa_by_name +{ + dup type dup/nametype eq exch/stringtype eq or{ + /CSA get_res + }if +}def +/paintproc_buf_init +{ + /count get 0 0 put +}def +/paintproc_buf_next +{ + dup/count get dup 0 get + dup 3 1 roll + 1 add 0 xpt + get +}def +/cachepaintproc_compress +{ + 5 dict begin + currentfile exch 0 exch/SubFileDecode filter/ReadFilter exch def + /ppdict 20 dict def + /string_size 16000 def + /readbuffer string_size string def + currentglobal true setglobal + ppdict 1 array dup 0 1 put/count xpt + setglobal + /LZWFilter + { + exch + dup length 0 eq{ + pop + }{ + ppdict dup length 1 sub 3 -1 roll put + }ifelse + {string_size}{0}ifelse string + }/LZWEncode filter def + { + ReadFilter readbuffer readstring + exch LZWFilter exch writestring + not{exit}if + }loop + LZWFilter closefile + ppdict + end +}def +/cachepaintproc +{ + 2 dict begin + currentfile exch 0 exch/SubFileDecode filter/ReadFilter exch def + /ppdict 20 dict def + currentglobal true setglobal + ppdict 1 array dup 0 1 put/count xpt + setglobal + { + ReadFilter 16000 string readstring exch + ppdict dup length 1 sub 3 -1 roll put + not{exit}if + }loop + ppdict dup dup length 1 sub()put + end +}def +/make_pattern +{ + exch clonedict exch + dup matrix currentmatrix matrix concatmatrix 0 0 3 2 roll itransform + exch 3 index/XStep get 1 index exch 2 copy div cvi mul sub sub + exch 3 index/YStep get 1 index exch 2 copy div cvi mul sub sub + matrix translate exch matrix concatmatrix + 1 index begin + BBox 0 get XStep div cvi XStep mul/xshift exch neg def + BBox 1 get YStep div cvi YStep mul/yshift exch neg def + BBox 0 get xshift add + BBox 1 get yshift add + BBox 2 get xshift add + BBox 3 get yshift add + 4 array astore + /BBox exch def + [xshift yshift/translate load null/exec load]dup + 3/PaintProc load put cvx/PaintProc exch def + end + gsave 0 setgray + makepattern + grestore +}def +/set_pattern +{ + dup/PatternType get 1 eq{ + dup/PaintType get 1 eq{ + currentoverprint sop[/DeviceGray]setcolorspace 0 setgray + }if + }if + setpattern +}def +/setcolorspace_opt +{ + dup currentcolorspace eq{pop}{setcolorspace}ifelse +}def +/updatecolorrendering +{ + currentcolorrendering/RenderingIntent known{ + currentcolorrendering/RenderingIntent get + } + { + Intent/AbsoluteColorimetric eq + { + /absolute_colorimetric_crd AGMCORE_gget dup null eq + } + { + Intent/RelativeColorimetric eq + { + /relative_colorimetric_crd AGMCORE_gget dup null eq + } + { + Intent/Saturation eq + { + /saturation_crd AGMCORE_gget dup null eq + } + { + /perceptual_crd AGMCORE_gget dup null eq + }ifelse + }ifelse + }ifelse + { + pop null + } + { + /RenderingIntent known{null}{Intent}ifelse + }ifelse + }ifelse + Intent ne{ + Intent/ColorRendering{findresource}stopped + { + pop pop systemdict/findcolorrendering known + { + Intent findcolorrendering + { + /ColorRendering findresource true exch + } + { + /ColorRendering findresource + product(Xerox Phaser 5400)ne + exch + }ifelse + dup Intent/AbsoluteColorimetric eq + { + /absolute_colorimetric_crd exch AGMCORE_gput + } + { + Intent/RelativeColorimetric eq + { + /relative_colorimetric_crd exch AGMCORE_gput + } + { + Intent/Saturation eq + { + /saturation_crd exch AGMCORE_gput + } + { + Intent/Perceptual eq + { + /perceptual_crd exch AGMCORE_gput + } + { + pop + }ifelse + }ifelse + }ifelse + }ifelse + 1 index{exch}{pop}ifelse + } + {false}ifelse + } + {true}ifelse + { + dup begin + currentdict/TransformPQR known{ + currentdict/TransformPQR get aload pop + 3{{}eq 3 1 roll}repeat or or + } + {true}ifelse + currentdict/MatrixPQR known{ + currentdict/MatrixPQR get aload pop + 1.0 eq 9 1 roll 0.0 eq 9 1 roll 0.0 eq 9 1 roll + 0.0 eq 9 1 roll 1.0 eq 9 1 roll 0.0 eq 9 1 roll + 0.0 eq 9 1 roll 0.0 eq 9 1 roll 1.0 eq + and and and and and and and and + } + {true}ifelse + end + or + { + clonedict begin + /TransformPQR[ + {4 -1 roll 3 get dup 3 1 roll sub 5 -1 roll 3 get 3 -1 roll sub div + 3 -1 roll 3 get 3 -1 roll 3 get dup 4 1 roll sub mul add}bind + {4 -1 roll 4 get dup 3 1 roll sub 5 -1 roll 4 get 3 -1 roll sub div + 3 -1 roll 4 get 3 -1 roll 4 get dup 4 1 roll sub mul add}bind + {4 -1 roll 5 get dup 3 1 roll sub 5 -1 roll 5 get 3 -1 roll sub div + 3 -1 roll 5 get 3 -1 roll 5 get dup 4 1 roll sub mul add}bind + ]def + /MatrixPQR[0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296]def + /RangePQR[-0.3227950745 2.3229645538 -1.5003771057 3.5003465881 -0.1369979095 2.136967392]def + currentdict end + }if + setcolorrendering_opt + }if + }if +}def +/set_crd +{ + AGMCORE_host_sep not level2 and{ + currentdict/ColorRendering known{ + ColorRendering/ColorRendering{findresource}stopped not{setcolorrendering_opt}if + }{ + currentdict/Intent known{ + updatecolorrendering + }if + }ifelse + currentcolorspace dup type/arraytype eq + {0 get}if + /DeviceRGB eq + { + currentdict/UCR known + {/UCR}{/AGMCORE_currentucr}ifelse + load setundercolorremoval + currentdict/BG known + {/BG}{/AGMCORE_currentbg}ifelse + load setblackgeneration + }if + }if +}def +/set_ucrbg +{ + dup null eq{pop/AGMCORE_currentbg load}{/Procedure get_res}ifelse setblackgeneration + dup null eq{pop/AGMCORE_currentucr load}{/Procedure get_res}ifelse setundercolorremoval +}def +/setcolorrendering_opt +{ + dup currentcolorrendering eq{ + pop + }{ + clonedict + begin + /Intent Intent def + currentdict + end + setcolorrendering + }ifelse +}def +/cpaint_gcomp +{ + convert_to_process//Adobe_AGM_Core/AGMCORE_ConvertToProcess xddf + //Adobe_AGM_Core/AGMCORE_ConvertToProcess get not + { + (%end_cpaint_gcomp)flushinput + }if +}def +/cpaint_gsep +{ + //Adobe_AGM_Core/AGMCORE_ConvertToProcess get + { + (%end_cpaint_gsep)flushinput + }if +}def +/cpaint_gend +{np}def +/T1_path +{ + currentfile token pop currentfile token pop mo + { + currentfile token pop dup type/stringtype eq + {pop exit}if + 0 exch rlineto + currentfile token pop dup type/stringtype eq + {pop exit}if + 0 rlineto + }loop +}def +/T1_gsave + level3 + {/clipsave} + {/gsave}ifelse + load def +/T1_grestore + level3 + {/cliprestore} + {/grestore}ifelse + load def +/set_spot_alias_ary +{ + dup inherit_aliases + //Adobe_AGM_Core/AGMCORE_SpotAliasAry xddf +}def +/set_spot_normalization_ary +{ + dup inherit_aliases + dup length + /AGMCORE_SpotAliasAry where{pop AGMCORE_SpotAliasAry length add}if + array + //Adobe_AGM_Core/AGMCORE_SpotAliasAry2 xddf + /AGMCORE_SpotAliasAry where{ + pop + AGMCORE_SpotAliasAry2 0 AGMCORE_SpotAliasAry putinterval + AGMCORE_SpotAliasAry length + }{0}ifelse + AGMCORE_SpotAliasAry2 3 1 roll exch putinterval + true set_spot_alias +}def +/inherit_aliases +{ + {dup/Name get map_alias{/CSD put}{pop}ifelse}forall +}def +/set_spot_alias +{ + /AGMCORE_SpotAliasAry2 where{ + /AGMCORE_current_spot_alias 3 -1 roll put + }{ + pop + }ifelse +}def +/current_spot_alias +{ + /AGMCORE_SpotAliasAry2 where{ + /AGMCORE_current_spot_alias get + }{ + false + }ifelse +}def +/map_alias +{ + /AGMCORE_SpotAliasAry2 where{ + begin + /AGMCORE_name xdf + false + AGMCORE_SpotAliasAry2{ + dup/Name get AGMCORE_name eq{ + /CSD get/CSD get_res + exch pop true + exit + }{ + pop + }ifelse + }forall + end + }{ + pop false + }ifelse +}bdf +/spot_alias +{ + true set_spot_alias + /AGMCORE_&setcustomcolor AGMCORE_key_known not{ + //Adobe_AGM_Core/AGMCORE_&setcustomcolor/setcustomcolor load put + }if + /customcolor_tint 1 AGMCORE_gput + //Adobe_AGM_Core begin + /setcustomcolor + { + //Adobe_AGM_Core begin + dup/customcolor_tint exch AGMCORE_gput + 1 index aload pop pop 1 eq exch 1 eq and exch 1 eq and exch 1 eq and not + current_spot_alias and{1 index 4 get map_alias}{false}ifelse + { + false set_spot_alias + /sep_colorspace_dict AGMCORE_gget null ne + 3 1 roll 2 index{ + exch pop/sep_tint AGMCORE_gget exch + }if + mark 3 1 roll + setsepcolorspace + counttomark 0 ne{ + setsepcolor + }if + pop + not{/sep_tint 1.0 AGMCORE_gput}if + pop + true set_spot_alias + }{ + AGMCORE_&setcustomcolor + }ifelse + end + }bdf + end +}def +/begin_feature +{ + Adobe_AGM_Core/AGMCORE_feature_dictCount countdictstack put + count Adobe_AGM_Core/AGMCORE_feature_opCount 3 -1 roll put + {Adobe_AGM_Core/AGMCORE_feature_ctm matrix currentmatrix put}if +}def +/end_feature +{ + 2 dict begin + /spd/setpagedevice load def + /setpagedevice{get_gstate spd set_gstate}def + stopped{$error/newerror false put}if + end + count Adobe_AGM_Core/AGMCORE_feature_opCount get sub dup 0 gt{{pop}repeat}{pop}ifelse + countdictstack Adobe_AGM_Core/AGMCORE_feature_dictCount get sub dup 0 gt{{end}repeat}{pop}ifelse + {Adobe_AGM_Core/AGMCORE_feature_ctm get setmatrix}if +}def +/set_negative +{ + //Adobe_AGM_Core begin + /AGMCORE_inverting exch def + level2{ + currentpagedevice/NegativePrint known AGMCORE_distilling not and{ + currentpagedevice/NegativePrint get//Adobe_AGM_Core/AGMCORE_inverting get ne{ + true begin_feature true{ + <>setpagedevice + }end_feature + }if + /AGMCORE_inverting false def + }if + }if + AGMCORE_inverting{ + [{1 exch sub}/exec load dup currenttransfer exch]cvx bind settransfer + AGMCORE_distilling{ + erasepage + }{ + gsave np clippath 1/setseparationgray where{pop setseparationgray}{setgray}ifelse + /AGMIRS_&fill where{pop AGMIRS_&fill}{fill}ifelse grestore + }ifelse + }if + end +}def +/lw_save_restore_override{ + /md where{ + pop + md begin + initializepage + /initializepage{}def + /pmSVsetup{}def + /endp{}def + /pse{}def + /psb{}def + /orig_showpage where + {pop} + {/orig_showpage/showpage load def} + ifelse + /showpage{orig_showpage gR}def + end + }if +}def +/pscript_showpage_override{ + /NTPSOct95 where + { + begin + showpage + save + /showpage/restore load def + /restore{exch pop}def + end + }if +}def +/driver_media_override +{ + /md where{ + pop + md/initializepage known{ + md/initializepage{}put + }if + md/rC known{ + md/rC{4{pop}repeat}put + }if + }if + /mysetup where{ + /mysetup[1 0 0 1 0 0]put + }if + Adobe_AGM_Core/AGMCORE_Default_CTM matrix currentmatrix put + level2 + {Adobe_AGM_Core/AGMCORE_Default_PageSize currentpagedevice/PageSize get put}if +}def +/driver_check_media_override +{ + /PrepsDict where + {pop} + { + Adobe_AGM_Core/AGMCORE_Default_CTM get matrix currentmatrix ne + Adobe_AGM_Core/AGMCORE_Default_PageSize get type/arraytype eq + { + Adobe_AGM_Core/AGMCORE_Default_PageSize get 0 get currentpagedevice/PageSize get 0 get eq and + Adobe_AGM_Core/AGMCORE_Default_PageSize get 1 get currentpagedevice/PageSize get 1 get eq and + }if + { + Adobe_AGM_Core/AGMCORE_Default_CTM get setmatrix + }if + }ifelse +}def +AGMCORE_err_strings begin + /AGMCORE_bad_environ(Environment not satisfactory for this job. Ensure that the PPD is correct or that the PostScript level requested is supported by this printer. )def + /AGMCORE_color_space_onhost_seps(This job contains colors that will not separate with on-host methods. )def + /AGMCORE_invalid_color_space(This job contains an invalid color space. )def +end +/set_def_ht +{AGMCORE_def_ht sethalftone}def +/set_def_flat +{AGMCORE_Default_flatness setflat}def +end +systemdict/setpacking known +{setpacking}if +%%EndResource +%%BeginResource: procset Adobe_CoolType_Core 2.31 0 %%Copyright: Copyright 1997-2006 Adobe Systems Incorporated. All Rights Reserved. %%Version: 2.31 0 10 dict begin /Adobe_CoolType_Passthru currentdict def /Adobe_CoolType_Core_Defined userdict/Adobe_CoolType_Core known def Adobe_CoolType_Core_Defined {/Adobe_CoolType_Core userdict/Adobe_CoolType_Core get def} if userdict/Adobe_CoolType_Core 70 dict dup begin put /Adobe_CoolType_Version 2.31 def /Level2? systemdict/languagelevel known dup {pop systemdict/languagelevel get 2 ge} if def Level2? not { /currentglobal false def /setglobal/pop load def /gcheck{pop false}bind def /currentpacking false def /setpacking/pop load def /SharedFontDirectory 0 dict def } if currentpacking true setpacking currentglobal false setglobal userdict/Adobe_CoolType_Data 2 copy known not {2 copy 10 dict put} if get begin /@opStackCountByLevel 32 dict def /@opStackLevel 0 def /@dictStackCountByLevel 32 dict def /@dictStackLevel 0 def end setglobal currentglobal true setglobal userdict/Adobe_CoolType_GVMFonts known not {userdict/Adobe_CoolType_GVMFonts 10 dict put} if setglobal currentglobal false setglobal userdict/Adobe_CoolType_LVMFonts known not {userdict/Adobe_CoolType_LVMFonts 10 dict put} if setglobal /ct_VMDictPut { dup gcheck{Adobe_CoolType_GVMFonts}{Adobe_CoolType_LVMFonts}ifelse 3 1 roll put }bind def /ct_VMDictUndef { dup Adobe_CoolType_GVMFonts exch known {Adobe_CoolType_GVMFonts exch undef} { dup Adobe_CoolType_LVMFonts exch known {Adobe_CoolType_LVMFonts exch undef} {pop} ifelse }ifelse }bind def /ct_str1 1 string def /ct_xshow { /_ct_na exch def /_ct_i 0 def currentpoint /_ct_y exch def /_ct_x exch def { pop pop ct_str1 exch 0 exch put ct_str1 show {_ct_na _ct_i get}stopped {pop pop} { _ct_x _ct_y moveto 0 rmoveto } ifelse /_ct_i _ct_i 1 add def currentpoint /_ct_y exch def /_ct_x exch def } exch @cshow }bind def /ct_yshow { /_ct_na exch def /_ct_i 0 def currentpoint /_ct_y exch def /_ct_x exch def { pop pop ct_str1 exch 0 exch put ct_str1 show {_ct_na _ct_i get}stopped {pop pop} { _ct_x _ct_y moveto 0 exch rmoveto } ifelse /_ct_i _ct_i 1 add def currentpoint /_ct_y exch def /_ct_x exch def } exch @cshow }bind def /ct_xyshow { /_ct_na exch def /_ct_i 0 def currentpoint /_ct_y exch def /_ct_x exch def { pop pop ct_str1 exch 0 exch put ct_str1 show {_ct_na _ct_i get}stopped {pop pop} { {_ct_na _ct_i 1 add get}stopped {pop pop pop} { _ct_x _ct_y moveto rmoveto } ifelse } ifelse /_ct_i _ct_i 2 add def currentpoint /_ct_y exch def /_ct_x exch def } exch @cshow }bind def /xsh{{@xshow}stopped{Adobe_CoolType_Data begin ct_xshow end}if}bind def /ysh{{@yshow}stopped{Adobe_CoolType_Data begin ct_yshow end}if}bind def /xysh{{@xyshow}stopped{Adobe_CoolType_Data begin ct_xyshow end}if}bind def currentglobal true setglobal /ct_T3Defs { /BuildChar { 1 index/Encoding get exch get 1 index/BuildGlyph get exec }bind def /BuildGlyph { exch begin GlyphProcs exch get exec end }bind def }bind def setglobal /@_SaveStackLevels { Adobe_CoolType_Data begin /@vmState currentglobal def false setglobal @opStackCountByLevel @opStackLevel 2 copy known not { 2 copy 3 dict dup/args 7 index 5 add array put put get } { get dup/args get dup length 3 index lt { dup length 5 add array exch 1 index exch 0 exch putinterval 1 index exch/args exch put } {pop} ifelse } ifelse begin count 1 sub 1 index lt {pop count} if dup/argCount exch def dup 0 gt { args exch 0 exch getinterval astore pop } {pop} ifelse count /restCount exch def end /@opStackLevel @opStackLevel 1 add def countdictstack 1 sub @dictStackCountByLevel exch @dictStackLevel exch put /@dictStackLevel @dictStackLevel 1 add def @vmState setglobal end }bind def /@_RestoreStackLevels { Adobe_CoolType_Data begin /@opStackLevel @opStackLevel 1 sub def @opStackCountByLevel @opStackLevel get begin count restCount sub dup 0 gt {{pop}repeat} {pop} ifelse args 0 argCount getinterval{}forall end /@dictStackLevel @dictStackLevel 1 sub def @dictStackCountByLevel @dictStackLevel get end countdictstack exch sub dup 0 gt {{end}repeat} {pop} ifelse }bind def /@_PopStackLevels { Adobe_CoolType_Data begin /@opStackLevel @opStackLevel 1 sub def /@dictStackLevel @dictStackLevel 1 sub def end }bind def /@Raise { exch cvx exch errordict exch get exec stop }bind def /@ReRaise { cvx $error/errorname get errordict exch get exec stop }bind def /@Stopped { 0 @#Stopped }bind def /@#Stopped { @_SaveStackLevels stopped {@_RestoreStackLevels true} {@_PopStackLevels false} ifelse }bind def /@Arg { Adobe_CoolType_Data begin @opStackCountByLevel @opStackLevel 1 sub get begin args exch argCount 1 sub exch sub get end end }bind def currentglobal true setglobal /CTHasResourceForAllBug Level2? { 1 dict dup /@shouldNotDisappearDictValue true def Adobe_CoolType_Data exch/@shouldNotDisappearDict exch put begin count @_SaveStackLevels {(*){pop stop}128 string/Category resourceforall} stopped pop @_RestoreStackLevels currentdict Adobe_CoolType_Data/@shouldNotDisappearDict get dup 3 1 roll ne dup 3 1 roll { /@shouldNotDisappearDictValue known { { end currentdict 1 index eq {pop exit} if } loop } if } { pop end } ifelse } {false} ifelse def true setglobal /CTHasResourceStatusBug Level2? { mark {/steveamerige/Category resourcestatus} stopped {cleartomark true} {cleartomark currentglobal not} ifelse } {false} ifelse def setglobal /CTResourceStatus { mark 3 1 roll /Category findresource begin ({ResourceStatus}stopped)0()/SubFileDecode filter cvx exec {cleartomark false} {{3 2 roll pop true}{cleartomark false}ifelse} ifelse end }bind def /CTWorkAroundBugs { Level2? { /cid_PreLoad/ProcSet resourcestatus { pop pop currentglobal mark { (*) { dup/CMap CTHasResourceStatusBug {CTResourceStatus} {resourcestatus} ifelse { pop dup 0 eq exch 1 eq or { dup/CMap findresource gcheck setglobal /CMap undefineresource } { pop CTHasResourceForAllBug {exit} {stop} ifelse } ifelse } {pop} ifelse } 128 string/CMap resourceforall } stopped {cleartomark} stopped pop setglobal } if } if }bind def /ds { Adobe_CoolType_Core begin CTWorkAroundBugs /mo/moveto load def /nf/newencodedfont load def /msf{makefont setfont}bind def /uf{dup undefinefont ct_VMDictUndef}bind def /ur/undefineresource load def /chp/charpath load def /awsh/awidthshow load def /wsh/widthshow load def /ash/ashow load def /@xshow/xshow load def /@yshow/yshow load def /@xyshow/xyshow load def /@cshow/cshow load def /sh/show load def /rp/repeat load def /.n/.notdef def end currentglobal false setglobal userdict/Adobe_CoolType_Data 2 copy known not {2 copy 10 dict put} if get begin /AddWidths? false def /CC 0 def /charcode 2 string def /@opStackCountByLevel 32 dict def /@opStackLevel 0 def /@dictStackCountByLevel 32 dict def /@dictStackLevel 0 def /InVMFontsByCMap 10 dict def /InVMDeepCopiedFonts 10 dict def end setglobal }bind def /dt { currentdict Adobe_CoolType_Core eq {end} if }bind def /ps { Adobe_CoolType_Core begin Adobe_CoolType_GVMFonts begin Adobe_CoolType_LVMFonts begin SharedFontDirectory begin }bind def /pt { end end end end }bind def /unload { systemdict/languagelevel known { systemdict/languagelevel get 2 ge { userdict/Adobe_CoolType_Core 2 copy known {undef} {pop pop} ifelse } if } if }bind def /ndf { 1 index where {pop pop pop} {dup xcheck{bind}if def} ifelse }def /findfont systemdict begin userdict begin /globaldict where{/globaldict get begin}if dup where pop exch get /globaldict where{pop end}if end end Adobe_CoolType_Core_Defined {/systemfindfont exch def} { /findfont 1 index def /systemfindfont exch def } ifelse /undefinefont {pop}ndf /copyfont { currentglobal 3 1 roll 1 index gcheck setglobal dup null eq{0}{dup length}ifelse 2 index length add 1 add dict begin exch { 1 index/FID eq {pop pop} {def} ifelse } forall dup null eq {pop} {{def}forall} ifelse currentdict end exch setglobal }bind def /copyarray { currentglobal exch dup gcheck setglobal dup length array copy exch setglobal }bind def /newencodedfont { currentglobal { SharedFontDirectory 3 index known {SharedFontDirectory 3 index get/FontReferenced known} {false} ifelse } { FontDirectory 3 index known {FontDirectory 3 index get/FontReferenced known} { SharedFontDirectory 3 index known {SharedFontDirectory 3 index get/FontReferenced known} {false} ifelse } ifelse } ifelse dup { 3 index findfont/FontReferenced get 2 index dup type/nametype eq {findfont} if ne {pop false} if } if dup { 1 index dup type/nametype eq {findfont} if dup/CharStrings known { /CharStrings get length 4 index findfont/CharStrings get length ne { pop false } if } {pop} ifelse } if { pop 1 index findfont /Encoding get exch 0 1 255 {2 copy get 3 index 3 1 roll put} for pop pop pop } { currentglobal 4 1 roll dup type/nametype eq {findfont} if dup gcheck setglobal dup dup maxlength 2 add dict begin exch { 1 index/FID ne 2 index/Encoding ne and {def} {pop pop} ifelse } forall /FontReferenced exch def /Encoding exch dup length array copy def /FontName 1 index dup type/stringtype eq{cvn}if def dup currentdict end definefont ct_VMDictPut setglobal } ifelse }bind def /SetSubstituteStrategy { $SubstituteFont begin dup type/dicttype ne {0 dict} if currentdict/$Strategies known { exch $Strategies exch 2 copy known { get 2 copy maxlength exch maxlength add dict begin {def}forall {def}forall currentdict dup/$Init known {dup/$Init get exec} if end /$Strategy exch def } {pop pop pop} ifelse } {pop pop} ifelse end }bind def /scff { $SubstituteFont begin dup type/stringtype eq {dup length exch} {null} ifelse /$sname exch def /$slen exch def /$inVMIndex $sname null eq { 1 index $str cvs dup length $slen sub $slen getinterval cvn } {$sname} ifelse def end {findfont} @Stopped { dup length 8 add string exch 1 index 0(BadFont:)putinterval 1 index exch 8 exch dup length string cvs putinterval cvn {findfont} @Stopped {pop/Courier findfont} if } if $SubstituteFont begin /$sname null def /$slen 0 def /$inVMIndex null def end }bind def /isWidthsOnlyFont { dup/WidthsOnly known {pop pop true} { dup/FDepVector known {/FDepVector get{isWidthsOnlyFont dup{exit}if}forall} { dup/FDArray known {/FDArray get{isWidthsOnlyFont dup{exit}if}forall} {pop} ifelse } ifelse } ifelse }bind def /ct_StyleDicts 4 dict dup begin /Adobe-Japan1 4 dict dup begin Level2? { /Serif /HeiseiMin-W3-83pv-RKSJ-H/Font resourcestatus {pop pop/HeiseiMin-W3} { /CIDFont/Category resourcestatus { pop pop /HeiseiMin-W3/CIDFont resourcestatus {pop pop/HeiseiMin-W3} {/Ryumin-Light} ifelse } {/Ryumin-Light} ifelse } ifelse def /SansSerif /HeiseiKakuGo-W5-83pv-RKSJ-H/Font resourcestatus {pop pop/HeiseiKakuGo-W5} { /CIDFont/Category resourcestatus { pop pop /HeiseiKakuGo-W5/CIDFont resourcestatus {pop pop/HeiseiKakuGo-W5} {/GothicBBB-Medium} ifelse } {/GothicBBB-Medium} ifelse } ifelse def /HeiseiMaruGo-W4-83pv-RKSJ-H/Font resourcestatus {pop pop/HeiseiMaruGo-W4} { /CIDFont/Category resourcestatus { pop pop /HeiseiMaruGo-W4/CIDFont resourcestatus {pop pop/HeiseiMaruGo-W4} { /Jun101-Light-RKSJ-H/Font resourcestatus {pop pop/Jun101-Light} {SansSerif} ifelse } ifelse } { /Jun101-Light-RKSJ-H/Font resourcestatus {pop pop/Jun101-Light} {SansSerif} ifelse } ifelse } ifelse /RoundSansSerif exch def /Default Serif def } { /Serif/Ryumin-Light def /SansSerif/GothicBBB-Medium def { (fonts/Jun101-Light-83pv-RKSJ-H)status }stopped {pop}{ {pop pop pop pop/Jun101-Light} {SansSerif} ifelse /RoundSansSerif exch def }ifelse /Default Serif def } ifelse end def /Adobe-Korea1 4 dict dup begin /Serif/HYSMyeongJo-Medium def /SansSerif/HYGoThic-Medium def /RoundSansSerif SansSerif def /Default Serif def end def /Adobe-GB1 4 dict dup begin /Serif/STSong-Light def /SansSerif/STHeiti-Regular def /RoundSansSerif SansSerif def /Default Serif def end def /Adobe-CNS1 4 dict dup begin /Serif/MKai-Medium def /SansSerif/MHei-Medium def /RoundSansSerif SansSerif def /Default Serif def end def end def Level2?{currentglobal true setglobal}if /ct_BoldRomanWidthProc { stringwidth 1 index 0 ne{exch .03 add exch}if setcharwidth 0 0 }bind def /ct_Type0WidthProc { dup stringwidth 0 0 moveto 2 index true charpath pathbbox 0 -1 7 index 2 div .88 setcachedevice2 pop 0 0 }bind def /ct_Type0WMode1WidthProc { dup stringwidth pop 2 div neg -0.88 2 copy moveto 0 -1 5 -1 roll true charpath pathbbox setcachedevice }bind def /cHexEncoding [/c00/c01/c02/c03/c04/c05/c06/c07/c08/c09/c0A/c0B/c0C/c0D/c0E/c0F/c10/c11/c12 /c13/c14/c15/c16/c17/c18/c19/c1A/c1B/c1C/c1D/c1E/c1F/c20/c21/c22/c23/c24/c25 /c26/c27/c28/c29/c2A/c2B/c2C/c2D/c2E/c2F/c30/c31/c32/c33/c34/c35/c36/c37/c38 /c39/c3A/c3B/c3C/c3D/c3E/c3F/c40/c41/c42/c43/c44/c45/c46/c47/c48/c49/c4A/c4B /c4C/c4D/c4E/c4F/c50/c51/c52/c53/c54/c55/c56/c57/c58/c59/c5A/c5B/c5C/c5D/c5E /c5F/c60/c61/c62/c63/c64/c65/c66/c67/c68/c69/c6A/c6B/c6C/c6D/c6E/c6F/c70/c71 /c72/c73/c74/c75/c76/c77/c78/c79/c7A/c7B/c7C/c7D/c7E/c7F/c80/c81/c82/c83/c84 /c85/c86/c87/c88/c89/c8A/c8B/c8C/c8D/c8E/c8F/c90/c91/c92/c93/c94/c95/c96/c97 /c98/c99/c9A/c9B/c9C/c9D/c9E/c9F/cA0/cA1/cA2/cA3/cA4/cA5/cA6/cA7/cA8/cA9/cAA /cAB/cAC/cAD/cAE/cAF/cB0/cB1/cB2/cB3/cB4/cB5/cB6/cB7/cB8/cB9/cBA/cBB/cBC/cBD /cBE/cBF/cC0/cC1/cC2/cC3/cC4/cC5/cC6/cC7/cC8/cC9/cCA/cCB/cCC/cCD/cCE/cCF/cD0 /cD1/cD2/cD3/cD4/cD5/cD6/cD7/cD8/cD9/cDA/cDB/cDC/cDD/cDE/cDF/cE0/cE1/cE2/cE3 /cE4/cE5/cE6/cE7/cE8/cE9/cEA/cEB/cEC/cED/cEE/cEF/cF0/cF1/cF2/cF3/cF4/cF5/cF6 /cF7/cF8/cF9/cFA/cFB/cFC/cFD/cFE/cFF]def /ct_BoldBaseFont 11 dict begin /FontType 3 def /FontMatrix[1 0 0 1 0 0]def /FontBBox[0 0 1 1]def /Encoding cHexEncoding def /_setwidthProc/ct_BoldRomanWidthProc load def /_bcstr1 1 string def /BuildChar { exch begin _basefont setfont _bcstr1 dup 0 4 -1 roll put dup _setwidthProc 3 copy moveto show _basefonto setfont moveto show end }bind def currentdict end def systemdict/composefont known { /ct_DefineIdentity-H { /Identity-H/CMap resourcestatus { pop pop } { /CIDInit/ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo 3 dict dup begin /Registry(Adobe)def /Ordering(Identity)def /Supplement 0 def end def /CMapName/Identity-H def /CMapVersion 1.000 def /CMapType 1 def 1 begincodespacerange <0000> endcodespacerange 1 begincidrange <0000>0 endcidrange endcmap CMapName currentdict/CMap defineresource pop end end } ifelse } def /ct_BoldBaseCIDFont 11 dict begin /CIDFontType 1 def /CIDFontName/ct_BoldBaseCIDFont def /FontMatrix[1 0 0 1 0 0]def /FontBBox[0 0 1 1]def /_setwidthProc/ct_Type0WidthProc load def /_bcstr2 2 string def /BuildGlyph { exch begin _basefont setfont _bcstr2 1 2 index 256 mod put _bcstr2 0 3 -1 roll 256 idiv put _bcstr2 dup _setwidthProc 3 copy moveto show _basefonto setfont moveto show end }bind def currentdict end def }if Level2?{setglobal}if /ct_CopyFont{ { 1 index/FID ne 2 index/UniqueID ne and {def}{pop pop}ifelse }forall }bind def /ct_Type0CopyFont { exch dup length dict begin ct_CopyFont [ exch FDepVector { dup/FontType get 0 eq { 1 index ct_Type0CopyFont /_ctType0 exch definefont } { /_ctBaseFont exch 2 index exec } ifelse exch } forall pop ] /FDepVector exch def currentdict end }bind def /ct_MakeBoldFont { dup/ct_SyntheticBold known { dup length 3 add dict begin ct_CopyFont /ct_StrokeWidth .03 0 FontMatrix idtransform pop def /ct_SyntheticBold true def currentdict end definefont } { dup dup length 3 add dict begin ct_CopyFont /PaintType 2 def /StrokeWidth .03 0 FontMatrix idtransform pop def /dummybold currentdict end definefont dup/FontType get dup 9 ge exch 11 le and { ct_BoldBaseCIDFont dup length 3 add dict copy begin dup/CIDSystemInfo get/CIDSystemInfo exch def ct_DefineIdentity-H /_Type0Identity/Identity-H 3 -1 roll[exch]composefont /_basefont exch def /_Type0Identity/Identity-H 3 -1 roll[exch]composefont /_basefonto exch def currentdict end /CIDFont defineresource } { ct_BoldBaseFont dup length 3 add dict copy begin /_basefont exch def /_basefonto exch def currentdict end definefont } ifelse } ifelse }bind def /ct_MakeBold{ 1 index 1 index findfont currentglobal 5 1 roll dup gcheck setglobal dup /FontType get 0 eq { dup/WMode known{dup/WMode get 1 eq}{false}ifelse version length 4 ge and {version 0 4 getinterval cvi 2015 ge} {true} ifelse {/ct_Type0WidthProc} {/ct_Type0WMode1WidthProc} ifelse ct_BoldBaseFont/_setwidthProc 3 -1 roll load put {ct_MakeBoldFont}ct_Type0CopyFont definefont } { dup/_fauxfont known not 1 index/SubstMaster known not and { ct_BoldBaseFont/_setwidthProc /ct_BoldRomanWidthProc load put ct_MakeBoldFont } { 2 index 2 index eq {exch pop } { dup length dict begin ct_CopyFont currentdict end definefont } ifelse } ifelse } ifelse pop pop pop setglobal }bind def /?str1 256 string def /?set { $SubstituteFont begin /$substituteFound false def /$fontname 1 index def /$doSmartSub false def end dup findfont $SubstituteFont begin $substituteFound {false} { dup/FontName known { dup/FontName get $fontname eq 1 index/DistillerFauxFont known not and /currentdistillerparams where {pop false 2 index isWidthsOnlyFont not and} if } {false} ifelse } ifelse exch pop /$doSmartSub true def end { 5 1 roll pop pop pop pop findfont } { 1 index findfont dup/FontType get 3 eq { 6 1 roll pop pop pop pop pop false } {pop true} ifelse { $SubstituteFont begin pop pop /$styleArray 1 index def /$regOrdering 2 index def pop pop 0 1 $styleArray length 1 sub { $styleArray exch get ct_StyleDicts $regOrdering 2 copy known { get exch 2 copy known not {pop/Default} if get dup type/nametype eq { ?str1 cvs length dup 1 add exch ?str1 exch(-)putinterval exch dup length exch ?str1 exch 3 index exch putinterval add ?str1 exch 0 exch getinterval cvn } { pop pop/Unknown } ifelse } { pop pop pop pop/Unknown } ifelse } for end findfont }if } ifelse currentglobal false setglobal 3 1 roll null copyfont definefont pop setglobal }bind def setpacking userdict/$SubstituteFont 25 dict put 1 dict begin /SubstituteFont dup $error exch 2 copy known {get} {pop pop{pop/Courier}bind} ifelse def /currentdistillerparams where dup { pop pop currentdistillerparams/CannotEmbedFontPolicy 2 copy known {get/Error eq} {pop pop false} ifelse } if not { countdictstack array dictstack 0 get begin userdict begin $SubstituteFont begin /$str 128 string def /$fontpat 128 string def /$slen 0 def /$sname null def /$match false def /$fontname null def /$substituteFound false def /$inVMIndex null def /$doSmartSub true def /$depth 0 def /$fontname null def /$italicangle 26.5 def /$dstack null def /$Strategies 10 dict dup begin /$Type3Underprint { currentglobal exch false setglobal 11 dict begin /UseFont exch $WMode 0 ne { dup length dict copy dup/WMode $WMode put /UseFont exch definefont } if def /FontName $fontname dup type/stringtype eq{cvn}if def /FontType 3 def /FontMatrix[.001 0 0 .001 0 0]def /Encoding 256 array dup 0 1 255{/.notdef put dup}for pop def /FontBBox[0 0 0 0]def /CCInfo 7 dict dup begin /cc null def /x 0 def /y 0 def end def /BuildChar { exch begin CCInfo begin 1 string dup 0 3 index put exch pop /cc exch def UseFont 1000 scalefont setfont cc stringwidth/y exch def/x exch def x y setcharwidth $SubstituteFont/$Strategy get/$Underprint get exec 0 0 moveto cc show x y moveto end end }bind def currentdict end exch setglobal }bind def /$GetaTint 2 dict dup begin /$BuildFont { dup/WMode known {dup/WMode get} {0} ifelse /$WMode exch def $fontname exch dup/FontName known { dup/FontName get dup type/stringtype eq{cvn}if } {/unnamedfont} ifelse exch Adobe_CoolType_Data/InVMDeepCopiedFonts get 1 index/FontName get known { pop Adobe_CoolType_Data/InVMDeepCopiedFonts get 1 index get null copyfont } {$deepcopyfont} ifelse exch 1 index exch/FontBasedOn exch put dup/FontName $fontname dup type/stringtype eq{cvn}if put definefont Adobe_CoolType_Data/InVMDeepCopiedFonts get begin dup/FontBasedOn get 1 index def end }bind def /$Underprint { gsave x abs y abs gt {/y 1000 def} {/x -1000 def 500 120 translate} ifelse Level2? { [/Separation(All)/DeviceCMYK{0 0 0 1 pop}] setcolorspace } {0 setgray} ifelse 10 setlinewidth x .8 mul [7 3] { y mul 8 div 120 sub x 10 div exch moveto 0 y 4 div neg rlineto dup 0 rlineto 0 y 4 div rlineto closepath gsave Level2? {.2 setcolor} {.8 setgray} ifelse fill grestore stroke } forall pop grestore }bind def end def /$Oblique 1 dict dup begin /$BuildFont { currentglobal exch dup gcheck setglobal null copyfont begin /FontBasedOn currentdict/FontName known { FontName dup type/stringtype eq{cvn}if } {/unnamedfont} ifelse def /FontName $fontname dup type/stringtype eq{cvn}if def /currentdistillerparams where {pop} { /FontInfo currentdict/FontInfo known {FontInfo null copyfont} {2 dict} ifelse dup begin /ItalicAngle $italicangle def /FontMatrix FontMatrix [1 0 ItalicAngle dup sin exch cos div 1 0 0] matrix concatmatrix readonly end 4 2 roll def def } ifelse FontName currentdict end definefont exch setglobal }bind def end def /$None 1 dict dup begin /$BuildFont{}bind def end def end def /$Oblique SetSubstituteStrategy /$findfontByEnum { dup type/stringtype eq{cvn}if dup/$fontname exch def $sname null eq {$str cvs dup length $slen sub $slen getinterval} {pop $sname} ifelse $fontpat dup 0(fonts/*)putinterval exch 7 exch putinterval /$match false def $SubstituteFont/$dstack countdictstack array dictstack put mark { $fontpat 0 $slen 7 add getinterval {/$match exch def exit} $str filenameforall } stopped { cleardictstack currentdict true $SubstituteFont/$dstack get { exch { 1 index eq {pop false} {true} ifelse } {begin false} ifelse } forall pop } if cleartomark /$slen 0 def $match false ne {$match(fonts/)anchorsearch pop pop cvn} {/Courier} ifelse }bind def /$ROS 1 dict dup begin /Adobe 4 dict dup begin /Japan1 [/Ryumin-Light/HeiseiMin-W3 /GothicBBB-Medium/HeiseiKakuGo-W5 /HeiseiMaruGo-W4/Jun101-Light]def /Korea1 [/HYSMyeongJo-Medium/HYGoThic-Medium]def /GB1 [/STSong-Light/STHeiti-Regular]def /CNS1 [/MKai-Medium/MHei-Medium]def end def end def /$cmapname null def /$deepcopyfont { dup/FontType get 0 eq { 1 dict dup/FontName/copied put copyfont begin /FDepVector FDepVector copyarray 0 1 2 index length 1 sub { 2 copy get $deepcopyfont dup/FontName/copied put /copied exch definefont 3 copy put pop pop } for def currentdict end } {$Strategies/$Type3Underprint get exec} ifelse }bind def /$buildfontname { dup/CIDFont findresource/CIDSystemInfo get begin Registry length Ordering length Supplement 8 string cvs 3 copy length 2 add add add string dup 5 1 roll dup 0 Registry putinterval dup 4 index(-)putinterval dup 4 index 1 add Ordering putinterval 4 2 roll add 1 add 2 copy(-)putinterval end 1 add 2 copy 0 exch getinterval $cmapname $fontpat cvs exch anchorsearch {pop pop 3 2 roll putinterval cvn/$cmapname exch def} {pop pop pop pop pop} ifelse length $str 1 index(-)putinterval 1 add $str 1 index $cmapname $fontpat cvs putinterval $cmapname length add $str exch 0 exch getinterval cvn }bind def /$findfontByROS { /$fontname exch def $ROS Registry 2 copy known { get Ordering 2 copy known {get} {pop pop[]} ifelse } {pop pop[]} ifelse false exch { dup/CIDFont resourcestatus { pop pop save 1 index/CIDFont findresource dup/WidthsOnly known {dup/WidthsOnly get} {false} ifelse exch pop exch restore {pop} {exch pop true exit} ifelse } {pop} ifelse } forall {$str cvs $buildfontname} { false(*) { save exch dup/CIDFont findresource dup/WidthsOnly known {dup/WidthsOnly get not} {true} ifelse exch/CIDSystemInfo get dup/Registry get Registry eq exch/Ordering get Ordering eq and and {exch restore exch pop true exit} {pop restore} ifelse } $str/CIDFont resourceforall {$buildfontname} {$fontname $findfontByEnum} ifelse } ifelse }bind def end end currentdict/$error known currentdict/languagelevel known and dup {pop $error/SubstituteFont known} if dup {$error} {Adobe_CoolType_Core} ifelse begin { /SubstituteFont /CMap/Category resourcestatus { pop pop { $SubstituteFont begin /$substituteFound true def dup length $slen gt $sname null ne or $slen 0 gt and { $sname null eq {dup $str cvs dup length $slen sub $slen getinterval cvn} {$sname} ifelse Adobe_CoolType_Data/InVMFontsByCMap get 1 index 2 copy known { get false exch { pop currentglobal { GlobalFontDirectory 1 index known {exch pop true exit} {pop} ifelse } { FontDirectory 1 index known {exch pop true exit} { GlobalFontDirectory 1 index known {exch pop true exit} {pop} ifelse } ifelse } ifelse } forall } {pop pop false} ifelse { exch pop exch pop } { dup/CMap resourcestatus { pop pop dup/$cmapname exch def /CMap findresource/CIDSystemInfo get{def}forall $findfontByROS } { 128 string cvs dup(-)search { 3 1 roll search { 3 1 roll pop {dup cvi} stopped {pop pop pop pop pop $findfontByEnum} { 4 2 roll pop pop exch length exch 2 index length 2 index sub exch 1 sub -1 0 { $str cvs dup length 4 index 0 4 index 4 3 roll add getinterval exch 1 index exch 3 index exch putinterval dup/CMap resourcestatus { pop pop 4 1 roll pop pop pop dup/$cmapname exch def /CMap findresource/CIDSystemInfo get{def}forall $findfontByROS true exit } {pop} ifelse } for dup type/booleantype eq {pop} {pop pop pop $findfontByEnum} ifelse } ifelse } {pop pop pop $findfontByEnum} ifelse } {pop pop $findfontByEnum} ifelse } ifelse } ifelse } {//SubstituteFont exec} ifelse /$slen 0 def end } } { { $SubstituteFont begin /$substituteFound true def dup length $slen gt $sname null ne or $slen 0 gt and {$findfontByEnum} {//SubstituteFont exec} ifelse end } } ifelse bind readonly def Adobe_CoolType_Core/scfindfont/systemfindfont load put } { /scfindfont { $SubstituteFont begin dup systemfindfont dup/FontName known {dup/FontName get dup 3 index ne} {/noname true} ifelse dup { /$origfontnamefound 2 index def /$origfontname 4 index def/$substituteFound true def } if exch pop { $slen 0 gt $sname null ne 3 index length $slen gt or and { pop dup $findfontByEnum findfont dup maxlength 1 add dict begin {1 index/FID eq{pop pop}{def}ifelse} forall currentdict end definefont dup/FontName known{dup/FontName get}{null}ifelse $origfontnamefound ne { $origfontname $str cvs print ( substitution revised, using )print dup/FontName known {dup/FontName get}{(unspecified font)} ifelse $str cvs print(.\n)print } if } {exch pop} ifelse } {exch pop} ifelse end }bind def } ifelse end end Adobe_CoolType_Core_Defined not { Adobe_CoolType_Core/findfont { $SubstituteFont begin $depth 0 eq { /$fontname 1 index dup type/stringtype ne{$str cvs}if def /$substituteFound false def } if /$depth $depth 1 add def end scfindfont $SubstituteFont begin /$depth $depth 1 sub def $substituteFound $depth 0 eq and { $inVMIndex null ne {dup $inVMIndex $AddInVMFont} if $doSmartSub { currentdict/$Strategy known {$Strategy/$BuildFont get exec} if } if } if end }bind put } if } if end /$AddInVMFont { exch/FontName 2 copy known { get 1 dict dup begin exch 1 index gcheck def end exch Adobe_CoolType_Data/InVMFontsByCMap get exch $DictAdd } {pop pop pop} ifelse }bind def /$DictAdd { 2 copy known not {2 copy 4 index length dict put} if Level2? not { 2 copy get dup maxlength exch length 4 index length add lt 2 copy get dup length 4 index length add exch maxlength 1 index lt { 2 mul dict begin 2 copy get{forall}def 2 copy currentdict put end } {pop} ifelse } if get begin {def} forall end }bind def end end %%EndResource currentglobal true setglobal %%BeginResource: procset Adobe_CoolType_Utility_MAKEOCF 1.23 0 %%Copyright: Copyright 1987-2006 Adobe Systems Incorporated. %%Version: 1.23 0 systemdict/languagelevel known dup {currentglobal false setglobal} {false} ifelse exch userdict/Adobe_CoolType_Utility 2 copy known {2 copy get dup maxlength 27 add dict copy} {27 dict} ifelse put Adobe_CoolType_Utility begin /@eexecStartData def /@recognizeCIDFont null def /ct_Level2? exch def /ct_Clone? 1183615869 internaldict dup /CCRun known not exch/eCCRun known not ct_Level2? and or def ct_Level2? {globaldict begin currentglobal true setglobal} if /ct_AddStdCIDMap ct_Level2? {{ mark Adobe_CoolType_Utility/@recognizeCIDFont currentdict put { ((Hex)57 StartData 0615 1e27 2c39 1c60 d8a8 cc31 fe2b f6e0 7aa3 e541 e21c 60d8 a8c9 c3d0 6d9e 1c60 d8a8 c9c2 02d7 9a1c 60d8 a849 1c60 d8a8 cc36 74f4 1144 b13b 77)0()/SubFileDecode filter cvx exec } stopped { cleartomark Adobe_CoolType_Utility/@recognizeCIDFont get countdictstack dup array dictstack exch 1 sub -1 0 { 2 copy get 3 index eq {1 index length exch sub 1 sub{end}repeat exit} {pop} ifelse } for pop pop Adobe_CoolType_Utility/@eexecStartData get eexec } {cleartomark} ifelse }} {{ Adobe_CoolType_Utility/@eexecStartData get eexec }} ifelse bind def userdict/cid_extensions known dup{cid_extensions/cid_UpdateDB known and}if { cid_extensions begin /cid_GetCIDSystemInfo { 1 index type/stringtype eq {exch cvn exch} if cid_extensions begin dup load 2 index known { 2 copy cid_GetStatusInfo dup null ne { 1 index load 3 index get dup null eq {pop pop cid_UpdateDB} { exch 1 index/Created get eq {exch pop exch pop} {pop cid_UpdateDB} ifelse } ifelse } {pop cid_UpdateDB} ifelse } {cid_UpdateDB} ifelse end }bind def end } if ct_Level2? {end setglobal} if /ct_UseNativeCapability? systemdict/composefont known def /ct_MakeOCF 35 dict def /ct_Vars 25 dict def /ct_GlyphDirProcs 6 dict def /ct_BuildCharDict 15 dict dup begin /charcode 2 string def /dst_string 1500 string def /nullstring()def /usewidths? true def end def ct_Level2?{setglobal}{pop}ifelse ct_GlyphDirProcs begin /GetGlyphDirectory { systemdict/languagelevel known {pop/CIDFont findresource/GlyphDirectory get} { 1 index/CIDFont findresource/GlyphDirectory get dup type/dicttype eq { dup dup maxlength exch length sub 2 index lt { dup length 2 index add dict copy 2 index /CIDFont findresource/GlyphDirectory 2 index put } if } if exch pop exch pop } ifelse + }def /+ { systemdict/languagelevel known { currentglobal false setglobal 3 dict begin /vm exch def } {1 dict begin} ifelse /$ exch def systemdict/languagelevel known { vm setglobal /gvm currentglobal def $ gcheck setglobal } if ?{$ begin}if }def /?{$ type/dicttype eq}def /|{ userdict/Adobe_CoolType_Data known { Adobe_CoolType_Data/AddWidths? known { currentdict Adobe_CoolType_Data begin begin AddWidths? { Adobe_CoolType_Data/CC 3 index put ?{def}{$ 3 1 roll put}ifelse CC charcode exch 1 index 0 2 index 256 idiv put 1 index exch 1 exch 256 mod put stringwidth 2 array astore currentfont/Widths get exch CC exch put } {?{def}{$ 3 1 roll put}ifelse} ifelse end end } {?{def}{$ 3 1 roll put}ifelse} ifelse } {?{def}{$ 3 1 roll put}ifelse} ifelse }def /! { ?{end}if systemdict/languagelevel known {gvm setglobal} if end }def /:{string currentfile exch readstring pop}executeonly def end ct_MakeOCF begin /ct_cHexEncoding [/c00/c01/c02/c03/c04/c05/c06/c07/c08/c09/c0A/c0B/c0C/c0D/c0E/c0F/c10/c11/c12 /c13/c14/c15/c16/c17/c18/c19/c1A/c1B/c1C/c1D/c1E/c1F/c20/c21/c22/c23/c24/c25 /c26/c27/c28/c29/c2A/c2B/c2C/c2D/c2E/c2F/c30/c31/c32/c33/c34/c35/c36/c37/c38 /c39/c3A/c3B/c3C/c3D/c3E/c3F/c40/c41/c42/c43/c44/c45/c46/c47/c48/c49/c4A/c4B /c4C/c4D/c4E/c4F/c50/c51/c52/c53/c54/c55/c56/c57/c58/c59/c5A/c5B/c5C/c5D/c5E /c5F/c60/c61/c62/c63/c64/c65/c66/c67/c68/c69/c6A/c6B/c6C/c6D/c6E/c6F/c70/c71 /c72/c73/c74/c75/c76/c77/c78/c79/c7A/c7B/c7C/c7D/c7E/c7F/c80/c81/c82/c83/c84 /c85/c86/c87/c88/c89/c8A/c8B/c8C/c8D/c8E/c8F/c90/c91/c92/c93/c94/c95/c96/c97 /c98/c99/c9A/c9B/c9C/c9D/c9E/c9F/cA0/cA1/cA2/cA3/cA4/cA5/cA6/cA7/cA8/cA9/cAA /cAB/cAC/cAD/cAE/cAF/cB0/cB1/cB2/cB3/cB4/cB5/cB6/cB7/cB8/cB9/cBA/cBB/cBC/cBD /cBE/cBF/cC0/cC1/cC2/cC3/cC4/cC5/cC6/cC7/cC8/cC9/cCA/cCB/cCC/cCD/cCE/cCF/cD0 /cD1/cD2/cD3/cD4/cD5/cD6/cD7/cD8/cD9/cDA/cDB/cDC/cDD/cDE/cDF/cE0/cE1/cE2/cE3 /cE4/cE5/cE6/cE7/cE8/cE9/cEA/cEB/cEC/cED/cEE/cEF/cF0/cF1/cF2/cF3/cF4/cF5/cF6 /cF7/cF8/cF9/cFA/cFB/cFC/cFD/cFE/cFF]def /ct_CID_STR_SIZE 8000 def /ct_mkocfStr100 100 string def /ct_defaultFontMtx[.001 0 0 .001 0 0]def /ct_1000Mtx[1000 0 0 1000 0 0]def /ct_raise{exch cvx exch errordict exch get exec stop}bind def /ct_reraise {cvx $error/errorname get(Error: )print dup( )cvs print errordict exch get exec stop }bind def /ct_cvnsi { 1 index add 1 sub 1 exch 0 4 1 roll { 2 index exch get exch 8 bitshift add } for exch pop }bind def /ct_GetInterval { Adobe_CoolType_Utility/ct_BuildCharDict get begin /dst_index 0 def dup dst_string length gt {dup string/dst_string exch def} if 1 index ct_CID_STR_SIZE idiv /arrayIndex exch def 2 index arrayIndex get 2 index arrayIndex ct_CID_STR_SIZE mul sub { dup 3 index add 2 index length le { 2 index getinterval dst_string dst_index 2 index putinterval length dst_index add/dst_index exch def exit } { 1 index length 1 index sub dup 4 1 roll getinterval dst_string dst_index 2 index putinterval pop dup dst_index add/dst_index exch def sub /arrayIndex arrayIndex 1 add def 2 index dup length arrayIndex gt {arrayIndex get} { pop exit } ifelse 0 } ifelse } loop pop pop pop dst_string 0 dst_index getinterval end }bind def ct_Level2? { /ct_resourcestatus currentglobal mark true setglobal {/unknowninstancename/Category resourcestatus} stopped {cleartomark setglobal true} {cleartomark currentglobal not exch setglobal} ifelse { { mark 3 1 roll/Category findresource begin ct_Vars/vm currentglobal put ({ResourceStatus}stopped)0()/SubFileDecode filter cvx exec {cleartomark false} {{3 2 roll pop true}{cleartomark false}ifelse} ifelse ct_Vars/vm get setglobal end } } {{resourcestatus}} ifelse bind def /CIDFont/Category ct_resourcestatus {pop pop} { currentglobal true setglobal /Generic/Category findresource dup length dict copy dup/InstanceType/dicttype put /CIDFont exch/Category defineresource pop setglobal } ifelse ct_UseNativeCapability? { /CIDInit/ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo 3 dict dup begin /Registry(Adobe)def /Ordering(Identity)def /Supplement 0 def end def /CMapName/Identity-H def /CMapVersion 1.000 def /CMapType 1 def 1 begincodespacerange <0000> endcodespacerange 1 begincidrange <0000>0 endcidrange endcmap CMapName currentdict/CMap defineresource pop end end } if } { /ct_Category 2 dict begin /CIDFont 10 dict def /ProcSet 2 dict def currentdict end def /defineresource { ct_Category 1 index 2 copy known { get dup dup maxlength exch length eq { dup length 10 add dict copy ct_Category 2 index 2 index put } if 3 index 3 index put pop exch pop } {pop pop/defineresource/undefined ct_raise} ifelse }bind def /findresource { ct_Category 1 index 2 copy known { get 2 index 2 copy known {get 3 1 roll pop pop} {pop pop/findresource/undefinedresource ct_raise} ifelse } {pop pop/findresource/undefined ct_raise} ifelse }bind def /resourcestatus { ct_Category 1 index 2 copy known { get 2 index known exch pop exch pop { 0 -1 true } { false } ifelse } {pop pop/findresource/undefined ct_raise} ifelse }bind def /ct_resourcestatus/resourcestatus load def } ifelse /ct_CIDInit 2 dict begin /ct_cidfont_stream_init { { dup(Binary)eq { pop null currentfile ct_Level2? { {cid_BYTE_COUNT()/SubFileDecode filter} stopped {pop pop pop} if } if /readstring load exit } if dup(Hex)eq { pop currentfile ct_Level2? { {null exch/ASCIIHexDecode filter/readstring} stopped {pop exch pop(>)exch/readhexstring} if } {(>)exch/readhexstring} ifelse load exit } if /StartData/typecheck ct_raise } loop cid_BYTE_COUNT ct_CID_STR_SIZE le { 2 copy cid_BYTE_COUNT string exch exec pop 1 array dup 3 -1 roll 0 exch put } { cid_BYTE_COUNT ct_CID_STR_SIZE div ceiling cvi dup array exch 2 sub 0 exch 1 exch { 2 copy 5 index ct_CID_STR_SIZE string 6 index exec pop put pop } for 2 index cid_BYTE_COUNT ct_CID_STR_SIZE mod string 3 index exec pop 1 index exch 1 index length 1 sub exch put } ifelse cid_CIDFONT exch/GlyphData exch put 2 index null eq { pop pop pop } { pop/readstring load 1 string exch { 3 copy exec pop dup length 0 eq { pop pop pop pop pop true exit } if 4 index eq { pop pop pop pop false exit } if } loop pop } ifelse }bind def /StartData { mark { currentdict dup/FDArray get 0 get/FontMatrix get 0 get 0.001 eq { dup/CDevProc known not { /CDevProc 1183615869 internaldict/stdCDevProc 2 copy known {get} { pop pop {pop pop pop pop pop 0 -1000 7 index 2 div 880} } ifelse def } if } { /CDevProc { pop pop pop pop pop 0 1 cid_temp/cid_CIDFONT get /FDArray get 0 get /FontMatrix get 0 get div 7 index 2 div 1 index 0.88 mul }def } ifelse /cid_temp 15 dict def cid_temp begin /cid_CIDFONT exch def 3 copy pop dup/cid_BYTE_COUNT exch def 0 gt { ct_cidfont_stream_init FDArray { /Private get dup/SubrMapOffset known { begin /Subrs SubrCount array def Subrs SubrMapOffset SubrCount SDBytes ct_Level2? { currentdict dup/SubrMapOffset undef dup/SubrCount undef /SDBytes undef } if end /cid_SD_BYTES exch def /cid_SUBR_COUNT exch def /cid_SUBR_MAP_OFFSET exch def /cid_SUBRS exch def cid_SUBR_COUNT 0 gt { GlyphData cid_SUBR_MAP_OFFSET cid_SD_BYTES ct_GetInterval 0 cid_SD_BYTES ct_cvnsi 0 1 cid_SUBR_COUNT 1 sub { exch 1 index 1 add cid_SD_BYTES mul cid_SUBR_MAP_OFFSET add GlyphData exch cid_SD_BYTES ct_GetInterval 0 cid_SD_BYTES ct_cvnsi cid_SUBRS 4 2 roll GlyphData exch 4 index 1 index sub ct_GetInterval dup length string copy put } for pop } if } {pop} ifelse } forall } if cleartomark pop pop end CIDFontName currentdict/CIDFont defineresource pop end end } stopped {cleartomark/StartData ct_reraise} if }bind def currentdict end def /ct_saveCIDInit { /CIDInit/ProcSet ct_resourcestatus {true} {/CIDInitC/ProcSet ct_resourcestatus} ifelse { pop pop /CIDInit/ProcSet findresource ct_UseNativeCapability? {pop null} {/CIDInit ct_CIDInit/ProcSet defineresource pop} ifelse } {/CIDInit ct_CIDInit/ProcSet defineresource pop null} ifelse ct_Vars exch/ct_oldCIDInit exch put }bind def /ct_restoreCIDInit { ct_Vars/ct_oldCIDInit get dup null ne {/CIDInit exch/ProcSet defineresource pop} {pop} ifelse }bind def /ct_BuildCharSetUp { 1 index begin CIDFont begin Adobe_CoolType_Utility/ct_BuildCharDict get begin /ct_dfCharCode exch def /ct_dfDict exch def CIDFirstByte ct_dfCharCode add dup CIDCount ge {pop 0} if /cid exch def { GlyphDirectory cid 2 copy known {get} {pop pop nullstring} ifelse dup length FDBytes sub 0 gt { dup FDBytes 0 ne {0 FDBytes ct_cvnsi} {pop 0} ifelse /fdIndex exch def dup length FDBytes sub FDBytes exch getinterval /charstring exch def exit } { pop cid 0 eq {/charstring nullstring def exit} if /cid 0 def } ifelse } loop }def /ct_SetCacheDevice { 0 0 moveto dup stringwidth 3 -1 roll true charpath pathbbox 0 -1000 7 index 2 div 880 setcachedevice2 0 0 moveto }def /ct_CloneSetCacheProc { 1 eq { stringwidth pop -2 div -880 0 -1000 setcharwidth moveto } { usewidths? { currentfont/Widths get cid 2 copy known {get exch pop aload pop} {pop pop stringwidth} ifelse } {stringwidth} ifelse setcharwidth 0 0 moveto } ifelse }def /ct_Type3ShowCharString { ct_FDDict fdIndex 2 copy known {get} { currentglobal 3 1 roll 1 index gcheck setglobal ct_Type1FontTemplate dup maxlength dict copy begin FDArray fdIndex get dup/FontMatrix 2 copy known {get} {pop pop ct_defaultFontMtx} ifelse /FontMatrix exch dup length array copy def /Private get /Private exch def /Widths rootfont/Widths get def /CharStrings 1 dict dup/.notdef dup length string copy put def currentdict end /ct_Type1Font exch definefont dup 5 1 roll put setglobal } ifelse dup/CharStrings get 1 index/Encoding get ct_dfCharCode get charstring put rootfont/WMode 2 copy known {get} {pop pop 0} ifelse exch 1000 scalefont setfont ct_str1 0 ct_dfCharCode put ct_str1 exch ct_dfSetCacheProc ct_SyntheticBold { currentpoint ct_str1 show newpath moveto ct_str1 true charpath ct_StrokeWidth setlinewidth stroke } {ct_str1 show} ifelse }def /ct_Type4ShowCharString { ct_dfDict ct_dfCharCode charstring FDArray fdIndex get dup/FontMatrix get dup ct_defaultFontMtx ct_matrixeq not {ct_1000Mtx matrix concatmatrix concat} {pop} ifelse /Private get Adobe_CoolType_Utility/ct_Level2? get not { ct_dfDict/Private 3 -1 roll {put} 1183615869 internaldict/superexec get exec } if 1183615869 internaldict Adobe_CoolType_Utility/ct_Level2? get {1 index} {3 index/Private get mark 6 1 roll} ifelse dup/RunInt known {/RunInt get} {pop/CCRun} ifelse get exec Adobe_CoolType_Utility/ct_Level2? get not {cleartomark} if }bind def /ct_BuildCharIncremental { { Adobe_CoolType_Utility/ct_MakeOCF get begin ct_BuildCharSetUp ct_ShowCharString } stopped {stop} if end end end end }bind def /BaseFontNameStr(BF00)def /ct_Type1FontTemplate 14 dict begin /FontType 1 def /FontMatrix [0.001 0 0 0.001 0 0]def /FontBBox [-250 -250 1250 1250]def /Encoding ct_cHexEncoding def /PaintType 0 def currentdict end def /BaseFontTemplate 11 dict begin /FontMatrix [0.001 0 0 0.001 0 0]def /FontBBox [-250 -250 1250 1250]def /Encoding ct_cHexEncoding def /BuildChar/ct_BuildCharIncremental load def ct_Clone? { /FontType 3 def /ct_ShowCharString/ct_Type3ShowCharString load def /ct_dfSetCacheProc/ct_CloneSetCacheProc load def /ct_SyntheticBold false def /ct_StrokeWidth 1 def } { /FontType 4 def /Private 1 dict dup/lenIV 4 put def /CharStrings 1 dict dup/.notdefput def /PaintType 0 def /ct_ShowCharString/ct_Type4ShowCharString load def } ifelse /ct_str1 1 string def currentdict end def /BaseFontDictSize BaseFontTemplate length 5 add def /ct_matrixeq { true 0 1 5 { dup 4 index exch get exch 3 index exch get eq and dup not {exit} if } for exch pop exch pop }bind def /ct_makeocf { 15 dict begin exch/WMode exch def exch/FontName exch def /FontType 0 def /FMapType 2 def dup/FontMatrix known {dup/FontMatrix get/FontMatrix exch def} {/FontMatrix matrix def} ifelse /bfCount 1 index/CIDCount get 256 idiv 1 add dup 256 gt{pop 256}if def /Encoding 256 array 0 1 bfCount 1 sub{2 copy dup put pop}for bfCount 1 255{2 copy bfCount put pop}for def /FDepVector bfCount dup 256 lt{1 add}if array def BaseFontTemplate BaseFontDictSize dict copy begin /CIDFont exch def CIDFont/FontBBox known {CIDFont/FontBBox get/FontBBox exch def} if CIDFont/CDevProc known {CIDFont/CDevProc get/CDevProc exch def} if currentdict end BaseFontNameStr 3(0)putinterval 0 1 bfCount dup 256 eq{1 sub}if { FDepVector exch 2 index BaseFontDictSize dict copy begin dup/CIDFirstByte exch 256 mul def FontType 3 eq {/ct_FDDict 2 dict def} if currentdict end 1 index 16 BaseFontNameStr 2 2 getinterval cvrs pop BaseFontNameStr exch definefont put } for ct_Clone? {/Widths 1 index/CIDFont get/GlyphDirectory get length dict def} if FontName currentdict end definefont ct_Clone? { gsave dup 1000 scalefont setfont ct_BuildCharDict begin /usewidths? false def currentfont/Widths get begin exch/CIDFont get/GlyphDirectory get { pop dup charcode exch 1 index 0 2 index 256 idiv put 1 index exch 1 exch 256 mod put stringwidth 2 array astore def } forall end /usewidths? true def end grestore } {exch pop} ifelse }bind def currentglobal true setglobal /ct_ComposeFont { ct_UseNativeCapability? { 2 index/CMap ct_resourcestatus {pop pop exch pop} { /CIDInit/ProcSet findresource begin 12 dict begin begincmap /CMapName 3 index def /CMapVersion 1.000 def /CMapType 1 def exch/WMode exch def /CIDSystemInfo 3 dict dup begin /Registry(Adobe)def /Ordering CMapName ct_mkocfStr100 cvs (Adobe-)search { pop pop (-)search { dup length string copy exch pop exch pop } {pop(Identity)} ifelse } {pop (Identity)} ifelse def /Supplement 0 def end def 1 begincodespacerange <0000> endcodespacerange 1 begincidrange <0000>0 endcidrange endcmap CMapName currentdict/CMap defineresource pop end end } ifelse composefont } { 3 2 roll pop 0 get/CIDFont findresource ct_makeocf } ifelse }bind def setglobal /ct_MakeIdentity { ct_UseNativeCapability? { 1 index/CMap ct_resourcestatus {pop pop} { /CIDInit/ProcSet findresource begin 12 dict begin begincmap /CMapName 2 index def /CMapVersion 1.000 def /CMapType 1 def /CIDSystemInfo 3 dict dup begin /Registry(Adobe)def /Ordering CMapName ct_mkocfStr100 cvs (Adobe-)search { pop pop (-)search {dup length string copy exch pop exch pop} {pop(Identity)} ifelse } {pop(Identity)} ifelse def /Supplement 0 def end def 1 begincodespacerange <0000> endcodespacerange 1 begincidrange <0000>0 endcidrange endcmap CMapName currentdict/CMap defineresource pop end end } ifelse composefont } { exch pop 0 get/CIDFont findresource ct_makeocf } ifelse }bind def currentdict readonly pop end end %%EndResource setglobal %%BeginResource: procset Adobe_CoolType_Utility_T42 1.0 0 %%Copyright: Copyright 1987-2004 Adobe Systems Incorporated. %%Version: 1.0 0 userdict/ct_T42Dict 15 dict put ct_T42Dict begin /Is2015? { version cvi 2015 ge }bind def /AllocGlyphStorage { Is2015? { pop } { {string}forall }ifelse }bind def /Type42DictBegin { 25 dict begin /FontName exch def /CharStrings 256 dict begin /.notdef 0 def currentdict end def /Encoding exch def /PaintType 0 def /FontType 42 def /FontMatrix[1 0 0 1 0 0]def 4 array astore cvx/FontBBox exch def /sfnts }bind def /Type42DictEnd { currentdict dup/FontName get exch definefont end ct_T42Dict exch dup/FontName get exch put }bind def /RD{string currentfile exch readstring pop}executeonly def /PrepFor2015 { Is2015? { /GlyphDirectory 16 dict def sfnts 0 get dup 2 index (glyx) putinterval 2 index (locx) putinterval pop pop } { pop pop }ifelse }bind def /AddT42Char { Is2015? { /GlyphDirectory get begin def end pop pop } { /sfnts get 4 index get 3 index 2 index putinterval pop pop pop pop }ifelse }bind def /T0AddT42Mtx2 { /CIDFont findresource/Metrics2 get begin def end }bind def end %%EndResource currentglobal true setglobal %%BeginFile: MMFauxFont.prc %%Copyright: Copyright 1987-2001 Adobe Systems Incorporated. %%All Rights Reserved. userdict /ct_EuroDict 10 dict put ct_EuroDict begin /ct_CopyFont { { 1 index /FID ne {def} {pop pop} ifelse} forall } def /ct_GetGlyphOutline { gsave initmatrix newpath exch findfont dup length 1 add dict begin ct_CopyFont /Encoding Encoding dup length array copy dup 4 -1 roll 0 exch put def currentdict end /ct_EuroFont exch definefont 1000 scalefont setfont 0 0 moveto [ <00> stringwidth <00> false charpath pathbbox [ {/m cvx} {/l cvx} {/c cvx} {/cp cvx} pathforall grestore counttomark 8 add } def /ct_MakeGlyphProc { ] cvx /ct_PSBuildGlyph cvx ] cvx } def /ct_PSBuildGlyph { gsave 8 -1 roll pop 7 1 roll 6 -2 roll ct_FontMatrix transform 6 2 roll 4 -2 roll ct_FontMatrix transform 4 2 roll ct_FontMatrix transform currentdict /PaintType 2 copy known {get 2 eq}{pop pop false} ifelse dup 9 1 roll { currentdict /StrokeWidth 2 copy known { get 2 div 0 ct_FontMatrix dtransform pop 5 1 roll 4 -1 roll 4 index sub 4 1 roll 3 -1 roll 4 index sub 3 1 roll exch 4 index add exch 4 index add 5 -1 roll pop } { pop pop } ifelse } if setcachedevice ct_FontMatrix concat ct_PSPathOps begin exec end { currentdict /StrokeWidth 2 copy known { get } { pop pop 0 } ifelse setlinewidth stroke } { fill } ifelse grestore } def /ct_PSPathOps 4 dict dup begin /m {moveto} def /l {lineto} def /c {curveto} def /cp {closepath} def end def /ct_matrix1000 [1000 0 0 1000 0 0] def /ct_AddGlyphProc { 2 index findfont dup length 4 add dict begin ct_CopyFont /CharStrings CharStrings dup length 1 add dict copy begin 3 1 roll def currentdict end def /ct_FontMatrix ct_matrix1000 FontMatrix matrix concatmatrix def /ct_PSBuildGlyph /ct_PSBuildGlyph load def /ct_PSPathOps /ct_PSPathOps load def currentdict end definefont pop } def systemdict /languagelevel known { /ct_AddGlyphToPrinterFont { 2 copy ct_GetGlyphOutline 3 add -1 roll restore ct_MakeGlyphProc ct_AddGlyphProc } def } { /ct_AddGlyphToPrinterFont { pop pop restore Adobe_CTFauxDict /$$$FONTNAME get /Euro Adobe_CTFauxDict /$$$SUBSTITUTEBASE get ct_EuroDict exch get ct_AddGlyphProc } def } ifelse /AdobeSansMM { 556 0 24 -19 541 703 { 541 628 m 510 669 442 703 354 703 c 201 703 117 607 101 444 c 50 444 l 25 372 l 97 372 l 97 301 l 49 301 l 24 229 l 103 229 l 124 67 209 -19 350 -19 c 435 -19 501 25 509 32 c 509 131 l 492 105 417 60 343 60 c 267 60 204 127 197 229 c 406 229 l 430 301 l 191 301 l 191 372 l 455 372 l 479 444 l 194 444 l 201 531 245 624 348 624 c 433 624 484 583 509 534 c cp 556 0 m } ct_PSBuildGlyph } def /AdobeSerifMM { 500 0 10 -12 484 692 { 347 298 m 171 298 l 170 310 170 322 170 335 c 170 362 l 362 362 l 374 403 l 172 403 l 184 580 244 642 308 642 c 380 642 434 574 457 457 c 481 462 l 474 691 l 449 691 l 433 670 429 657 410 657 c 394 657 360 692 299 692 c 204 692 94 604 73 403 c 22 403 l 10 362 l 70 362 l 69 352 69 341 69 330 c 69 319 69 308 70 298 c 22 298 l 10 257 l 73 257 l 97 57 216 -12 295 -12 c 364 -12 427 25 484 123 c 458 142 l 425 101 384 37 316 37 c 256 37 189 84 173 257 c 335 257 l cp 500 0 m } ct_PSBuildGlyph } def end %%EndFile setglobal Adobe_CoolType_Core begin /$Oblique SetSubstituteStrategy end %%BeginResource: procset Adobe_AGM_Image 1.0 0 +%%Version: 1.0 0 +%%Copyright: Copyright(C)2000-2006 Adobe Systems, Inc. All Rights Reserved. +systemdict/setpacking known +{ + currentpacking + true setpacking +}if +userdict/Adobe_AGM_Image 71 dict dup begin put +/Adobe_AGM_Image_Id/Adobe_AGM_Image_1.0_0 def +/nd{ + null def +}bind def +/AGMIMG_&image nd +/AGMIMG_&colorimage nd +/AGMIMG_&imagemask nd +/AGMIMG_mbuf()def +/AGMIMG_ybuf()def +/AGMIMG_kbuf()def +/AGMIMG_c 0 def +/AGMIMG_m 0 def +/AGMIMG_y 0 def +/AGMIMG_k 0 def +/AGMIMG_tmp nd +/AGMIMG_imagestring0 nd +/AGMIMG_imagestring1 nd +/AGMIMG_imagestring2 nd +/AGMIMG_imagestring3 nd +/AGMIMG_imagestring4 nd +/AGMIMG_imagestring5 nd +/AGMIMG_cnt nd +/AGMIMG_fsave nd +/AGMIMG_colorAry nd +/AGMIMG_override nd +/AGMIMG_name nd +/AGMIMG_maskSource nd +/AGMIMG_flushfilters nd +/invert_image_samples nd +/knockout_image_samples nd +/img nd +/sepimg nd +/devnimg nd +/idximg nd +/ds +{ + Adobe_AGM_Core begin + Adobe_AGM_Image begin + /AGMIMG_&image systemdict/image get def + /AGMIMG_&imagemask systemdict/imagemask get def + /colorimage where{ + pop + /AGMIMG_&colorimage/colorimage ldf + }if + end + end +}def +/ps +{ + Adobe_AGM_Image begin + /AGMIMG_ccimage_exists{/customcolorimage where + { + pop + /Adobe_AGM_OnHost_Seps where + { + pop false + }{ + /Adobe_AGM_InRip_Seps where + { + pop false + }{ + true + }ifelse + }ifelse + }{ + false + }ifelse + }bdf + level2{ + /invert_image_samples + { + Adobe_AGM_Image/AGMIMG_tmp Decode length ddf + /Decode[Decode 1 get Decode 0 get]def + }def + /knockout_image_samples + { + Operator/imagemask ne{ + /Decode[1 1]def + }if + }def + }{ + /invert_image_samples + { + {1 exch sub}currenttransfer addprocs settransfer + }def + /knockout_image_samples + { + {pop 1}currenttransfer addprocs settransfer + }def + }ifelse + /img/imageormask ldf + /sepimg/sep_imageormask ldf + /devnimg/devn_imageormask ldf + /idximg/indexed_imageormask ldf + /_ctype 7 def + currentdict{ + dup xcheck 1 index type dup/arraytype eq exch/packedarraytype eq or and{ + bind + }if + def + }forall +}def +/pt +{ + end +}def +/dt +{ +}def +/AGMIMG_flushfilters +{ + dup type/arraytype ne + {1 array astore}if + dup 0 get currentfile ne + {dup 0 get flushfile}if + { + dup type/filetype eq + { + dup status 1 index currentfile ne and + {closefile} + {pop} + ifelse + }{pop}ifelse + }forall +}def +/AGMIMG_init_common +{ + currentdict/T known{/ImageType/T ldf currentdict/T undef}if + currentdict/W known{/Width/W ldf currentdict/W undef}if + currentdict/H known{/Height/H ldf currentdict/H undef}if + currentdict/M known{/ImageMatrix/M ldf currentdict/M undef}if + currentdict/BC known{/BitsPerComponent/BC ldf currentdict/BC undef}if + currentdict/D known{/Decode/D ldf currentdict/D undef}if + currentdict/DS known{/DataSource/DS ldf currentdict/DS undef}if + currentdict/O known{ + /Operator/O load 1 eq{ + /imagemask + }{ + /O load 2 eq{ + /image + }{ + /colorimage + }ifelse + }ifelse + def + currentdict/O undef + }if + currentdict/HSCI known{/HostSepColorImage/HSCI ldf currentdict/HSCI undef}if + currentdict/MD known{/MultipleDataSources/MD ldf currentdict/MD undef}if + currentdict/I known{/Interpolate/I ldf currentdict/I undef}if + currentdict/SI known{/SkipImageProc/SI ldf currentdict/SI undef}if + /DataSource load xcheck not{ + DataSource type/arraytype eq{ + DataSource 0 get type/filetype eq{ + /_Filters DataSource def + currentdict/MultipleDataSources known not{ + /DataSource DataSource dup length 1 sub get def + }if + }if + }if + currentdict/MultipleDataSources known not{ + /MultipleDataSources DataSource type/arraytype eq{ + DataSource length 1 gt + } + {false}ifelse def + }if + }if + /NComponents Decode length 2 div def + currentdict/SkipImageProc known not{/SkipImageProc{false}def}if +}bdf +/imageormask_sys +{ + begin + AGMIMG_init_common + save mark + level2{ + currentdict + Operator/imagemask eq{ + AGMIMG_&imagemask + }{ + use_mask{ + process_mask AGMIMG_&image + }{ + AGMIMG_&image + }ifelse + }ifelse + }{ + Width Height + Operator/imagemask eq{ + Decode 0 get 1 eq Decode 1 get 0 eq and + ImageMatrix/DataSource load + AGMIMG_&imagemask + }{ + BitsPerComponent ImageMatrix/DataSource load + AGMIMG_&image + }ifelse + }ifelse + currentdict/_Filters known{_Filters AGMIMG_flushfilters}if + cleartomark restore + end +}def +/overprint_plate +{ + currentoverprint{ + 0 get dup type/nametype eq{ + dup/DeviceGray eq{ + pop AGMCORE_black_plate not + }{ + /DeviceCMYK eq{ + AGMCORE_is_cmyk_sep not + }if + }ifelse + }{ + false exch + { + AGMOHS_sepink eq or + }forall + not + }ifelse + }{ + pop false + }ifelse +}def +/process_mask +{ + level3{ + dup begin + /ImageType 1 def + end + 4 dict begin + /DataDict exch def + /ImageType 3 def + /InterleaveType 3 def + /MaskDict 9 dict begin + /ImageType 1 def + /Width DataDict dup/MaskWidth known{/MaskWidth}{/Width}ifelse get def + /Height DataDict dup/MaskHeight known{/MaskHeight}{/Height}ifelse get def + /ImageMatrix[Width 0 0 Height neg 0 Height]def + /NComponents 1 def + /BitsPerComponent 1 def + /Decode DataDict dup/MaskD known{/MaskD}{[1 0]}ifelse get def + /DataSource Adobe_AGM_Core/AGMIMG_maskSource get def + currentdict end def + currentdict end + }if +}def +/use_mask +{ + dup/Mask known {dup/Mask get}{false}ifelse +}def +/imageormask +{ + begin + AGMIMG_init_common + SkipImageProc{ + currentdict consumeimagedata + } + { + save mark + level2 AGMCORE_host_sep not and{ + currentdict + Operator/imagemask eq DeviceN_PS2 not and{ + imagemask + }{ + AGMCORE_in_rip_sep currentoverprint and currentcolorspace 0 get/DeviceGray eq and{ + [/Separation/Black/DeviceGray{}]setcolorspace + /Decode[Decode 1 get Decode 0 get]def + }if + use_mask{ + process_mask image + }{ + DeviceN_NoneName DeviceN_PS2 Indexed_DeviceN level3 not and or or AGMCORE_in_rip_sep and + { + Names convert_to_process not{ + 2 dict begin + /imageDict xdf + /names_index 0 def + gsave + imageDict write_image_file{ + Names{ + dup(None)ne{ + [/Separation 3 -1 roll/DeviceGray{1 exch sub}]setcolorspace + Operator imageDict read_image_file + names_index 0 eq{true setoverprint}if + /names_index names_index 1 add def + }{ + pop + }ifelse + }forall + close_image_file + }if + grestore + end + }{ + Operator/imagemask eq{ + imagemask + }{ + image + }ifelse + }ifelse + }{ + Operator/imagemask eq{ + imagemask + }{ + image + }ifelse + }ifelse + }ifelse + }ifelse + }{ + Width Height + Operator/imagemask eq{ + Decode 0 get 1 eq Decode 1 get 0 eq and + ImageMatrix/DataSource load + /Adobe_AGM_OnHost_Seps where{ + pop imagemask + }{ + currentgray 1 ne{ + currentdict imageormask_sys + }{ + currentoverprint not{ + 1 AGMCORE_&setgray + currentdict imageormask_sys + }{ + currentdict ignoreimagedata + }ifelse + }ifelse + }ifelse + }{ + BitsPerComponent ImageMatrix + MultipleDataSources{ + 0 1 NComponents 1 sub{ + DataSource exch get + }for + }{ + /DataSource load + }ifelse + Operator/colorimage eq{ + AGMCORE_host_sep{ + MultipleDataSources level2 or NComponents 4 eq and{ + AGMCORE_is_cmyk_sep{ + MultipleDataSources{ + /DataSource DataSource 0 get xcheck + { + [ + DataSource 0 get/exec cvx + DataSource 1 get/exec cvx + DataSource 2 get/exec cvx + DataSource 3 get/exec cvx + /AGMCORE_get_ink_data cvx + ]cvx + }{ + DataSource aload pop AGMCORE_get_ink_data + }ifelse def + }{ + /DataSource + Width BitsPerComponent mul 7 add 8 idiv Height mul 4 mul + /DataSource load + filter_cmyk 0()/SubFileDecode filter def + }ifelse + /Decode[Decode 0 get Decode 1 get]def + /MultipleDataSources false def + /NComponents 1 def + /Operator/image def + invert_image_samples + 1 AGMCORE_&setgray + currentdict imageormask_sys + }{ + currentoverprint not Operator/imagemask eq and{ + 1 AGMCORE_&setgray + currentdict imageormask_sys + }{ + currentdict ignoreimagedata + }ifelse + }ifelse + }{ + MultipleDataSources NComponents AGMIMG_&colorimage + }ifelse + }{ + true NComponents colorimage + }ifelse + }{ + Operator/image eq{ + AGMCORE_host_sep{ + /DoImage true def + currentdict/HostSepColorImage known{HostSepColorImage not}{false}ifelse + { + AGMCORE_black_plate not Operator/imagemask ne and{ + /DoImage false def + currentdict ignoreimagedata + }if + }if + 1 AGMCORE_&setgray + DoImage + {currentdict imageormask_sys}if + }{ + use_mask{ + process_mask image + }{ + image + }ifelse + }ifelse + }{ + Operator/knockout eq{ + pop pop pop pop pop + currentcolorspace overprint_plate not{ + knockout_unitsq + }if + }if + }ifelse + }ifelse + }ifelse + }ifelse + cleartomark restore + }ifelse + currentdict/_Filters known{_Filters AGMIMG_flushfilters}if + end +}def +/sep_imageormask +{ + /sep_colorspace_dict AGMCORE_gget begin + CSA map_csa + begin + AGMIMG_init_common + SkipImageProc{ + currentdict consumeimagedata + }{ + save mark + AGMCORE_avoid_L2_sep_space{ + /Decode[Decode 0 get 255 mul Decode 1 get 255 mul]def + }if + AGMIMG_ccimage_exists + MappedCSA 0 get/DeviceCMYK eq and + currentdict/Components known and + Name()ne and + Name(All)ne and + Operator/image eq and + AGMCORE_producing_seps not and + level2 not and + { + Width Height BitsPerComponent ImageMatrix + [ + /DataSource load/exec cvx + { + 0 1 2 index length 1 sub{ + 1 index exch + 2 copy get 255 xor put + }for + }/exec cvx + ]cvx bind + MappedCSA 0 get/DeviceCMYK eq{ + Components aload pop + }{ + 0 0 0 Components aload pop 1 exch sub + }ifelse + Name findcmykcustomcolor + customcolorimage + }{ + AGMCORE_producing_seps not{ + level2{ + //Adobe_AGM_Core/AGMCORE_pattern_paint_type get 2 ne AGMCORE_avoid_L2_sep_space not and currentcolorspace 0 get/Separation ne and{ + [/Separation Name MappedCSA sep_proc_name exch dup 0 get 15 string cvs(/Device)anchorsearch{pop pop 0 get}{pop}ifelse exch load]setcolorspace_opt + /sep_tint AGMCORE_gget setcolor + }if + currentdict imageormask + }{ + currentdict + Operator/imagemask eq{ + imageormask + }{ + sep_imageormask_lev1 + }ifelse + }ifelse + }{ + AGMCORE_host_sep{ + Operator/knockout eq{ + currentdict/ImageMatrix get concat + knockout_unitsq + }{ + currentgray 1 ne{ + AGMCORE_is_cmyk_sep Name(All)ne and{ + level2{ + Name AGMCORE_IsSeparationAProcessColor + { + Operator/imagemask eq{ + //Adobe_AGM_Core/AGMCORE_pattern_paint_type get 2 ne{ + /sep_tint AGMCORE_gget 1 exch sub AGMCORE_&setcolor + }if + }{ + invert_image_samples + }ifelse + }{ + //Adobe_AGM_Core/AGMCORE_pattern_paint_type get 2 ne{ + [/Separation Name[/DeviceGray] + { + sep_colorspace_proc AGMCORE_get_ink_data + 1 exch sub + }bind + ]AGMCORE_&setcolorspace + /sep_tint AGMCORE_gget AGMCORE_&setcolor + }if + }ifelse + currentdict imageormask_sys + }{ + currentdict + Operator/imagemask eq{ + imageormask_sys + }{ + sep_image_lev1_sep + }ifelse + }ifelse + }{ + Operator/imagemask ne{ + invert_image_samples + }if + currentdict imageormask_sys + }ifelse + }{ + currentoverprint not Name(All)eq or Operator/imagemask eq and{ + currentdict imageormask_sys + }{ + currentoverprint not + { + gsave + knockout_unitsq + grestore + }if + currentdict consumeimagedata + }ifelse + }ifelse + }ifelse + }{ + //Adobe_AGM_Core/AGMCORE_pattern_paint_type get 2 ne{ + currentcolorspace 0 get/Separation ne{ + [/Separation Name MappedCSA sep_proc_name exch 0 get exch load]setcolorspace_opt + /sep_tint AGMCORE_gget setcolor + }if + }if + currentoverprint + MappedCSA 0 get/DeviceCMYK eq and + Name AGMCORE_IsSeparationAProcessColor not and + //Adobe_AGM_Core/AGMCORE_pattern_paint_type get 2 ne{Name inRip_spot_has_ink not and}{false}ifelse + Name(All)ne and{ + imageormask_l2_overprint + }{ + currentdict imageormask + }ifelse + }ifelse + }ifelse + }ifelse + cleartomark restore + }ifelse + currentdict/_Filters known{_Filters AGMIMG_flushfilters}if + end + end +}def +/colorSpaceElemCnt +{ + mark currentcolor counttomark dup 2 add 1 roll cleartomark +}bdf +/devn_sep_datasource +{ + 1 dict begin + /dataSource xdf + [ + 0 1 dataSource length 1 sub{ + dup currentdict/dataSource get/exch cvx/get cvx/exec cvx + /exch cvx names_index/ne cvx[/pop cvx]cvx/if cvx + }for + ]cvx bind + end +}bdf +/devn_alt_datasource +{ + 11 dict begin + /convProc xdf + /origcolorSpaceElemCnt xdf + /origMultipleDataSources xdf + /origBitsPerComponent xdf + /origDecode xdf + /origDataSource xdf + /dsCnt origMultipleDataSources{origDataSource length}{1}ifelse def + /DataSource origMultipleDataSources + { + [ + BitsPerComponent 8 idiv origDecode length 2 idiv mul string + 0 1 origDecode length 2 idiv 1 sub + { + dup 7 mul 1 add index exch dup BitsPerComponent 8 idiv mul exch + origDataSource exch get 0()/SubFileDecode filter + BitsPerComponent 8 idiv string/readstring cvx/pop cvx/putinterval cvx + }for + ]bind cvx + }{origDataSource}ifelse 0()/SubFileDecode filter def + [ + origcolorSpaceElemCnt string + 0 2 origDecode length 2 sub + { + dup origDecode exch get dup 3 -1 roll 1 add origDecode exch get exch sub 2 BitsPerComponent exp 1 sub div + 1 BitsPerComponent 8 idiv{DataSource/read cvx/not cvx{0}/if cvx/mul cvx}repeat/mul cvx/add cvx + }for + /convProc load/exec cvx + origcolorSpaceElemCnt 1 sub -1 0 + { + /dup cvx 2/add cvx/index cvx + 3 1/roll cvx/exch cvx 255/mul cvx/cvi cvx/put cvx + }for + ]bind cvx 0()/SubFileDecode filter + end +}bdf +/devn_imageormask +{ + /devicen_colorspace_dict AGMCORE_gget begin + CSA map_csa + 2 dict begin + dup + /srcDataStrs[3 -1 roll begin + AGMIMG_init_common + currentdict/MultipleDataSources known{MultipleDataSources{DataSource length}{1}ifelse}{1}ifelse + { + Width Decode length 2 div mul cvi + { + dup 65535 gt{1 add 2 div cvi}{exit}ifelse + }loop + string + }repeat + end]def + /dstDataStr srcDataStrs 0 get length string def + begin + AGMIMG_init_common + SkipImageProc{ + currentdict consumeimagedata + }{ + save mark + AGMCORE_producing_seps not{ + level3 not{ + Operator/imagemask ne{ + /DataSource[[ + DataSource Decode BitsPerComponent currentdict/MultipleDataSources known{MultipleDataSources}{false}ifelse + colorSpaceElemCnt/devicen_colorspace_dict AGMCORE_gget/TintTransform get + devn_alt_datasource 1/string cvx/readstring cvx/pop cvx]cvx colorSpaceElemCnt 1 sub{dup}repeat]def + /MultipleDataSources true def + /Decode colorSpaceElemCnt[exch{0 1}repeat]def + }if + }if + currentdict imageormask + }{ + AGMCORE_host_sep{ + Names convert_to_process{ + CSA get_csa_by_name 0 get/DeviceCMYK eq{ + /DataSource + Width BitsPerComponent mul 7 add 8 idiv Height mul 4 mul + DataSource Decode BitsPerComponent currentdict/MultipleDataSources known{MultipleDataSources}{false}ifelse + 4/devicen_colorspace_dict AGMCORE_gget/TintTransform get + devn_alt_datasource + filter_cmyk 0()/SubFileDecode filter def + /MultipleDataSources false def + /Decode[1 0]def + /DeviceGray setcolorspace + currentdict imageormask_sys + }{ + AGMCORE_report_unsupported_color_space + AGMCORE_black_plate{ + /DataSource + DataSource Decode BitsPerComponent currentdict/MultipleDataSources known{MultipleDataSources}{false}ifelse + CSA get_csa_by_name 0 get/DeviceRGB eq{3}{1}ifelse/devicen_colorspace_dict AGMCORE_gget/TintTransform get + devn_alt_datasource + /MultipleDataSources false def + /Decode colorSpaceElemCnt[exch{0 1}repeat]def + currentdict imageormask_sys + }{ + gsave + knockout_unitsq + grestore + currentdict consumeimagedata + }ifelse + }ifelse + } + { + /devicen_colorspace_dict AGMCORE_gget/names_index known{ + Operator/imagemask ne{ + MultipleDataSources{ + /DataSource[DataSource devn_sep_datasource/exec cvx]cvx def + /MultipleDataSources false def + }{ + /DataSource/DataSource load dstDataStr srcDataStrs 0 get filter_devn def + }ifelse + invert_image_samples + }if + currentdict imageormask_sys + }{ + currentoverprint not Operator/imagemask eq and{ + currentdict imageormask_sys + }{ + currentoverprint not + { + gsave + knockout_unitsq + grestore + }if + currentdict consumeimagedata + }ifelse + }ifelse + }ifelse + }{ + currentdict imageormask + }ifelse + }ifelse + cleartomark restore + }ifelse + currentdict/_Filters known{_Filters AGMIMG_flushfilters}if + end + end + end +}def +/imageormask_l2_overprint +{ + currentdict + currentcmykcolor add add add 0 eq{ + currentdict consumeimagedata + }{ + level3{ + currentcmykcolor + /AGMIMG_k xdf + /AGMIMG_y xdf + /AGMIMG_m xdf + /AGMIMG_c xdf + Operator/imagemask eq{ + [/DeviceN[ + AGMIMG_c 0 ne{/Cyan}if + AGMIMG_m 0 ne{/Magenta}if + AGMIMG_y 0 ne{/Yellow}if + AGMIMG_k 0 ne{/Black}if + ]/DeviceCMYK{}]setcolorspace + AGMIMG_c 0 ne{AGMIMG_c}if + AGMIMG_m 0 ne{AGMIMG_m}if + AGMIMG_y 0 ne{AGMIMG_y}if + AGMIMG_k 0 ne{AGMIMG_k}if + setcolor + }{ + /Decode[Decode 0 get 255 mul Decode 1 get 255 mul]def + [/Indexed + [ + /DeviceN[ + AGMIMG_c 0 ne{/Cyan}if + AGMIMG_m 0 ne{/Magenta}if + AGMIMG_y 0 ne{/Yellow}if + AGMIMG_k 0 ne{/Black}if + ] + /DeviceCMYK{ + AGMIMG_k 0 eq{0}if + AGMIMG_y 0 eq{0 exch}if + AGMIMG_m 0 eq{0 3 1 roll}if + AGMIMG_c 0 eq{0 4 1 roll}if + } + ] + 255 + { + 255 div + mark exch + dup dup dup + AGMIMG_k 0 ne{ + /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec 4 1 roll pop pop pop + counttomark 1 roll + }{ + pop + }ifelse + AGMIMG_y 0 ne{ + /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec 4 2 roll pop pop pop + counttomark 1 roll + }{ + pop + }ifelse + AGMIMG_m 0 ne{ + /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec 4 3 roll pop pop pop + counttomark 1 roll + }{ + pop + }ifelse + AGMIMG_c 0 ne{ + /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec pop pop pop + counttomark 1 roll + }{ + pop + }ifelse + counttomark 1 add -1 roll pop + } + ]setcolorspace + }ifelse + imageormask_sys + }{ + write_image_file{ + currentcmykcolor + 0 ne{ + [/Separation/Black/DeviceGray{}]setcolorspace + gsave + /Black + [{1 exch sub/sep_tint AGMCORE_gget mul}/exec cvx MappedCSA sep_proc_name cvx exch pop{4 1 roll pop pop pop 1 exch sub}/exec cvx] + cvx modify_halftone_xfer + Operator currentdict read_image_file + grestore + }if + 0 ne{ + [/Separation/Yellow/DeviceGray{}]setcolorspace + gsave + /Yellow + [{1 exch sub/sep_tint AGMCORE_gget mul}/exec cvx MappedCSA sep_proc_name cvx exch pop{4 2 roll pop pop pop 1 exch sub}/exec cvx] + cvx modify_halftone_xfer + Operator currentdict read_image_file + grestore + }if + 0 ne{ + [/Separation/Magenta/DeviceGray{}]setcolorspace + gsave + /Magenta + [{1 exch sub/sep_tint AGMCORE_gget mul}/exec cvx MappedCSA sep_proc_name cvx exch pop{4 3 roll pop pop pop 1 exch sub}/exec cvx] + cvx modify_halftone_xfer + Operator currentdict read_image_file + grestore + }if + 0 ne{ + [/Separation/Cyan/DeviceGray{}]setcolorspace + gsave + /Cyan + [{1 exch sub/sep_tint AGMCORE_gget mul}/exec cvx MappedCSA sep_proc_name cvx exch pop{pop pop pop 1 exch sub}/exec cvx] + cvx modify_halftone_xfer + Operator currentdict read_image_file + grestore + }if + close_image_file + }{ + imageormask + }ifelse + }ifelse + }ifelse +}def +/indexed_imageormask +{ + begin + AGMIMG_init_common + save mark + currentdict + AGMCORE_host_sep{ + Operator/knockout eq{ + /indexed_colorspace_dict AGMCORE_gget dup/CSA known{ + /CSA get get_csa_by_name + }{ + /Names get + }ifelse + overprint_plate not{ + knockout_unitsq + }if + }{ + Indexed_DeviceN{ + /devicen_colorspace_dict AGMCORE_gget dup/names_index known exch/Names get convert_to_process or{ + indexed_image_lev2_sep + }{ + currentoverprint not{ + knockout_unitsq + }if + currentdict consumeimagedata + }ifelse + }{ + AGMCORE_is_cmyk_sep{ + Operator/imagemask eq{ + imageormask_sys + }{ + level2{ + indexed_image_lev2_sep + }{ + indexed_image_lev1_sep + }ifelse + }ifelse + }{ + currentoverprint not{ + knockout_unitsq + }if + currentdict consumeimagedata + }ifelse + }ifelse + }ifelse + }{ + level2{ + Indexed_DeviceN{ + /indexed_colorspace_dict AGMCORE_gget begin + }{ + /indexed_colorspace_dict AGMCORE_gget dup null ne + { + begin + currentdict/CSDBase known{CSDBase/CSD get_res/MappedCSA get}{CSA}ifelse + get_csa_by_name 0 get/DeviceCMYK eq ps_level 3 ge and ps_version 3015.007 lt and + AGMCORE_in_rip_sep and{ + [/Indexed[/DeviceN[/Cyan/Magenta/Yellow/Black]/DeviceCMYK{}]HiVal Lookup] + setcolorspace + }if + end + } + {pop}ifelse + }ifelse + imageormask + Indexed_DeviceN{ + end + }if + }{ + Operator/imagemask eq{ + imageormask + }{ + indexed_imageormask_lev1 + }ifelse + }ifelse + }ifelse + cleartomark restore + currentdict/_Filters known{_Filters AGMIMG_flushfilters}if + end +}def +/indexed_image_lev2_sep +{ + /indexed_colorspace_dict AGMCORE_gget begin + begin + Indexed_DeviceN not{ + currentcolorspace + dup 1/DeviceGray put + dup 3 + currentcolorspace 2 get 1 add string + 0 1 2 3 AGMCORE_get_ink_data 4 currentcolorspace 3 get length 1 sub + { + dup 4 idiv exch currentcolorspace 3 get exch get 255 exch sub 2 index 3 1 roll put + }for + put setcolorspace + }if + currentdict + Operator/imagemask eq{ + AGMIMG_&imagemask + }{ + use_mask{ + process_mask AGMIMG_&image + }{ + AGMIMG_&image + }ifelse + }ifelse + end end +}def + /OPIimage + { + dup type/dicttype ne{ + 10 dict begin + /DataSource xdf + /ImageMatrix xdf + /BitsPerComponent xdf + /Height xdf + /Width xdf + /ImageType 1 def + /Decode[0 1 def] + currentdict + end + }if + dup begin + /NComponents 1 cdndf + /MultipleDataSources false cdndf + /SkipImageProc{false}cdndf + /Decode[ + 0 + currentcolorspace 0 get/Indexed eq{ + 2 BitsPerComponent exp 1 sub + }{ + 1 + }ifelse + ]cdndf + /Operator/image cdndf + end + /sep_colorspace_dict AGMCORE_gget null eq{ + imageormask + }{ + gsave + dup begin invert_image_samples end + sep_imageormask + grestore + }ifelse + }def +/cachemask_level2 +{ + 3 dict begin + /LZWEncode filter/WriteFilter xdf + /readBuffer 256 string def + /ReadFilter + currentfile + 0(%EndMask)/SubFileDecode filter + /ASCII85Decode filter + /RunLengthDecode filter + def + { + ReadFilter readBuffer readstring exch + WriteFilter exch writestring + not{exit}if + }loop + WriteFilter closefile + end +}def +/spot_alias +{ + /mapto_sep_imageormask + { + dup type/dicttype ne{ + 12 dict begin + /ImageType 1 def + /DataSource xdf + /ImageMatrix xdf + /BitsPerComponent xdf + /Height xdf + /Width xdf + /MultipleDataSources false def + }{ + begin + }ifelse + /Decode[/customcolor_tint AGMCORE_gget 0]def + /Operator/image def + /SkipImageProc{false}def + currentdict + end + sep_imageormask + }bdf + /customcolorimage + { + Adobe_AGM_Image/AGMIMG_colorAry xddf + /customcolor_tint AGMCORE_gget + << + /Name AGMIMG_colorAry 4 get + /CSA[/DeviceCMYK] + /TintMethod/Subtractive + /TintProc null + /MappedCSA null + /NComponents 4 + /Components[AGMIMG_colorAry aload pop pop] + >> + setsepcolorspace + mapto_sep_imageormask + }ndf + Adobe_AGM_Image/AGMIMG_&customcolorimage/customcolorimage load put + /customcolorimage + { + Adobe_AGM_Image/AGMIMG_override false put + current_spot_alias{dup 4 get map_alias}{false}ifelse + { + false set_spot_alias + /customcolor_tint AGMCORE_gget exch setsepcolorspace + pop + mapto_sep_imageormask + true set_spot_alias + }{ + //Adobe_AGM_Image/AGMIMG_&customcolorimage get exec + }ifelse + }bdf +}def +/snap_to_device +{ + 6 dict begin + matrix currentmatrix + dup 0 get 0 eq 1 index 3 get 0 eq and + 1 index 1 get 0 eq 2 index 2 get 0 eq and or exch pop + { + 1 1 dtransform 0 gt exch 0 gt/AGMIMG_xSign? exch def/AGMIMG_ySign? exch def + 0 0 transform + AGMIMG_ySign?{floor 0.1 sub}{ceiling 0.1 add}ifelse exch + AGMIMG_xSign?{floor 0.1 sub}{ceiling 0.1 add}ifelse exch + itransform/AGMIMG_llY exch def/AGMIMG_llX exch def + 1 1 transform + AGMIMG_ySign?{ceiling 0.1 add}{floor 0.1 sub}ifelse exch + AGMIMG_xSign?{ceiling 0.1 add}{floor 0.1 sub}ifelse exch + itransform/AGMIMG_urY exch def/AGMIMG_urX exch def + [AGMIMG_urX AGMIMG_llX sub 0 0 AGMIMG_urY AGMIMG_llY sub AGMIMG_llX AGMIMG_llY]concat + }{ + }ifelse + end +}def +level2 not{ + /colorbuf + { + 0 1 2 index length 1 sub{ + dup 2 index exch get + 255 exch sub + 2 index + 3 1 roll + put + }for + }def + /tint_image_to_color + { + begin + Width Height BitsPerComponent ImageMatrix + /DataSource load + end + Adobe_AGM_Image begin + /AGMIMG_mbuf 0 string def + /AGMIMG_ybuf 0 string def + /AGMIMG_kbuf 0 string def + { + colorbuf dup length AGMIMG_mbuf length ne + { + dup length dup dup + /AGMIMG_mbuf exch string def + /AGMIMG_ybuf exch string def + /AGMIMG_kbuf exch string def + }if + dup AGMIMG_mbuf copy AGMIMG_ybuf copy AGMIMG_kbuf copy pop + } + addprocs + {AGMIMG_mbuf}{AGMIMG_ybuf}{AGMIMG_kbuf}true 4 colorimage + end + }def + /sep_imageormask_lev1 + { + begin + MappedCSA 0 get dup/DeviceRGB eq exch/DeviceCMYK eq or has_color not and{ + { + 255 mul round cvi GrayLookup exch get + }currenttransfer addprocs settransfer + currentdict imageormask + }{ + /sep_colorspace_dict AGMCORE_gget/Components known{ + MappedCSA 0 get/DeviceCMYK eq{ + Components aload pop + }{ + 0 0 0 Components aload pop 1 exch sub + }ifelse + Adobe_AGM_Image/AGMIMG_k xddf + Adobe_AGM_Image/AGMIMG_y xddf + Adobe_AGM_Image/AGMIMG_m xddf + Adobe_AGM_Image/AGMIMG_c xddf + AGMIMG_y 0.0 eq AGMIMG_m 0.0 eq and AGMIMG_c 0.0 eq and{ + {AGMIMG_k mul 1 exch sub}currenttransfer addprocs settransfer + currentdict imageormask + }{ + currentcolortransfer + {AGMIMG_k mul 1 exch sub}exch addprocs 4 1 roll + {AGMIMG_y mul 1 exch sub}exch addprocs 4 1 roll + {AGMIMG_m mul 1 exch sub}exch addprocs 4 1 roll + {AGMIMG_c mul 1 exch sub}exch addprocs 4 1 roll + setcolortransfer + currentdict tint_image_to_color + }ifelse + }{ + MappedCSA 0 get/DeviceGray eq{ + {255 mul round cvi ColorLookup exch get 0 get}currenttransfer addprocs settransfer + currentdict imageormask + }{ + MappedCSA 0 get/DeviceCMYK eq{ + currentcolortransfer + {255 mul round cvi ColorLookup exch get 3 get 1 exch sub}exch addprocs 4 1 roll + {255 mul round cvi ColorLookup exch get 2 get 1 exch sub}exch addprocs 4 1 roll + {255 mul round cvi ColorLookup exch get 1 get 1 exch sub}exch addprocs 4 1 roll + {255 mul round cvi ColorLookup exch get 0 get 1 exch sub}exch addprocs 4 1 roll + setcolortransfer + currentdict tint_image_to_color + }{ + currentcolortransfer + {pop 1}exch addprocs 4 1 roll + {255 mul round cvi ColorLookup exch get 2 get}exch addprocs 4 1 roll + {255 mul round cvi ColorLookup exch get 1 get}exch addprocs 4 1 roll + {255 mul round cvi ColorLookup exch get 0 get}exch addprocs 4 1 roll + setcolortransfer + currentdict tint_image_to_color + }ifelse + }ifelse + }ifelse + }ifelse + end + }def + /sep_image_lev1_sep + { + begin + /sep_colorspace_dict AGMCORE_gget/Components known{ + Components aload pop + Adobe_AGM_Image/AGMIMG_k xddf + Adobe_AGM_Image/AGMIMG_y xddf + Adobe_AGM_Image/AGMIMG_m xddf + Adobe_AGM_Image/AGMIMG_c xddf + {AGMIMG_c mul 1 exch sub} + {AGMIMG_m mul 1 exch sub} + {AGMIMG_y mul 1 exch sub} + {AGMIMG_k mul 1 exch sub} + }{ + {255 mul round cvi ColorLookup exch get 0 get 1 exch sub} + {255 mul round cvi ColorLookup exch get 1 get 1 exch sub} + {255 mul round cvi ColorLookup exch get 2 get 1 exch sub} + {255 mul round cvi ColorLookup exch get 3 get 1 exch sub} + }ifelse + AGMCORE_get_ink_data currenttransfer addprocs settransfer + currentdict imageormask_sys + end + }def + /indexed_imageormask_lev1 + { + /indexed_colorspace_dict AGMCORE_gget begin + begin + currentdict + MappedCSA 0 get dup/DeviceRGB eq exch/DeviceCMYK eq or has_color not and{ + {HiVal mul round cvi GrayLookup exch get HiVal div}currenttransfer addprocs settransfer + imageormask + }{ + MappedCSA 0 get/DeviceGray eq{ + {HiVal mul round cvi Lookup exch get HiVal div}currenttransfer addprocs settransfer + imageormask + }{ + MappedCSA 0 get/DeviceCMYK eq{ + currentcolortransfer + {4 mul HiVal mul round cvi 3 add Lookup exch get HiVal div 1 exch sub}exch addprocs 4 1 roll + {4 mul HiVal mul round cvi 2 add Lookup exch get HiVal div 1 exch sub}exch addprocs 4 1 roll + {4 mul HiVal mul round cvi 1 add Lookup exch get HiVal div 1 exch sub}exch addprocs 4 1 roll + {4 mul HiVal mul round cvi Lookup exch get HiVal div 1 exch sub}exch addprocs 4 1 roll + setcolortransfer + tint_image_to_color + }{ + currentcolortransfer + {pop 1}exch addprocs 4 1 roll + {3 mul HiVal mul round cvi 2 add Lookup exch get HiVal div}exch addprocs 4 1 roll + {3 mul HiVal mul round cvi 1 add Lookup exch get HiVal div}exch addprocs 4 1 roll + {3 mul HiVal mul round cvi Lookup exch get HiVal div}exch addprocs 4 1 roll + setcolortransfer + tint_image_to_color + }ifelse + }ifelse + }ifelse + end end + }def + /indexed_image_lev1_sep + { + /indexed_colorspace_dict AGMCORE_gget begin + begin + {4 mul HiVal mul round cvi Lookup exch get HiVal div 1 exch sub} + {4 mul HiVal mul round cvi 1 add Lookup exch get HiVal div 1 exch sub} + {4 mul HiVal mul round cvi 2 add Lookup exch get HiVal div 1 exch sub} + {4 mul HiVal mul round cvi 3 add Lookup exch get HiVal div 1 exch sub} + AGMCORE_get_ink_data currenttransfer addprocs settransfer + currentdict imageormask_sys + end end + }def +}if +end +systemdict/setpacking known +{setpacking}if +%%EndResource +currentdict Adobe_AGM_Utils eq {end} if +%%EndProlog +%%BeginSetup +Adobe_AGM_Utils begin +2 2010 Adobe_AGM_Core/ds gx +Adobe_CoolType_Core/ds get exec Adobe_AGM_Image/ds gx +currentdict Adobe_AGM_Utils eq {end} if +%%EndSetup +%%Page: (Page 1) 1 +%%EndPageComments +%%BeginPageSetup +%ADOBeginClientInjection: PageSetup Start "AI11EPS" +%AI12_RMC_Transparency: Balance=75 RasterRes=300 GradRes=150 Text=0 Stroke=1 Clip=1 OP=0 +%ADOEndClientInjection: PageSetup Start "AI11EPS" +Adobe_AGM_Utils begin +Adobe_AGM_Core/ps gx +Adobe_AGM_Utils/capture_cpd gx +Adobe_CoolType_Core/ps get exec Adobe_AGM_Image/ps gx +%ADOBeginClientInjection: PageSetup End "AI11EPS" +/currentdistillerparams where {pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse { userdict /AI11_PDFMark5 /cleartomark load put userdict /AI11_ReadMetadata_PDFMark5 {flushfile cleartomark } bind put} { userdict /AI11_PDFMark5 /pdfmark load put userdict /AI11_ReadMetadata_PDFMark5 {/PUT pdfmark} bind put } ifelse [/NamespacePush AI11_PDFMark5 [/_objdef {ai_metadata_stream_123} /type /stream /OBJ AI11_PDFMark5 [{ai_metadata_stream_123} currentfile 0 (% &&end XMP packet marker&&) /SubFileDecode filter AI11_ReadMetadata_PDFMark5 + + + + application/postscript + + + Print + + + + + Adobe Illustrator CS3 + 2010-05-25T11:06:31-06:00 + 2010-05-25T11:06:31-06:00 + 2010-05-25T11:06:31-06:00 + + + + 256 + 116 + JPEG + /9j/4AAQSkZJRgABAgEAlgCWAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAAlgAAAAEA AQCWAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAdAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A735w/NHyr5VuVs715bnU WAb6jaIJJVVtwX5FFWvgWr7Zm6bs/JmFjaPeXB1XaOLAalvLuDGv+V92H/Uu6p/wC/1zM/kaX8+L hfy3D+bJ3/K+7D/qXdU/4Bf64/yNL+fFf5bh/Nk7/lfdh/1Luqf8Av8AXH+Rpfz4r/LcP5snf8r7 sP8AqXdU/wCAX+uP8jS/nxX+W4fzZO/5X3Yf9S7qn/AL/XH+Rpfz4r/LcP5snf8AK+7D/qXdU/4B f64/yNL+fFf5bh/Nk7/lfdh/1Luqf8Av9cf5Gl/Piv8ALcP5snf8r7sP+pd1T/gF/rj/ACNL+fFf 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 Yq7FXYq7FXYq7FXYq7FXYq7FX//Z + + + + + + uuid:EB89571B7F69DF11AF0CB9E432C5A167 + uuid:EC89571B7F69DF11AF0CB9E432C5A167 + + uuid:EA89571B7F69DF11AF0CB9E432C5A167 + uuid:E989571B7F69DF11AF0CB9E432C5A167 + + + + Print + + + + 300.000000 + 200.000000 + Points + + 1 + False + False + + + PANTONE 3005 U + PANTONE 309 U + PANTONE 2905 U + + + + + + Default Swatch Group + 0 + + + + PANTONE 3005 U + SPOT + 100.000000 + CMYK + 100.000000 + 34.000000 + 0.000000 + 2.000000 + + + PANTONE 2905 U + SPOT + 100.000000 + CMYK + 41.000000 + 2.000000 + 0.000000 + 0.000000 + + + PANTONE 309 U + SPOT + 100.000000 + CMYK + 100.000000 + 0.000000 + 9.000000 + 72.000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + % &&end XMP packet marker&& [{ai_metadata_stream_123} <> /PUT AI11_PDFMark5 [/Document 1 dict begin /Metadata {ai_metadata_stream_123} def currentdict end /BDC AI11_PDFMark5 +%ADOEndClientInjection: PageSetup End "AI11EPS" +%%EndPageSetup +1 -1 scale 0 -211.304 translate +pgsv +[1 0 0 1 0 0 ]ct +gsave +np +gsave +0 0 mo +0 211.304 li +372 211.304 li +372 0 li +cp +clp +[1 0 0 1 0 0 ]ct +103.096 139.708 mo +103.039 138.986 103.005 138.517 103.005 138.517 cv +103.005 138.517 100.816 123.759 98.21 119.355 cv +97.7979 118.657 98.2549 115.79 99.4053 111.543 cv +100.078 112.706 102.917 117.68 103.487 119.283 cv +104.129 121.095 104.264 121.614 104.264 121.614 cv +104.264 121.614 102.707 113.603 100.152 108.928 cv +100.712 107.038 101.381 104.949 102.138 102.716 cv +103.106 104.414 105.423 108.525 105.933 109.951 cv +106.036 110.244 106.123 110.493 106.201 110.721 cv +106.226 110.584 106.251 110.447 106.276 110.31 cv +105.691 107.828 104.542 103.509 102.969 100.318 cv +106.459 82.1528 118.362 57.8726 130.565 47.0405 cv +50.96 47.0405 li +45.625 47.0405 41.2598 51.4063 41.2598 56.7407 cv +41.2598 144.53 li +41.2598 149.865 45.625 154.23 50.96 154.23 cv +103.39 154.23 li +103.012 149.654 102.886 144.591 103.096 139.708 cv +cp +false sop +/0 +<< +/Name (PANTONE 3005 U) +/0 +[/DeviceCMYK] /CSA add_res +/CSA /0 get_csa_by_name +/MappedCSA /0 /CSA get_res +/TintMethod /Subtractive +/TintProc null +/NComponents 4 +/Components [ 1 0.34 0 0.02 ] +>> +/CSD add_res +1 /0 /CSD get_res sepcs +1 sep +f +161.875 105.281 mo +163.221 105.281 li +163.221 116.492 li +161.795 116.492 li +161.469 113.393 160.348 110.974 158.432 109.234 cv +156.514 107.497 154.104 106.626 151.196 106.626 cv +148.612 106.626 146.534 107.279 144.958 108.583 cv +143.382 109.886 142.594 111.626 142.594 113.801 cv +142.594 115.812 143.246 117.646 144.551 119.303 cv +145.854 120.962 148.682 123.093 153.031 125.706 cv +158.085 128.722 161.482 131.355 163.221 133.613 cv +164.96 135.868 165.829 138.45 165.829 141.358 cv +165.829 145.108 164.502 148.315 161.844 150.978 cv +159.182 153.642 155.979 154.973 152.236 154.973 cv +150.064 154.973 147.297 154.375 143.932 153.178 cv +142.549 152.689 141.666 152.447 141.287 152.447 cv +140.499 152.447 139.89 153.057 139.455 154.281 cv +138.028 154.281 li +138.028 141.358 li +139.455 141.358 li +139.481 144.919 140.642 147.772 142.934 149.92 cv +145.226 152.065 147.904 153.138 150.968 153.138 cv +153.873 153.138 156.196 152.366 157.948 150.814 cv +159.697 149.263 160.572 147.197 160.572 144.612 cv +160.572 142.681 159.852 140.832 158.415 139.065 cv +156.979 137.296 153.972 135.018 149.4 132.227 cv +145.674 129.923 143.186 128.11 141.937 126.776 cv +140.688 125.443 139.765 123.982 139.168 122.39 cv +138.571 120.801 138.272 119.052 138.272 117.15 cv +138.272 113.48 139.476 110.502 141.881 108.217 cv +144.286 105.934 147.418 104.792 151.276 104.792 cv +153.396 104.792 155.707 105.213 158.207 106.055 cv +159.374 106.436 160.19 106.626 160.651 106.626 cv +161.034 106.626 161.291 106.545 161.425 106.382 cv +161.563 106.218 161.713 105.852 161.875 105.281 cv +cp +/1 +<< +/Name (PANTONE 309 U) +/CSA /0 get_csa_by_name +/MappedCSA /0 /CSA get_res +/TintMethod /Subtractive +/TintProc null +/NComponents 4 +/Components [ 1 0 0.09 0.72 ] +>> +/CSD add_res +1 /1 /CSD get_res sepcs +1 sep +f +216.34 130.229 mo +216.34 122.917 214.667 117.157 211.324 112.944 cv +207.98 108.732 203.66 106.626 198.358 106.626 cv +192.733 106.626 188.244 108.741 184.888 112.964 cv +181.53 117.192 179.855 122.837 179.855 129.903 cv +179.855 136.779 181.519 142.372 184.847 146.679 cv +188.178 150.986 192.518 153.138 197.871 153.138 cv +203.443 153.138 207.913 151.053 211.282 146.881 cv +214.651 142.708 216.34 137.158 216.34 130.229 cv +cp +224.653 166.999 mo +225.182 168.304 li +222.846 169.364 220.197 169.894 217.235 169.894 cv +214.789 169.894 212.518 169.573 210.426 168.936 cv +208.336 168.296 206.369 167.319 204.537 166 cv +202.703 164.682 200.466 162.487 197.83 159.418 cv +195.491 156.699 193.698 155.012 192.449 154.361 cv +186.986 153.249 182.294 150.365 178.365 145.721 cv +174.438 141.074 172.474 135.772 172.474 129.821 cv +172.474 125.391 173.602 121.221 175.857 117.307 cv +178.113 113.393 181.171 110.329 185.033 108.116 cv +188.889 105.899 193.17 104.792 197.871 104.792 cv +205.128 104.792 211.242 107.205 216.217 112.028 cv +221.188 116.85 223.674 122.783 223.674 129.821 cv +223.674 135.748 221.76 141.023 217.928 145.657 cv +214.095 150.293 209.189 153.194 203.212 154.361 cv +203.784 154.819 204.806 155.976 206.272 157.826 cv +209.391 161.687 212 164.306 214.091 165.678 cv +216.185 167.046 218.264 167.734 220.331 167.734 cv +221.854 167.734 223.297 167.488 224.653 166.999 cv +cp +f +259.776 146.473 mo +258.979 154.111 li +227.787 154.111 li +227.853 152.591 li +229.544 152.418 230.729 151.984 231.42 151.291 cv +232.11 150.593 232.459 148.992 232.485 146.473 cv +232.521 112.728 li +232.509 110.965 232.423 109.75 232.277 109.087 cv +231.885 107.234 230.135 106.309 227.005 106.309 cv +226.938 104.792 li +243.22 104.792 li +243.282 106.309 li +241.546 106.427 240.358 106.895 239.693 107.717 cv +239.035 108.542 238.709 110.213 238.71 112.728 cv +238.585 146.826 li +238.59 147.406 238.639 147.937 238.747 148.429 cv +239.27 150.917 241.243 152.158 244.665 152.158 cv +247.604 152.158 li +251.407 152.158 254.046 151.73 255.535 150.876 cv +257.02 150.028 257.964 148.557 258.371 146.473 cv +259.776 146.473 li +cp +f +270.119 113.882 mo +270.855 113.882 271.533 114.14 272.159 114.658 cv +272.778 115.174 273.181 115.812 273.336 116.571 cv +273.487 117.307 273.362 117.946 272.959 118.489 cv +272.556 119.032 271.992 119.303 271.256 119.303 cv +270.495 119.303 269.786 119.032 269.13 118.489 cv +268.47 117.946 268.066 117.307 267.916 116.571 cv +267.761 115.812 267.886 115.174 268.33 114.658 cv +268.764 114.14 269.358 113.882 270.119 113.882 cv +cp +275.101 125.583 mo +275.051 146.943 li +275.06 148.436 275.136 149.497 275.269 150.124 cv +275.441 150.994 275.827 151.583 276.406 151.895 cv +276.995 152.209 278.309 152.366 280.348 152.366 cv +280.395 153.792 li +265.72 153.792 li +265.755 152.366 li +267.767 152.366 269.003 152.18 269.474 151.814 cv +269.941 151.449 270.181 150.218 270.207 148.125 cv +270.299 133.002 li +270.282 132.177 270.23 131.532 270.133 131.064 cv +269.845 129.666 268.923 128.965 267.373 128.965 cv +264.189 129.372 li +264.174 127.701 li +273.424 125.583 li +275.101 125.583 li +cp +f +299.717 126.315 mo +299.787 128.597 li +292.118 128.597 li +292.063 147.671 li +292.063 148.191 292.094 148.638 292.183 149.021 cv +292.508 150.597 293.372 151.387 294.761 151.387 cv +297.021 151.387 299.6 149.906 302.527 146.943 cv +303.758 147.981 li +299.704 152.48 295.83 154.728 292.135 154.728 cv +289.581 154.728 288.058 153.559 287.563 151.222 cv +287.359 150.246 287.257 148.315 287.249 145.434 cv +287.354 128.597 li +282.338 128.597 li +282.28 127.172 li +286.584 126.738 289.38 124.034 290.673 119.06 cv +292.14 119.06 li +292.127 126.315 li +299.717 126.315 li +cp +f +311.427 140.01 mo +319.449 138.461 323.033 135.679 322.196 131.658 cv +322.025 130.839 321.502 130.092 320.645 129.414 cv +319.776 128.734 318.904 128.394 318.011 128.394 cv +315.972 128.394 314.303 129.597 313.017 132.001 cv +311.721 134.407 311.198 137.078 311.427 140.01 cv +cp +311.381 141.522 mo +311.449 142.227 311.544 142.894 311.677 143.518 cv +312.7 148.409 315.455 150.855 319.937 150.855 cv +323.197 150.855 327.513 148.887 329.655 144.946 cv +330.741 146.066 li +329.398 149.104 327.935 151.306 326.357 152.675 cv +324.783 154.045 321.037 154.728 318.351 154.728 cv +315.466 154.728 313.074 153.92 311.172 152.303 cv +309.267 150.685 308.014 148.396 307.389 145.434 cv +306.327 140.378 307.15 135.782 309.851 131.634 cv +312.566 127.493 316.063 125.417 320.356 125.417 cv +322.045 125.417 325.076 125.848 326.24 126.703 cv +327.389 127.558 328.122 128.707 328.426 130.146 cv +328.937 132.567 326.205 134.854 323.48 137.016 cv +320.753 139.177 316.713 140.678 311.381 141.522 cv +cp +f +99.4053 111.543 mo +100.078 112.706 102.917 117.68 103.487 119.283 cv +104.129 121.095 104.264 121.614 104.264 121.614 cv +104.264 121.614 102.707 113.603 100.152 108.928 cv +100.712 107.038 101.381 104.949 102.138 102.716 cv +103.023 104.269 105.034 107.836 105.761 109.526 cv +105.788 109.207 105.815 108.888 105.843 108.572 cv +105.199 106.097 104.221 102.857 102.969 100.318 cv +106.183 83.5933 116.528 61.6929 127.673 49.8696 cv +50.96 49.8696 li +47.1719 49.8696 44.0889 52.9521 44.0889 56.7407 cv +44.0889 138.125 li +61.4629 131.457 82.4121 125.362 100.442 125.623 cv +99.7725 123.042 99.0107 120.708 98.21 119.355 cv +97.7979 118.657 98.2549 115.79 99.4053 111.543 cv +cp +/2 +<< +/Names [ +(PANTONE 2905 U) +(PANTONE 3005 U) +] +/CSA /0 get_csa_by_name +/TintMethod /Subtractive +/TintProc null +/MappedCSA null +/TintTransform +/0 { +1 3 1 roll 1 3 1 roll 1 3 1 roll 1 3 1 +roll 6 -1 roll 2 index 0.41 mul 1 cvr exch sub mul 1 index +1 mul 1 cvr exch sub mul 1 cvr exch sub 6 1 roll 5 +-1 roll 2 index 0.02 mul 1 cvr exch sub mul 1 index 0.34 mul +1 cvr exch sub mul 1 cvr exch sub 5 1 roll 4 -1 roll +2 index 0 mul 1 cvr exch sub mul 1 index 0 mul 1 cvr +exch sub mul 1 cvr exch sub 4 1 roll 3 -1 roll 2 index +0 mul 1 cvr exch sub mul 1 index 0.02 mul 1 cvr exch sub +mul 1 cvr exch sub 3 1 roll pop pop +} bind /Procedure add_res +/0 /Procedure get_res +>> +/CSD add_res +/2 /CSD get_res devncs +level3{ +gsave +clp +[-4.01667e-06 91.8907 -91.8907 -4.01667e-06 85.8809 49.8696 ]ct +/0 +<< +/ShadingType 2 +/ColorSpace [/DeviceN /devicen_colorspace_dict AGMCORE_gget begin /Names load /MappedCSA load /TintTransform load end] +/Coords [0 0 1 0 ] +/Domain [0 1 ] +/Extend[ true true] +/Function +<< +/Domain[0 1 ] +/FunctionType 3 +/Functions [ +<< +/Domain[0 1 ] +/FunctionType 2 +/C0 [0 1 ] +/C1 [1 0 ] +/N 1.3901 +>> +<< +/Domain[0 1 ] +/FunctionType 2 +/C0 [0 1 ] +/C1 [0 1 ] +/N 1 +>> +] +/Bounds [0.920245 ] +/Encode [1 0 0 1 ] +>> +>>/Gradient add_res /0 /Gradient get_res clonedict shfill grestore +}if +level3 not{ +gsave +[-4.01667e-06 91.8907 -91.8907 -4.01667e-06 85.8809 49.8696 ]ct +clp +/0 { +<< +/NumSamples 256 +/NumComp 2 +/Scaling[[0.00392157 0 ][0.00392157 0 ]] +/Samples[ +<~s8Dikq=jUToC;>$4p- +%1/1`;$.4?Pj,pX][+sA'N*Zc=A)]BS3 +(`*r''bh;p&eP]e%h9'Y$k*LO$4."E#R:P<"U,#3!s8T+!W`<&zzzzz!!!!!~> +<~!!*0)"U>;?$OmRW&JGio(E",2*?QCI,:"T`.4Qi"/hf"91c73O3]fGf5=%V'77Kd=8k_rS:f1+i$P?>?t!MTAS,UiC27[(DfKf=FEVkQH$apeIXm!$K8#&8Ll%%KNK0'^P*;,qQC+)/S"-%ATV8*TUnsuf +WN*##Xfek3Z*UgF[^WcW]">Vg^;%J"_Sa=2`lH0Bb0/#RcHjkbdaHUoe^i@)g"P38gtgiEi8ESRj5f=` +k3(sll0@U#m-X3.n*oi:o()DDo_%nNp@n@Wq>'m`qtp +] +>> +0 0 1 0 []true true [] +/DeviceN +GenStrips +} /Gradient add_res /0 /Gradient get_res exec grestore +}if +np +149.133 44.167 mo +143.681 39.3052 137.08 41.2583 130.565 47.0405 cv +129.598 47.8994 128.633 48.8516 127.673 49.8696 cv +116.528 61.6929 106.183 83.5933 102.969 100.318 cv +104.221 102.857 105.199 106.097 105.843 108.572 cv +106.008 109.207 106.157 109.803 106.276 110.31 cv +106.559 111.51 106.711 112.288 106.711 112.288 cv +106.711 112.288 106.611 111.91 106.201 110.721 cv +106.123 110.493 106.036 110.244 105.933 109.951 cv +105.889 109.83 105.828 109.683 105.761 109.526 cv +105.034 107.836 103.023 104.269 102.138 102.716 cv +101.381 104.949 100.712 107.038 100.152 108.928 cv +102.707 113.603 104.264 121.614 104.264 121.614 cv +104.264 121.614 104.129 121.095 103.487 119.283 cv +102.917 117.68 100.078 112.706 99.4053 111.543 cv +98.2549 115.79 97.7979 118.657 98.21 119.355 cv +99.0107 120.708 99.7725 123.042 100.442 125.623 cv +101.954 131.438 103.005 138.517 103.005 138.517 cv +103.005 138.517 103.039 138.986 103.096 139.708 cv +102.886 144.591 103.012 149.654 103.39 154.23 cv +103.891 160.288 104.834 165.492 106.036 168.277 cv +106.852 167.832 li +105.087 162.345 104.37 155.154 104.684 146.861 cv +105.159 134.185 108.076 118.898 113.466 102.965 cv +122.572 78.9131 135.206 59.6152 146.769 50.3994 cv +136.23 59.917 121.966 90.7261 117.696 102.135 cv +112.915 114.911 109.527 126.9 107.485 138.387 cv +111.008 127.618 122.399 122.989 122.399 122.989 cv +122.399 122.989 127.986 116.099 134.515 106.255 cv +130.604 107.147 124.182 108.674 122.031 109.578 cv +118.858 110.909 118.003 111.363 118.003 111.363 cv +118.003 111.363 128.281 105.104 137.099 102.27 cv +149.226 83.1704 162.438 56.0361 149.133 44.167 cv +cp +1 /1 /CSD get_res sepcs +1 sep +f +0.5 lw +0 lc +0 lj +4 ml +[] 0 dsh +true sadj +27 175.304 mo +0 175.304 li +/3 +<< +/Name (All) +/CSA /0 get_csa_by_name +/MappedCSA /0 /CSA get_res +/TintMethod /Subtractive +/TintProc null +/NComponents 4 +/Components [ 1 1 1 1 ] +>> +/CSD add_res +1 /3 /CSD get_res sepcs +1 sep +@ +36 184.304 mo +36 211.304 li +@ +27 36 mo +0 36 li +@ +36 27 mo +36 0 li +@ +345 36 mo +372 36 li +@ +336 27 mo +336 0 li +@ +345 175.304 mo +372 175.304 li +@ +336 184.304 mo +336 211.304 li +@ +%ADOBeginClientInjection: EndPageContent "AI11EPS" +userdict /annotatepage 2 copy known {get exec}{pop pop} ifelse +%ADOEndClientInjection: EndPageContent "AI11EPS" +grestore +grestore +pgrs +%%PageTrailer +%ADOBeginClientInjection: PageTrailer Start "AI11EPS" +[/EMC AI11_PDFMark5 [/NamespacePop AI11_PDFMark5 +%ADOEndClientInjection: PageTrailer Start "AI11EPS" +[ +[/CSA [/0 ]] +[/CSD [/0 /1 /2 /3 ]] +[/Gradient [/0 ]] +[/Procedure [/0 ]] +] del_res +Adobe_AGM_Image/pt gx +Adobe_CoolType_Core/pt get exec Adobe_AGM_Core/pt gx +currentdict Adobe_AGM_Utils eq {end} if +%%Trailer +Adobe_AGM_Image/dt get exec +Adobe_CoolType_Core/dt get exec Adobe_AGM_Core/dt get exec +%%EOF +%AI9_PrintingDataEnd userdict /AI9_read_buffer 256 string put userdict begin /ai9_skip_data { mark { currentfile AI9_read_buffer { readline } stopped { } { not { exit } if (%AI9_PrivateDataEnd) eq { exit } if } ifelse } loop cleartomark } def end userdict /ai9_skip_data get exec %AI9_PrivateDataBegin %!PS-Adobe-3.0 EPSF-3.0 %%Creator: Adobe Illustrator(R) 11.0 %%AI8_CreatorVersion: 13.0.2 %%For: (Mary Diarte) () %%Title: (logo_color_eps.eps) %%CreationDate: 5/25/10 11:06 AM %AI9_DataStream %Gb"-6Bp2f2Pp#o2n/pqR,4,%FCG'cqcS6#7\b,MND(@139M]b(.+f4cF,#U)U>4f'lQ=CZga0;4=HqR+.hQ;lS]&LdEc>u@I.>23 %q]F^Dqtf,mGOPRXQT*6<^3Q#^]MCJ(9'2$brlaWb^%u/7-/D=Fni+nSD+m-X<]f0h<-*C3hW?NRp>"X*b9,WUHM?s1&+qVd5. %q`!t.r:$QY@eYrRS($[^Y>4ON]RB>?-JqPuoARsO7-Dhq=33@?bLaTo$t5LppPVL"+66G_;E"Vp$lPXedl-E %f\r+m++O(\r[knTjqhClIe3HoroSIcl4e19q3S5Cs4r6Tg&tH.m=5(!rb6'5bF@`Sf_44`DPgGmRtgB[JFB2ck)1d=>MFY\SU'T^ %Df'`(IdsVVHhH4\+$P-unaZ;LGQ79$4Ehn(J,f5]L]?1"qeUklES7hPDM(`iorZ[%fMm-9i0iK;HMdZ1(E[#CP1hnO`g^!`%_m%t %[T9d%ee_Y:(Ya7?CN"gNe98q#]2C+Ae(=TLT3f;/pFgH6rSlr)G9>`Bn4-[EY?nsh7+cq_q"ALpeZ*uU"Q+,&*S(`=2$/\^"6K$I!"B]C\>(a9E-*SQ,CA@Y[&$N`prU*>H@fGL35.pm'j)Dni6N7XLPi5u7J(3:4nrFl"^#SNb %pW`]h]q&lKrUne7K&?Uua1)*Bm`"KMD:Z,7dnOO^\SF1smdT5rM_B"3\*iCn]_XiOGCTSt>AIsqn)=;2i;[!>s8?8shS[Z/*YSA] %hg/)USkec34d?Z08dYJh$#6:>[8K&VUs3)::9@?j[LYK+/(CR6C;FiX5LeEUU"4Z':[[uHc$g1G:+,i1F@lk25+UX/dtsZi*`IQf %5P.GA(7pcZ]f0<6s%j*C"TLh@(mBoC^4R$6s%iqi"TQBLAL?VD(_cfXJ&-s!IqX]$M->WG_>c3,p^'b6'>0N@PMQhX?sN1ce2a'" %-L1asV`CVWUfioCQ=pG9eFXc`)#T%j0KrWJRMC,a`WuO]?SFNEJ,#X^%M8CWC;K]'Ip,:X %&t2mJ5J0j4!.$9m,ATdIUKN@U1iG-&1ihkEoUPbHn"l=]]r6[5TFQoU0G?n\:Xc9,>8SI[&%)\X@=0mZoITnNF^T=R$c-Z44#D$N %#/Y8o@DN\p2X!/0$K6U'$X$DsGNp3n3k^mB%=0WW,%IC^6S3RF[8,&sp[k/Ne'94TVDY9^Tb^Du+oL`>SXqQCaDUB,9BN565%1qY %IE+Qs]&Sn=oY/I'bC(D(7l$:tFIs$,X7TKM9C:/kekZ!kT0sDGJ"39PqG6s@5_&O#oJ'^k8GIh)oeX$8jnsVoj(j"uk-k#k^Oc::E",u7_Aq#0;c`kGD?SlQaT@EJo2e*A.h80H]D/*n@ %FmGl>`pL4+mMXUl(_")pfQIMm0E6]L_f*%[gq2.h)-4*[YuMasF*HE#46cP\1B+);V11[hZN9ZV/%3p@?MRCk/<7>qq5iTNGL08* %e"qPu^\_Ad#'&/tRaoe+Ai&j)[s2TTT?dIP4WjYATAQ#-376K@L"U9\hodX/]7tV& %3BD0I"&NKFHhA%b9d+p2gFCM2a$FJG>",TbT]bn#eka9BrF2Z^4Gu]lYIJNFs-.M3^D,:mD4K]4oF,NlqenG')BK2JF=5[83&i"" %#9sZN#>hZST2(2p0HPJMnO>2S'C\Zn]P@6%G;A'X2eRWWSE"NM&pKkp9A%1HXDGI&24ibR!d"ibu:^OrUD85Q0 %4`KET\Z#.*@mG3tp/iNOlTCA=^77[^N'6gdg7G\iG72$(&%Ejo0V#t7)7CYPJj%'fT?e5FZneI=corR)`-d].,mZuUC^N=XQ*G\P %[-`@D3DYJ2Q.E$Fe9D%eQ8;,\&^mb\F'Oq&6ln]YieiE6679CjS(Esp`Y?6FWN/&.E2a8L&BoZKb] %kA;7+n^>(t%\Ksp>@c\hSENqQD*KI+0M+oprTj44&jKnI.r(05LEi6*.-@q*UeM._GbQ.a^!!W %KQST*J5;u$O47CPoBW@7JZ6kReeL[2hAqFGaUC7YMk"1eJs+\kraqUFm&*)@)K@MB41^C[kR:i2SHFD=ck`\hltSXMYFB*L47sLP %0&Y"<";WJXOc+;%#>d#2$q>Q&Vph5460O_WG'c8*KoQh,UOFEokLY$>YgQ!Q^1]_5r-%KU@Jkei&"nkHNEb:`0Tt#Y*V2cmB+r>4 %E*roq0`KkLmb?^Si==ZY^k]qA+:cl7fA;]:Lt!:rde7Z,i"\gYg/St%lq!6On0s`73:X,Pifo64L=,'&;8kH%$M+\p]j>2j,._s5 %%^jjaiqVqGY[GgrqtG$FhXlHQ2ST%Wrlt?/kWA_`^\R`V`ui&D,7XYNc#;IJg99Cbl,Q %nV9?cr;QZl2ql%B=5$@Q,8BGCFhH`bI,Nh!hpG-$ho$7qDq]X7pfI3UXPM\"]PC)WE& %&$Ol=gNEkjk<=mohgQ)r'fTM"n\t7+Ye)"WGf#nV?\-48?G(XTiJQ`m`nfEuC];l^b,!b6Y*HV"p?BrHZ'A7Z>-Y3>B-e?<^Md')X&@r>T0j85MR1tisR %3FZ^CVu^VIEDKD9.iq>s-+8D@"S7$i=C;n`+:%*=[R?E+/12k.LoLt:%Z>6O@#8!)d>r,NO16)uSCSq1bn5_$SYJ*(7tW^u'9pta %`F]?`G %`-Y_!+?E]E.D+bpCMGE("]*giL#+$rO!Sb%=;$tE"ZMjM`o3M/4"c?,NmZ2o\F@p.=ec:]%#a#`IMVUA5:.sEX[,H.mgo@1[3QCuQ`R#sWEOIKmY8%!EfabaJ&e7QHAU*Im&[^"nRF:./B[gQcB^#XU%AIYr@&h8^6hQ#IR8$d %EhBhrhk5I$0b8]jRKg[m/EZO^2aoOAcW`S3K1)U<5on6Z6s0`m7jk/r%d?YTE`/!81brL$4rf[b=PVc?Md^gq`gVF''i+Kj4$c[0\?V#MHR)3_I#-SjiDhjAh"5=g0:VF(1@ikr_NXfTA0=Z=ZBlet`1+U*es(4]koU\MY>\>C:dq %"TrZ.c(XgsY4]*/*p;,OfLFlTW%9F8:9E!#2Xn=:$.ZEYaDGg(RjHtSbC[aINU20OHIq;f[V=$(_IK']h\O&rVq,.@JOI])KboLj!V4hZ+oiN`Y %Ts60_147-N`-XoE>snF>U-rbN+chW!Y"Q0J6nd-0oLtK."0H(Em_1\o3&K-ltnhE5eD[LMJa[af.8O/^Gr/uc/TV3'=Kqi/lq(u/I='cEp0ZQ2(3$S`J+Ro9:>TE`_dmgMCYQ$HkN0.CX/XG([1D1>Q$+>fW*u6X[8>sCT'WCrEjrFsgdCC/5/=9r %m7=%qek4cZ\Z3>[`$:=[?W3M!p=4Jjg.7D"4NJ/2nheY4I9#`)V2M9]b9jMO0Oh,V$P@i;:a$=8?&<\lK&$c)$+I-O^$WRVCn[&Y %n.$=dG4]-f"Au*0W]&Sq2MGNZ9Eiu2^YhL:C,*rdp!<,ZUdeL\5&,m3lT9UNpppOQNS+)hkNH4'p$L_5Gr.!eWp'N=RZ<%.D2Dq; %f"\TR4au8YeDR+F_&ONjfe87dK\n.$FM)P>EAs4a9d6ZAfG\.YO[2/cPC1;_g*uB:F;=toA/2]04`)[O]]jo'N %=_fh_er@:Rf"*Zh"dS;i>^+bf)!h.9=;+op=D@q#[5-7K:.Jlk-Y/s.Kbieg(h_UUOX'G?`%)Lfgc/hsbjcb-@UP%D(N'I2EkoKf %M13@oNau06_'ODPP^d(m[UW,qhlZ %Q+:KfN5bS^j+a94O&>-jecV4_7I!%?m+[E6+*Uu.,Z2)d/n2X>H\Q4\7JH7R:kk#g+tp"@Ht)[R*?uahhm(.sc33>`6E:Z7#!$u^ %8KVQ-oT$n(&Y_^E(?&DLk"0YZTCqg*i:&aU=tpi.5!5bdIprV?jLX-;/\B9%LW1(+mN)%2jmac%39Q0D/:ktn3j=[@0s`MTr\STdM/PZ^dj0-bP$_DsBTa;j2*eXrW;XbJjO`-TeG4J+BaSp+btkJ,VeS[+?"5>r=69fTOJpd%7Bma(6u@O?Hhsqm1tnQTHG,B\@6g\gh8kOR"XF %r,`^QQ\>sfJcQtg^2]U$*C$=(i&$[nQ9i%4Ei3J=7LCkK#aa)d'k4C'gq:f9(aG-2LF\t-FZ %U%96cc)Ib6366q\S9Vnnml0c^l[Ea:fOid6D=h6]h8&6Q2KqPV0A]gAOR;:mbMJ?/A9fda[nur=A\,O9mp&-M1KtDeX %fG>&Un_)g+"cN?QeTZLPcC#B-hQTU0W$B&:o8>tcf*e62+Rf\ %5,jnQi%3!^[[>)on[!"MU`cZ,*ft>okXT2;2&8)l %b<-(W_6C]Pg09$L=+9B^mq7[OaJB`>%tme_HHTr5fB8i`Vh&bh"ETHfS^P$rV\CFNNbB/c(#K^=IL:^k68<%k.\fhJNQX\2'gXP(-afec%A8iU2%$5=R`bU3+d"F#>MO*UM5g0iMn?#VV' %KbSdR>>WdZ8;8$+l#dFNI'7HeD#7[70D0j.=FY)a9shB.gF&c]FF\DFA8aaOIH/4^Z8%<'QXB7r\MXp=jNPC3Y3,82j]>_\][S)h %=tQ#JeP^+YAS!.o-`k0O.[:*+4m^R5Mi)GQ*jS5pKG*9(`&H*XgFrKh+E?kgKf3EPQ3(2XpplA %0EIClCn5Lr<(>&e#?DNL_WjJ/E(`T5VuVq3qWmGbo?.6.UNbPCW<`(qdDoPL548!LnI9,IiLaAH)Z88IfOQeEi@OMtnrO(-?5X^$ %gfl'hXI'H0O%\S@`m!5uXR4?"D.akNpEJ;[-JA#@=o+Em4sjl\1c[+Om<=ID$H7-g8hWXs"IZ1pld;Dl$;jdVVC0YIE9+7% %""Eb;?Ukc53=4M]qTWfL+KDWfhqo=R/C+CB>1&b/h'A6J5t %+s^Fs(Pr-:n8bjn\i?YocC]o1;_=DI:i#fu3Cce]DBXFtE:Mo"N`4t%]I^1&/@6s&VNMX8[`5tYcRjiH9))/Cs.W0=^5iES+,.d+t^k\X3Fcmr!TGZQU) %cWl*N^<5K#O+(%%k_JI-1lFGM(=:S5bKTBaSc-5ZmP\k)oZ4GOafe^HWs]-7I:05LKmS(9Wp]/$JLg%>gTJAqlRqWB\?c+>m51)I %Q*0t"eOQ@W[#H`fgsE=_RDUU7bUMFb=QnX6>RNrrS/0GA0grApCFF2!+lYh"=b,L`m(UUfGI=1C5DW`d4:R;4^ %#;u8oo)#'&l$lu^s,F,ZAk9sc.Bic:)>t)^>g2?Fb2g=.a'Y&*Et$keL]9,^s"])=B('*&mcVOeAsKiTT!c>rm%4L0EP2+IZ5YDD %lSn/N46Gg8?@fL",nK?t\sp[J@<@uA"Lf,ZoZ@Wbq<+aQa^#Tc7Hn,/8]qXYTa/Jd'_P?QD2PKhbu@qR9?=DgBr'3X0TrM5Pd;-XVar*;qIs=;uMrDX<(Cj'<0:QFl?,gH(0i>n"YO4 %kfM[Ef@a6]Ya9\1:9T!67b1:.?KgBt/pD9)eDXXjq$s]!N7W&;FY:.]qsKb)XaTf!(\r'0B!(D`5$lWV)D:9QH;-n+Dt3@l%Nu1* %6X^4>Vr$C.FqXWof6*0V\1!tE9I92FNfd(AP4(Tro@2\sK22sUX;ZJ\'2K'$2>G\ZI1?2CWe3sQTg1g8gmH(E2K+CV-?-`j %M8q''[Sned`e>\L^XJs\W]nWZWWo#G3HJ'jV/**?/u_l-YL+l#QPWV.DrRj]`)fptJo_,\E9d%fZc)SG-\Ec8MT^@I@0WH9cf]L\oj %+hd'tEti]hX0X790,J\Pe[t.)\[LLlCnse$@a9JrJ5U_KiuangR+XWMX$VWH29rIlZF\,\?fj;^dLS#%0RC'43.'-%R6,i*0B+^! %YLbdYLX*Hs/8%nZCu:=13p*X$i&n"elrRf?2a5OkSqg:fKbaIMFNU+M\`ZmLRS\:` %8>nXHV`[bOI-)%WUM5q4,fr`9B=X)dabqU#0"JpB<=%e>B@q9cr9-Oa8SR$q*:sa:i7Z$69ZS^/I,s)'hL9bm"r;4njNOom^QgS. %&"*H@(jHb3p3^7'hD#c6AhGt4l,hF/YO4H@g+"?B4kt$f^%a@b_QX %bT^3o7&5d?ZbnkO&4oWQ((Lp"imJWJm5nS1,Hdua2%7!.)&I%C9-YeO[N(Y&D`r0*l`0/[Y3(W=>qb%-\h)71Z[-Lr#.l?t5&tB( %V/,EMa##CGVp*AQl?':9a@S!&VbYBf]_0j0&pF-(WMWM?:1':UG$6HH\^PAgAY$C?-0RLScObQUVaF4GIcAEte?aMDJ^of93SK5B %j5^p@B;6H7HAE&!?V[g(d9@%VGqFer5MG5/mnA89"eAd=mV9C&COTF=q&AKMh`%_SC,7eJ0?X,Ljs:DH\tdTQK=k7/oPmm^2LGCi %fu$H*f>=R1/kq=rDD4 %>l%j1$nfY).:Sk2ELDo.1Y\Y7inhqVFKq&aAgnD<7dTI&0\4EeWdHVc\Qk!7GgsoVW-"He-cVf3I?k[IM-e6S0B87spb#mlki'rA %a7o#LSU5V)KmE/F(\_n"k*+4-s3:,bh;1*]?? %/Xj"6"#IlQ.:GJt@jMGDAr$ZL+Fg$DM]k6iiDD7a9[*$b>Yt7%a;nV)H,g0&*%Sr?cU?=ZB2aY52N45q8@,fLH(.b,RjDkc1CK;& %$TWK,`TbOJ#k:pl%8W7Co^(P.V-$=_.P*;#bRVi`g9O:EtYu.O`LRb?`/_K\If?9]NhjMI"0L%C[bjt\I=lR[M %X2FKa5O+P)iV]@=jX2*\V7"N(DZ$p?1s/XVJU55+rQGUcYLJ8+1M<$BbYla/.^9(%GM$*UEsmDFagEZ1rO';M0'De3bS"Gk_NG9D %ZPK7d3C1r=c=\XfS#BmC]/;>(:I`b[,O)(foM^a2pDEZX=moV17cSOd!<,3_:'\R/N#XaJ;RnANo.[Hdq/*^T5\P[)=deQB(*W@fO$;LVANAu!4 %j3[GkO-"ijoEBsP`7TD$dH&+%$EJm.1$[Vq*.V8,9r$HBohA[dMg9BMJD=o^=bQ_'Oac;[qqfih0ai]!M) %e0dHQM,/QZ;kNj7-U.40n3J;Fo@2^U>_>i'Osb%ePI9Jn>4Q2UH*Wb6K[u;6hl\?aXs`,092.SFG!5YE4o'7a^piVYioDQJj=0*L %.H9D;ioDQJj:`b5SkL>(-BY5(#+qeUjYqG,Dt'"Ki$lE,QVW*#q$m>rZ[;ZK-2`uk^MhXumgo+jGd6qpqPh-r52D";D7OVriQ?m^ %)'g+ErQlp$8QZn?L(oQR**pUicu0mNcF1K!WgufO,EIe?I4:"*B"i+E>X:P+oD>L?oV"IWraLrVc1XnDlZPD;Z^c!I'9:`UF"Hp`ZcaSr##uP6bTL5#VSo+-pm7VLQ!eJui(5\="I#,I<5\T@GG",/9NT6XBq&nm`rWW82A;[j<-r$u %l=]PM\FosYIkDLt2g/hq:l$7J-7n*Rb?Ll+N/?JFg7m#&!'.f3q[GW#9IFWK't5cqYt7*NYPD4isMX:)S'("L(.k2Y[Dk%,PC %n*4kEYGYsF69lOmkm]*l#Y6C`6j3PsrLP#n7[K1`l!lINqMbb?T\p4b7l21t;+Z])*@AA6;*-c&#=f53f+F$H_O3p8W$r^.KemT) %W%T,JL83&@QXEd0W/'jFSn^iqp.n@X#n&bYT]6CuplG9m`_P5kQ4Y.NNjY!.nA'srcZe3Epnn4f6qKL39 %G.F56s5-l/i0G-'^iEA3.=d?X@LeKJ9riW0mqdV:rlHi:Rp1gRo@4QhJa@=1iT#CS<4LE8:4YM*^";5gI)<^Ld0sNf\&2H`L,%<7 %@BME(R-X,nf9p'kolc]S1X)@rb&Kc9-fpeSa'Y^P?m%'q2MDTcip%oR'$Js8cH;[O4n1Ka`,9T=G.;#H=6?1W+SSAPQ]8l%Tp- %Xd"f\?7)t^(cAUXpUlTeKo@VD>q]`/+f!4$*e@^D.d26q[GMc@Xaad58i@u>!1ultGj,j3@mF]OR %S`Ya:RKNCcgo9(YXjlfB]nq]G!C<:LD;A:d*XAoBVO3.l9-.p5pa!Ji7l>j?dAdc+fLU^d[5Kj5BPfUPG8Cn$263nNj]jro]/211F0#buGMT48qZkZl$gR.L17\Jp$&6.*jc!A.A4"@'gHfWb)7JiiJ>]Re?+m_BA) %"uc4f"$b!K\=H=Q`[S>JZ!3*6H>_cC/*E#S@)c0T&2Wnb&Z_80K-3lLLg>)+CcYP'"gro\Kp1k2@L3]0k7JS%Z#tr3;_6^5([IAA %Z&Q.CA^iur9k7`,(*fb[b.=]kNr_pE[>&joR;YRu:8MXr(%P8$W?,rEA!2QeC:"GkEk'9p@p46f49CcLk %H-=K[[d)mLC""0S]=EK:C=ElM6nIfUGWZc9njAREi<#g7MOP^W=XlNbSr^.Q_QK2[FSjY[6-j-&aDpSrY_N-!"=L:>! %nLK31O5oGDU-T0--&-lA.CGL).qMcHWpI`7pm6&oND(3n%U6ng!0<0i#5J2D=j6&GmUt+DkQsPM&,L\#k2.UFE@B[qE4]%p19jZF %PbN[n/)i,.'Z4,OV'p\]+M4L'7l(\nJ&X_?Yj%bqArO4)h.GTWk?KsXTIT27ihJ\Q45O[uogqS%A=*d"XHF3*7-'.0&s4F`8jntu %1_F^?!ej%bE#oohe&)Is#*lM3MajOGO-:LBbE8#,VJ&BHH:a$X(Str6elB&W)s,N2mW(&YlRG'D!t/al0_3EM.SgL[XEk%%8Z4$^ %,oN]']Q,Bm*p^s'k/iKK,*O %Oq#fuJ@\0Y5Ef#r3"6u<6kY:qM"i0_Em^']>&ogfTAU,3f922@Ec8DAg%e0#c9bQXcq7i2TWn-;Vjom? %k=9![d3HW)3o+N$-HXlhZShDL9k+aj&Ar003,ZgaF=B^Ye;StM4ADXc*]'aVu=>PDs"P]_mYZaK:$WdZ'`6]rQF+@ega'1$Z.< %n&Hl[C>.0B2/o#k\'Tb6pgm*,H1i&$;_mQp_)h4m=H)4LLI?N+a/InqbbO;YCs?d``MbG!X,icYY/aV-qA@,rmgW6fKqEQZo9GO8 %bTt``;6;AFZ?pX(D42GW;<264kajN?bRB;;Y39#A3R<7VA"n^nZ9_?7qPYh2%:d)>M^=:+n&B_X5S&h.c8epL0$sL0RM7((H<-3Y %Z8PM;>\Nk[26"9*6`F>6gQKheF5L&_g&oAma0i!E2QsH1>c@7mh%R8@:RpgD=XN4sm)QGCa2js[P^$`:_F\=XV=Bm``\G&6)(2\J %B[B!HI#-J$X'JP7M4`U_7@Q)J@@"kmXVeK/9+B.:koNI/_+R?DC(_%;Yd]ej/(p?:&sW)2nits2#HE$6MPI,G]Ut&2DT"<3YL^P)RSfiU#Y@60KL]5%;,i5>P8K7AoLn?4"\\4(+Lps.m,jV %c`qbUbO^7PGppi5iYTn05JOXH4.Jc#BEh?*l.Y+4baeh)b635FW*9!k;_0Q`]i`L`i0d_TL=[bc94uVq@,RV3I4b:s"a8N`p#%P %^;f&c4icTO$K?GJN+'t4`/\4'`8e(I1P8Dj[Tj3Og.g?0Q>/?u-ieGmO(u/@H)sQ(!+%WPcDDV&_/#lO,M#DcM,4;B*`[)GTS5M7 %>:L1s_lV1S1k2MmC'E_%qP;E2Do//P'@kg.D;:j_F6oP<#$kCeX@=/XJ/qX2VAl#X0O_VIfod?;>nM66A+&nR[5FtdQ5Rln'#EhT %B73@s/:(rPs,G]ja16T8c,BZ%oACR)KGD-IB_Z"-^R[^BkSa1Ra %$:W94Gg]pis.;+MS^f:k5r/sB`\PY*2r=@cBsmWF^YuI_\%(Vigb"!pGPmF-m$S2bQ.Rl=mOBAq:Wgh:6]FQmj<9:liOG#Md3C`4 %+d"NdfemGP%#:2AZ4'0.7a8-S!,!a[%KE_^#=gaXc@-3%9NRL,Ik`HMH!E(ni;2]W4QbV##(N?O2fhD=ZUKiK,0!I%AM>Ec;ta1h %A#da-V;e("+dI/;Fqo1da07@@-L,E=Njr#R75EjGpl*,:bSYo.jM9%bdJRekcaVn]1[H@lkFKVk8Xu6fWc>\?`iMn7()GH))\g5^ %P-t!=;M4M'LJh`\0$;Hj>e09'A`KB#oo5,?.;d@tnLr`=5%q6'`G>N-7"4$#b]qmXk9uC0E,k?1\U6!Y_opqIK@dYn:pca`5^2BY %XTb!RTp@.1g]aj\qfh!KOuAM+-;7n58J)75q1XoO=]RRb#&28N'uN5?Cc,<9;p5FO9j?A/(rfM`@krcn7GsKh`afj&)`-**+d.)_ %\o-P#\paaPiEnJrR_-->GKcKJ[fp&e`IY@XVdq&J^RF%O %H!RtjA^"^K"C5ot;cSm0r,[U-c;)W&Fq@-#fVgm<%,k:=#oP,5[rH=IZ%r/BR*/Vj7S>@sm`)4A$mV?Q^qf<9PRuD`@FnL+id+g7 %j,1[ejBYFh_NghDGTo+sgI>0#J[+=;*g"r+G2iTdIO)bU>P()iG#ZCi^YtBgp>8k]*_@7Dje>tTBVC4,hbLq31]:dt4mnX(dC0rK %AN]`O]lVfidF`WC7"NOnR?i%P/MI.cH(!L;Gj_$S9,H-%Hd(94,'s*DVQaoIfO8=t'4%b7k=Y)gCl1R(V0;h!U$"$_u %KJN %oP?W8T_",9o-B-M1o00Y %bht?T92G40k6GBRu$=1kN'OPUC9NJ,>@9^LLa#IC!"rV %l#L^ea.4]&o2M]A=p/lqEBYqOL3V%a.a\idSi%%PI:Y+YMhrL5fsj._I=9u&B?KKN+SWhq`Zc4@+f=!6,$Np;hW\F1@m0A,%S(Wg %5&e7__21SX3.C8d_3o0#i.OX1!/"u#M/F)jM'=@K-Eos*c_rtfABBnN?R:@!ft=lG+bs'U>_J'BXi3M^AL:%9(jCP %*CM]1l$ek4Dj\ReP([*WPC,S?qrNu[K!#Y)`SHaud(L'UGFW7qqQ^^Qp4-d8-(@\t=tMTm=FeJ)CFK9jJ[6hEC+gY+*')JSf=-Nr %k)mTnCrYP$Y]QbUO#L0]#:"^P>QdV\6B>!5?PL$4?K\cCY,Rm)6D=M*rESt\,AZXH>:hl(jpQj;8OY:A[SO$>M3h;CZk?:<%bETFbKd>R0;V=NY7IWX[* %\_pC%5Ohr(&`C$8_QUiQ%ugN>5I>\Idso2L`?b%J3\jTrc)Z>02G?relX<;X,0;sf1nIjV77j@'8Jg"BM]!.M>'5o#K8Y+)2L3BQ\^R?9 %GOne%L:'N\$CR+]FYY65HRDYhNIm3oM=[K-583<@AbqZkDpA^]Qef[-#2,C&&t=6NcFTo-0f>GePrtd'22IpM>^Zk<20p$H %b56Ig@if($[ad/?CSj:7!U4pPo+tMfH2>ge,uHC2L+N6=D]D!*V/K=a1ZNLMNkT(Z^7P(?5KiqL>3>cGQ_3_UG.r6B5-rDVG<%J: %G4s6*.A+-p-f8)A\WDQl>aL$1qNQ?i;X=;#'%bs/:t.Tg,O*OJ_5m0@"]nO.;bA8\W7l5i6[R1oAA=O&lfoks/t=00B"FEDB3'QA %ihda^9T!07PZ`tXoto\`V8M:bB]])!YRsUr=a3jE67K6_,dZErISK@Z'e^6>"D`,k:[ %=Z$\9*e1;Sf,L;A9Ac8mjHp>HZ?[,BI8dD2K7<`kY=R/!hNoMPHK.#k_\Fb]u0X?$Y@LPa:]ADm&?pjb']:^A6%NG@,cCrU/_3GMbQ12Oo9W %(Zm[6_`*:`\bkmV%tFNB`Q+%C5.p4%TUUD`=H$P_pFlPaIV`SIc=$I75IWlVC]!4C2:0+qKUZ?+r9OBQDSCh9^GF3)-rY$D9aJ2_ %2DN2-]J\#_NStXtn,`H7!@E!k!;@8;@/BZ1p;5]*lBApWhd`3r$O8>t3S;t*.#m2!i-[Vs<,VbMD]=FOHYnKp,^hk9T]q!=WO(`IoFkuH7g:`n5$*0C9>>7q$f6Go`(&cjKp!R,A"^<=Gq(FWBZ[YbRG-^Z&t)p/&&@HREle?dg=tmJ!)$h%;Za%6d)OYkL+M`fGT1LHVu]Q$ %c*9"sE.$PtPR@OsqiG9de]dEONK(s_TSBAB+K+uc4E`:S#fk.Z63"DG18e$[k;536CT2@DQ=.g?i^%-nE[f^R`S]*Y6ltQe,U+g!2k-IX2C8-gSQT2 %DsM6ZGJ2[776oic^A1R:%j)=c?[dD$rnWJCJ%k`aJU[+t%H>[2?f(^YY6O?iKioGOIc1J@`M.q0uSms0qsEREU=W %Xmt2lPU6OHAMBlMBnTtG3'Zg'Jdmu!J/aXDIr.o`dqZD.s+WlSakX %+l]j5/Go^f6u??73!?_06V]pP/NZN/?o1e180;pXg-.)#G=cqd"YNU*`.A@Ff(O2)\.)N5(`hg. %NnUNQs6ODf')_Hhk@q6so!Ut18E-M/2)csb5X_G#!;$TK+Z%JG4Cf?:FLSE0&-?aeLUq>NCPiN'dK;a"lQcorp:`ER)^V!#`V.dq %*T9M%kG!.I2Kk8WE1?h8NO:"=1)4RLLb\K?VMJp?Q4bg4K;G4W-__"Y(G^-1X`]hQW&(uH('M(:Q'(LD%O?9H4V)e"r;`]agMR?" %9EK$o.1JZu*GAC7K>+E1$NT._am>j*V'7Snop$*h@A3t[cA@j2_plCJ=Bj8tcMApET*"\&A&4+ggJ %Q?6b)6,*ksiM6FrULO"/aVCm;*mk=(ntO40m0!m/%cHMkE'':^Nk?XNOYbqPa@c>]eP[UZYeU8Z`oSVk0*XsAD$'g6-Q[5OF/oVu %WP,st;a6tt.<=77nSeZO)HcXVQNOQf,+VGrCP0c\`GGtq;BSiX9dI)6-S.L3oc?ob#`&)k/QWLf85p"Wi?IfdJYTPLQPm@m[Y\PF %`aFS=j%V!.`(56+A2.<NGrGE`BO+KV8Ab>6UFa`aS4:@F4(XmOYXLcPbMaS&a2r5[^Ap:N'al49JaA"s@R^W*KCMR4r"r#`'r2 %!_u<*BS[jR%a;XF^t_j3r=/io6QcgP!ENF##:Mrl`"16ZP>l*:*.WGM)Y,Z1-Gcl4UH!)j`<^4OZ04##$1K:H:i*%eU0D@`PFEE, %Jr;VKagE22^iGCHj8`e0(H?mt%Fbpm(d\pEQ&bNg[0'/Y^Lig8^YruA4IRU3Zlt2B]p&[lg0Nt(8glNn(Dc$\D %k6>BF]UPfG"_tZK9VNK*Zl]qedt1.;AHEWF%Int4?r@)^^gf_dq)O(U#i&c'8I^i5]G-d?83DA(-LYN440Qo+q:hiNk^*`UU_1s( %#t3dr5_jY8kQ"IA!]R.)h//m*i\&_P(>)*r)N^Ne8k'k?7a$LLoqALW6#R^C%#gDa\M4W6b7LC]+WD80?Ks-+V$NIK,VoqKSUmSO %K-$iD)lR'K:%?_acph6LC4VpM^^u3C<*3@N^4G[iH_1F-"La0YWs4_#10%;me\Kg+g,ar/?UgPo`UaEk?kFf7P)cRU(,]KtP=Q;mQthc*L;QP# %LfO)3`,_Tt\"0fklQ+A%;#du!Ms&Wgbh?JrN?Y.g1+clNO[7B7$)$Jb=gGn*m;@N_54)ME"E,Isdt"jp(T)VkD/N_GcmNQ.!a)dF %:.pI3!'8ddOQb`eIrJ]C"Ro"#m/d:oMQheCN?HDn"%krLWXu'S4"fdh&LM"2qW./L7EHX#Q9FY^E8>]LfF7_g!SC*G<5;!f%agZA5tg.44>$.t;^)k?O-%.$<,F_r=! %6.n1T-FQrLXEK4#-3:EG#D$>i!3i3m"BeTn1H8p74$,nc[%6k],[bYuj8fa+Bf8^E/sZ\M*5WW$AI]+WV%d@8Cs`HM'flqp[qNS2 %A5`Q,[NSeqkU&UVe?2"GA]Z>XE>G;29IKRlmtpo+iO:9.<>O@$(UJ<%YfM'WPUNE$MM#^3>I!45F'9TOph"c,,$.UM?ka\Xq:f_`Gb]$"Emiqm':Hf4d2Z\90j%'DAZJ%Q?"uiLWWe']--ZCD%U&hU+j15YJT[F""L9um1be'J0"qFHuD,r@k0F&V80"lgn %8\,%M),GK$OepMtZ(s=P7sa+([n.<'IEsd< %'hg/RKNNK5b)qk>h#m'AZLJ:6-o@U#Jha)-5mM?A,tG5Q`tBXL(hko0;8^BYQ#8_2&6(Y)@;@Z\D#$UG).so89?de[fLZ0/l*3kD %3qs>4d?qL54@Cc?;TbDnjA]u(8:;VUAN\3%F&Qe0#Um+jGa$\@D7-C+EoOP>S,c4Ar;ffBP%-YT1BL)KMHiDXShmlJTQ5?XnSrO) %$c^&?69qTo++.VX,_61%,A/$A)rsHp(eNA6%Y3.Tl>pi;q_[1BLFsf&Lbp=^nJ?`^g&3N %i#FTS\p*86S_,A<0?9@G4KR9pHP(r="?J2P:gt;lX6t$0.lO.dR=N!Yq'7ON!okP,"a"aJG!"kY#6XWBM,8b90#(!nNd)gB"4#*N %<)9)4ciZG7W[1.Em"'&qd%:h?'GA?p,-XbXB+sTh^ogQb/:]QrCfFCK$Fs,^&51/KKJBET]j_b3U(o/dRJgSim %LV,snFuH3QmSe#2ME86k,="NU%Zl3U"*U[P%#"nCScDd&jb-`o?4OfVMG@C,.geWR>GRG^8H\*HGs@C*jS>U,8BjRaORs7,Z]X/+ %Kd"c7e%3,nhQjhMTZZC?SPa>jF1&>Cq>=Y.]@m84:gSl^S.j7m@d?%5U)a8;1Pkt!N]+9c'UVLSglB1Q%&f#RAJ/N%9nM.XC',c) %oWFGaf(Fh$-8sfqk)#ptaKXSPo9kf#Z5<+O]HYKhZXRH\i)mODJOcb,?H#MDP\[)$Kq.6!n]Wk+P_0!n,?8h\V%%#A&M2\#oM5`* %Fe+>s#\4N8QtL]WWe*S#[>Kh_"?^e,6suQr(C2BV@,asiZD7`0;e:G;.2sYR:;YH^&F>>-J;)KN3nqFU!72A-Nkg0;>6M5H$q5lo %'ZNHX>$5[1C8dOg)1k[`"uP>,Q[_G8F. %?!Qe[;Zif]bS*X0K2C/[#!,r>#"-QMXXi/AW2cGSQ/*;s0P7?j8!QPb0\BV>1=g8Bl^/6%c_< %V:?j')o+nB$K5ed5:JItFB/Jtf^Y2g#%FU\/t]+$#Ut<7T]rrej"R`O(o@MQbbDsk@%Wto%:ma)2M)+T/UQ=X7ZJlXMe06=HWNnN %9FW8`:=PAe6X^(ScBn>_C7kkpplWD(0f79fY-K$T3d!1XQI9.T%_`js1458Yi/*4Y[Na %7"l`d$$HD0i0/,9PLJ\laiLOe0sh^`]c[S%UHFUoY^HZ&!4/Xa#pDkBUt%b!WfDYOeM6%Q5oX3,H^I5\M7jCF5dH9:L&nOW*hgmA %,4f+%EEqVY!!#QaMJ0r(J\H3q^j=:60i&:K[$$i:KbZ-*2G20"9/QD8\gk^%FMs8ad6kX0`@8mfMUdunan0fj>>'fOlFgId%Xn#e %L'@.]_2=E`\"WO'm5Z`?7sR3H4[UA*JPB[?m:EsnDrj[3%4YAAU8FS&]Z;O?l$`#Md88.mfpZ5BKQE2A?n&k5AU-MpT)`6q$:$S)&8>Ra*LpKU%XfXrZdLF)SaP/WPDu/inO\&B %H88LY;$4]Mg>7A)&kEuXVGZT6TkPO[ZBuiTiPVCogch.LfWqOe!"tq;HGZb_W&S,r4Bf]4:$H;4h %MRV"]f`E;\b!Ah31BdP?8Q`Alp9?krO<.EAC(qU)'SN^C,SMpiLOY1r%B1G+]IqPSeQ5IO>c[?7aV-Hb]\%3(JfFN9Y*58:m_-94 %&O]q3dAT=&S7$F+.fnhC;#9UL)?qfWnYtTb2'_[j7$%YH^.XD?"Ld<[LmAYX#+65M4[G%t8CMY1k98u=S(6!925VPepL7nYOR7Z,7.ET+sKl:_6dF!JWOcPDkNWq;R#MR_Wc5*6*&1f,s-VU=&EaYQinL!X*oWerq)3b?NjN7]P_gjHE-JK'Da=u0U %_"2BETOq1(Lg_-&QV7I=iqP.@i:7kDQ:ZX-qNV7CNI23Ik_guQR^T`#kClUio.J.p190CI(q^h?,$@4,Wo=I3_),gq3\nuXUK*[L %M<8NfW'k&ZnCE,aN9DW.Yd;[:;@8U[[Wpq?;t%OFZbt0`1YX23Ur"^&G0d"n]%@>T"td<\jtfg0S&n$U22QXl[(9Bsjc5Tm%Hu>0 %&#g?GK3++dNoCp5PX=u*P6QmZG0102DN_AdV(X&V?UQ7uY#lQD*7VD00Il<+:e"IHYaI$ak(VfQR!;;UJSYaI%QG'TTrp!cl6Q1: %;0FrP*WbRP;JT?t?is$#-Dp["rQVX\]#WLcV$e'e5k.n#*=P*,2j,]:/;/9-Rh-:87SOfP.o.WQrD-m062kD/mZBiO4_:2kHkWrptjmk/YYU!?QJ %jFeK%OA:mJ-9\.O*E,s(VpmmWVof,cg\A(U8fE`*Rj9VjZ:[t4@"f*K2:Hfr-e<)=phAA2igPHbV,!jU3L.8mot6gg"ADrF26$9X %^p]jtA=<"]446,?dO5?Z,-$FT!R-Z[U[d1N`fn1V/O.@cJ12nSBEM0JQ$6]f:TN7FHrZh_TGeZY5m1dH:24/b;5[(R5Km^AXAl@T %W=\^&W"XEO:g3'_&Y5eT.LjIsFQ'_9)$)OF79mU+JaUCjl9>7QEFJ'1aBP)n[`D.J?JSD9]N&oZ3X:@6$.M4EYGg%L66lqh<[;Gm %+$<(j.MoCK'\;@K$AE[[!NTB/'p0Y%<$kXAZNfg;1eL"9*-CnBc4]T$;l=Cr)ji4>!3X76HOr(,LauLEkUNj9.PYV0#Tejp$L3e8 %6J1[e:i$m`Hl%T!aPH>QP1E`QXC??@"es'mlhMn6Xp?CSrjo!n@"9//fQ?)T8S!a>)L# %3Ku"+JWJf_,ITpb*gCu&"J.1b51*]-0i\Z"P'BT@4Z!:FX$'W97PDBhp8;sCJ!W`e;@fclogc/'?f./OSKF!pX)8`W]X]+E80h#R %_W69GC?utc5R-"DIq0#;:/u/YU:GoV3K'B8Q2!hIXI_[UO]K*[Z5BM@Qt0I#B96`;j4#>23g7sP]#?3)"BqG'b$3VR)]Wf`2k"jpt>-:.*3Bn.Wi\oq`$QpuqOTWYS%!_9:^"6(EO7N`DM %W)X,UAp*>:b%e('o0sVWZ(($+$bOEb7%'F/(WFHf4YdR?8-Q@bT1Z\X%#@_]*#8ZbQ[Qg86fZSc3>dOjJ`"g%$`fkfX[aY %L6T2o=cXH,P+nXeL.R\XkQ/UeaihW+U&cRSXAHD0dhGATiqh,I8mst:!k&j)4saFpopc^%S^mC,b!V+1b-VRNZ>\-KPDL",eZbq< %3ARW+ZJ9I%KC#XI!'LocN"S)5<+C[T2n3:1T/5-)1N2M7EZ?.R<#870=;"d3I3qM1JASu\e>]2@4!M %:1#?.+eTL=,WlO\1XAUk(kk^WfE/*C]sMR5kY!ps$ikR6JATka)4*BJJ]a=e5.R_s]Z-Y]Tq4\[N4p"]WYd>(-GYR1-92`i4TcHuh1U&;u %R7B*I5""XW7iL@Y^eR&7(("^mBjgI9K")"GL]PSj!*N!X@$ONEkGsehFo!PV8^^A.`e@ArXGrjf6S:/5GVob#S2H.lAW+D.@^1$jZ@[J( %UansnPUEXDfLZtm'B-k\'VcI+#Se'_1-%Ws"%?c7:pI=0>Bg"52_YES/tm@M@jG0;p>!!V!c='"7'AOa+DFYu;JP*>rEp6(5d*0# %G>;Gg]UE`Enct@'(5V.8*4eD-+T"F7L8bE7AFduicJZL#f81LJd"@_Y`IJld!tBXc,ZL8_?+PnAMoOtet\%Bh2 %dDItCncngHc@SJu_'!shL89\oRX>UdNGK)oZT+J5>9?A:1GIuC]2k\>\fZ5Ff-p3-(CdP*-99n'YY$6.F>$D`;>(E%rn-I!aqOsg %\uoc"8'sn5-+"Vfh/#)t,AIWc-9k990^s,@CCYt(Tkm/kPN;"eTqa>d+KGG[\CjNDaVm"o2"t+;7_"%d,]^,_\=28)%E(\FQ7Q.b %YTO6?DC6iQe.epI"Grj"0`^^=ankL7-(o<7!fee54Tm78OoiIV[&Z/?%NCcl7'Ii!T[>Tt6'#kOm:?nC-dbMLHN*%JXon(l6?po+ %^#pdrn`n9+*$1%[DK5fB[l4@78P)Bc:EH.LOs',.F!FP#--7$"@JV[Mhr#;PFAP5Y([g28(8ek`.Cu:0,=2Um[BNE@`5YN^6hB?r()" %i!Q3Md6L+W$92e=#^`MFr`>YS\SD2(iYXJNOVJeuNnC(l2Mh]i.FYoX9d6Q;53_<(P\K6;1c&!9g4_k>t_b"bM1Cu`Q%@r-%Ues)c0RWL2B=MZM4`P6"+0DQ$YA=a,!BEg %VQ(FXkQ02(f1RGgE/kKNXi0362GAsqJW6&dXs,Jc;,(GVpN+Bjae4GW0$c?(cjoDT[##a4HuRP!M5Z7TiO&).MXiTP+?%JTP0^T`k>r&X;X\3%#9KB %p='GJ+aC`W#e(9h3_D"1jItF&DLnf"QW@#eF<<>2R?0gR=iX?@BJt]UH"*)FN'XLTcJ.Ua-[C>dW>\inN;6ZY=3V`dYmSe\>XWXg %)P?3INDS'<8#Gp:J#1gErM9E%<[k_,Z&46*+cnO0"g]]:qlWZWp^HH,!CD%cZO8GXX>5%E;q_%m#-8i96QVK=')W!ea33bY^nKiC %^-[R2kV*BB\==Ul\")f*Qj>e+>2>QXH(Mm<_6YDkg;c!WmH>O1>n0SJJ1*G'B)pCec=VSMZPhu28Sed#P"lb:Y;r)'<72["QJ#\h %#)<&]FsH1OeHf58jr!9t!3kTC;#$2neB^&rJ!#\^nT"3;#5+SHV<\]&-j4)/:+k*/)>^ET9(.TWq43(8\*[uo5!f2TScKEUAg;eY %G2W=_Z"O^IpOOlXSUAg.!P^?r)fV^f+U>[hGL>No:*F*4'6k;jP;WWg#i`Xo0`b3_?l]YpQ!J3b8qM4aYh3e7dF@Y]#4KtX4.1r- %>]u:HfIi>GHe\PWD;"oZ[FcJkQj`"%HHT^A(RWm;9u'.7>&pJo=?ukBrl.&506'((7kbub=g`,Y77"YrOOf@.1Xri\^=ECGln;Kt %5EREG-VXgVOU^@u$G`W3BLF8/DrncU?Q*;g7g*T9!D-Raer:P$-](e&#l9jR]Ur\#YmCUW7N8l"_"!ABrphKo&7t9rCn0Eo0$o.D %\:]ukiR-@nOp2hoiu&,2*\Z2/d\kHK+iSF^q96=E'_4\R>Rul/H(=heE`U4'E5R\k"50_f1G]ek>=q8\QY[^0(C^FMooisr7f[RC %+PtbM=66cmf+.d0%+#DXKR`j %$I;#%s/.CXR5$$9W1g>6O;7in*)s0s_%:u0$&'V0i6E87ScoG>it2\bR+[hK,'_+;cUK['TG65gK0[Bq:'76>Xu*rV9ABeR4o.^] %PZ&B9X]Whh)WH#B*+`k1EAZnXl!d_X`"jl4'ne?V<'o0@Ii<1$"\*T:H*WRA-;&\B)?3$LrGhEBQ-dFl1^a9Q]\>k`8 %&9)cf./[L0UDRkDAJfAZ5h8G;c3a)?N75=Pa.b=`:.U%a^8\Yo$(lP.O\R1bmQ9B_b=^:)!?uh^'oWeg!i%bijoN\l+;!uhg%cI* %7DP(%'o_a4"RUGidKAlQXg2pkG`pFSS/&np;PNe+AC#DK7h*[iPn?m%39W0n:eHsZ9tfV0%WbY+$O,i&nKf=A]pQrG+_Uj&NPd^W %aF5BDLaP9saji-H?ZHD(D$oN)'otJ;T>6)V0J$!keo9%I5k6-I'OCWhML'\Mf%<<.kT`5] %*pd*M`sN!._8M00#i%im<#.8Q0X(mk0Y=fipIgc0M:.[[]X"CcM)I3R+\:340uW*cfrHl"`$=oI]B+!LK?TYOQeXjH,5$piAEok+ %Kqc@*#6cFZO9T0OHr%scA-B3BHo2peMTK0J)l:aY7u??ELFRhq<"6s%OU@E+0/98L%JFf&p^;34PJjVMe"J49/3JcB?QgT&=L*<5MOu0rj_W8Oc-qKA;\=[M6IpL>UMt]m//sHGISGA[=SgU+0os]ND %^kl\(F/SbLS@De>3Dn"@MB[;[c-ekp$GK@BO#A;2RrW'\R!(hr;B3am&8hh)!m4YDaA=;*Bsk7"]%u<*PjoR`b^_L8FA&$S %f_%M-N:$\3PFH]"KI^;c4-0(=H$%2p?_dB\O]s480rTeVElAWgJeWAt8=634,TsYkjf5sOWbeN/"\0eKW-D4?`W60/jW+HRK,^$% %Q0%oG9,H]a-grA&KsS,'9M:=mQ"q*iqpW^Z*N:GM+YfgX4oqQSUO`KC-d8)`>oIBm0A!%BGn_Rcj@6)<2'.W2:#ff(g4M0IiP\<* %J?Q,%WKu//17?$Hg%LZ:-dSnGq9g-_KT:*J%6Df]D?,)fZiR!tPf&bTK+rur<8m0I07gS+%WDnuJ?l)/bG%]5K#a9p0XPfA6?&\n %6./20iqSHpg61#uAfX"fX:,Jfe:>3PWb@]N>h%lSd4gYK]FRq/1VD8$k?k!>=Tt*O&)K)T%JaP5FNB>UaXK%H>:,A/o>?=!0gB.s %<44YRDb:+kPGpm#,aOs!dfW8Fgq?=MPcJ)Cj%_m"m1=rfPApVNr]+kGhI)+jTc6*2hCol!.c03W1Fn(6D5qtT/'<0f_BEG5O!X9) %%1$LY#Nr2HXj/GF@fo*us"D0o"02[qRoRuKJe'bbN9qDM.+#J*2)_i>b1C)HS+$I#Z0 %.">FNPuMl^+HfS+;HdZ5lH?'7XNP3`"M18R]qS).TX&6qTQX2aHIaReM*2(Z2`.18EH**D8&g-]hk7NaJ<3'*32-cT[&9GT.9SYCY_[UPJkjlCC=cH(#V_sP_@SUea5K,Gi-ht(ko""3,&?rmcS@>#Hc7>(kAm8JC6&s %=j?8QA8A=d$guN@>_"#aO& %.OI=c1\;AJrN$E!^>Q:5n%3P(fO6RZ3R2@N5a9dZ+>?f93$js5<\,rNC.`";kNF5M7*3FP.$B_J"?t<=Gm6- %p$FmD]=hn_:0r3*M7Dd@db1JM*-PA-XD:7U%s,h3hZ4!cd:o&8@up=Qe7)12DD=s*C%gWUFh>0e#d9TID"42i7!7a>9nj+d"%lC0 %<\%;=k2`UU&I(i0\WgZ21K %S]KhYk!6'bR8d6K9R*BJfLm?a,EN*j=LIH5$kI`Qdq.TA#uHAl+d6ElDTM,]^b@pn0\Q$,,`2RLD(B5I-MMT;be>5ADO&I/fk=#Y %P8>Sp_2F]1cqa%J\1&kj#@J[(E%s?Z3/TZ(&!U"Gk`aQlJBIU\1/RA@*+D"ZKnpAR+Qnh]J7GDMTN+*d\$DZh^R,3n3ic.j&KQf1 %W<(A%1I.MIiXCkZF?^p3'+daVrT1L8Ij4A-lMIB3lO\U7QW?UMs4XS7"JW,5,o=B>&B^Q2LB0Z_mdKV`LYK<46;l*EPJCU>( %%c#1_h-I%j>f!i9m+KgJU6I&Ffb,.7f\0tGagKf'!J$jKBi59E!D3mt[cX9d`rpXDJ12okk)!MP(@C/d0;_hAi/*\qI_f%1RKNm0 %m#$SD[cc@qmqWg-p&-5ZU=.OiBJnOE"T.>WHCZ %R-6(8TEq^[(HU(5L:GSN'h;Y-R_C]U]eP!je'3Hi0[;eF3QL.6q9^["!_uGVI@!mFUce_#Q:aAH'.&)H$(_BZPG[l@,JY7Z2/m8g^bk(/Xb$0#]9JsC)P/%J_El`bDe_PAObcE1a>T4q=KGGbr(N=oc>1kBA %hV&6@UZRIOls6ks"kK=so3=B#65ZATSGRY4P'-[.\IRVrX;]h#?bQoV9Gqi]d,[c:P^X+M[=cm)Q<,A1<$r%Q]fqt@//$b)-%ATTp0]e*Fp]\7Mb$C`"+60o-$;">>j'Ijn\Ht`TK%a\Wgq'Zs=ff<_a[(rnXI0Q\# %SSs23[81e^iije=TGSJam5':nYhq7=YAP$u&9B`p4cjePJo<0Vi9C^6r_J4@[4U:./*N6;]qTX5j^/oaBjCcB%-Qt@<3TbBtn#c._oB1j1Tq.r[E %V6D*^XK@W;,qfuE0=n3+B:-npbUOCV$Xil&HDBgL/8N1@#cl:HOkN0BQ[<38,G-ASN;uG0TTA$o[%]NG#B9GgA3%!EfN;Wm.reoH %7/ZRLL.gGjCDL5Q\G$ZA_ODX6/HKYrE1&ZJgm:A1"Ge#BOZ+%^i5<6k.c88G*R!TfesS %gqq[K6[VQP"Q-(Eb@(dYF2UWq8d1NiVme5:c''2J!6tB!!#/kM^roHWY8D*WlEIkE6AGN!FE#cT%[s\Tb&N]oE8ZutTtEWe<26bY %AT7qEkNioCP3g#$osOl.2q%63';X4P5qPZ#`bulNW0YU7#-lXb.%:4=N(m08aTV3o %Z5?1pnsGM#J8sn-EYgL9/^""NUp4>.p^U4/afU-t?u_i=a:qc$^*@f]LK&2PZG]ju-9ACH,&pl\afsD.^7X[&$_@Wo8B=OH&]tL6 %1)lQkf6SsKmKBIh:kSY*!ti@epS)Uqb/>"B&B-U#!"3QY<`?RY/,_L?QB$X.26W'lE/fk6)CZG4fFRLF_6/RB4B %jEV?T%8MnL96??u>mNC[j"I[Ag@B?H0J:O1>2X'HSsr#_c@TeBKMX["+Y&>Ig7_YEiXgB*B:1lb.0.(2BnY\f3\&mi(q=.NYDlM^fnliHT%gRD %6ERB@8/U.oasl?"Z=WOM1cWi3_NFS?QIU=r3]0AQK2_?`=S%j*8JDdlk>c=#_+((q"1jQ(csX4+hS(266n!q(b\JN@4&H7%o/d1c %VB\gb\j7n"5Z]0^JD83.9d*mQWbF/Cfd?h$)\)lO%5J6+7q-dH81sts/UB]]<_5Usn8%e.Of%+QIYRqC`>l[94+KQoF_7J"FNHd!LC(4%j\/@TRJUtB=7L#4qZSmcbArbmk:/`F:+LOWq+C0f4To>Eo2e(mHn8`Eh %@;9NkA4T,O.HVX(EJ$&8F9e2>DE1A-^ %CG[[__4$Fde>CFu?j?)$pQ(]Fh%R0aq,6P5B6H %Up6"f:iY0*fdmH@L,3rtX&hD..H9#PR&h^PQ!(=8d&!VfKc%p(m>="6N20C1QKaB!_FSkl1(OV0jS/_r%rAo*'gs?$.-mcGb$'ah %S>VYnG5k[d"\(fuR!PW/ED=plk,!#d=Rk$Xadaq.3q<@@2=nu?1n)!RCkN]Zh["X?+-+9[]0GXfE1^_-Fs7/I$gVsKj@2eb2I",X %^E3c%c)bh2"6**d8kfNVa]cUklDbTA$Y&5K;+"f7JY7A3/D0/*,a0h9%oHK;A>E1e+Hs>r#i![d`-;A=C/e)rj^]VVaDr3h!-TGZ %2=TY:H;a;'28TWM\8oSH);)K'%\GAMTL`'b%"d#OU2HRPbi8CP4$Z/FT;?r[M3mggIHPRM#jAnIF!A4nVX'o[.WefAt[(0qB&98nkR\j/=#< %'8ul48uVDRLbehSnW^quT5+[QK:4Vd6E=;K!0,S>e>GUK2e>?^b>%o[+A>>W-HGBo9aj\3eB5WXidD:U_SN!.(/'g^HABoQ+Ot8' %ENi?SM&M]iP_9s=:p:LJr:(>_1G11tK0q3,8/S'>JeSHp"FO_%YWRfQ+,LoZFsK!=9=i8V.iKnk7`Qb< %MSrdWA]5HB?l1#TP4,,mCD#Zc0kne=_Fc60$8FT@^-Gb!V0BM*Y;Tj0d1!3tjGs*cAc5.n64"_S]g*N&CusXNW#iVQ1''[(>4b2( %h:VqM[R'X!g<8iHUU!J7gi?gje3kLh@O3Y(+bk5,OH,!HQZXikGR+SWYUi`f7@(k`,7#dKJ.bA5bHdr[h2hpVo*D.hYJj?g+k$lF %K9?A0cn6=bZE3D/V@hB@5J@IEPo]8tV%6$^`"aBg0-a[HS#4q9EhNMDdH-,8%iL@*`@Hh@Uq'+=@u<(9@VNtG,2+"SW$C8/#nGsn %-j]/^[2:)Tq$T]8'k+f>isfBqo'b)Nb!emEB9.fpfAu".j2K*G %-4WugHKgSM=Ct>A0aSl*g-OYYi'5#:D-EX3^`:6U0$?AC9gD`(e3rRJ05/:uW=9`e"q:N&3:]ZM@Tc!LK+Hs4L1"C8-DG4+JL0fp %Q3M$$J1gu9j(Q$d5*(Y,b`bk'oH@UnODQE/m/Z)PM&)3,)ikB?kBBG=]U\a,:f+bcsbdT %Glbs7,MQD@i!:>1VnH@Pl)a;ggIG&T6)e;2V^M'dtYim)J,jflB% %gH-0/KH?+P%^P&Y1'3m3nrhpLj^C/TLIdATG%5J0O+[U>66k'Gh22\)?R$U[%7nDo4p\2M8Z?%NZ@bTmFQjq/5SA@Y+I:M>NM:;6 %3g""52CFnT/d)SMadRdcFdb[@=ksFc164U+1TtGo$#0(5:I?B1hMi,[qa@.L1&B]u0fqM>_A!M#$[mIadXY6V^ %-`L\_*SCmIGmDmU;18j,@eiEX?mH<[b"p<):Ldh/36(5iY2[Yaefa%C\Uc)>,`FURIun9"WhGu7^%O%jY1!*):Qg#KP4V"#4qJGc %#Md=-#cHb`puc;JXG_GN\qQYsF$t#?LTsV3buQc!SB$&nI!'bI\E`M.;n!OcEk*?2ia+YX1)&%l'9P60(nHM.So3mj2bIa8U.AV; %NBUO(<;O,pYo3boNIRCQQ(!N0l:,0(UU@[q?/6@%M0Q:FBUJueDT8>D`2cVO[e6ERjs&q5\um!-2\iYX&`7e6,iEdo?kZu$NCB@n %>O99fG,$3.,:C*#QCIE3'="$*ELu!Bd$a+F;5@nb-FFZ)g5U6GN/F0aTTs3FDHuUm(uFPWkX>\O71j_gNdH?!U\JDG[M.i:((pN8 %ft+\MT'jO":W-d>`,BiE]k=+WRgAS&XEcHM]c4fENHLkRMr+f6n&F'W`8u2Qpq+rQ'?gG^(4e*q^aqE1_dh'P46Z5ZFSHLrGSXmO %)&iL/0%7]@.n#.YU<>F!J=!"hYirAuFI=kp//hg.NWQXVW@=XeSMje%>l>f_Gnl7R& %Q %>mA%rgL#^OYYQCoV&J]@'$g%a2I)#C45+QGDH*tiC3l1"5jaQU&`6L-\Nn@u+4LGim[M/SJ4eg^qV=#]+DO72DK$h'NrnHN@T&:PF0$!]3+*a,6]ZW&o_+jnUWhrl(bE@GLoJ'`Xn_9Nr=bA.oAq'5Vpb*.(sT^-6UFO6*Rg#h\5Km-r5= %&+hjG2Tb8G4EnY@[mkjg.1YuEWgk]@c6NEdQ`R+*l8)'5A-WHr%^"(^"d'3gQ`b"]g??FD=lhQ!np_%Fd_K??bY&#dm*IO_;E8l5 %T6^W;'4b!hii*6AW\0iBQhm`&OYt+TeKs(@U$.rF-[SJl8a=5M%3] %>meed2]80&>h-1$gs/gq("lU$EBDXa7XO!JEES[R`M4@%+8t);N5MX7R0[996DJ[H6HBhd9=6)Y&4[#>.iM!'5[SuE?*\Vcc30HW3qNm %EAtaO/oGPN`i^8?2\EUPNUlCh"3s@Geel;B/E1L$@N4@[Mh[aXSErXHG2k;)@lO:)Sp*\X:.1.$Of$s%eCjWI-J#RD'g!!ngPrZ/ %`oTt6n93i?qiS^9;8c2IEAoe$2NGTpOfDkiRXR0m"lSIM0SK,AGB=B\Q#48EP<$UraDR<)p,f3o0*a.57pQGNOkaoo/# %cB9fp8XB`pJi0CJYgV]D\XU()bOI@2*7JJeRGt!0iN='ggh\b*9X*\R1$s'CH"Ql!)Qo\b.o;9mW67(U`Ci&*@5RHoFd\BS=d2C7 %90I$e5)I_mh,BXVC77Mib;[s_VOD)$C)F-4\ZmK+[/-:s'C8s[9O8spY:`&l"IE5WGHAta2To^`0V%>[croe`0$ia;PI/M>3l,pl@7BSSVp9=bNd+^=%'Db%f.JlG'Sl#`h^mO_b-AP"@6;GPgZ?GG %@*tNVge@l4Di_oIbP9h'#O^%HG8Z1Jm)0Q"`Y4l_I=C60PO]^ZEl68Zj`6sOI3*ArGM&]=R&;<6Wk3Z0ZeX/t-+h'l4i+eeAa/XR %Gq\.U*dGPKK]4fCisee0LX1@"q7Ob2hW7\.k$IN([.ie>Hc5A9MYJfjr%.=7LcW;gAj=Cbhdspuo'&qhmrs'R4,j.Pm/Pm8!Ss7I %ZhXhq\I,oN%D!Dg]%I^BBbdKX0_A7#FE0E9jF&";\+"BMXDfJC<2II1eA@F %8mV:i$)4;";Q#f\W^dd]5qCK^T^%\?9b:NO8[,dJ=VK:g*?))HY=iurJ_+ZC/0"B!Uf<[-Ckbi(6pK6G.[qPEHZY5\e;bOf %a=^o^"M&'(LpBZV3QH$j&7P8L1FK^j2d&,PFI`f<4W>(jK]/9GftKO]Wfq>(:W\*6@U?9j8K30`e?[h/et,^J\3b %%Toli"imoJ>CU`]$l?*uqkpEZ-"a#mGjk.MSo3YpRAGls$%h$`a=C]O*/s6)I3Rl"WRD*'c(&%B6;=DU]JAp[Ge;0fWfMnbpSAZWB(u %s6k@R[04Di9PF6:7ebdP)T&-&d3*WW6g9M7GPcO+gDpN>tV[f5j7IRJ55L(pJ1hfp6akeaLFaZd1B/dH/J';a46]LQupMF1. %k>tS\YW\5Dd">,mDm/'US=]]Lk2b\IcVG?6A4\j(Tc'%/)sVnNo@_C1mIe_O$\jh*[MFn,07U]bGCI/7.=?sZTJA8!m6A6CHb9-# %D_UPfVS0gQV$<.<)ba&P@![0@;Y8:n^KdDGgRCl@@5?)S-$ILARbp!r(=V,Lg076tMeR)a4W01B_!J5K[&EEIC,NOL?o9NM__,%c %7qWph0#%;SjN[?NTCoL(GK^FUhp?])h:(cgMt^/>nIA\;o4n'4gG^e25+J619pTIpD7rnY3XPH1JC6/"fIFjZ`5g(c>, %Qej8Q+5]3#!I=Y;mCT^Z4(c[">1.=)W*!t%52u/3.h9UKiBC**hu.qGqUbI?rOkQsrrTPg5ieJB?X2Lda"KYP)f+gAp[M^D"-\X+ %]\Ya-J22N@02?DFYHQg=Sm3iaTZni*]NOh;OE^/3hO^7/I=$:GBR!"C+5ot-nM%ZSpWqGmBD3:@iJCjgndi)Ws6Qhtn!@"&9C;Z[ %Z`*3iHbT4RrJ4Xmhlb;/DSK=bd!lpggY''K!5IK6AVW+*qq9V+lO%"!Rd>0t]f5t?C\p %^"_CfO&5KsEhg-P"K%JU3s'^Yc+'D4Ju[QqZ)VaBG&fc\YjB^SG&hYQqr?ekm=I4bgH3T%a0N@5[s%RJp..RFkHgX"cCjWL8.A_7 %+Zua\s)Jk,j%4^%T480S?u$cVo2X[GpQF*S36Bae8WL!X090qqcMi1iVIP\)h_?,MP5rbh_tiNKS!,eomh^qYP^@Yui<^>c]=U;? %3PROVE0In42VXk-Dgq]UqVq@,H2V9#2#mM4oXNZ&'8U4'&=Kf\LA'BKVp:uQh1iY9G6Dja'&I&WUk:fmo&83ddtoGY1A/,"lWKk\ %D>i$AhhJ8hHIN!*FN:,ck>@cH..2KtpQo+LgC"eMjb%"LLsW5SKqRd&6=YL0hh(5[l/pa)[3sf%q+J9Y+&B&Bc7Zgo]/VlXA1MR^ %n_h[gqtu0(]plG*`1rk90@&JRHuX1QG'S7Rqra08GNc!84=RJ&^GOY^b@8;6hpqlsXlOu4NF2ABf@`(AD[R70.JN>l2"*]^rj1^- %Eje-ZDKQ@2IXf(IiG[aO[A;upFd7G@Ds_EAbb&gkenS1m,!ist=2VJJU:8>8/oT&h/f(-J/fnFX:G.)Vq)ENDNE4Qraj%+;70Oe4 %oUg'@pQ5DcJNJdID4qp7p3M;LGl8IZTC;\:$TLt]ck^M5Df;d3(T_$roa!b!X8-&bjRi*Cjnh,WNgk4!AKO*(C]=s`))$eBd8 %@H6>XhR8n01i;EFp#aNpmJ2M`/ZL*=+X#t5lsrD/)^OK#TKH9:M03<]pqY9J7VEOt.lPIWO %nG73P+qmKPO5Gl%qb^Hq8N#5.Pa#keVT0(2S35_1tK=h*@\p=iMqHePMr41m^>!VL0A %4mf_Vme5.:F1ctSqq#]LKmhPFr\/`sBa>(dH.]CVU'@N3rBhYdk1&@66LF_hU"gm7Q42eR'TYGO+D %[\qBrB.(/AQL"cLdB2T@fG;0^8YdF)IS#FLXbUXEC;nb-3I=DJqj-pGqiRui.pfY4[WQ(@Q%[tCIK'GuW_[g0No'ATQ?;Cr(DHDh %K!kO*@k?a=W>]!Q,L(SII[[kBBh,sUqf]?SS*GS+Qs(g'!9cqOqL[6ZKii)jDBF/l498?hPFM0u+"trX(MH=R]eaMLh!Xp+Q2@M\ %72=J4=FQ8Lb8mHtgYZ-$D_1WAQ4H21iQflMYF!M\r@R.1Q?%IJfko1:QL\a1f'dN+pU?C!laPU*;Ehg+ZbN?lVIdHqQ[?'P@kk3-K95+7hu`AnhihnTHp=9+0oA4V-dLVsZsleQ`m]_hNkZtKS$:BF)#+H;nuGI`RF %q8=NAYtrRUZ_0J:lS&;:F4#U.1JYh$O.ZVNH1L[CMnST=Z$dGTW?C(%4#50:Z1hPjI2ANfqKq,=hKQ7!?0ltUHf)e#q(=;f0_'AF %5'l[Dq%@iFQ49`4oY!ALd+A.S@J6:gI7XA9fD$tQj=cVcq_j;rKm+79CQIsm,?],(UW/AZUNND>40420mEcnbd)Rk^c'n0:W!hWp7?dddR&04'9MpZJ8JKD %$\i0[$.th:EUO4dNcE]u!kpo'p7?e1Y_-ghQ67aPRD'roDngX.-Hqp\sOEFFhTGE:^lfX7qs1/W9YH'"Wh+m";e/lMB8io@q_CIqS'oJ7AO<^KAp[;;kg7ZhN6(h/2VC\5Uo9 %4N%-bon$]Fq@P3lgIrea[dCO_f9ZkG!LA%bNU8/lTe&2IpIlu^-Buj2Q9>o((\*mjj8I6smk;gUhEA@$cBCFC0r;>/Pl32_,OE+! %>qPGf]8@e9;"JPC&?.%-+^30HFnAY7VYQ&8*Aa/H)mhPmp.!NfB\FD"H_]h^(ek6]GKm!RFl-6;aZfgDBh97e_/T7slg$Jo %gmfqJ*VWU`N`f(PKY7:u`2kVMP2=b92K'W',OW$X/oBp#hK/.&e])]Z=1J*jfU@6kMuaFu3*hc\FR,k(Z=[q?b^Xt:FEbkPq]F^& %S=KTNonl/h5G+I;hgYKa-#VF\[iZE_H)G,4[R=Ff_ff^Ir$l07XnDTD>U\@,EBM3Mq]-(27[@DY4;r"l=7m[cM->(3qf`b8\_:C+N-` %Y^4'3j*U`?n*p9-3.)NQ:g^H$IsR%W^">of]mJ1ZhOhBdD0t/'6Ih\6SoENjr+W0%3s'^i-dNK:9NuZ:-n7"6CZ#(Hm,[=,p;b@_ %-1pp.Zg@56o!Y-$pJXi]kLh(t8R$P+/a3+L4j"PAh#1*Y5KY&;.,,aql95U-G)XA@@'agO^^rjW;(.Ik6Yp])u1,.;-o#),M.$2h/PW+`68^IhJd)dgB%- %'*i@&!r5/8JqrJPg0k2:^HTJ1`.N@4A06E<"VYU.Dip?d:g:O^J^53giu_'KiH5[\HMf(D]ZrQYUs@1Hh.,eESPG]$j\qJ2ANY8-dps%t8TV"%5sK^UX&5>86g:=F4<0kJH>/M3$OEj2g\\s%09C0Ib_:+jlMXT!ZhIeE[40>E"6[9jZi`Gq;qVl %d4[#ZqXa"Pq`eig#0QuEfg/Lg*U7%M.TmJrST/@dM.1UpF%!8r[,1T.2^m748nhYNg)gN7'8neUo$+M9mn7;Cp!5/PQoD&fDGK&Y %(KG%CmI.;hcBen&#*X'!_mAp"jF)<#rL'E]H?Cq>U:`Mq8o+3$3_[BBe\<`/BmZ5,1Fr:SHSDtIdLdSd!:qFH.iSh&gV7Ytr+_LnP*tF8bANY)d@hEk`_,<.h\k*c/1I/_=dVm^j1AK/YO;<9V7Uj2dV,rUN %#s-6LC/a9@dB]4P;TO.Ic;Mfsob!.D4+JYrf)V806Q$-0N&SobYcMNAHtfNCN%((c1,X["(`G]:pd$!<;Xg?'=o&&LnM"XIB\CFj %2*I@lYn#?u;9nhb>FmDGAf;e-A,Zn#k'+#dQ[dQNn*p@R*D0JJ]&T.WNMW+m!Zpr!#7h&q?N%D?W2_0lbPg_#0/J[g!*p6nmp3(q %oUFtR(.Z6,%DHXshb:]jrp[)gqp4hO?@DMDe\D(s%_84dP7GpL"mu-M?Dh49'pUm7V_T)\><2etf@8pUcin4q%#?P-Iba_hk>\na %j(BQ24#+dlNbZ`mCOY1A@W\l'>j!Ap.o;*!-Xo.0&Bjf#f6 %OW%<]4tOH_IQWoZ->*tVE^k%?k7k*o%0PK,K%"Am0DVn)D-W(djNNM"NP(0q<'P:M-iDqF8c'j %lM0H"iqs&Pi2hrVhS8r-ooJG)@d_'I.kgbNhk'dAQ0qSI"j4QYPiNAp.b@rS8.09AU_?E %gM$r65t>""MdlY?Nm,)l]cQfgXS6M3lIhZYIlegRY2?oFb5,m9>[;5+MXofPZbVROWu')i'5]XpS=G5lY2)1[Hd,u"3L(=IqrX5- %YM[Gchc;DA1>1?koB6!Kp?8Z4ad*B6gShFX0j*ZYp[t7I>FUH23A?dVA*_H#sOfCFA$S[JbTrNC:t"u %JJYg#7@YB:$n`k[AYG.6h/]l=MuhJ%r("coY+2;tZ[o>sE[Ej$6rW0qZ.MRc,brgcqm*\0%89p#b4%u;_J1n[A\0L^q7PtnUL7>*n`4=m<=&-rU#Xn5G.n,SO3"OHLd?9HOYbJ_sF7%rTaDkk.f*N.MI:(LVKIP %>cbYcEF,`XgU!Ba?bN-[.a\1[TDoCX/?d@U]kdVM@=e",T3qP9EE<,5p$+%t^MSG,N3^1XUW!B-GCe`t]mn[$#n9B9QqSgSDam21)Gl?fhT@\r_s&4CFo&")Xn"G?n#H6_=XOGO6!+hEu7MZN\(b_=/:*i$ZCb[,3h4&,mE+t*=gfT?a %Gfcc+n&XKQQo+W^eoj;:5PtT6.@`_WEp77dDo(B^(\1-Q4nDfR)iS.G@#a^%?BNbU4kKO6S9NRa1-->Llg_mL7"Q<(cZ14+2;`4:RJ2[Wt,U\FOk9_bCVQB-&UOZ?2qb %HL/LQ4I4*Ef'.:C'CVVbEpHa_3QFJ^(jX0mI?l^7j9uG?]&]_1*H^JmBmFY"_Iq7,kfguOY&$)6O8X?8$<_54kPMl)dGd\(\]4PA-T"]A.2-rRKbYZ?(Kf2R#".=eGA*KB3 %T'&!#al]6jVHk&6,ZEITJb3J3PfGrZ]\"538(ALcG7Q2,.HJY:gV2ju[T4a/3b(3VF]/%`84Vsf5qYn-&NG/@[e>YX:?E8oa %h9V^;8'UM3H+3H3GFWL++W*@ch3I)4U30rnhl,f+#[q_3=i='m7dn=.6N3\+o7Jt1^Ld>qF,%)#B$JBIG&cQ'.q6)(=S(@r"jWR' %D\.pkJoh)Onf;a`2*OJ@iiUY]+5)Op*hl`2G>8Ib0.:H:msQY^Ts9Hc"n"M)HCuZETi>uU60(1#V;_6kqcl,s)Bl`a4LiXrT<4"\ %+!3B_`aIIiQkJeX74=6)f_kkQ^rE[FW^-7*.QLNFUK=Mt6q]BcP9?oPf*,G&.Y&C3H_k8RlpO]W[QjgSpR6BGD?5JpPXLT.3Zc`C)m$n!d-TqQU@.T8%DSTg]IhjUW0$Xsl0BGj3CqLh(Pj.B5CgJ_) %PQS2iloZ*egm;_?pQVYX_)af/(-o&TL7\P:HG(o(.- %D?o'bbR&.71ut\o/gCYE'J"'t':?&gQjm2^mSlZ3X=j:2MT+j1F!>)f`g&!L-nLd8"OB*Y?*(*K*l/lC<_=GICef8/daK<$;E!J&W.400V_lN#l^b0jfM^BNN1gUR,$ %31^A&+$$fffr"NRZJQtsf9T8oP'o*.#\;&BhH&_7Y)CaY7CDO.JQ&-B %!F5pQNkUf:Yhu0O*4-Z+i0-n"@iLq`DNBk=dIVC;]o(-Qcq!f[0)@p#*ZK,,TF9NWiqUk=:'^SsC)tbSo!)9D!NWP-eIICj.Q_$\ %euCh%NF*!KghbF;cFpY-\nko)n0phrj.6K-V*-J$T4uWPrk"k"b&?D1%@k^U&Z7AQ%Jt(R_J4b/3WKDi#JALcVj$`)plML0[%0Fo %@";F8TshQ/eFHE<)83?"#"5/ER?-tEOB:_apGYQU_)-HWGUl_$DE<[QI^EnN6`>'[FUhkZRUF$H %c@H1sUJ5Mj\?fm^!DUmL.;m$?Q#5d>96fcPo\J_LAU(8C<"i,\b=t(>Oj*>>33<.J1`KJF:Kk)sjUF?bLc^m)rjW4Z@)&nGSZ$aH(Pjr+bD8 %eAgA,>V:YMQ_1a#FQ*5EA\9S,_mp2fr*:BJh"KU8%*iFt#oH*!8=aCiAF9GgJ]GmL %-4N]t(Wlc5:6KY7P*70OhIQ[%JKY'P,V]=07j1&7Teh5WEL&%kibLGhOfIOALr8dbg/LC!@&?Y(@Z#La-5U]IiSrkea< %b6&bn@%AH]bW3B*!IQ@H,ckCLo%<,@9Cnn,A+-YpjnKB\W3tX(/"OWrC%OH5 %5qYcW=g99#'NA$LBJ$r7Z,cs39gDGBHa?:^1:mS/6VaEZ:&g+^D1,BB>;&RW+WJ;7Nsia9[ZLcdYD'2?ZFgD1E&5]_:GRa.(9b&c %[B):GSdlLbl>k[!AV2t12,>b/UD6S+XYRFC`9C5eYq-r*']Jr::W_00pW7?$a_/@kV4cNMQhigd#)T#R$fPQIS^TU#f%8%%aDO&n %D-[934Cq&ei4$!Ib0gSVY,rmDdIdPKP8+=?Dn<]% %en3qb+4d\C[!79NB."`KN)iOReKph]RBXfE[28=%nEI+>RgBN?ODnG\1mV9>M\Y_igV"^#eR9BN.)@ZFV,E*$B6#n %H1UjJ"^!ufl*[f43X-'86`P&AAeR3`)3mJD!Mue %VHian.[3d5\rT"J&eF9=]6tMGE6L6^<9#m,pk-)h4>\#\b"i@'D?8gCe(U9g7A3mGW'Mj"IKR"7]EJSd80Et`_nMB.H=HrF+Ls'h %8VFr'/('E`XT@(Ga2CMfN.S*A3V"_;5(PB;"TVAZ0rsCKTI;Q]$j&\HPZFXOrKujY(0(KhL7CrgUoUhgAu,ObKZ"[njc#?<'H>KK %o<,$Jgf?D!mn694K6';2<3,^q\BRGD99*V6rV@)nqY7bPA"kt/XOE)-"98Zj`+?nL-r`)E'=_+,^R2&n!X>9_1UeSfc8'<"jE@*cf %"LRAb<-l?Eg0cM%$\ffei;=52:hCW)'B"te/&#`rY3'5=ME@\(2h<)Vg)6C,Te&(+$VKnb?=%p3D/7F7Wq`t@[.quD>K %YioU%?rP$?_`S%ai#8[X.Itk-+Mo`EhE2ZtAt]c,3L(a2U17mCO9YpO7QJ@BA((Ob5SPBugpko2KT8VjA;f*HbgqC-l^jO`%3a:\ %kF`r4aU!XI7s7@gojSN&QLll0m5e1]5sD/O7<&S8!ZK+j#g3>h@*JscObGin>r;qeQ0\K%=53kRL/E)&R_fOgYc\n,H?!G>'mU6+ %5CfIQ)o/tW$L]O6;Rfj[W]r$B9s1JD4b+fRT#/$'HYaBuFr;dIL,o*T*,hl97YXXk9^[e_nN5k&9A8qJ,)\-nk[)AIAV\7e&:Up% %DI+1YU=eJqk`mSdj&%eYcX0-<@M_2La^uB3*'7Ar^ti0]i:jl3q*>ak_2k,/WElS9^mHD7(l)B4Cme'^7rkMnA%TLTB?0G4l$dKedmUpqH@&bSG>3jfV/q_V/7h3]ONT]r2B`:\6>R'4\[#YS,TBc%),VNa6XP."7WHOd9SRlS#"'5:&i2$MPXY"T*&0rH %]/bhjXS8MYLMj7Rcp#p]9Gb4_b#E36!;p-4&Sj[SQPM!7p]fD2!cN]F88*gc8mpAh7b39T)HYE0)\'h3A\#I3KEK5SodL7T4RrS` %XZCE5nrfHO)GMhD@CTLY.$i.FORCgZ5eWq\F."DB@RT_T.6(_11[?nHo+Sa%kCt:j!Fkm#i%&!(``qL,1JTbK?OuQ-Z&.Q?0)\Kf %6;T6B$LCN?@p,R(PUm:MK;kPafti4)fUi;=N(?-YnZo)Sp>rQ-@De4Uq80dh):\H2qCR&Y+6f(m0Z;34g3?ga#hHWE[SF49UEKh+.=k8m1/3Nu/%iiP?9fs6*j\m[];LGjUS[pW^PnpSn4Lrb( %i/f%!!@*2A&(bP.A>b8pgY49TLeH>TGVmg?AH>KKt"!Z*eer+$B>NU>H_lEp1%+;c!,=4?/6T6B?b['._\-?^:R% %/lhYQ8$"Ib9i!XR$r$AJ`rSE2A__:Y#80_/=C+.ub5d(2$$;8WgRgO#*X6aG-85fY?V#eA,7mf$5^Ih)&\V'Vr#DC^Q0%.['E^Ko3J2*/$l7b4^MS^s:I_9$E,^J1jaVIQAJ %:i7(g#=ogi?t(lO1c=h$#QUpXH'ZAq1?Dkp*/]=7-$C?a9S'CcaptQVkKkZGJB([U=uIYk %0;O@7ThiRZ)P`QJZY!#Rbj4:,3EQ>3U[Gm4eT%]&`DSjTbC@dPn(%4t*oGXO %rC[!l\8=NeG;m$cs\m.DBD3#W=tQu^)eKT4l7[HX:6l]1>_4l/037pbe_fDOB%_DSMWFh]!G8.IsZ0cHh@Qb#M0Yi5E!(Zfhu\\^#X %%N2@3_])J1FF+L3'nj\3<1U<4&Gk!t[Q)p^*J_XG-EG2aoIQU>>C'10O=D&ReAX< %3&rg#/LU'(K0rtB0@@bf@]-!U0_6uAQ!kQ-CZH:%JXAA#u)rGZ2'cA!*Y:^cQ5eXA33N$-^/jt1nV=TLegOA.A)+usp]+"Z6bmLIW(uu'n(0n:$*nSiPVW`IH@)NAGU8J2R&^8+XTh:ga/XQg0YD--2IObNqitR+aUPV<1Y8db>3/3=eMF@!U+i%L;8q[E]SVBF5baIZWA]hl=FO`9h5[0t-q$Tb#A;0>\1k,Afq`6d0b %K:0Y=)2NEl_Do!^;3Jj0WA3'`JN3Q;>"EW)F9=;6L[c!#g;1"qR,V4sj.p`AKT`Rq!EX]%5A%1KE@?adRK37;0s1h6E[G\^T>Nnq %=G.1K0gV2FeI3E1k\_ee"mU-mIEEf>'r[e%8GU.q$PWa[*$Aq;6imAS(5![HK,oMuSZGHXZWu;Cj7&MSl,tE?jcU3[E+3CU$Lm]9+MrOCcE'9O;@6Q(Ob,L19c^<<% %`?A9`1\lB*;+QGSG3;2QM6KY4Ka+\;+[ZFY:j/X:)L#N=Ht=B8cXFJHba1Pp,6B-Wf' %i6.PQpc?8O]&G?_KRdkrj>$A/oar0KXJ,K>60nmU$UPO&*/KoG\;_q&#AO&ib<';lTK!+0 %A]&#&'am&@pL.bP1L$./NgqRV)T^<`Z^#2.N_N:fDQ(p5buo!:ZbV;Vj/j"ASe1P$;iK2f,ktnPp%&U+?gt/3GZbO>=%,JQO>LqF %Bn,Kuk#XY!%=E6Nn!sKWEeiQ,h$%hjFLE!,mB2Kgd,sckBKm,7,+Ir#ND_embK3&Q1eNSN^gO31F?3CZCL*M;s %eK2$D*0`BP%GJqBUB35*lp>(3ldBsG%h"OqN7D6_=c0/!U;aUsM#I/lIN?5b-$5oRZK%#*^H8'CZHK14PRH9T$$Lt2?Q1*eXd[i+9j9jh/Vq(sdXjP6*;M.G( %2pR.B?3AM\>^*BpX3b'.p;_*#@LE1fd*89De\LG4)-:(%!5\s1kn,/VDi>E\.eqYi\6r:"Ft=DQ\q/XZoeM.n]@*BAEQ=+G9F.20 %m.1mO^\63\*UX-#5-L!RF`D]`XObE^kHlZXI(9ej@*&cgLa;#2nKrKH!)u))m;0TCeXGj&Ca@PkFV7.PT''K"PVr?UX#G/47^*]1ZUR3d*_:hI*8M=3^VGcR"4t-jaYF#QEK0^(SFsso>[c3Cj11nc>7^D25$Dk_T].E1\ab%d8Dpin %H,qr-^a[S1*>Kg4*7K4ccs,oPFA-oA!53n-cjAgYN[,(](6PHRGBTg'`/iJ$L_2*LUrD'tZOLPo4+>Bh4nD4LW"K`>;gG1u'#n$lG_'IYBTSTbSVg7RJ3>cl9'f %$$c?!2^Dt7h2Tf'l:E@-Vl&e$]Wq.eX6goKL8@Z/]3=hWn_)n`^>pk92Vl3q$jTpApIL@VKY:D;-@e%4amFdK\aDf\k4KN!c;RR- %@V3b,bKgF9bX\-g+<%/`kofjTm2;#99b=`M2i!kt`H!Iq@,ipeflW/uNZ5Ni%BqO^)N1D&kK2T%cGPV5C]+TM=(edK%V5X`a!ADoFWtiD)jD6`ufs7nj:3BLjaj0"r[C^^pV?kOEF\LcRj=c7B#HM%lnZ=R=#\=uqfLslOB,,<\%NF]rBZ5BZDKT7em97;p>'ZCdY96J%FZ'[#*NT(&T2[2t% %DLbb\;-=1@AcWu)eDGD.=/4F_*J*:e:1n0 %b7%EP4!l5lDSV`SHi`*M;HZHQS?].)i0Yq0U2g:E%62T,)BLl`,_NF(j?P\Y(B@^^AOk`hYMcW.ASL4Y5o:>=A;2io'Np_Y841,l[_eG_ %fa'0gZd(,?<-]mN'IAaeU0(NtQUNO:,(t5pad!u9"rP?^0*kKT39#\a1JugnBok$3TF=!7$`n`uTj],G&?%.W]:sU#(7)r*q,/gb%*$s4PYCr3.D8;eA+mJ,=GerQ@kece?+FY5 %^oT=]`I893h:t.Z^,gf"g;\KqS9ROocVI?o?iL$#p&rOH=qWB,6qu&>"0oga7aOiJa=LT36]O;3Tdk:3'=<"81i+u/Is.-\`ChQp %UFoLA`$.s0qn57QK!,I&.hMY3b@U'^+21]!ksnm[d`rPGou$b4FdI8l7Y"m6jqaE-F:-@Z]^ %.Oon!P,EoK,>)HBdKJ7B!(rh3AXfR&e6!6d@?_"WM.Nn'kY+;U/j-gLhoM,Te+"P'`Jiu!aM@-NU`WbaI0`N]>D/(Y/2M"'i]@gp %orU#_#:k9=<>&\lOW1/@8&YYHQaL6=F([p/U]b+nXV_9:K@GG\#R,;MhQ=@&luR"koL3+NeL@]J;bnLe/76('UP6-bq(lFS&eW+Gb=@jUF-j@T*)^IA5!Wu-&/I,s&l57kd:k&Xs+Hu>`1F9k+ %,n3d8i(D+Jed#sP@V'!]=67LA9Z\OqP1?Le-B0PlUe48O3)3EK'R?1p"@2`N&NZZI5d_XLat]AZackfnPUU,uQ/e"NQ(i/PagX"6 %5+alm$7#]D,MZ'T91L,k&fH,%V3>kfP3K(4OqOn,W-KnXD4pO('gG$Z4p.@mBrAK\,sR,-qu`?]O;fr0kpH4,aUecAOjS]E5]84] %#4?BuhWS[/:QX>Xo6i$L'sKBQtqB!C@E^YD-dK')3hDD1[I8@=K?9ak0.CM-l-#JkQIAlL1%$I\9egJj?>&ehHeo;hD2G3A\[ln!,O.g*jJs$"Y3EY)T&tE\E61GoJ8E5hG?trr %$>G2*AAR"4Ba^#`0bQ$cEt3W$BhNag'&VXr+Q(h?jpGgrHd09Xq?c5fV;G5UEjfOOP*DG'V)^'h1 %iC9La(C"0@K)nEd1X/d6+8JN]1T'/##i-PpX%1B-N)fWqAWrgZI96sFZ7mZne6W,O0mNE"si+AG1e,q.?YfsEuo_K %]b.YN0G&i3T6%X-dk4g%@F:hlHhh@& %OWc4gLP+e&nJW,Q[IOS;4;OPZ`#^OZ_&tNEk:f%/%X#A&/TjYl>q$gOP+#&oMEqV!W,O0$hXQ??f?t_Ng!73V(XPqO1UB!:0,gJH %!q>RI]&\)b-I@?NjJ\Rk-^a'aqub^H*:O(_Ag]ojD'4(Rb;IHV(n%=MJtiLu=<-qr3ep22b@FE],n$D.bBO6`Y2N$!BHVO/eBJn; %VJ)g9Rc^VL[a["Xlk`^Qc,SVn^D:q!q!#-:*?nCM^'_41Z;(^o]s?q:Q:rA33G^Z:(8,4."`W^gQ)SiQ"nKPW\oKc:A!dHfu %$`#('b4L[4Z,eC'TO`kP-4F^T0sYg(ohpX#kYU5kR!9X*1C\G9U.ti@Uoe.3f!(2GiK)c9R9oG:b+Y>f`H3"%Nk^C7p5KPL^i:5m %W0#NkigD><-](J]ca\3HK;.&9W/AM_/O#ZuV!"m+.%R@P'8GpD@<5=o:P\EJE+"*%I2r'?#m)^T\0BZ>N?*"nj[^=B!O&",O+nI( %>iZht6M3h'0dAKePE$];A-/rJ[BQL)5f'J'L=R+Df.!ER5V#G,EgheR,&#%Up+*O&2IqSHe>RRJ%S;70>Q6I3g\YR]]91Bqq3C'p($pP9[-s!5WBF=J@`7OF:f%f_Jo>qb!Ff;*%%Jc+t'UI)U4- %K%J[X.>5XuCl1(sHZNjbf50GbP26762fRGF2^^lkH/R3b`mtUBm5lr$@UMYi6!B`$GPgHL:."cF[:]_*kWQrpr_rSD1c#jMBY]T\ %qL['Icg7a&Q2U9_[((JYXOmd?M5';9)&n`)ES6O90L]b:$HOt'F2d<(Y:nk?M7$f3GZ`WMf08p_=(ji.9WOf9T1ap)$B^USc6Ec9 %(Hd`\jrKQ^]^0db].\QWi2Ikl@A#YhfL%9<8JY\gSnH+'H`,_QHj5FN&F^3G-V4g+(nh&*/6WOJ^`G^P?S\*"acbDGC>^UF-/hG: %7o&#%V'ItsR%&VJ%/;R1D,d7a*dg7]N[dHF#[msE/04>ITqZY3i%&?fWCi,<+SoR<'0o,!NcTmCOr"PF7hY1i_O@+t2X?E2.(I;TjZSX;u[c%+Q7YVnfO>30.tL22+^HWteM^#)V[,HZK0U5nqBpqt6=G$a/^re$]?!Q'%ULXGd*f%(9/mh='(G %6*&2&9r`(P1/>W,e;@Is=AY*)j'i2MlraG$o9u=u83l>E`tIa]SmtAIMM1@k,f7#(YYV %iB#?9-f2pH^+ga;)r:fS`Z-aK!0#m(YDMHM%YH#8I11!8#V,uGX@+ujE&HQ0?TFc@[,1D50+ncAVsK&qQ-%WbS)*bVSXBbqU`H5Ksj5:pfLA<,Z?/\:tF45"Ac]%h(O35*Au5'=?O'TV6o(+7okha+!jB`@L$%"V5GE61A<'-?"A"ZT#.e2E3!kdB)54ZI7%g:g(FT %`Af87jN8$OT0KP+@!lAA!r@^!cj9HO=lV,IA(Gtn$o\gpT:_:ZP!B+(koiJ@n%qR11[b6=lr@5 %[+pS$_$5]K^aPk:%+%;POY@92JLI&2(LI0!Oh[Af$phSk^*W5>hkV2sGu/1._!B:_,srI)hdIp,1jC%@rD2P>DdDqI9$cNsppr2C1`1nIg[B#H+($,f[rT_5LW0R7"`7 %_B:'u(82@%d0f[l(a`LUf^[m=rh_p*hg@ndN@%h5]SJsRj%DSu1!l#:$Ep,mjJF95?-]bT31hGprXYPN<@Jbk_F%(\T#j %6R.8;_TgI-KEH+?XSo7]^'-8]`B)p#F*rnS>cgm:fsNQG"aics/1aZO:cf9WBK=Eg17n)1L5'ZF_._lPnIGLq!n$Km'uY>EFF]H^ %L/1ulrKOYe4$NQW9kc-UTVt(/!#i^u9U^e%3G,+>!NWG&[AmnU3\^ZOSnR\57N`_0.Ycdn-n>SK(s1l+_+)(MiU%*>09A:kd3UN) %2=6Hjd5TPXdc%H7MG=mOOWtnZCB.Fga,H\"5d3M+*%BC9oG>Le@TdWJ?'r/lJs,X&m$'RO_];QIn`)TS3KV0KU6b2,fGF3HHSVoM %E>q1T?)5P;Yd6/7SHQ0;!:If67BQC#!?+8lB@(1k@r\T+@e?6OG#iqoI;OGto=:J@:(Wf/o`>-&SkIX+3NPmlJch-h5iIFT=&sp] %7uh`nclj><.=F_kO+*+"Zp:,4+HE8L`/8:3EBX+hKu(EIfKTM>VjRStr+%f''1+NIM1RWFg,d%0XYg4X&N8K_mI=i]Wdlg.IAYqL %]4?=[Yl()TI,o^n-mWI4HEb0EZ6#^kKY;K%:GND+ZR`WaZ%mG#n$o!SWqV"/hY*>S&7&m)96LHi^iXQ44lt65n)[/oS-*(hg5a+:4^\KaReMHpA+92RY=bRb %^fM.@.V'jQSC^2$n9=fsR1a8=Bc&[:mV)O,(nDqI+M'^qoJMBDH %K6@ukDN\DMEHG&l/V)B51(*A']("4*Guu@%=agZXYJE=f\gEsVV67Y< %K@kag$K@c^7k'@D&g4"/7j/`O-tMmAOMXSgiJJKt`fi<$)6G'$`Sc0U#1/QdU!r3q%g9o#&/0fFj)X'Eei)c0G^)j/1\FQa',s[*NcU:mNhhWb47^s1s>CedC7[Jl!UMNh^gKL9#.cOE91M&-@N?odQ %\Rj=TdSKu_oc=N;'%1KS63%2?P#?pAi]q"9$d)'[6PZi9!Qii+$B+7J#b.V4rP7sa+n_-*+DK>WCF@"5b"As\aO-?Y?NX5McklcO %Vo;fs/"mu:lm"2^UU)D+OQ*ci^)1a=7lF20g*,N!bmG/>8Cpf^frc;$1&%2kMI>Kpoh6JNN"f39$$/7<9P9hS"k7LcEbY2O>UIBg3kj%\VI6 %kDdgA,bZpnh:M-FN7g7'j(G];"W8d,AI!sg/GW=A`5#W000R>jcP@H4-Hm373CK8;/aRN8Y2P,-b31%%N8 %q5`4QQuS?R-t@]8hDrucK.hcP#)o7.25=j>J4g+"rh,c %6pWP*Vm)8K-d57J:uFNfC)%tu<1i^h1(0"+jghNRnOU_7)n&0<3.T@KBJi>s%O7r!3to7:&J'@Fh(kB.`O\auIh8'BW;`H9OT/Aa %4IH[T'F%GL2!#sX\)Muj_FtZZVsIjOSKD`88:(=[E#QT2Xt!*[,6-'T"@=('r!+t5_P,V'3)NEjC5#!p%A9XA&/k6So`3ot,CdiQ %7-SD3KP.fN1%_p\JUu%g+mX41*_;5jd7C:rS\_-Mk6ukR0K!'W)Q4-AI0Z.D*t(@@-paW`XKY< %.?bqc!@l4rXpun"$D:(77K^F7p,8>V`hS_%=Ya\#_OE5q"D!rL<'b$0H#56I?^)(/M,f?iK?HnWZNIcN0A>C^9Yh'.?UQ<-p,"o( %4QHc6\.=jDe"G=UY:1&6_A:J<5NdJDA(f$IAeeAJY@PK4;+F4Zsh0((?*H:_p^]5pZWA""s\#7Jh]TTGW5I6=roG<"+4A:pd+(s,9<6D>'E#"[eZW6Z0g!@q--/cp]( %Qn&Vh5rNrf#A97\4huWl(F?I;RdgE<5A=`p5=2=7a**3j]%]'5SM:&cI %F64/JH:JH$m012&)"#/i6R3S-f=b %J_NE]3of4[Q%')A!dMo;Hqaj[BZ0oO,mtrB+aK_J5%e9k;au-uYj(+o6OH5mRfM7Q>rDnM(CVr^[hoEmnFcJdY:39V(eUq2XVgOF %!U1U2fp.GT,UH3d8:d`g(B0\gA/0WEM'5k9QPi>++B+7ZNAr)ZbnRNO_GY5@6B$Qr^h&@Ub(+ua>62WU)D3/6A>fWE@0'"&3HgEf %=hotDgeEpd9M*H5;'Q.-'jCo&WbC:t/I;XJ,r@FYQ1.Y6X>"o;i%bq!8-"%oUH8hXb.sEbb.)mW?VtX,(`_ZmV9ZFMLV'O*c4J56 %D4U6f`u@L1pCa,]N$S$h$V>?\7W[p9QA")`<^J*BDGJW;&TZf`&\rH5?fYF5U'Zo%&n&sbZe0;_$,SuI*C8t7?3Uu4(MLStq+[dk %8U0o*m%c0gjkrRbOXMVPBP'3YGG@,Dig=]8fWYeHTY"EOdQj6=Mk,hKV^RX(7imjYb*mG=94.L]-rVkc9s#35['0MiYY'2'CZdq<[o./o)-i? %lC+64W!C.OT9Q,mohgAJ)C?O_bA%uMs)YW(=#IGJUe4>',Xaq<!95hZk)%1)Llh/K?B[J9PRTO?#f[?1bApJ? %,KlhE/d)M2]QNiBY\'oWJqF+R`WVF.kkk9??HfX864%'5.1$MOED@_OSCALZ.#Rs/i+ZT&Kb]##$qE'<@5H;lVTPot/l:paJWd,V %9XmS&R;_WbrM5+0V\,+:H/Z>b`Kc6:15Q&9CX]'X!>c-C`fTb>+#k1P)0/8N$9eJ,&Dq,p&5V@`6_-M4)+jtfL=#Ob^_(u[(8Dbi %-9Aej;h,N,=Ni8>liX]Y6&%)E@:]<&* %da8:G8I:)FVih''im.rNKiN.7P^S+n./;BK@']:;r;s'ce,iH@1K,u`d0KSkc>d"-m&1-dn1*50BJo^no7nQW)_LWDgIRH %Me@/95:7S8k?.[IA]VEAB]6`=%\?<+7R\gN+<)lRarVWnWZR %bU2hEA]6;kYEFhF3b2)]7O%L*L>X3/Q1H16IFD+&kQR9T0X,'N!fmCN-n#tA\4k:'0o<4fEq6/-$!e^F*RYiU0;0o[PbpiscH$B@ %%\--A7EJL/N]#fU!V<'u.L84X<-7LoNl3(p%gR8VGB9]iXOA"[bK/&1`/&!oHdRZQ^(tQ3ZFim,827HpSpl30$\j$o=eIbhbWop* %"H8pmi.H*:`Br&bL^Jq*^?d>2)M:J7"N$;GJ'+7KMM]Mk$%-\3G(VFR*5gb]0-biO&eN2X[WGJ\4+q,-'`0Nq8qC+fa^#Qn2pJn\q[n=? %=Y/u(+N_$Y1D0?'5l)D'c6cNM5UEhtgVU7I#pf2*-4Q#1@V'.d(l1aR^C$'O@%=p#WoY@5Z:PBai]qY,AL!BCJ5?pV,&;SHr.8I9 %rRb18G[i!;%VRl9'o.jX_D[BrV&C13G=g#+RD>`d&/G^nop*7rC^@mO%F>F/9LA4gD(d`]^u5<#Yn=;h(tFF(J>#8:pbD)8"Q(nY %?nqjUM3f(Y9%2eqXL8fV5/]O;,Z<4kJ\2.LE?L43ZpPc_M70:^Hr7+rTIspIQ^XZn?lfXW&m316$%2OtPJC3HUWFXa&I_Pm?+V#n+JNXNo8-XYg#Qg_[@7UGf1789W]mi/:rN.s-*\n;=l#-1Tp-%Rq %/%'0EY$oj!InR4$4$O:palCY5l$@=\4E'^AoJnq^k-D*CIo2L/7.A!%iE.3O!<+@PhHuW %'L.M38pW@O89(@pZiTW]cpF9gi4aX5[NadIaLOs*alr728>qT#GFp0s/b6Ru-<;7IM?BNTL9LmmAd>&>m69u>2q4A=E%n4"hSm'Z %+Y"+"Tif,N/n9Yg25UkB)aV[REl&JX?HWW9A=.t490R$^+Y1G^!+b^8+PC6!>iY8O8J%'4bYPWr/4\^h>u/C*dr_!kCCq7#/0V?$QRZDEC,CARiqjX9*CEOhgUAHS:(h*Q?]#U" %9\eah7?C2Q;H0=.<^[:;AB\A_$<<5LY0c35"(T+q"j.R1,DrG_j=iS139_/LD^.[m*:f7c#ElX>gs+uu#+M*Ou&jcYq`5a$Gb`W>&Ak4DLX+ZnR %";"FR+pL_6BJLsPPB^J41(QXd"sROhjGb@p'[)8%#]jTg!1:WUM6.hcEWRLteBHhZL)a>);.a(paGkG#V_8k8eN1)J]HkSM+E?l9Ma.NTXd+=2mdf1+L9E=_ulXKGI*ZU8:E>Z8fa#<*lt'"qHKU"5jW&-<0-Q7Es-6raWcCSf$Qi)*?:,\#u@Cd/7XJD%3ohOl'$TPBF %i=J/"5G;-l)%HO8Q@`.n"H%dRAG]#[+d>m%!b!^8&PE?E/IOP`GccN68CFQ+W/t[q6F;E$30O2qAirRQX/$7j_kdbjU#*7=kT@uA %^ireG$$/-NO?H+@E)%"_OqaFECJ1on+M+/H\MtZT=DZU>0-a.R^t+)?!N@M83Zr8J:2#=I5]o=H1Yq#^b?^/Z6osmMpeLXT?7@GaA=E-GX3O&4UC5E+j%c0q@QCH5"V/e_;PYbMDh9Y)e=/<.$:7AMUZ^gVkH4c %#UnjD%dgg*W.<\lVq<5!2N#7W4(@U2eN)#jo;3+M9TSqN%J^d:aCC6#AcY=)K9[b9,FnN>6(fbRJT].*lq*6i2\f*f+E1F%Eh5:@ %`9!Zj^rFk26hi9T(pL8aoFdS#uP)Fjjr'jS)pJLfmq@eiCFFKR9U %#=*sQc6NT'U\G#l&6!KmMZ>XMfo?oT7oiZZ09`_?!]dgLA-kZ(og6YIZQ[G@GnL6b;q4h(k6S(r3C'?\_8*aepp;bY[TfWMZ".MT %^h?%c37\eL'6"@:*`j]BL'2E5^4^b,-H:HWi,$3C'l7Z@dK/#d6M;K5d<0T8PYVrL10)W3tKJ9(:>'W8_ZX$P@K0/*O`+<#B5iMRB/T->IuFd@_WsGL[iA(3j:\LD*#1kiTFSLP)sb[OW6&8#W5,GrJjU=WRIFMQORI!"7W-2`Zlo$L`qa\6ZR?U\CF]&?Wb.E-V".(1r_?S"aAu*&WV9 %gJDC59DO,7LS:d\ORLi]Q37^%6MNm)'S;6Rd1L8)nJ;j+37p<1Z>=BPUJ;4F5XpqSOsLbH&p#dc`+9bu-=VfZ,_cs2"!so9;Dqh; %!a3\:E\*j2&gU?rdV+":7KnR1[[c!C5uF#XW?%H<`a3.OHU('c/0tlQMoT?X7j?eT5UX:%3E?j,!WXhoV8V&/ZXbLN.%leBNd39K %PUo.:6!1h:W?#ql;pp[=^c?lS62OrF?O$]7KBE6;f8u_#+mGI)ktihY2`WSmaZOFTaMM8MFsE %9SQ4c@W]e-0FTBp6BF8oj9qI^7Li'b^>_C1a=4rQCEV>.#+Lbt9hm\LO\\"D>`\b>pcQF:I%Q6tnEsgVqSgF)@O`"tj@PdR,`Mtn %"=!BSGV%(]EDjAlaJ8D63u_-AT6M1uPOKI]=C-Q;.WnXcl2b"!0Ppm=9$!B[7*4^\,-EPodNe=9M$:jYPu[H.bg8%LW<2,.e:9=^ %5VriG\IKqc.'as9H4hk>7%Z'!*eu"NFs*e7'oifK:_5>.N^W:KO@IXYA>6WgP,q=6_Fl+aLBh[.-s()OP)uES)I4$Zi06h,'4auZ %%\GI'Wb:=2fMj=<;^pc.m6q>BL(GoRB=<]>58B#P7hHJ<]?\b3(^A@*Jjr94gPUP-LKr*b4"qPr*AXa=C*#W:;sLGU*g8-T.73q_ %bbmQ^on,EW\qU`QEIjr;h&S"-88:!+dkcbX",9`-?\\ %8ui:c4LpKUSH9u205ML,NAfjj8(cNnURkrHkfSVU26K;L5uDM@)I^P-d*)7Q0)iXurWA;/-7&CGR;?nQ8'"er3'1pW2`Po3Ld$B^ %+qhOA#r@kajU<\,aWYKY/Op`eF[>f*n1kf^NUS]`'F0G5!_eB#_'pr)^h>Ha9U@kh"$s'lNIhh!I+THs`NJE\QkF'>a?i/>Yt-2p %%q5rDeScCA15"G-_LMgnecTG3pBcV/F[V:*92JhkC<_(5`X]s98@6=.>X9\QOM/`mX7i>6;;@NoN]%[^+ekio/d@6-GbIRp6>*": %j0D!+$W-LN)tIN.pGL$Vk-A#giK<-S-")]aTc!cF4+YoHKrP2bfPqI(?Yd`AZK=+b,QcsA&HN%e-">`?[J;),ThGHSXBV>(R1/8O %X"?;979WA`BGLZD/_Fp%@#5%+D+^c$(dSko*,2pVY4W07F7QdB\-4GF.9lBQZ>a7Y2kato#o3Z9J4`bu()D-Nca0#j=Pgd0_+HsL %3N*2lFEhfC]&F&4G[OJe#l?JGD>X(S&dB?V'0Khr$i)f!-mfaD;Zg/T/Ibg=f.%Xe_7$"foh94^%4L/)LcdC'ltCDhJOP$:!Z\QV %eoMGg!ccF,G+D>e5VrV6ctDQa,SCAjT^*C*Xp*($)OSK-"X&*oJS1&XQ9*r=^lSf:21>jSNSsA8/(nItL"dZDj1pn7a68bI_M3mo %D*&m8E3]h+)S44',f9VXCRW1l,0gN8<'rAp#k8[N9FR_@L(*H^ES<'VdL[BI,Vq^86U_3(ZpN'OZtAl`fJB"-;2l0e$@V;BJXNfqu4Z>aC#eb/>PKOAmcN0's?pD&B'R1RY`hOS04G]ZMlN:pMYV-?34rtqq9cC&-t2,FYu&!dRU1XTV>]0PD<"#dYB`b)/f+&LUZeXAT>'8'!`)D,Wi-oL3d.F,^Bn2sFL:cX1LMep+>Ga:XiH!pnLF7*qpEXm$,F!&76n?G.`\KC\3uEkM;CInNK.&^O%;nYUj?Od1]d"`]WZ+gr38*G27)jt$0bQ%m %U5kIPQ9%9e>!h!E>Y(!X:?EL)i(UgE)(6L4ObEkf0%K8nWYV;sQ-a/cq1&l9"r8C*"E-8tV\7']3/iWp$c4ua)$iWfm3+T.cX>=Z %V=!rF>`Sp=-+"tO)Q889XtAC9_m4LiBL+%;E%jTg@9]ld%O'.8Y(8R^i,h*?88'l?2Nbo(Q2B-Q;"c[iQO-((JB%YH!pd]_e2F)P %>N2h4HRT"cF6Xq0IKpie(^`Sa:i-OO"Sb!HOlHP"SHq2g$8^VGPU`[@>)+X7Jb=h^j0>2:3/p7`/Wh/;N_I5:$\P3H*Ce<_6X;R0 %nS9WpA'["R&];JJ\5nb$/A1X$7#oGn6MJ3Io3ii-<9fc_qG7E^##TVg^]l#5mq)qd(TrN*XHj>@(5/oR;E[cd^N^5`U//'=&K0rm %L`tG=o!6C-UCluMCehn.mK;D"-^V2d:lP;oH]bA@Xur\Rro]LU]QU81AP+aKo:T,F2QZt]]<*6DuNMTG!r`t$TNj:`+jVG=Y#AX]-KJV>e7ka<:*NV<7"S-:TMQ]e[;T/H]&qH_l@OE)\9 %h3d,.EYsiDBnKrCJRE"&Bg;\)a\i6SZU6`_5$a+X)?fJ=E7b]@Sjm3CY3TtS5tI6 %#X&L71Bk;u_fk:>(qTp,L;sB2M$h@Bi(Mo,f<0bu>f(/>U*7O'[1TKBY8%P*k)+8rBr*Gi+[sXa#'F%Q(Q(WC9?N_;?N2"TT1r-s[+Cir*MJ>F]/@agG_Jr@CSo %!D@)'\Q!b`7V"6$&17-s67.h`b131=)5J<9jB7AUpK\7XNmcV^:J2nq;:Heac!cMR3@h(-FZ)5qde0F.TOh&MH]!R=!s"#W>a*&K %--k/Koh,%u0Ii6lB\P)$9B)rdE:m/bQF7:SVl?QQ63=t+.@qAX@M(qh'FQieQ_C0/1/EdV>&M.'Va_g)rY1N28Nn1/"%Q*j-r>h( %B0eGV@#kC*Vlc![f>*#Z=b1+STQ+:!OCT'(\A0m&LuoC#Cf8X.$."Q`2+J3AT\"oriXiNm'?T_LHk(96DZl="#X"\4L1Z=c*l9A1 %-++d:72uWCVQa;t>bS7>OD?]d;6aUH-]ejH#Ct96]"D\&CRN/k)%,lKB^%4N'P\I/pk5oj9uWBnF;.6/J0Dc?Qp2g)#g($M5%>VW %$6sg'+U)=I(gNt<0cl#h6dgo)!"R`e3fXNM.,s]m[`sLEoDtVW]U53_ZXUmB*)*TrWdk^r$#OP-UNKjA7:WPfH!Y*(;a;])(pqc8f@bD=+W..1,eAs!S)h!l@B`@(VjkGHCD9s]eDI##N(Tm %mL#-M&QjuQmW7&@9I$2A@#O`t2jUEl_ZVi*96:5/:IY_I+gLu0o.g\H91c]8<`?rK&1Y5:L?)uV)&>j("(Ua*Ru\+l#H]-^k8/)H %.Dg#H)Z%Y[0fKZkJLbP/#QfX"%Ln3c<@gFqN6c!EI3S6E"eRM?KG=e'a-],Tb8>RJOAp3=!WZ4c=r_cf/>#jb,P6Je$gVh"Y%[3) %#V9@+!N^Hlb,,tucThiY),Ek^SmYjB:sL9( %raGN)?nHu!A9aU]/oXc4L(bU23$9'e"Hn4#K7hAZ)5k8;V:_"l"H:/g^Y#U'(lWuU7oooq#r>O9%Y13=+T7Z.\WH=mW![TpiA#R\ %0msm;]Qmc#:r0\P\<8=PH\&O\_b`-Gob^'#"B&PVAG+57GVbt13`I"[c3fjt1e!.?**l&NZ_:VN,!&jqntIPXhpKsY!8WQaBfK^k %(!Sao! %LY,Y==$o%<$-(B#A&Y<^P:I@lTS+sX6kg)j^_!(i5)*DGZ0VB7cYfKP'W*&%``N>\gbH89>SMr)FD_up]=7VH_emW %$lu.lR]FP?i@n5o9GF9/^!*6U'L"Dh*t),!iWrQ"B]?'29,rV:o#dA&&2iO;@VW0h)ZmZS:i[$9]HN_t_`@?3&J>Kq`:PbO-2HRNh=K&GX(U_BA9g#6QeMB5o^9W;b8N_Z5:TKjn[ %"`FSj53gab,0pfX@65)il<=t3D.(?<;\"m&+@!gg6;"9_3RRK/>7Z;c^]l<2-bCCF]T8"d"Jg"cXKJ&!. %asSFP)?r)%`b.(mOlRFm?k78qeO4.M?mHS#S3Xi@r<1_9kn=O3!+`&q0`qhF3]dO?95MfT,89+^3BGI,Yp+ZSGVM@9kJ=PT86@6j %5@N&mV'S6SNjM`t.p*h:q)@CSAbG:SL1\ODRQT5U-(`+cO>OODi(H,i3%sr,>aM9$JZ0 %.fg^LZJSKTcBGajaRF2.'R$7h[HWe#@8Q(mmlJIW!\$FX@@?Ir5buLu7ir@A"n[\+!GHAe?%]a,/p*RX*nKm<(rI"Or.Jq`N6r&F %*lUs2[37+GC)f!i;Ge.UMh0.tg!sUG)Jlj_+;#cRR-EGj93Fi[BKW0J;R^"t@=JI\X*0qb"S)m9(1cn=h6&8_Hli^60Rsh5AWeJ8Z54`_,'R7La1b#d^9:AHq(S9lJ9#JA# %YjL\D@W7BD-o6 %;"g7n-sRFn#7-U"3*=&V3Y7TglHMm)M?*#Z+I7!//XV(pC<:!:0_H@Z3%n+Zgm;+JaKDCW"QcOa[>(VhnkNWtr)%bZ9,FU2WLo)%)hZJ+:Rh=!LL`7OLdH8eD6rpc`Z'8[S%CS\KpQW'fh]oXRRrZcZB]r>q.jB&H %PQ>gI>K6=B1#6:q38?8Q\[+tq^;UY68>4ZhL$[cC=g'S,gE,nK/G`P.90eC]RmKUsK]/U8#,AS%_[2LrM/,oI^DX0_f-t_6\e=N_)-b:CjVr'=L34PmE:T\sc26^)HX%[iKm!kYn:l'8rS@ %3B3P/(,;a1C^;NR'Lp.uYTZ>o'*+aN;;e>AW?+F*]i5]6a]SWGcjl1mVSaYJI+]KAl4b7J7T*;^VF)`e"2>B=7V,3 %=CcqqEi&ts+d(/4_+q(ar;hS20\T2fMrbF]_,:Lh<27ZhN#F/fBY:>ZR<151,52>:6X\.K+''`bd0SA3C'&J+PpX1pTYs"9.&SM% %Vk+dG]_*[6>0JA!TJ$m3m%+P\G\lSqVB-c*OR_8->J;QEdP6HSUh>`J4h:VC,&r^NdpfGI,X8gSp]R4pGiSQ%Nu_mr;ioc0\r5&\ %OA-aY$p[a7U-oDB'`dtk+(qP5K-aC"=%EW16.[LV&$04\8msHN@P^mN9O3\(_YXrqCXB[=*>#/i6GU6EUZpTDIH?]1"Obla,](VX %0he!(Mi3d.!kTt*5f@.@aa>EOr/dqf%c0W,1Ncpmci=fE[!XYr[0=/]k+TWjjFWS/,='3NbAE&Ij4iR!LB.Oh\f4(fJJb0M%G'PZ %&W%Ya73WdI<:eS^$b6%jB!E,/AK%)*B!S*TPR)]I(JTJPVYk`)2l*:p@R^sIc\(gRTG^-&Z6L1"/_U:d4[Z6'&Oh0lr%,"0TjuHbO6Oj!@MiT@ %#%7=5j@'+R!aTFU+sK:j,>3qBjaYY;5l$cY*:&>a,"0XAR$:%I*?5,T.<#M#NbFMQ,tmJkUb3R/N:T?3*t!p\gQEj+5]7*F@N8!E %U&ct;'7aop_I=(ELe=#t\mBs-7Q.buYC%/HN+94ZJ!Wt-!Gr9h5X?MaO@),!M[Xp7&2H1l53joVLu)CBXPKd %#/Q[F821eI`Lb-`OCS?V9=QH_TE>6(:]!`1#U_Acik#oWZ:9[B-r)S]"nsA$b9iKs4d!00?IR=X'1@/o2$%_HV',X@7F1]oj?Gu$ %F(3uIeUjW]"9J(pPCTe2KIq6G+cV$e^^UnF9/1=>Nnc(Q+Y=%iK.EIs4FMma_@,-C"#b?g(/KJ!FN(nC):]ecM&,8Z@PB_FS(e-9 %:=76ASg[\G\@NYF*tdO&J/VdlO:&L$(o8JV3^TD!O"bhd!_-83$eT*MAH4g?AtqfUQ@])EOoV`M"g41`2]4DH%^6$S2`V9i3Sa=E %0ms904cHB$2M0a7U4h$b=L?MPiio$""1QC6Wf>m:OZ`d8,We*M#NNueW5-=Q*Bk9a6Ottr!0Vt-8\mRs;ImIBTB0"O0ufkrS>%I2 %l6ZS4350d.97S:r6t8NdjG-F)aQ#c%faS(G"9U7'#mEnt=;'PF]aEi%J4*I]B2MlcO"FtY657Bf\)j]2X>(:iACZHSc-YXT>2.up %EM4Z"HR@jh;E4s5Ulp1g:ru9c5c.A(+CjA%-=W)fr*ZMd^_J8Gp0\U0E>s+RM-5%RY5EgE1M3H:49kl1M!;Mo&VZpsTXq_mVQMb3 %d@piA[-Hj0e%j=KO73.RbafdUL/+.i&k+I+Z;":*Q%QhHj^g(+U5*dF@[RN*VM,qU>c/Q/>R6hs.6N=;k6@I)P>qg-qMGi0utF\b2G%H1,'aQD8OY)!N"hX)Ka%]P2]+Wo]hC`2CpP$>"Gl/JJ@=Dge^s:D$5h(q,O@A %?neXmjE+^h,@H-)"JQI==25Rt)oQ;i\dG@q;;$:8,;')7,/`EKi*i8eS1J83gPe7*'=):;&t`$eEt!4D+jIn1D[52E/OfuO">Yp4 %M:q*N-#576&<]@>E65L5M[JL5/BJLeA8\\fHhn*U)cMkUA1Bhd\25e-0:9^XZ2b8D^pI3),8%^ncu01_'0<"l(MA*k*'05$S6J[C %_WAd6\-hD20f1"@C`'=N5Gg3t:[\dlaUpmHaB6ha+hJ@!91dN!$u?-$[CL;eij608^M@U!+.!T>.&,;Q4H@7eh%@*Jl^9F_2kPBm %B#Z*@[D<-*-Bg1U)p,VEanB,*CDudh=P\M)(7M;.WQmN_N)'RF@Z/6fBT0Q,*t.,O0eh0$"!)gq<-tGa5N(T8MTnUY$FjYQaMMao %MhpT5/?kh5KBY^^AW;CM>Y_+`0>T(VF^6K.'SKZY6DL]Bb9A/Wg)!))^Sg%PcrTLPd\a27M""7bFc$J^oWE^Qc7q_WrN5Qp<'M^? %Ls.Ehdp"HfR:,,_$2>Z8O!_l1n8,6L"%H^_B5:memqC7C]H5pfO]gOYoeU["D:?R*gH>.e\SenTa8CmYaf5=UG0AE^9hi-9c/SpTl+uH6T`<;Ec9R11-p.9T_'9FZC&K\qdA,]7i4XfUp2+jT$C66m<;q*_K;cp %6T"TC)+40("9KIl'bi'9N$2Kk*sP1ln/_!c:]1O.\hH7c^cHHfLAYh=M7ig"/sdC^+r\k;a)D)"\uYAVi&FR:"71"k@M")[=gp_\`eYdJKu]%3=on[XW=H^AlT/ph/``5&+&N^EqB\3gZ=IW0>Z!JP9UOIC;O[^?7)1*H %@VpP1d3Q(%c6\1hBM2"MdWq/j>3U'fU!)\(O8rZe6F=^>+GAgsi"@rALrh#(N$!\Dj"/+/;3#0P:eU%9DtukeGA;,l3#D$+(1rt6 %1';8Y3)X-B0F4m,rdNU5CJK8Y2WMb&^'Y@:kD/:>!1Y-_4,D.Im>387T$S?>K[C\\fsZXBQ/keFfd+s9F.^ap)"B> %`9e]'!Tm0VA9Npql(l!0dFNh!$*m"`=%"=d9$5Yg2-?R:aAIiSf)eNq6:2Q'4qN06N\sMJ@Sd"(1FNNiTR-mM*1ru>iSnmF8>2EU %a5MbpU?l7*Ohpd=>aX4T1fB@99;s9UVWl5"JP=`.5!sBId2&$DV,pP_jLBLCR1.6KD^X9@.%[(!2;mKLW=D2TE2%3f7$ff[Or.", %Fg;*#CW*(4D=h'&k*/u@`&/4iNW>Jdc&up`i3^*&M]g\=lm-gt>4LA\^'(S?\`pB+;]#os"$^6!L;5U5;Kn\AR3`g[D'eGI29$+P %bUPeYBM3NjfOlKkLo(/%98Bs>C('Q2;DCfk/S7G7VN#h@1XPb:7CmiIHgZPt2Q#t]iW`LtOmS3."F0&?UGn`NnNUujOB%?rT[AThN:l1R*>q4qo %MjnuR%uI#QFsn5bs)Sg&WM`\pQZjEBrnV`Le[=jJRo&b,.sRr[$s/?3N>ficL-r(Cc]mF7("5#lOF4sUm%jWY3oaq2lu3.:nu.8Y %Wl>T($'LXS/jFJ=]gt_$Onec7BGWfDl.+(49,Xf^(qg:V@K7Z,7-oNEmfeQY"(+a=Du,r%<%h!@IlGL"aY-H:idD;aI9OFq,3ltq %NhT8qV;F0u5:!#E7KF[%Z?JXO]43=1:;s<),gBh#ZHu!!V/e*VQ@Mfun19Hu'-fgT73pUpe`"Z]$)/]A.$ktkl<*YtmLLT"q5-lBcM;`J=ZKk&iL$)(r1KQ)dD*L,UZq9d^7.gUQ5&c>aSl:abAY[9%%5`rYM[e.d;'XK_ %,\Rc&$I`Gt'G4hR)'d/!4X.q4-q9.21Id>*H]/l+HU\-YgIplZcj22[9^Om&k8=IXC'cV&:5TgXh"]BejN^E-1^"ae,")%`8Ji>\ %>RsqXEEj(m]o_N@#*RFL@h:O^/b0F'/g8s\=Xb%d-Ur0@r1f!r"o^mb_-1#)TU?M$?617d=]H.nc!56FB?I %%eu)_\i(aaY3Q9@U(Jot)j-iB&=Ys>pBVoP0LXG(jU\,)d5Kuu]d)IF",(Q"4YnAQ)16qtVAYYgfWlZeiHE=<7hVUf+=JA#9$fF# %D$0)3SB(]IkQ"`T_j:r+d+.>!U,OETJYZ[DAD]_LZR!ejg.P"H8DlV0k7N?c9+:kF,Irh&N0q+",+<@;D_dKf:>ZY?I2=t`;9P%9 %d26`=BfA#>/_-$DeKacFE7QOh=3AgXq3#$f9>6fE'_:sp;_6VQAl?dsrO5\5OYXVJV!J\bXZ;3D0RPQ^:K2,Q*;P3D=#b%+T=X,g %r\c/Z(VPEg\RN8-XS$Ab_cE'$Gn=!haNoY"]PQ,E^"DcpT]kn6\^k>eVLMgpbW5<6K.dZS0-'R]"2Z5$]JP;[[8ARdLVNJi"/U2W %%sm7/Tmee7rU!Xh?Z&kYB>no+I(!0qX%>5EQb!oFPKa)@o'7MpRYVI*4;jq[7\3iboDru %f9q#seZ*cTe#J_%\XZ'Q1/X=RO<2Vj@.T-qEp3mk1==1s&U72c!TSiq8B@,3!C]Pa&A8JiN!2]&8,!h9OE%X-i,Ar;R#+fj%KiSJ %E&,A()j[/q6M4cNEQj"?>E'b!JB#CG&0^9DE-T0;@gTG:=;PZq,B5)ELhE'#5'1nFOsUgKZf.a:!LC5d4qCVZX,86[+QK8R@14Vl %Ik6a+!B31m5U:HBfF/tOE?uFQd9,uEeL.5:EN%qOAAoHd>o!de&@<>549iBsT$J]G/BmKl!-sW`>bW[0X(o.Ad;+Q.P9;bS7CW)A %2e]8mb!X:hTbn#KA!*jJQ*;QU)'[6@D:lbs`"[mF7@19p)_EQUr?fTJ8=+B$SQjN*@CoC,/p/'F$lgUrpcJOG!:Y7dAHE\k$,R&R %PTNW8,QVu:LquGX(k/n9`%+L"C^>M*C8I:;a93(O^4n^Z3(-:Z[sSD.`>AV"$=^.Q_fJ.T?NMr<"BQ6:>H#msZt6aNLjNcn5HKGK %Eb)PrO_?$7;LJZ]8g>#?+k-gp,"Ou[paYd7fUTV[AE"+2kCR`]SKSD`V&fs/aPjS6D!'iSa#p"[nkF=:8A?%&;=J$@i!8qCqPEilj,"a8e+3SLT1]f4p %Ra$p^I$=.W!D1`uH-Zi)D6sTlR6SjOcl32Y%&L)_=pT-cki,RK$\@Mu=!9kQ)!E\?e;7-:OTEm3nJ3-.65Xiu;oOfh`F\4fc!XtW,iQ0C6'!(1 %35K8S&FOGH5!u.Rem.QH])%\shLY:>Y"g[%J@neM0tRL-*risQ.XR%3+mK1A4ima54^nJW$,Y:;@,MCqSr"=r'I_-^D5^Mg3eF<` %Nc7LC%RUq[G*rC^VW:Z,d6\DtOKCqT[:"aNJjRD(QVl^XfCX/s(KGh&gD;M]lkSS9g*D^#T7CSg+l+H))r4:R+^Fi%#"tSbNYYu+ %=KR.O!2D/[1I`/`,_1Pgpd6i[6(5]0=H)n&H]6:'V+\uXmVS-Z&uU[#>(jAAOC2hal=b):d3l<7BHI:V^*QqB_nD=,T,>N!*dWdn:GWlUqifVKcjI>p$,3oY\:%ctRp:;l;hP_igt#TLZHoAE8]WbtJ;?c,Q'ioN %VS^MJU7j*j77?&oQ7lB+ng[QLIpsXCFSYb'j:WW)-AbP-8_^9C0JK!HNW"ufX*E1ol^^&j3@"YRW$:uNjZ!,tMPJaGqj %F^6KQ><,VH*.:C54i1DWkNa+ng7&%r1hh*;U0`6GUMndY5?E@3.)_X;_68tB-F<+/u[NU06BQ$K35IbS:H3B%U4_Ru]r!,6J %lm6[>)3bqGf0UB].&&]4?F2!EMYd[Q/$kTYcOe/]U/!Deo(;Mli@d[/\ZfV%a:?5nbm!O'H6,]40nrG^k\El4Le,!RlL?RFX@NnF %FlJ5@OuH6!i(q>OGa+d>9aVFLbr(qT+DUPhBX.XRQ,.L8A[-''H&8_tD2*^#VHH'b2NeM:GU`"f+Gp073s\E,GNM.;lTpR-8HoFN+`OE %FHVG\,ae]4=>\l2JpfI-TU'S6C2Ae/lEdhLi'Ab"59G8i^E/=PjkR`[%9B^uT%*)AC(j`(9-W$2\Mf*kl\KHjRe3],npI5i0eV27\#'PO?8`.ZIUImE$P`(12bc;_j&Of4PlQ;W`n53DGW3Ul]q %iKJl="fee98BESC!&p;c&@<#h$oHi!%Q+PC3Y\5+Jnt#f^.pB*V',Uj_+T@B`R&`WW?a`B;GIBeiJ;MBO`g`VVB',N'?B9\0bVb\ %h?u$6>XrlsW]C[-3D0Em0h1UE"U-,]>/&WbJa!7XRG+FF[?'Pb5\#S[tfs,GK^&)3B8>!J9, %XRkg%BNMGOE&%Ape1O,lL?sT/;A=A7!BYt/2?HB91P0%d^m)[,"?/'P3._aq$9/)[E&5>A7/3&NcQ-UOH.?uGION4CndVCZVLX3* %IE(UJ`^@Yj9Xkehj+5ST53E#n!MaXDGl]OK&V94Ud/]5%cghBpTDMp]2A"">P7rq<]>8s5Jd=8d8c'RH)V@rZ*fQ\F`eP^A.E4,0 %iL\NW1V:gb*.oeAQVbhq3Ec!=RQhaMISfjY#.PcX0m+U=(p4`\Z%$"Q_1e[(;m8.g %"IX=EZlVTPJnGiDJml$H:MQV-=&&r&?YU_F82.st_aqV9b2l<)%NHoq:\B[%cb[]u)EP4B=(00r%:@_<4b2u+Y\cer`#)rZl:bD/ %K"1lQ-s'f^W9,)6`Y%*^@5+>qQJL*Gbp>Vt48%RB32VmXh-X %WP4on+3br`=>5ET7mZAGU'b<1mLrK\U0T)Z9I;&rE,%1n_lu/.MTSO6Oit:gN!`>Y%Zkn&9W7EEJKR*Dkt\rC2)m"Ze3^]Lf2*NU %gX5dp5Bp7-!Z:lWNPWlJ'uLbP<<-If/?TN;j;SH2&CD[H0WAH8Z*]c*XQ88`LY;dkgdCl;[]iqc_a1BF+CE05hR67COA7tDg,)p7 %;1+FO`pY!)Y]"_D,t_E75c`N<]50c^_Mic^J+P>*OTAu*7Rc+UV(s:VGmCsUYUPMA^o`;sp]`CR;BnmKfhdlI%"I@ZZ7-%sTITF2 %%0pK3P%$T57tFAqRkRat$okC72F9dR5CkoDQ`l/W5ZCDu_Ogng`e1J-X7(nJSf:FE?qV9?n7b>LQYC>9[LAJI2O1Y3]<'R8F`kSZogs,,X]('Pb'mFg^IhF?mcG&Hhd3A#D\cB.aE_!"_NWdC^rqREO %9L#&la>fO`PdHlu(C_KjN_lG5LImaZCd#$A7BIq;Uh#:2=d>^kGVBV/t$nr-_=*&5fq6O6E&-V'[)JNS2FN1m+9%Q9 %Vu-JZ>+D,?^TPW)+T@ZRGB^A8a"2iF3oFK\%QC'C#o=UYo]V&RWZGG+6^0`[`t3Ah?nqp#^(I,Tgu6J9;#:C4GsLnp+,6_CJe^&6 %r6_W(7/`;k"r+ibUUnXp7Q#/'W>qcRaq&th3M[*?6p6bVrj%,(O=C>^UAXL>PXOeGLmf_Vr+/<:`*Dcfj\8FL*;QN%h\.%\22Yi- %UtgH`.?q-%-W;I*HOhNi,#(e+6WBIZ+jki@RMe\XA#P,PhH(;8@>045*`K1?& %;%F)DV%5<:_'d_Rgi!2t>b^Q$0LoAtFm8%T,qe?>b %$^YNsW@aLi:NsH)7mW6]bf-J"re*.Zs#1A_&,sNqIb\W:d`\E1NN]eK+"sCl%=,)OO1a%=IBp!SOUmKNF6E"gWJ7a6YB;WXZQ]>J %5Pn>Aa7)t??^UNAN(oLPrWDW^;j6RKIsu@"n+$Si47?oh_FJR$b8DaS6u>opN3f%:Ipf.>1H8Z4-.%p\rp&!m(f?h#N#\*s%3LlW %P^0`j9(L?YYUnh#_>J),4Gr$r!>E?k3MET;Pj50GaPQG7O$;2bj.u-(@&7sN&)dJ\A?#m2/^Wcjq*AiYN+' %V4SWm%Nnq3$&/KFBJ)s\FqOW+0`8_;7,T/VO!!3r+oS//n5944UXko;IaMp1dDA,4oSN^QdK[Wqn!:N+iin2uq>`K8p7ZlE=0KPKdr&0(lMV" %YWp\)\DD/>Y86iaf^BIMJ3h0)$FM)FraDqW<[)N`r>6FZ?riBKQabJQ336D9rY2t51UeR0`qL1G>]=(i-e6:DZIZ&\L6.a*u_.cKMa'Wr[Tt_5cD,&9Fg@rX)paIqe>!jpn %H_!Y%48fK1gbdGs@ZWo=.`C`!^?G7QBq=a,I;/@9'CO[*".k#D %O"']]@ct=o$)Yc#Rh&`<1ER[>T68b5pb"N=*2L@K`S.?f\l[Ya=K$]?s%3 %&c>^'mn[$a%ThLFI[uaJ-i;o8&\`^rIE!p8kOVABGc %0Ql5JjG.5LoB)^@U4]F^2FT%KB1)5598$I]D@f=N.9.reD59CT?;gjic&cHRF?nGpj_L>NGBT %aue+LRd0X^X^(\6lM"lj2HIDaIOgUGImG'0=:@M%=c;O>#QOB&$,L!<5kJe %Qm5&D^qo$HJ:JK(%XU>j$`a)!Fo:,t[esERr4if-8e8Pe%%sOVmUd+?D,5o-l5Lo;%MVd/'L;^BDilo]<'@s/V'E^TEqH?qCt::b`#H4L"r`D&HH&pSUX@O*Cia?cTQ9eb8HPiQZ(Gri`NmHW-jAO"1%`<8!n$QQsFAOh2GE2r"!^1-3mWoSr$Ba66oUaY>; %Wo[$ar*P$b:UU,"Q9Wpjmnuk6INd>2<6`2$PrF2dQ(P)PUI$c6:Al9ao9BUN1[+XY_/B6(A\g54e&lK$]Oh,#.e$5LEL-KkEKEc& %7qsK%Pa,$\JGOKCL\5#YO)F&O%;;eHX0mB\(,@`^rhMTTmhuriUn/ZS!QJ%4L1OM$4>B!3["bTJeF0N1:5stBTWq>\@KcJ1*7$<$ %bks2Tcj]Af,QI`I6?C8;=pKOXlel^-H`*1GWI8H5A\m1YCYa)#Br[#eh8We0:>1?U#:P#1f$0[/[(Kn),Tq/U,\dB,ss[cO,["o0G`16Msu\ %VSc+7BgHGWF3/I:/jJEW$JX?!gfiORWglMp:UDL]4.rkCeuWBCK9LYd[>[CZA7IK4kIM>hD/hbj:JO5TIro_co4\$"k-Y$17[ %@EM8Hf1XVm",HPdq#./jJ,bYglG&L)GEkF_3NQLC_!:K6.dR'ZZL72AB"Z[.H/(!\H5GsdF+=ke?Uia)A'[oiHh:;0jiN-q?bNTg %-9M'(rE*nqR2MC9+Bg_K6G$1"ob&%FDS'9Df?T8]bAJp);mZG)pAF9M5=dpLWm,s9lE,\^^")KR&'UF+e*7F8> %.rc0e][7WV@HQI8Y[40RNPDOBlf,N^IAKrehWrIbm8.G(g8:Kj8IDS!<`^YbXs^/rhT9r%_C`XIpnbt^V^_t:B%e8%+^ %;*QnmPh,IlSa(Z?0XJ=Wn8,F2c#rpY\fO[QTU`]3[gp!LFhMpcbn<$D;nMlk$*[%ka$C]8A'aYjh"sJ3CJSXLn@]oP/`-25daX\; %$Kd3!@"3fl:78YFgrnaVQ^\lI0\jlfGj`nG1_CcbQE]MX@H$D7(03EU0dcJGi$n/8qMD\86XCQ!6O.m"&+[9Y"N4qbk%0>1&:]gF %gf7]$?Whg7%2X3nC^C4;Ti>F]^p;o,DdBms"#g\cl0*Kc]\S*Ur]_60jgkZ6.b2]?.s8!/WHP6Nm7@c:S]h+>Wqg\9Kg1jP8F+gE %Dq(9Qc)tXg^3@app%tH:H07-jp^NWr[gR]5(t>ME,8gA*4OM,kjef$LVdd2DSs_`GQG:saJ\P[7*r^W#\,-f>AA`k$c-6.>Cp_^< %=f'DelfU3Tdf39F28o(#e&<@"+)=leW]-s6>)V**plt[`s4./+WR7njO'96'DrkkT2&6uADu-Tk\<43bmu:uc<`s1W,O$NRqF,%p %7Fp/H-DgekVE2iC8B]!Pp:JW9Xf<;<3Pi2(c#H_K^Vl2CrVPTVmqDOsk"M.PB4@V(YF!QS[7N:UXfM:K*V>V8'-D@1qB,0>pq4aD %YMX6s\")fWgP7;m15LX'kG][al$!!Fc\3:\:Geu]TDEu!`VodCF'a(4Z9n;oDu"IK %NV["3M\Un<:1-,lIE`l,.OI12qF03/^\oY2$Ii-[_/3P%+sbQ%@L`4.YhK?i5",nK@n0)GJ$3e_%uqItT.quh4.1;0IV %9t+$DK)sq+ptma,R2JA@NR(_AgqBf^Ps>>XKALckP,07Jk!,/GG7HO?9'pN&4R,b_^\FP[W/YYapss]U?X?Gss8(C8nF`[6W:q[N %:j(7eG4CRHQs6\.6okTDZuOGPZHX1r4`R5C_O[Wcos\9(WK\ZjI2N6PnP(18`pI+92!FR^IU46C\#335WEjNX&SX?;5J( %@ZBnddoeGlN7a?G\a@g>UEL\`P?PF#bMMD,C3k%GTp*$(BiQRj^6O$X_``>NEXul&BUdC",548fJ25IqH:"9D+:R4OI"bBW_06Q>(iLFZ\M]lj]!1bfK]olFjWP[[&oY7mH0lX59VE %LYR'?frOeK15@h>I)%Lm2>,me,^GI;rjpX_>cUZG>^"j\*OZ=nM9bj6qAn*oI.rq5WA&su\Xg%[R?d6@hg.0/dnAu:Y3j6_QFm+e %G.CgWP6e=';gmiRUeNKKh?U/IQ32kq;/NF;\$n9oO`%cT1Yb#nh9ntJ`.;`[A$n$IS(2i"HsVj:Ij6uSAk'D!(&,B*A9Q.ubFA'6 %/T];,aO3)R+d?Zd_/Xikb!]jNp8up(,Puqnr+g!Y_U\`GST;-,g4@LhmlE2sn=Y@_RZ>q%^?tCAqk/)^7@!lE63;4hm,%=(bUMKkZe$K4I373--,^abDu8rO%@#?)J.L>E";q/F*)JPd;Dq6WK$[IC,frDg<#b*m=>PVg)W$l\D&=L2W\gT(;m/;sr"2EQ^_pYf4?D+X8SK9_+:1hV"[UG5-eaEB3tk\Y%QM %:tgKF*H1P\[jJ+QW+NO>_*71@uP5Cu*Jda*Z\lhU)-Y&48QYAeS?C^$!hNf.hk$hZXEZZ3uA5O+I`8niP,EQoQM)[5LeX-%=NureihP[#\drhp!?klo<7f77M8q27;;(jSlI=*Md2CeD>kY9FjGS-A]P6&8H@PLF,m32@kfpSHEUhH4gG: %m^%c1-uS%#cP8=,Gd=:DO3SM(Fo-::Z:0IQ/4[/*jj%H/HKcCZ5l9PMl)%N\B,JsI#b:F,#FY=2#>4ZA+F*S1`1.@NW7Pr=o"VO_J.?*,tf'+^d8]rb(f:"0VH*K?2K^.rkY7Im:0@Tnu'm'kXNiJ,YQQJG(3n6KgW>7-!REONeP6/+WXM"u=# %Z`@'3JZ8Rq5$(\9X'`ADD?&N4HG,n8. %0N3aTLtn54GbI*\jGlZ`Rrg0n,6i(kJ[_Wk2ThSeWsRO/)a-)i'"&M-=iML%Ce*Y8>/W-ieH5CeZ&[]ME2CJG3&FXu()V$B4DX"C %46efT>8Q[2RWW+c::E&Ko@A2tgD_r68OaV$at@FAt?a3=0oQR!.P,);Sf3g?g34W]=n8Z'kAjHG.^EXu)K`&QiA?0q\jI?it'K<"'cG+"L?);Uc7n>T.?mJ^1\Q9GlC"07g9,ug))2(KC&fSeN %iDrS[C$>Kuim;P$^O(8'je1>s`=TpT8[h$F'^=r7G%3-CC=Hrs^5ps/fV[*^NLNMiVI+7T-f@P@hNN?F0l<#AhKD8.Kc:L@1,5lK %Opl3V]/c4]ZA1697c]jR_91%8EGTHT-oRI^@?4l_NVXKs:c2p1 %.V$C!CsX-ml.DYT1Qd]dH>`Z&-*DXEWf1EffVmS?M'$\1X4t.cin;_^S9qqk[$5i74'0RE\Z_C\LUj0=qZe5*1Z,92arg-NR(tmZ %"&Ni"O/oaa^fC*kK%V@r%5WVX]LcD>`^mN9Gi2JZR\YNB#&GSQIqKdI*GdL*88OH@rEI>/Jnc##r9'd472WV-)`2XIeYB$>tJdn>"IN]NHc-0P0A@[(+nrB\n*&JhJY=k2lIe)]9t`9J\[To %'ME(PQn:_#l)T%/o=,&"g9#P[WnVFBMX<7p@,O$K\hY.0eUZ5+ci*=GjWQU*BW3/9@G+,n^:X:YagPn;/F='FW2?^PC)6cd3c^l!kg8VZ@d=q %/gB-XWF=e*Wo6HGhFq3?!(j6;UPR3oja+D;/_nCOJ?diN3ptRg0Ha5U7i5@Vc/6Gs %V;j)F!&Ei!)5Yho==)/_`'O+;%eBuT?c8<]=fQRVa.sEDk!B+$JVK)j8h/3tT\/Fs.n"mLmq7BKKDI5f,WV8/,X_Y]Y=rMBP %aGs4?=1\(MX[enAS(qj8eS8XT,GdQ%Z`3$Z6hm*ANe\O3Rapss=',6Lp`U\5/:9i;0qOBb>P'XuO;-I*f&jSQH;J]M#@-o]Y?g.2 %\aCB2Em8T`keq3">FX<$QWU11s]CfH=MP>;AVa>Vf1`M]5](VB[3@)WtP8)lErkK"Yf10pEQH\2oR$^)j'RS\"40: %[j4&:`d[1I!9`kg!IStZ*Z0)fNr%j<023<+m/(>!Y/lYY#/*U0]MS#m!,oP/ %rYUU+aQ#tXf#rI)`r2Ue\6W+/[)b1W:cg^B@(9cs %S;*7Loh`i[jEVD2KiN&c"Xn'/O1!+bGlQ'qrC?;mf%rN;]NTcr\77bWcfR0t>Ks46bLX@td_3W1a+\hbWV=bdrI!(@Yd3eFCOONS %[^,ehn_Qb0VW*q#XhA,nFIWSjd49Yp?7m-,2c`-p4kWqAm.<2^0&FY,Of5?c.C>l;7=;0`#M<2eV8=[1SbE4&lKPcs''l3j't]3"W:do\7"h>5%8/+l\"eU1m#g#Hmm %iPSB-pZU"+)#sNRlHYYX1[D07I,)fN.Z^&,:\RnVrT8m5Fl-Qo?.c]*A_UJ"?86S`Q/YR-6qs0N\E:Q2NTs-LYZt5pC[O?Vb:_B: %%KE(m?ZFoL;DG.ZV"V<3C %.c"[<:@F$g[,JMcTB$j\tU1c-s)NlBdj0E$b1-cIA'JGbZI,-??+$[trkHA6p4YND/kDl_qrS56,b$OMgZIS7'r/[`de2,X! %Rr:MU]?sPF]/Hog5@$dM"c]AHF'5`rG4FR*Z\n@R\h25]X?Tg86Rr:&ccg9uR0tBfkNu#YUYEJ.mB#ZfH%mg`\Fm#&(@TB)6G=Ih %8l'$h_rL>@o0`ro?-UTr/j-h8.hs"8hUlgLe:2Kp"5:$>@pbSf\b&#?*/:lLgnW/B %iQ=60[s;km4Xnsnh*n)^n8qY@KVh+4`pQmqCraKM[[-ksJs6%?WOVoG0@SUP"V$L+2IGP=5NVW3Ko;M;0OL>O98.-P'Ue-G2;$&1 %]Vf\B^:jO6ds(WN`t+c=`\93L7T%nnDI(T>e>loX!Jdq>Mj66g_9o.7\MoX/Wh!n_JEoZ5*]3O%u?G2nt?!B!@5Nq@qCbE/' %>,=aGb$2:92rSB1qWfAO!?R=lR2GOjpQ'M5H[PZLGM)+AM77h3I]h>\A&ggHHErc^EI--'R>nIjq>G-ifRL88f_p,4-E`:TO,=aa %a.Eug^gX3a2Lk0&5==tr<_XS`pH8+!Y],>D:lBppYCR`Qb;Mm=_U5)=]78%[>&q"6("X^=m)>+W=JX^t/>Rk5[bBC@mNct#l@A7uI+U4-)`)SM++Tsqi7P$Mnf_6a$S*@/2>mRrj.R9VoDA<$2#[8DIIC_268@s_2W&jiY*D)]\9t!gaqpVuKonG\H-n*YR-+(cm+1OSR@4e5 %cATdk2m6iF8n\+_-QB,+_7bQIPIJm".a(#7M%_VC+KZs/gSp_PM?/Pel3^jRWFfb@'UBP\n$*N(k226LVYntg];;$GrGW#7JE-qe %TRnjR)mjetIl"]]i=oW]qs),WP!?#/%U2H,F%?2rk2sPKnm(b;7VO*;m>+Y[3$0SVoRcK)a'WobL&:tm0J$Ke',.,t:lrPW?Q'S9 %r7/sU_jpa8kHMJFII_Yc#4GJ,QYGU3Q4.GI1U>Gd#'J %c*%;6[r(I1qshhU86'>82QdHW3tsl<2!h]^rDN1/S$PH[Mmp?Fd@@mLo'I)hc,ofph`n3N@;smj5%68?W:;V-9'p0Wm!$l/]I2DS %Y[4udc9J'?]X$-R.m\%PET(Zk['3eBJ%EL4Q(4b*/on)&rDG(BKG>SeYJJ%rS::-/qZ.NEG%o:ok)9a[PZ%$bSb@>N!"PSZP\(NL %s%2H>2ULs)p7e3DW4Gh%I't(3[ncl0V2fItL:-.0V7V.uDuN*r8YJ;U.Bi^JWd+&[@8$.T;ULr:DUJhZ9bm)HO;QTI[7AiDcBVc5 %o^U%UdUqh-ZS`[["((r6`J;Y*"2D.RPCI_ZTt]Wqq2uKjS($'GX-Z=(V;cl&IY]n/qiTiEhjbc*]._GkOcGs?3EYaQe[cHt529@F %=Pl/$PF_Ad8Zgq!Mu<8)JeIG]/tt/6!g]-Jq.o<\^$#]<#V];Zc%Z7ca@hofGEK:#Bm`;)IFiBa8a0gak]W7u/Nc*DJ!Cfiq\uRh %:@B_+n`jGdn,;%hjPhV!R[f8a&La.H_VnoBeKk>(F.f$bQW#b9aE[?F8T#E@83d=#XZXIjrh %1>(Ui>(3R-h:Uu:n"*hDI&_!K3dX@d&c5bj1Y?;li!$em]j@eSitjk1KklY(WXLQRG#j(p>/Nhh8m:eX;j(Z;rSEo-80#8'giH!qn;*X-b89n %%60)NG&ZqdEuWqOG`t3K>lM62rtJ_:'n>gCX\2@#U$Lq,ZJ%%F:R$D#iH=C$okHMc;JZ5H;_@S%]oB5A.^eIF=K@orE8J-117t0i %1/0@*P/iS)Ia+2@g>RGE-hD_0gPWShpF?[JAN&FZ)Ad/'>efm]=8#EK<]d!8phALnej["=K3.Ff,9('TQ"p!ilX5p#fqK(mHX&&M %]^+?mZ#&PA_1TP-(;JEZ8l;+.W;!N\Y/o#7UFRRiD+3pIomU"X1?[gTu %9&;D1i:t^;UXIhJ7n;kA(-ZP`qU2R?%ZR^A1pADpO*4B#G9n]A-'NOAH))g,4?>/m<\?.AGsO8D"akOec>k>+<%O!jd% %(@r0PbUn6n[p3jd<9hM94+?V1NAfRH4(.ARTBk2AP`"r_N4N+GK6o7Eo;;ilgl#+;)LL_t(GDi.C,9+Rq^g7^V5[%0/;GZ %k:8K7;RQ7N)",8ap6Xc-\C2jt5H2t`]:Z]NV-Kd<+3gLYrM!=7>^7hQ$d0WL04(_3PGi*,3m*aHc&9-\9()2_/EiQ``VGR]]r.^^ %HJY.Y\AdiU@s3D&1_/ZugjD3e?Fj*`Z$jB[\7P42XY&BLSaaYJ1#mr/H[_9JR3I*9oThO;2=*nl>DGc(luo8?lNCB$k1EuK7#ESHDk2g44sj842q!l.uB`U_F-LTT73O+H"< %f/KtSrS>ml[JCgQ;%u;<--43b`a3Y!s(X-oCY-8=G3R-HU8PpPB3(uE)\$mIY1CJ4$+o-.S_*BbNLM&^=f'ZfqiLqEYfGbTG,KX` %RX;:S`YTNelDkF+i.jeike(t&g7m]b^W\Y*#@Cr'd`1n(=#lPnrISs5A$;0reQ;VQ\YGYGf&28AG)P,"H;@gNq5,'=_^g/lRghk\ %!;e9^LMFj&Wk/dr0=T":6.fMHCnA,.B5_L.r92DGc1:&bqt]BkUY#`%HRpLl4F?m)X`Q_g[nh,^4*Ld6$5^k(R9IPl:VJ<@Hi'Yp %aN-Q[F%_B%#aFF#+YYj2Y^?QMYk2o:o#2LW@;6Ec6U1gpeJ$aW"hRmp)V"$b0Fa'2Ub@5;^ %5o'+L@d_d11Nc&cRV&'1n(dPpmIsh#WUmp_U'-_'n]K$Nk;T6`T<'C+0j?`gho3(QdonrTro'Br4Nn_qn($OhG-6)[W:Rgj^Z[m6 %SMc_!'5sk">.dh8d %./*1>fipJoIl-E=n8,%fX67f%BDmnEd`1>$ERG`fiB\Us,DsFP=$0?GQD %`QTmE+:D9>f=mPk#905!^"uIFBc?WVY@`#]X;&h/`@(??MSEbAei)DkCi=([,W7Y\-iU%IG>gLo*)&[(,ZII_-Trr!DrTfZGE;b^0$(;JiD7:VDdm-JqZDkH#5-Al2KmbXFA=KjGjWIRF4-s9#Fu\Q?2Fu %XV;5)HElXRWuJ)]?g%C=p9@cuGko`bqYj?%@p(ikr8OBLcV)qsR1%7s\s2@BO1Ss!UP'55b-uOI,pZ %ra28NM>\(YcMM[01rKM]gYhMWoLbK2%FXVCUukd^7=\C0>gH*d)ms^9\Q02YHPjmi1YuT:n%n"\<7;5AQE'^DI_WTEds,5HVgo+6 %48o-3_KV:1Y\*a$9FWDf;>4.GfcDc,XM'=3n[55ROSl[g7'!..=1RRU/1^em)37[sZIG]MIZHq^A9Er/QrB`>>FcPK8<;+[BJiF>)aR#a!A/p`NWb"CR1rduJ\,(6+U[7I[`[uiJ)\50:Y+[AO=/$Yh0TVRA+ihIs/n#R''iSO %AVqdGepd&'a%1l0fkoo;c$:A-a5b'/CYXd-cRCpKR3_*^G<^GF)o.[7GMG*?g_4FT]mX/RV)G8*3:>Q-H#6[$TR;at)I![I4RW,A %D_)+;on;XKN$3hr]<.#r#p:I>`qBEJ?lE1 %mGb%Ro?/1]R12b'?i+EX6$F3L>qd33ecNb':o2UMD,0$Kh[PcboR\c1/8?GK-hNU%#/(@t%"$-WD\(^HTY;P^Qg#5#GP-F?akpF` %TSL_&b&t4.r$H#>kbrl@2Q/9"L(u>>_Z1S*AC1>N;5@FYK3f8c!e8)Rghb>/@$E7+5uCi=XgNPnr6ffOK6!+Jro*UrL"W-78B6k+ %FE=X$(_fH%G?$,g/-md0G3ZQs6]47b*NIa@?^n3?]/VeZIN@4pIAXVKQ(kSmb<\LJ\+HNmTL]!6nRQaLK7b4ZKDL0,JSW68adg$QB9blIE(BH-jFG=)hZFGA4('?f:HnF^\B`Xl[;hi[[h=9F!`<)h1s"Rf7WUAIO4..*Adg&ZVEB9[!aD`#^B] %KBUg,d/)55U#hp`&*:,KZ,gNm^!BQbTpB@UBJhKE^\(o$dm7oQo[#9?)+>G:VK>Z2[MlM>h2^-p`LnT@gW/Gt`cTSi8T,-8dV>m[ %:RPqG@aG@?ln/2lj"r5>\jonbM#OQD)98c,X %17gAl3?7ZnnR;&nkq[2pH(=gq_4\YrERW.jQ%D5DP7?q$O^Zq,Rqd(SXb$?Uhn(8%-G-AO %V;:Oi[!%oO35anSK[CG:-Gm_mcYSgb'A;ra9msO5HG*[GJ*K..5IWmIBbV %mj$dM,d8'Arf2EC`+>jj>C8Z+MKJ,9@K,ZYY!7?f/CZ5bSK:g,cN4dNXbdQeTfUa&I=>f-F@CGc8>g&CMf1,L=38Btbjb#T^0KI@ %OmpJ@#JX%dCFX(3^6d"_?PRa&](^oC`OA?G?goste4a(oTA=;NN'FT`hj1$53MGtNrp>!.pj!if,NN&BMHsK%InlRBqrU[]Zp&ul %aT5oAmEaZDp,*Bjhp-MKh78u$cc]+.p?q,!^&79HhlWmf9tp#'^f*;NN[BYGh[`m,`Td*X_#2qgWq.;.4P_po2V>+EgkPbD-fu*^ %O,ooJCq:Alj7hg"DjftVAM*e3h_$@OETZLuipVQkg:+%YjCk?JJm)lA#9K(n^VrcJpK8I=-V>L$G'XiOHjl*u+*)NUXf[B'.W.Z@b\l" %klXFVE"q&:r0aU+j/T=J@<]*Z?TA&OSJHOVFRe;L\/-=9X'uP%i-;E+o9jpsCBk4sS%^!Y3ke5FYX(;'K%Nmt(&.,lAse)'fhVe( %&;e=]M7[\l`*G(mf-?[nFGPFb"*=1o>l5$S[,as,&4$..e([L>>[%>%DpjTfJJ]7V]ao0>/fN`%`G8"-9#_0l\B9Q/$GAPhFnL_/:7G854gHRas\R%-HY-%!s]Q(!tW++lP %C3=/a(($`MLQ0==cFOKHUIqr0h.hguKBAu9_Spq;ohIuI;63U(.@Q)#*BNG%2lm9ALI:?s/I6@)9fQmU#-oun2M=jeKe=Pi77O"`S%`gZrm;#)ZQ'iDu:9f7TXPi5u.R3A+WUeMSc]!&=[4#6@U^N_baXb(U4oCGaq1`=>okOli2!cB3rq/0Vt>X*b8P%aUJ$;*7r2Z^jQU4?EL`:Y)_>6S>A*P)7oL`#njh*Va+F_rP34XJ]^o/o&+\ %AtU]e_R3'5c>5OGNYCb[.P3)g>cCJ&]%dQ=Su4V0?Z>]1K'kS70KFD1F!eWt=ll;92Tma"C@F6n--Prb4VsM1c<:b8p"-+E4G,@8E1a1N4j[R]T&5"'A1OP/4BNl1X' %ftNn,P2/."<*dEqQMm?/2U:+l903egqD!nGD.6_dBM2f8"p3A*%:TOMdOpb>FbbP):C\(hN\qV^Ysg!`(WT\kRX1g>gI;]s4VBo; %m5:"Z\/]?hH,?f"AP[P^S;ue'$`?+.nr&L`d!;V\0RBSZm),;tofPGVOZ00oGr*HrQ %g+=V[A]13$ll7Pgg,Soq^qq-"`Tj+Vgj"_;`ZALGK;t:6V1nYGNR>Jg^"[Vg&r?1s*lT=Ia64 %-TCo':H1@;>;DRXS@g&-eh%M.E5g3KFluE\P(df\dp!u/c?OY7+\oZpD+P2>ik&7*A]62p@%dDP[p2@Is)LNrHqM!*"\u'\nq7nY %#1q/g1gq,iQ@*ZC.4oeK(-X_'bVC,Smt5Bs\DA<+IIP:>:%SoWmIUNq_2#9LZ8l$+gur4LYt[22XG0oHg3+B\UBr\=KJ5P,ESrb8 %8rm&FEN(J]"mspQ*.P0.O?*-TaqJX?Wgc#lA:7&Z]W8a>`To-9#q,1!:KDXIB5@FZa!Q %.BS*Pg1o90[m`N,_8'4?If7m/?_dphq'OAetb35*DV?7'dp%F-0HguA'"d- %QH._"Z!?O6U=*YM\/+Df.g?YQh+^U;s`M#@Kc+^$DZ]qk(\X/)K-i!BBgaoU]D)=g4X@XtlPP<%K2`E,QUrObnc7eb@l5n3W %-!6U!CCr0]j@62XV:,bqE(TFc=J(.OZ1/ctba@htm@%I@j[sqe=$g`4dYEA78f+gQ/,6D5>#U*@ns7?Td7n)8L%XQfbNJbL=Jg6BbJTtLJAi)&ki0`)bid^0Blg="+8u]-MKrIk3+.$.[oirj^i>qf=5(!H48Y9q[T/a/+0`8Up@.0kWh3,!<1FiMWrK#gY6< %/ee+B0T#P4VXq*j)(@d0[Sk?b\<,MrTS4MR.o8)Hfq*3LiKo+KQLk`T>m2:h4j=H'`ifTV/B9dbGL9NlUWH?miM$uQCm:<.S_)eD%%A@YWg")'1a(r5D8Oh/+Yr-P][D,:mHn27I^4S= %&"&r8):DN//8iCH?-NR/"B#Ohg7R^lQ6Aura_c*e6mk.=TDk@fn.P.dkAXS#)K3::h-ZFl]mic;mu"UIoYE@g/W:d4T8jld005F_ %3,@XYKp*!lI_+5^mP-.[?8Lp)(A%'"n[oG#>T@:..'p%CjTXQn %o%4pC.pe`NC[/eM3]dZ'[P`<-'s:UhVinUf[T3oK$(['_SAC5L.r`2nH[D^)o9IcgtarW %S1b^?mD[u4kllOWrStm``J\P<2s;60BgdH[hQ)ED>qPJn/Ub=<1]AD5H2+h?(Z)73kWN%+c@c2Sq=Ln#Ok[i6C?<*i@R9E+s1GYD %$0Rtfh+\6CPV@=\`;c*B_uKeX8S3G'C5H`^*,'AV\PD?0f;&]*^PH.[=*#>J18:E5X9JL:aWu=69#B'ntnm]s&_XZ3oZsX\a/cD*rXm %LCTI3BR7kD(rpZ.N5Y=")-%=Ya.>Znotb[GG40FCl#X.Oeg3+/%YT_k)nA@/+=pmK0'CU:(*hI0H"oPS>3LhOg*b,>!)DbHtXDcL#3lU:+qSGkl/&2qA %WrXT4%5YOn"adU.Z*WoHm*HX=\U*ngdG7T&e?QcQ*8E>[IkIP#Jqft&[b"3SG&8$lSD"9-GiK`1ge9Kd-Fb<;+B(f*42!qe4qi^s %D!e:387sH$?`m2]A0WYAI/48aMs2g)i9d5]ss$?2Sfe,LqKhQa*96#IPqrh,AXF(>.3PI:.uYJc/?HsB)-6tIPJ_UL%AfT8G1JHDU%>Qb\23/MJ:GLfTiUkkgZTG %q#:XufOC\s4No5U?I$5.RgN8,4A&M"$fuKK$][-Fp>skbf*jCpokU?DB^PN1@XBWW691I6O-'15!2brj+ITn<\243%5)W3/>I!B1`>i>;XO' %l?i6#1!LZUj@ifK'uLMuJDtYoXt/u-!i$Ob=BlY]YKJhJ*H=6Q3KA?H[76C\.rhu:nl377=-sUE;E]P."n&qrh:W\&=u+/h:6ERqgq1Y@1s!J?"58,+mmO,$_*qI@T=R$Wu`#T=bq1Xnrg["BWe= %Xp^fVF,WgqNAKE"fK[\1&tJKTGJ#Q5*2,B_eoKd2QZ=(3'Q\^D#Jn8\9:2AJU_S*ERG+\1t30u$\;NV-HM3puH95r>cL?cV/$'a!Ih/@D-I %be_tfCL?8`cfQL8+>(:,N.I[Y!T*`4/Hf4bCpAXGc]MF+DsnbmBO5V-rS?kWNYaX_Oe&p4BldTF-3J5="84CA3"[k1af?ru>1qtB %2X:ukF^:JQ.ik'dm*V4-2Y=KD>rF[iW20fTc>HYe"7f`4:/#U^liNu['hI!4G`W=eWAQUtRe4W[WOHshT95QfVtS(qSH.Z.SYdLN %g$@o,Dg3Si_Ca"d^"$1!3k-OWk7or>Lo#@R5qT">7pFV1l-C0Qn'RploSUc!g)5enYu]W&MJd3/YY*bp0YG\._9S/8S"bMZ0YneS %lP3C'B?5C'<3U^[rAplbF0Eo*<3TNebM($`92mTSJre/5N!H<>j5KUD5Z#:/QsfSM]\]?6bqQu7hY-7V'teJ^[U647_.K'BS%&@! %M$c/1S1j(`.bG.CljJ-!$9#d)X1Rg@.bLgQTBE.\1JI9n`AU-gRpgp9.CK^9fTK^8U4UWD_0QXC)cX&pA)tnRSt^h]0YtJrf;N'j %hr@lV-$b.N2QL%ef,o).MI?aG<0sQA'o5hM:r-b2@,jCOS"kQo34!RT$8u+fi=3==TRtEB_7!I;[8=\OAFl46_OI6NbpI(($+Up' %GY,&1%tC`4+]kCi>ua4oN=%QgS)n>cP-)ki3%!D&;c1$f@eVr0;p68nfKH[qGfmA\]8>q$;LBP!S(KnP8G:#TG8&_@MbS)`=R4e] %p/<`1rH6skCl^1)pnh(^fO?d"=M&bjgZP@(V`^H]S(R]CA[Q`eN%T:Gg=2\^4%:$u`tk.G6TMsa)g\mI0WaY]M_916/RIV"T=QM$ %LY1TH-bDV[]8%de^Kk_7]%B0SGh(R1bg=>+SZTE6_`bT`4gs"kmb=Cmf>NQX6l3JU(7R`ac%M/p<:-2U@Jdm`-?VfG_Tn7(SpuMt&@?/\9St(G4\'`V@DT`9SJ>]JA+%#tA(FQ8Y:ZJPh`p`J@FKaO6G`PYF0kF,[C\SG8ru %j[G^7EijpP(hK2k:-?HC)t3+!;ZE4pPa?CLX5A$e*i&)e^-3LA/LQ$an*@<@o3>^pc)V,_`]@l=EMfZkO0HnJdb40XF),0Sf?RX3 %_E+m;n9T_0etA3GHGRc$3W=d(onm %FVaBf1<2GfHYKa9@^M>Xi^R$_blI(e.dJtCH1SYtS7%mF",p4_rsJ(.RJjG<1&,HUK7]`1d3c<>_[3,rl@cAUQS^VW$Ir!BX\ %5JoN8l/[>GFk-h3HdC5s=QcNeUjmu=[XJ%6B38fKMT=FHhAkitN%DWX3iA:35s[R/JnA+tpUFi2K\d'dljJ,lCR.Jq6K\,rhVaji %;9_aie2;6jp8DRT7c"R*Cl`<8:A+hBCl`>>Gse_b>dM'E.o`i.kRF]J*n@pu:>tkcc1\)JS$t)`_DU)m9.]slhVaji;9]<&Xc7,) %J'8`$#1m[PC0'6/]IC#:*nBTbH@p"A7l@&rnH.;rCnK%8h^Q2uY1XlH]QAeoR^hK;C>d^Z2;:32)M3;l>k>g)QVbpW %lB<7KRdKElFZH:XE6\+3AjC==NGCGCg`4uQSrtJ2B!2aWp:+`34Z2cLf>9E"OKHYgf]$6PME*-("F?qC(/a7)_8FEua+?gH>m5-s %]7:'N<5JEr>m7D]]3nteXA_;8._?6tlVV4p_5#.*Sm8P(]*Jbsq($^71I_03(Xnj^f=TFgFK0XWDgeuG(B+n.?!U#3Vj1Pj %N]:(:@(Je5@P!EuV:>g8R'V!1hF6M#73:@LSjYH`!T7/mmj>UJL`;_"ni0^"nPl+:`/S"N'6-FX!#g$l+n?jN:4 %EJD*SXZNl(SgJaBd>WUQF\3sd!k_55LfL#>fCLO"aL1CAb)MgPSa3Opb"4k`-qKi)N445:M+Co519j>PS*ir&)+:_qbCsqhfgc!I6UT>9,='u',_6r*gTV307pQ2KCa&O[-'u+-@DVhs>h$`Kik;S&rp>_h.aG@%,K/D=!/=9rR/FQ@=;(2s5Fj>n( %NSM$68)m=GlbC?j.6?2!I2c"mmb"@"d[^q*n^&-Y6TRm>;c49:0"[dA=T(+^JAUAr00tR4Yr_:?p][`"bE]7:9?NkoBncDBc6AZCe4DEms-KiolAo %:1I#lj,I!q#J %/RbUQ\jn*%HN'reVaf6gb[P^foD;o9ptN=,@quXTRhI;Sl#OGOhHS3CW3B@]FH>i5O^HtXnkrhe'9ON$B)@0jkAQX)GrnG=3iM)A %CDk%?'Q\E/_<2qW$Mg4Tp$A9un..pHo_g8t-o_-#,$!sgl`sTjWDgM\@"TuF6c'")]nACm[9hrm^K2-;CTXW_\cdmhm49!SV-4f&Rh(mbgRuItVfl+S45G3-s)fW;IY&;u+JQW"&Wa:5Y2-3*_n9NIHdU`>;]!l?"e//O!6K5b6r_ %h!`h-%/WkN"-rlKK^kCXI]T#K9#q5.Q0^;2lnG'Hb3Jklh>HRe6>%3Le!\eL$d,T:hYT?dXh*.f+BBe; %q"e9B.ioG6RIU'>aeir:mBAhMS)F4<,ZXL*%I5l9PH:DF++>TDYeG)qeoF]7%oJ%=&PEk86CKBI3b><*SR#1E7/!4PhV\"*o=[_c %WZQt?Wr@+'V/AJD0@Xk&pRSmtdXTd6k\Q#hT(MSQkOPZ?Ff+[EolM,LV4RFaK@KM?A&K0'O"Xt9nKcQq`foBLl$/MahG43Jlt`,n %9#pbqi2TP6fD`ImiDiJUq9Nub5pd33L'C_1gUfpikBD;Mgh"!nMpQN..;h)iqQ]ugnLBd'*=W.IX<2Gc#JCd=X#Ka5%q89"4-;_n %4FZJ>_IBiD-tYoB %rPLf(>u2mtWn:u#WD[,K24fWKQUtk'322eUf5"eB*bgo[-6g6]o""^-Ii!BQ-u\QVUD2SV6Vb>m4Fm:d)5b@Y$P[/dg@tA1RS/P^Ppj[NgYO(Jj/Bb %%J!E@oDEOpp%\J8pWbq`aYQH7=h^:c:Z$g&*tQ,>j5OE+pO;"l*=a"F9d"$,"'6UV0<[heC#GW>_ob`d%pe!GMA[+O6H/lclZ8c7 %LMrE3#'Mq43>:\3Qp5bhIUoX?iSsGh#ghM %f)HJ+YFX_5@jc;8da*.*j&7pf/t+T&/h!Te)p/"\C"Ijr2S([B"B$"&q9$f$VsXHeP7aRbO5.e9';N"l*N"mN]h%9u=Im6PXZ[d3 %[0N]Z&lsLf(W`4s#6"lC'2A2!Wuq=fH3Dt_JPi^[Ti)KR;PAY9_a09qOr(Z.7krQc#O+kX%9=DF/f+F-"L:$1d:^5if>^nmG4oiI %?^\bRq\;:#_Cdm;nd;;Imu$::*7GAeSOVZ(oQ]1au/C^a=58=L'a]nAKif!/t14(rb]Eu2&Z%bcqeccq;Q;! %aSei"2+)spf)kfM)pllXt"@bDR?egdi#@+PR>4kH-$5 %BW?>s?N7ms'UmE#2p9cmkJt=t=g-2i5L=G1Wrs%m%oV(D41e'+T$4'QNAetncVBt?O5j2;f&In4?g5K)oEl;akC3+drjJ0(lG$A> %??P4%Ge#?[]punYKMJPO?]ARbd1>D(-:]nLrH'DfQ=j94&q[GpFng*ik/b90g"6d((/d*H7>P:Z%.gZUiIKJ]Z==c/De( %#q2jY*$o/0\$c*&iqBuf;YS"bZm>4$JAO%0UbYrGOB#^]drO&;>1);.(o6>$([fXO+^s#Y;b)\uXYZr98Xi %&+*L=9^q^CrcU$%!(Z\ZY7MErE>Z(V5n_Vg*>'FM03k24^'hbR=#oqA\_9Rr7:4mZh<.W8%+t/o1%X6"Z1j+"4C[q7RH^k=Ecf.n %@gA]3oT&PBS\03CprTM='E$k6+8m-aiYV)=_nPo@kH2%>mk6ALI6$aEemP[&K>(,0K927[`5#7Dqa!isjN"3Ra="7;ZKId-90Wtf %-\mS$[eIN.]CJ64F"'gYa:1T/n!NRQ=o'd75/LR^l^=\6/UdC%U#4ZE9T$Q^W6+XK^3BBpJVIfnRF&1`Z!]cUJ&$td9b^_Z6Gfa3ud#5XI$Mto`!M[fbg.t+NGjq0$O)I[smAa^'ro3NJ.\4-JMtE^Ln1N\8e8@GFZ(GbA %j%-"G3H/8]UYQ/d0!r3>B>h0ANur9heKL(m1)-*7/sn<"e%NuoM15ei-3VD,9ZN:XrWaS^nY7eRD9:I5h?hr&1R3AXX>CF8akg'!NQB);]K>VmIRLC$<.tG/"PmR<_OLQ#gra;aOX: %W<+q`^g*MSuD"@mHR/'@J&+_T7+Xb#3mE0>nS\30p(@M#$C-4\[1`,>'AfP`u0376"STa]VJ%]-H['.8[jC0Q7ZDZH%8Q1@Wb=Mh(8kS]b %QGu"8f&\m8BJNr7Hg9a-Y@(H]?ugX*'[V$%ZWf?Kan?@S>`9PIc,P %SSI2f6-4::\^sep-hf5u[R!&Mp5&mKI'J+m!(u54*IX:cl?WC-C%42DjGIA;L_O=G^eae$J\>S<5M61Yr+naYRf`NB>j>!e&Ha"b1:CQ1LZ %Pttb+5!39!Kkp1";`_1QdP7Lk$+"S'-:P4'm\^2n&!KUi:MQDZ<,Cd:b\5`[a].g7?.17_IS)"A/gf\+8m*nq`)iCF?CJ:`V[(s* %h&3'm'O.),\@6PXAMU6T*C$ZpOtZ$6;@]Uh2b_=0oSfF9FtBQPP:rS>n%WSaA5WSL;uqjM4lWB6jciTZGTh;);poE-FH\2"XU=2g %\\?5)/4KrXD-4oFWaUS62;9s0]9dF'Fd!WkpQH=*57$Z(rF;_5GPI9]odZ,gPN#BtZ$HrqFH %*h2WtRt^N40>_:pKC>n'DZ53qX>E#PIq"0lZ7dRBH<.,7WhXR6[_$48SM:ihg2Y87HBu-6*ag&/S>jX_s6u[ha]*_,EkcD3<;VIL %bB&Z>?nID,lKVeg_c6G'?"9mG4f7SL,u\dg[S54,@k)`s\Z?^cPpHic'XA_ %f>:s3VE2it%F(8tNfJ.EJYa,Aq0pQ]f$m[mHA_(',u]f4/X_JGkNb9(VOD=f.QMnd]Y<.o_pH!L@O#*`l*gB)=D1_*c:J7V)gTO4M7Btn(Hh7Q]@*^;S@gIQIEh-O'KN;h5Z?A%.?kVh;tZcr@4Er%0Gt*]"mOMQr5V), %@4@!`#ZI!0j&'Th>%+GY%U&+sg&:#[pJ%%_p=GP.g@#\JVXh'Mb:`rj)<1Vg^"5ORZIB9(p&3(,6J6cg9P#/B=R.C^GZWS0.Ds3H8dS+"(X5t1hPT4P0mab-F]P'=[b:#e0&AnacZaqrhhQ%>b_3-e5qlFlTRS*;A5/XnGZ %5EpYr=mN&,MJ)U)Y^323]oP*\.K`<]3-e5qHL9JQ.Dj2u7]L'Hb'5\)[IgGg7]L'H9$P2T)6*=`gjoQgaqn;1;K_;&@9,[+WgVal %[cP]>RCKOaa]q7prVrOn8D0eZ)E'b3a0W=&U">q=q:!fV>E"_ekB,\3260+3CI:DZHVtl!?Q4kbk!aEkI;Pq/UZt\Qg\>6+SI&_2q8lu9eO"t_%Hm_#?dH'p9/[B1' %Qs$,4Gj"miNlL[YSUsmO=[RaOiiqa1?^t.i/YD5G_GOGirH*Em@`i=0GTeL3^OMJp(02aq>B(DWES#u25B,Cn1Um(ml2&989"hHdA5aQ[Y %;l$"J<"P1Lb0V$ic+dQR)VX/op%N.G;#a,R$Y"SQ^]^REYnU!P,Eln@&ZRL\N8BS+oe?MBlG?dP"(VZ+NP8ZkCkR%'jhnfh)-q/q6pIFb$)co6S.G=Tnc-\m&Kd"K8i %DDs8oEr=Z_gT,Vej;1Xo\(ao97OX,^\SCg!07/tZmrHXtbrsI0RRt_PMgd^2ERV&Oc]3_o`<_n!m/'5bTD^`25=\)&Q+ru5_8!i[ %1'Nj29j[uM+*7mIKpd(,du?>K)B>f\>iqCdU-fd_D-"m)mTZ^$7c%^ZS5uN-`M`2"9X`n.03Z'Ns.k#d`R.m6,LK^#f %P%1`b.A+.g'H7=]P<_#*h"cP41R+>$iGaBMR"So*h^6K%WP9N4auo*'U8*XQF@DXo`:?b]?6%gA[tX@i*L4Ai[N!3Gb]."Ggei%T %)Cl'aEA*>ef>\B7Y*gDs+%0Uj"=r;c&b$#>@eM^GFc^gN]sq,jWpKa'r+@NUAGVf8?qlNJD=+qm@s"I0:0G %HutWUk3t&r<1p*Pl0-l+MXZuIL=khP-srqnKan>2iW;^.]t%o2NFkl7ikX %R_'YlJWTS]+d(lBW,qAeefib=Ok@'S*QE8F<#7E6nH39/%!33#3LG(if1>qG?RL.[/Kk1gC[@d)$#`Od9hl1dm2_Ttci&#`9Xe!N?7bd1i %q<4&:IN]?8"_F&\q9uhOKL<^ts'ZrYFMO8Nh&Y]b*\_1C2Z=\7j'(.RE_G+_,dtjE&$g8i+:e.2g%hqp\@3/K7QgG7geC%6(FrHK#(Z+>BMrP)`S[.>,5-6J,XAa96@/9=r/C74D>\$>:o+99.Y`d[d" %WF-2Ee:d)e4T%`%bMUmJkagK]f>OO/IbnjlJNn!WcSt#,[*qBupaP\GC'TeFMSZG64Z%J+El?IP`;`46bu-5=IrS^Gm)7bM'RL+o %(.4n8f@^lGAE>@ld&JB_;>_WF9oXX%d;1qernP-oZqo#EUJ\P54QtW"+cZRgNs_QbB[EPW03M@5f46 %R!VV.7L!(Q4GMGVjuAdPh!?(GP96K"gr*jNZJWJf,PUminZ*NX,p&1Y*PF"5r).TeNaM8DVg=m>VUfoF+]0YB#:H(dnGRB'9gd.>GknuW2j'G)dO?u:M0`LB %rCW,D5Mu:CaC>Y\krqJK?+>R@%4Y]W!,C7rSSiVd4o]4XE!*nf0Y/q2D?/!'WVH58$1S2iZgQA?]c3VNRBdZ[Go_A0Jn>%NfAW&7 %C+TWt?ADlC(sj+MGo_?r$Z_R80X&AIO;F)\bj4aknbEUpR+L:5Q;]C@?s\UqMS/0A9HkZ%IX)#I+Ah,qh %TPD&*3(1k##aGQgDEF3<[`?KE\OMX$J/uNoPbXH=rX/:"oV:=E-\j+*;Of7MILI_1hfNo.rT]$@*qNJEStn6='lq;NrJejOgf"FoaArr*3q.\WgS<>PhNSN2_BmpqpDGme[Y>p-'Q0YJEgM1N#L/HMUs0+D;jG.^KkBOVU6`TJ[)We=[ %ee_R1)F\S6(7^dm>D#BD\$\Mt15K0^s(#2MjomBU]m].t(7aV4jsJI>IfM2N/B.$J?fuZ1k\\/(=gbXW=#sb>3\!j@+Iomjf!,%P %l`Jd<[_@(95r_RuH%k?aVhAEdqc6D`Cr93^$Y/;SG6dEAKlj4!;]/\0\!G9X+gu\hJ`rOXK2YT,.E"4e(bdEo;jFoRoA%'VT(7YShs2%coA5;s&f84%a_NY3n&lUcr-o2mrq`p?UhU2o?T1m-g>!)- %^C+k<7q]\FVB0E!Ta)P4H'G?A/F)?caGI67aej]B9t]n:_id_!"e-&JDd'I2TO?4bgpgnD6,=o!.;`1ml(DDtD<6KuRgoc`nOfqn %9/[e)Cr5UY>-dCc?k[e%b7.*&*>E=QQXXc3ZK\#i'6NfB-(>AO2LWE$9/[e)Ck>F=Zt.WOmA)DFEQP)16#g6i/D5,A %YR.VMC!!6)Jk]Tkj-!+A"e35#8iF#tMql,n;1D07GOU_Y4R6#0KpYk/H9jVCQk@6tmkS>i\8>L+$V*Bg%_60ud:ihB6n?iMF;Cnu %&+c^tj4hQ1Oh)Y<"4j5S4'4[eVh:ma\%-Rms8Q7-DIEdni;WAI2WZLl]CWMRk-W<\mp?F8 %i(MB%_TfOCH1aU8)I=ArVlsdu\G;RdbO=8E4EfVtFu:V#]>8Sj[6q.6oF_-!Ja0]($dNrHUQdSn$blg39%q)#1Mf,*Xo;qiT?eUb %+.7msd":YqPuccbf6g=NZJXEO7HqG:Iuo=]UjoaI&bY&(@k7gk-'"G2'>EDm"*IYH&8f'$k[qiYddMsphgZ=8`dHmJIH>p-m-IO\ %=67C28bKIkN@D-fZFl^.7?E`58bijI]2*/;WUTeZ#FkO@6"m5.VR$m6J:G]OhEHChh>\OTIIrj'577CFIhU6Gc%oR4cbfu/>H9no %R/D,cNbh_3Fa?*iN2QK4rq'^qgE$=g])H7:D+N.":J/!q$$LUQh%];u[:KcJTe:d.Er7gt);PI>?O& %_%*Ys?stL8kI(L:%`X.SqB7GT-t5i$k9Tu/Pi.3HBP//_fP:9d/tNUXH:t'oP"9h6AJfNi*\q21W'8_i%rV'E/Ht%`YZ*0 %Ql;GbC^Kk?6Z5QC1!T2l[lC0+d__:q`Y0%ZD[Cf]jXFVrVPn`+U3W729dMc4J4VI\L`@R46=?Gm6:iL)%nUbA@\=C-%1P+GcP"4$ %>7qHp^cW:75nl!2B24'9j(A[c&3_n?L`bja7D?c#0#@nH,4VH2RMT*("iF.CnpnYNj>.%:`su6cLH.qG6(hq^JjeW[`&LQA$()qK %SYbHHK5H/DGQtTB*ee)lp^P-\GnFX<13&V6F`E8J.#8<"%$18cCBEc$O6(aG(RGukjEX\S-cL@fKDh8u_'Bm)-q3_IoZ-K*"!O<5 %$+PV]@G01V$dZj^?l-4MUU:Z=rVE?Z:^BER1No#f31/h#iJ:?9-EnY8i1K(:cfg+^j6D&k9sF?E%/"81-3[q(a$TK?O&kQ?&7G(UEYXtTt9)4H>W__Vu>(<$:ib]CiW0F*/oKp:_%>m!V\$`h:&PU;W1_G?C?#:R%o %+WGAF!s4FEM^"2m8TBmO(mNQ=6A1A-\cNDC"st$Dp^M0q8.DL68lc-e"X["7csHjgTgbakd#nWZ:2mM]R;Y?B5n+s!LI@PW5XIm" %;FU_]7)]@oP\Alcm":G"XYs$rJV>H;K1g_OnXtCL?6fW21b>'84_"c.6&93E5L7DCM5GhDS-7h:a]ae'KfDGNe>`tf8"fBS:B)7E %$=j4rCfWgK"'S<]gat05F*cm;H-f+O%jKFerrb4komdJ[&"KCQi'SLN\gZim_nm0^Ugl]$"k(2[3eF%c1-&0LP,*Q0c9J@__A;5F %6PE>b'^RK4$j.E6L493j,b+j0?_UYg8392D$8W6A#Eof687*KlR#pK#0us`q!g%EO"sb3;.8XC*Da6cg!dNCucj\,6Lr6"Y:kT+# %#AXAj!p#@hK#],'JN5`dM"2E`"s_:'UU2/;-%RI:Z7&hY"<*ed.P#PX8s5%SU+1.*GG'LI%OD<+.WM\F$.LU:$6mb,3'..U(X/%/ %!.fbs80D7OCP+AK'EM5N:_VfR8ed]q?tY$(Pq<_IDF$U>_*@^UMg.<8!r;!NAfna,GcQknFFde\?p]e+@DOUmN>EEW-n`?D'^];3l9(!^2@qnd81^?kaL,0b7lSr!&*+;8&\Pb.T?0M`quI]c1ErJ>rZq"tZ-6 %Nqca6BMG3[=O8Y(Eg.^Zh%MJC/9k&`0_B".EEVX0^sAZ!5k=(I0IpN=ZK:]8,_4'8ha&>@&eA!<"LNd,+selaPU&nQg;&gWVZMXZ %/?BIra"_6Nd#1-s`.C>:.'c'jdp^oL%5NKL%V1-#TjnQH$]ebM?6AG=%V?6S,!Nq6C'."p7*EKH9d70s.,mK#4oli;2!;,NRRKRIr!PU, %Yq2"\(n(N/T3H7ECbJ9jX3#qL5sGSk$]"rFT]g2oL<+uXN(h*^=p,I'f7K%/JSl[UImFu\lBJXu1GF%2@Ud<%(1d__%Lg7Me!pE] %KhmQh2MsnU71#MfE575bNIK\D*tP0aMg:10]>UaDHmO`6j>-"MmkSAKB4JGpRjRs9Gi["2o]:rs!a$a<6FYB:cl9aTko %ggHDh5TOpe*5kjOM0B>3cClmaW!E:,!gn*?bU(R^#SeTcN&ko$llJ?cKErPk:*DV1"r'.>ZQLEZ`rrskqU3".\W.oI7WQ#U`9cP"<&W(Bq6]J[&:3ffHBV16QUM/^-*smp6I)8oDZIN]aMPac;Q0CU%*Z>6,X-+([DBP8#U21i"uU(_?@S,m_=n1,D>DF).2=SZr57USGh%6t/YQ^+h/d11C64>.?d)bVN#3M(?4K!f.)q2qKR\,_]oi(8OOB1W1O]-,`o;&iHN1m4"&7n-%*,`W()JlN^S85s'V6_M%Fa8kp#m8I"gOROI!39274J=sbJ %MG91e"<(EV?tmLS3&JNF/8c40F)_*eWb7nL*6D$U$91o]%$,pg"CfNP/MJm''DnssJh\@1!;`KFl36P]UrTt_"p*hFSY5^+9#S7e %epp%F(sNiM)g(\66WkURF-]qfj6GE,JMf]_#u3NYV?q?H8.u2K)"+0R=]Ln9io:^@$QHl/jH0sE]S[0:;hTE&1l4)pM$'Z;BF!TM %C`lmm)Mn2u5aeCHL\6i_L1d05GeZc^(`=qIPUC>\BL8r+clgE'(,Sjn %&?r+C^l8S#?P)^hKM4Zh[m9).:+Np#!q@\CM51:i#EAWaX=[QB"D3q':\(S8.i!=DScKacBK%4m0k/\_quepnIL3T="g3SIf$f)# %MOBg<:-aS*P+Wm$Hrg`^[$cI)LeaEtONAbm$.BW"nKXCBCMErH(l%$!rGQ(N);22:mOj-K8O==j:=\8iQn4PZXl^l_ %ak4B_JhTtMOTnua40b$C4TmZ/7s,6@.+!j09rJN:&.9Zo>4AY>oTKq\*.fsni=0B'a=8oSc3SI?:T:rk;\D$-;KbtO&p)@n?=jtD %WCSG:EsNW(14Db-M_l3I"sX*@qik!!A9j %JO%>?#`kXbKB["idgI.("po]\FL_P-9uh,sMBa@.0S9BD,T2)*]a:tW'f#8k8F.g6%LW>A3HUX0#H$$+dA>kG>S@=doA:)aY5/E

HqETt:bpS2,*K85WfV1>M0[XUNdMunKCZ;T!j2b,PIVSGI6qDbLd]JV>R*V<,R1+i':c(q7NP,K_\>\kN#A]"JG %T^`Eu'C9CjOi.$+*9*8-3KTqrOYA;S_!NI!`VkQPW=:UmaiR$F2n4VLL,]CCE_YI`M+k\fHDl7t7Y,*pBfrS]Gn7ms!YUQ?M$Ak- %R/,fVa`*:7oDb5"4@)P]:pdkQQu+#B^k=94n;1j^Y_%T+K,l%!!bF(=C)e4bNT"Osbnp#Ip/l'BEM`P2V,&.E=o^R3I;^ %jp,tG7Zp.e`c])"BME29,qLXKDjm2`J]"HOQrBFrY+oPC12dd[A]IFh4cg5;i+`@fL\_<5%e(-@!AOKsN!p-;OHc=B!fB=95RHB0 %KGSrd+MtWn<_eS2.?4P6-0TKgU8Sc'Kbt>WqlUHdZTSlDnBLE?pLj!$oeu6PI,KFZ;M6$2a!>C-F=ID/U@-C#g<./*@X&4fs2Vs: %0U2f"/d?s&#+C$oAn$=qXWQXb(:8K`Ua/u9FIPM:i]Xi>a'sRgZ@2\5QqmjE\U)2qWtB>mBKH*d]Der@0VH^YU!KX %6+;BY2Wf7@F[Ep;#ijl5UHMmD&aoNME?Uu_@5s4F9hA(7T^`39^W`g.T95h638R%QQ6jR"=pEsOU[Uk==fUJ=__Q,iP^T$^o_OS[_Q!l@&QiaA"Ose?h6#l!Tcl7 %LndL@JBB&Qa9o!o>6&]jaS:5GUaCh[ %AJr0M&0Fck=hn'J^8('_?QBZ97+;Dr@tS8[:0]ajp@$RfapSl!R'?eUJp^-bhD!K?JMk$C'TiA6h&ap@R((*a)SuhT.*uu=#X3i& %82qqM/2*W&$>`%p5RM!TE^H4fO^!XA`P;Ir)io7X,VM[Hcd>]gG^QclN_u/#p`q.;D?$eS@5._s8:^L*DX*CQbL0B4hL$*nQbkg)Z#mra'Gd'l_"]$e]j#qJ/t!"b@9]-UU[RnI)lUPN^\3pC8aj, %?_&#tQ:0.04oaKR/]OR[:-uA"DYZ)jHTWrS!,;-)ceB3?+=F&/:UO'9g<>&I++_C7)*DldNPMj4TadW',Y;_##+2m%mU%3c]R)em %[%?FPR5\918,LpGE9dJ*YB\[:?g~> %AI9_PrivateDataEnd \ No newline at end of file diff --git a/art/sqlite370.ico b/art/sqlite370.ico new file mode 100644 index 0000000000000000000000000000000000000000..0e8139434b269a3c61e96425e4766a1b3c7486cf GIT binary patch literal 2238 zcmeH}zfQw25XSHFr>T$v6$2|_V`F7OWoBe!VM4q@C6*!q3#=>*z={Ms08=U-yo-fkqewRiM2lRfuPEVKH^!52nXT1%&z3S1&TTcD`6}mXxq2c45 zWjGtoPGEKd|Lp`ie$4q3-ZgvIolf9Hg$I!xsn1;;HF0iO-^82z!QBep{un>DPJ+u* z=8&BPmxJ-VlMp0!2uA^{LWQ@4|0cLR0SZ%&$-uqR&YSI ZqF*kwfL@do3ek(W)Bxi#grY~#5*ZHmCLb2`r>0=7-kAw%1=Nt+-Xl?yn6GYTr8 zA_5|c3^J>zAfl*%h=^Q7WD*dWr*qyut)kxRyTj-6-sk`B?{CiM^z`iPtn9V++H3f( zy<5L*{hQ)14y)a&Xm>_C#Z%x%(Yi-*i!WQK6w28`v5Wdvr2;eB-R<`%+OEH}^-#0} zKR^F%-O_(#wo)nffnlzEYmz9W$Xn9|7Oo|WFmkIBR`hME6_aV2taM3{nH<-z>%N6c zyLQPD{kld`x6)lSk=YzB^JGx=gwisd))QTu`k&Xg)~Ci6S+bJsQezpe+^5#BYn^x> z@V%`W?%Ji^r9$`XYG`|Fmzdkv#Z=(QE}C1B-f5*;+eM?jRi)D@^{QLCSovHRE1Bw| zQg=})mAWp7rVpa+gJ4~LzPk254}9y(6I7po+4}Q4!GHa_{>)spTD`TZzO}$-U_`Ii z!%7vbQuPKsdY2`xlC1US$~QKM{6fb}meYK$SjiQ*E^Ty^se(}H*R^Zgi%wq+=7{v^ z_Y?)5slSCt!z4qpBv&Z|K_c*J;^5i7M7j@E;MruQe~wLNNH||4GkxJ-TYr5=Th~gi z!jN?aPrtX$se!5dzO{{X+fbQYn*{nWNiNf`YppjyQb~cSbnWlsb6lk_-01TAM;oob zr&u4+?_S>M^&83jLZtrM-?aZLi2?OGEwVqe5g=h`r8>sC|CHl|NFKn`tNT3M)ap|k>+HD?5|QH%HB$}w+am*NFSxPk6P6m zd@7Z{A=2picQo7uB1cL8KttD{k!U>rn@)fKI9M)3vXcA<0)H?F_iy|D9i32CU{hQ& z$CQ7WwT3$VOI>QymtvAHlOd@{_UjrT%LRc?lfTvIUlsG`N$OM05tVFz6{@YX*EVlI zH~l)Qe|#uQ<}%qze+2o>Q$M%;v&Vj3OAepK_UqezZv1D@)fZoXSf7VA27^CHCmex{ z!Dz@>3{1upH5kmW&TlA(ZCR19>E)zNU$vXvg4F zWL#!L!D*@%oOOa`qrq)9RNH?2W`o9OF<@S^(H*iH{UJ-OnzGf(X}pF}r+=#!+*nO} z%m!r~ydiGOh6C0>p?<$k@N~!DKJ;&XhcUpYKVu*OyN0SI&|ukM4;cLJ0AS5xNZQ-j z#0_oS`9Y`Cm)~lr+HF8AxXC~R_Q1W2-Te3c8U+0Y6W|ueC2cqP3og*r1YTe^Wc()3 z1|$Lqac(eK?f{U#)gG|d{1J=IqO#=DaZA`;v}KL8Xw9GV8S4bY6+G*%C32W4AZ0`g zD|@m;IDv=EO2KY-6xdY6NQMRbf6-y^}4`_lOlQrVVrA3SEFbB+;Y%&;@ zqPe)vWiqOLK@aJ$WNJj1u4d{iJMsyuEl1FVHLS);Ub!k|igkiY$R}qVLAv4&g;8IC zA>z?UF%`2SWDE%~36>}%qeVVtLxhy-U$I{&2$>m2G|Hg41X~o+TtZ9HB`;k}Cb^Q6 zEtRxVg)c!OoCJyn)Wl%2w5gaGGw9T4753l~k4A(@S%;u0E?g%VHKerpq}}0&L@L!7 zYPCkiOqACpSZ%5dJ6LNz=_6xCB$o8L3`s_G31*uJMf55h4hz~MTs6Un?1t%N41-*C zf>}yZo1v6L4Lc#787{?$Fsw0BS`93DTo7fY&{P)o&^nl*h%Ah_gc5}rM9dJwp+J$6 zH9YJ}`_M8+VZ5$Ru|;>4Ulw2OC`QKT%HK8qCxQHaKFSGFa)}ppZ*OQnn!F z=TS%02=TQbW6$W!l`vB9S(F|(FXB^aTO>t1 zPQyaQok`FY8c%5qRNe=L^`T5QZy`e=Mv_=UNn-+;^iWtu%iyJGE>^BEM2*H7o4P6( z0C@{kCn#w-fj6;1yU}XR+5$OX0I4L5Mkv;rB1za8_Lf~XyG!Mffi|RE;S#MiSTLs! zvqdrz>}8U955d$hM^PQ+>I7xdVS{+LyOdJLOFC=p-~Es&6sIAf783+DrBg>-kcO@k zOp=-u@25RAJmcglu-Plh`E1Dm(MAn#BHe6SEIAS(ttu{4Au(EuawLeey!w=y404fQV~UdO>uQQstV~S<2VqIM~PZ>RwY{z zzsi_~{Xu;IXCR+Qg>3Oq1^;*DT9;wHv+O`kj7Dn?!8sJxBvjraULqYzQ3+dVj)$PQ z-W${*FlA#1EiC4Pf}T>>iZN2uX_UMe(__hq8g#1@^f<8u?8(bca=*BwTR03?|z6(UP?5~S52AZU@H$B+-{vO zY({*RWK2utI1$Js97a72aitovQ#AxxX%m>om8wfGs^oxX#YLs3P#%NRW{70wIw;pxPf1e z8VWfWwHG)g%IgD8ltk6ue735ADM4?SvQlH|{kuwA?^i@ccMGY0g zl|@k>mrC^!vXGjJx|7lW?I#;US*-+onuhTCVL=xlL-`s_8+;m9NGG$#QZc1eYXV4I zZ1`mx6BBqXpG?N$HHar;Wyv4Ks30%7t=5pr2BEUWQIrfWk5TJ`2(l2h(XOHoO(#7L zS*ey}o0*dMvMV1>WE;kTq2rO1&Yns^;j*MJ^R*OI&{IMRLk%SZ8i3#uR*{gJDgtDR zx*1GE5*3fq@2pi44qrr6$MiW>4swPOT?DVQT+P6;C1FZKmZ&OTrf|DaudabBz*{Ya zpvPbhSPiC9!D=uVl?FK?z=k4`+5u^gd~*NUV=jpS_%XsZ*f--t@FWUZjgR{|DeBCj!|xV8@Y2uz4Ts(K}? zDx$VDq_!hPy)79`3wBzNvbKytAA{|xT19VE%Cfm(Z4yGEmeo;`-hhb3px2kB#Uf(L zMDUEm8iuucQWb|{WI+#CjWVO9lo=UjF_FXyBCpbkI+aG`rAUz%v^1Hlk5I#=2#%)X zWf@mHyfLpYnaySONf^_BRpKByKjoCkIIneOORSKMX3b_z0nA}7pH7h_;B9kWZ`@my zqDdXsAgFa&!=xR|E1cA`vMr7N`zEGtio|>Jj%TXkjlF(YLM4g zoziNpPQF^nB>cP?ig1xej16%f56gvvv9uOslp#Y<#v~AoLr$NMENhiEmr7qtyDNpH zfztY{K_?z3;E+F}$J}}N^H>K4)o6=<@JW4@n zLIjY48)gzZUXppYAB{Fx)+f|%)R3kUXh~O2Qrc`7HF=8+qBf^|{wkbR*@G_Nx58x7 zF1pp6pu}Xg8$oyv=P2sMbWj$QJg%&zOGUG%E<+*W%veKeTM+Stov7PmF`1nZsn*5> zZ=5gV`50QEI4#G(VKbV*id9#MV*{+kU1rP&Sa*l9}j3$eatgI6Z>ukl6knp;U zBqoG1$$SKJXyO>+^HY)!fn}?OEy8S;M|33*>VfpIzidJCl>!+BMi;LlSioX6=q#u+ znr=V@s*EL|%yJ^_(5p4DUovT6w5Ecc(V(GLO?vf3SCA%TBN2<03Y3*h=lK|Hurl6A zJ}m20N-b_7IaL|Q5LFCqtQ3kdfw0Z5kFi({3ekxOS0;iEmQ;ngTqaRrBZStdkqSH# zt5tIGgeT{8<>fqG)n(K;2+0y!4vNAkn>39{dUu<3HmVaj__-UQHa z0VADmV>qml%<)EoQz!WQR0pybR0V;|_*i8|L^Xw?+6NL68Kfs^4CEu=pE!?ZlL=2S z&FL)VxIvG=<#vg}fl(-zz``X6osE)9m|jHPSXNEIDNhXv>g?r;#}~_coTiWi zmgPV+PHGI2Y&7R+B+0v~eiaJ*09-X_DnP&LEbC*KQkz8!BBGBc{Uw0V1d9%V=a^`S zDun}4tzKo+qO_JJ0=$@Z1>%r1!WDpuR`Ha#772(cr&8p-q$JuKK|5D*v7SOv6SGDL z9Jexd%w*0Q3#we$9|%y^3Rm)$D8>P$2#GcaJ?3IXiHPYMt_h`8maIAB_A;@YQxG$V zSydmQoUoYE6sdJvG(||KqjWKlqp3Y25miQzl;B8_gq6uMv9vp%r+Kr=?oBFl9BIhp zs*t-tftfF8faT6a0m#u{8L^<{08D^cN|rwPF4eL$0DX&UeI_lXg?KO@NeSkn-sQsG ztX{0iWChK8(=|Q<`@Ec%Kvhay%Ee_5fM^)mB}FnuBc7^x8k1rpk|3&%gBY4lmu+4R z8%qNXGK4H%JuO5k^3TC9W!AD8S!u;I_7ocD!RmG@k;alVfiGTXGMJLp|{DhnhXY|Fw+0kcb2k`SvXtckGG}&!>n$zQ2b%jD>6sFOUF`p}K@dCu5pwY(>O~xIjaVJud zLppalp#pH2SFJP5_vU%bsdji$ zk|m-Ask;shXuNIEk}+gQJzht;l2d7jnA2NR<0`S_^AnVkis_vhBo@#WT~bvDM4btz zzLGPU>yzRyRlPN%+7@>gSVuunX5B$+p2nJA$ zQY85dQJ|_Yt~c8Oq{vwyTb-bXl5`^KFIZ@{BpcNQC+$jA!&b@=@dl6>Vph8$87}%n z*pF9$>*s!s)H#@Qut*fRPo3l^Qmm>{j5hL=c-}&33z*7lfHGbNiSu@QO5z zIw+!MN@N%zQcka}SX5?slNjW5PP5zRb+WNUG8)lTp}4)V1nVRGWgY6l5@hp`4lFyh z-dsX~0oB1A02}d;E&y-=jL4M&AVdlRmu_QRE;OzrebGvgjKt-Xq&7vRK&?)Yi8`1B zSCk476Xs9q5M9D+$c2QYIl?e@voa@PwiKp?WdfKv!jADt#G2H4qlj0kq#&tMU}zBJ zTS^g_lp4X3PcL{wx}YV&aAtGXVbk%oQo6|6xl}p^dDNnhGifkQH5ul~FdvlxG?5HD zrI=2jowan-<-@F|8fpv#3ji%@%(5k=50%|T)~NNS(1HgU!V;9$`GPJg z6flJh5X;6TmSQUgbtxD$=c_0IVAU#`vy!wM~w1fx;d4uE^FkJaZJFiYC5;^2UlA}Nt%(7ceW z2T~O_n`3jN4Zx*YI$U(vG-jt%c3PuGmEB%5`U5Exf<0=Ov#De*!y&R1uxhP!wNYl0 zL5(tyWW8a7g;uF!IT$t=V|p4cgLv0nOXiCXL{(4{sVWrHiiq0|LS_!Y>JUv*cG0Di zDo{?Jw9sj~VQnIsVmeTWHqxDbY+bkGyeGgvvPEyD?9gwji>mfX1PFo41E1uWDdDKS&zaMY&wsn@BVonBnWe(Fw3R8PxK_#fl z2ucrQvzCGhdjYHs0~T;bV;&}J^_vU&kQ2+gv?#_T?W!yp_Slp}70`}`EsZjq-Ynsz zqRnXc6A+Jh5oAVvf+2XYi-&dRoUua0 zRN^L*Nd;7CGr+h}NCHSr*%A%_AlTtmr)V+etYv)OXf;(Qh$hQaC~swqFfBp>2?{$x zC`fmdg6PJosZ0rH@FZ_H%O*}=3S^RQb*SvANN5?hmc2obtLQa@SU|_gFcB@+3F?bP zC|apz^MV~uI+LDCjxUfd18mQGRe8H5>J5+%#sb_rnhjRL?ucKIKEF_eu`~oiIinxV zYeh&$H`GQ;X=^&QN{yy-L0r-o)8Tl5CW?k!o-db!py=s_OD;8QCo?4KPUloW-_R_W zKUWEZ@?^;4MUrMRT1D8JK-WhYgW}b)GGl}i044~TqyXv#dyE1W3nhz)=qJpG7$VZj zoD!izj;t@PF2|V+jc3S$FC%e@tjenjgV>JEp?jm}_ZWf0p!VZ18G6QO8BZ75btg=(5| zCC?;$T286sg`Ae)*}R&ilZ;i1Cc#T4a~q=Pr(L+i8n=Z^A_yY!-n7;R9;5)L9NSQ8#s*$G=6j$nphfZ11ib&Xt43*bq$HRsNwUSG+rspVx76l0B0 z#8%WId;x=?q#gB@v_V#@)H`YcT?tJyFz zk-S(Z7{>W1AC2a%^ISEH1in zMi06=|$Sz7@Hr3ytGIJQ%Eim*3osl_v9yJzfD(CRwvOj{zGBDum`3?1BKq&VyxQB;8uVnb8CS#)#fgGE^**s|>&(B3(sm zcCW5n1|UX0O$PlG+gN%~66~{!gi_i>!Y-7cBo9TM=8)c!r!q) z*oKQSpwdJsRLy7Ynu6fqy;7NsV-d^^mb$^`FN-iGQF?C-PShF%Q?N_ao8bgiPMaaW zPfI~HIH`jHmJEw1&@tdqbY3@w5^#aix%4>@GG{0y*q8(%Y5=UArf0CF<8?g*9R#p8 z>m*}!ikP8HsX&`4n*gc-s6w0zE^BHqqlU6@SyZ`TJFfS^akxUjN|y^(V;me}U{$(K zFb+#P*bl2>Ix9?@Ar1!AiUCw?%Tov-3W?`I_|L16EN_wo7q2uY(_X4jt2k;XS=Ku- z%50Z(am=6odB2T}!ELYPvXGdEIEsM~T@5AZU`&FGG@U^0`l3PdXfm~uKdnpy>kZO3 z3l;RGSz90lV!K)h18{={=XeROkI-6RYNRLOcM(;!TbZ*vY+$ziNl&s|2$yZD6v<@( z;ssIzl`?_(aweW9a{4S31O>53-bvYUR?sO)TbwCY8r2H1P|#@+LZ6i)QIpNBG?c0( zwHR`lIg3~VsD;akD}zO_yr39`)}TZo;qhWsD4mMR%49k$#63>n*Cb~kP-vjUGRdl~ z*{E2vkalm-7Q*qgnaI10ELU;*d5lCkj^-r~N?>f#Eig&k5Z5Fzv{*FRSe7n^IH$%? zqG*E=gj{u&Ly|2}79u%N!grb|)L04Xz(NtSP@HhnN;%4Vy-+zpsIrVT>Gotm)am9; zg6#AM=z`=9#p9t`jdi*tRS0X~Ha-u|cw8kN?DEOJw91%b>_S#u2$Cu$4VSaP=7;=Y zvpeg{sVT5iLnEnZ-0YkW~qb+X*=rM4~Z9_=WlY8(sK>e(2JAWE97>4Gy3+xlC02>I?Jg@A%LaEF)@DRyrAxN* zQP!1c1WP!tgJQ)X>eeIHR59(v65)JC^w4e+f*qll%bm`c1y?eybOfydp9zbAD5&QF`N|eCrBao^ zj+9`uVkd*SOiIZJ1}30UN`Ax=kn)g^t}%t81S&!r3L{A_s%BLBGSwJirHGPBG0Xzv zTPy2GxuOq8ZQcL_@r6LEUEQsU97}0ENwEFVkkp>P_UbP9K8j$QT6h1RsY7 zHHc2;JZM4bic!iyTJ6q-08Z+W061$fBk^LGh}H=j<6s|BUX8ghvQP;7H9{=Fh6THo ziP0&H1)zWcziCvI+rcm)Yf9a zf(3k52d(BpQlx-$xF7Y{kdj4;%Al&k5#<(ucSBX_+r>%siwV2kRE=Ng< zVIV(_qDEd{WmS++Zjae*k8fKu4QsMj1c83mmS z!QF)PyG>15fT?O-;fhoAcv+8~SDB)XZCbR)sfh&8*+cAVBlZN-{o zRma4HwaD_?P^nSZ6A-z`=!}#~E5uc7r5I-Y-iosn^_QJFG31mjc~(_p!7krafHcyL zLd@@LFu%6dXZhEEb=F8ZR!oPqF+49NMH}Od*|9dy8WqJ{*hu7plWpibCB@&}Kn?Kda0{*xAZD6>uSH8ZPys>xt?{|0qCx;u_w)g+z zp7{ThM;zSle|}bhW#Kinm6{T~z8dB2`1CpmDCyb4Z?x9(M3R6hrM>*vSMXx9$h1;3|n{JURT-&S0FPP?vWl-jquSaHV1?b=`5 zuJyfkFMt7_eMb9s;Lp!jhjY$8>&$c8p9jw1pAVYPZV%r4%RL3S)xN_S=d?Rlao+iV zQJm4P{h96Bw{IKE8E2m>v^%5ynHQgR$=Nq_xU{R{9ON>C>c&pz`i++-yQ!}TlRIo8{_Ri1@XF(VC0~4V z*_y34W7Gkmm%a~fcf{g}Og5Jnt2Id;F>=&>_doE&lTVF(`k8SrO@4XGD^sV1i28sJ+|MNm@rjwjzG1sJ4f(U- zze64>&i&CmE!Uy=m-M_rs=jTj{NU={9=XN`;PpU1MXz~-^xxexYoYjZ|0TP|EF5XN;NZ@sx{K7^ zpIv$M>q{QrT14MEKH};Zrja)+UNIm#cj3BS_ZK$wPW87f@g8~P_Rh&WZBOsI;(K_g zG$1d|`|6T`?{2wk?a|`;HSgUSJ2m~9n}6b_IgwVyd9#lV=x=@bjt(C*kEyM4F5a=@ z%}%EteRjn=osW-ucJk%hH?Cb*XjwbEIOfO0w|Mi1Cr-4Ni-T9)efO|;r=0BZ%J+j8 z^-zwz>6-Jqk6D2{Ip%v)&!*Qad<&ra?$iYv`S0J@Sl%{a`Kl?g&my5)Eq2oK`ELI%z|LVdB~{m0&qzIWXV&tJ0t(cae^M^3u>>S^)Mdrfovwf6F8{l$-1 z){A4cx(n&r!`Hogb*H4U>C67+J0E&TXxg*!$&q`g@gq0%@{(PqEx3AkOX{L?G+oyI z<(wUs!xJVi{-3&dD;>C2#p6q_K7Qinn>r3VIbz42N5_q{r^a_&cp{TzUwtX?w$FR* z(Xb{N|<~?%mxoN7#4h+rwj8#=Uk&t786hV^49rH~;X%UFufF zqIpM`w<=!TJ*`!70y?tq(CbH_mS^@HXjyUV(%sQPf4=HZ0sg5L|BtS#9gFUo*upG5 zt3NE<^tU}1EML)o`VU`^?0@-i_RvhfsQqyBrh68vXIa00u+#nT?m4;bt|v5X*D(t| zjMPd8al=!-J(uy}q4T$A_r5sy>%$+NIP-~d{XSmwY4@|xzYvzS7F&Mp_ED~HhAi8& z=(fuGjnsj;djsYUJL8)SFSZ+_f(E_9JYPH@I#|hU#*+=)DF*xgT0r% z*Su4A!Ort8?DJ}Kio1L0FAiwkOU+R8_ro_1{%FWsJ7*nT`OwRIcJ$u<#0z8QFBrbB z!v-p=nQ_&o6OleI6xqI@PYd(3o+X~BHUYu6AwA&*ij=lI)Z~f?1FDl0F=s2p! zq^*+|`;Lvd^~S!ollQD%IHnty!auUVxpLVDBi~9cOGMA_I=aKvcYprnq|PqSj2}+j zziD7;))4yDYNv~S*xj;hV{OKU+4C;fKfQ+dasC6bT~o>IOQBa@oPM8v^b6yN(JwqT z>H4c4?QZiwT#dZB;f{WjS3dF9!w2DO%bk6WEEV=H-nVJU%ppex?f&M}`UU^R4fevOBeUgY?zQ|{`u%4fRvc`<>ap8>=Oc1F>ZtauYSZ{iz@ol_5Fj!i!~;#l*d-3vCqMC%Fz4z3$y-+xwKQ-rFUP$M;LZ`=X~=vKWg3(zR>rRr;dERYU(um zw^GTJvzt+ipHcP_}e0^x|^Sf_aw(^pJ?4svCzwCi?&K)p* z(%a~=4}|CL8uq;Q;+|ca9$49J$3kZFws$sf9NYPFn=Ux(uyP#wC^G-#`va*vJA`Yi z)^2RRX6y@7&Kj_GW?^t^(yG`pb78qiWx(Pv|N2+Ueor!9a?(I_4VdcNf3o>-q5t|>fJ3t! zst7mj9t>ibkxTZhK6bQUp;Zyo=~@-Bp(jpN4||VU7l7e5AM6Zz#BaS5u)nbBICx{? z_>q(75w3alJG+~YKQn*n@n!oq?_N5qc_q63RIpVs@%WMBAC^0uoV=k`vCeyZ{raW< zAWjf`4LtRDi~qHyyC$6Kuzu+m&ZnEU-MYGETJsl67Pl%c9J+DBfgk0~EiZG0me)@l zIlk|Q2|qmyVmly>9sL*oDf|DQ$o}%elY_6GKI}%})y1Q}JoV)3y6z!YKl94R z)U>{HE;-)i9<(=lq3R99|Ji%a+QbL_C!eCaKmAmWmJK5fS z@Voo^J~8a`yuI7wYfrq+TvO=1XX;$`{+By_gdBZt(WceoJAZi=z}Nb?hjibu`yZUr zjL(|3^lnzq(m&M1oxJiJY3$SobAI=xe%-y%-XC9n(Ng4W-5hc8WsfbG7Bsw7#=d^P z_brBJCeQrvlc6`V6&B;q|_s{Pcc#W!3G! za{u2teCP~>I`?+HrZ|x6IN;tB_bvF)^FPHuwz>8>3tJT<&bn*FM@j8G-%0B2^-iaA z@wkO=Esq$1 zz9M_IbD;K%@3uQvEZCmh@ov+F?K+RNT`z54zIon+TVFl=@$`+;uYfvjp+A~t1x;n|oG_$tJTm=*wX3hTYtPRvdg!g=i5_>l zF4^us>yEu6_np7&s<|T_bgYf=0t~L0-lV6_+4E)6{UpBu#^n=TG4PLW;$@Y`B z6UkOZrz5+BJ^hC4ZGNwLZs+}jhfNvuKmOjQg^i2fnYEzlwd&xUczNO$>)Nld4^`-a zxx<4|1iJ9-9#^$rF!cMu$lzNlYahAyf;U%AFAaGB-+9Ru<*)jDrpV0b**|`A^5)IG zhZNuq13rnq@PU>1fEhaBLi6LPxzraA1lHCTxD$~VZwR&wTk`6(IRgj0dDiIe4_rC? z%~flb%-!-`zYd`tZ=9fKoW^O5tAoNt&2xUb>e$q2Hy=K{;>)*eo1S~_OR4tqZG}78 zp3{;qw)A+sHlsTi?ET0q4-CKf!Mo>(>6`r*c2{X;o!@QY!qK0FuHX3`+jIDX^Clit zkWX(3T~h3WY@EAc-WB{y-Cw==wRp#6S4W=g|I%ZfF00=5$h2OvY3~Cw7Kg`8zb)7~ zI%ebSJ%^tj@gY9z`(8VCe$*{~%SOvBYp0G~F=o^AeaeS_Tv+?|VAsb^8gC!|ewP)s zHKV@lZ(2G3K)>#9WfQ@JyCN6gGx)wSk)Ni1o4e=j^h-zP^+``Rwm47Ci?12HYWKI7 z4;wdW(#xBkd(pP`8SMPT)w}v{UB3R%vzkNaPc=VyQM9}!WbOEQ?MC{Ni9;?ZKRI#H zw()PRzU;yGI>Q%$^yiF64_`m(oArBp&n-(*GgBSQ9;VEW9>!^6QPCuDm;6PrR#+Ms^SWa^kRoylLP2w>&oFLvD0_>Kg-kog6i$vbI$r zr<(gee8lIx_B!{vAK!c9{mc4QXQf7KZomK9(w z7hf3A{IoOIeH=b@qCKCv_p0-&>=$dOH-?<};w9(X(wkDRG1~rvpV26uA0w5=&bu|c z>BS9)Us_kXVd_l_(gt@x-|2^Jo&S3Q5rPJXukC(HUiiEVoFp-V%7)P9xQfBvHR=WgTojQ;$m zz{XF0xcRMBw*^<8GuoU!Zadp_@5i?nCsu@y$B*8?O<#HPt*_=fBdg()dD~@)2WT1LH{J-j#p5uE+S&4@*Vw75VvNj?Vb5^PF>bY#-bGNBzimoBy`xp2ZkGW#(&5 zi(I>3JPlF&PpEk1o;i8ERq?}HrNKw0S7;BDJCJ6d*5IJg@mkdt2p2_kpV{Hu;^ zZdFYA_=;0E_g=bd&uZ(7)ABRcZDMY%eR&%;wE5*t%R3UcebuU1IDYA|U1MANZ#sW| zxh1^2#FV z=kW83*Du+3(tDz`?<@Jp|C7B#6Ar|VjnKJ;Cx$|A@H5;S2EOso)G=_}hK!z<7rvg+ z`y$6&{a*%MFnZ^c-6u{aZ`gWIua^Rkb-TrJX-6`dTK46_Wp5sNZSC&YmTcV6Z{rf(2hE-4%zb{tvfK0gGn?KS|LUmD4}Rb` z2|+Y`$F7NQgnWX!*S#k7>I+|mOT~A(SZ-NWQ>g4a-IFJOd}^&2$$ma?<(7-1-2s%+ zIk%e{Ja+a*-LaeQy8OkX(J!vLD-zs3bJfQ$o}J$2&270UzFTfpyfWp)f^)^|znGcq zWWDW*=U$OlS^Is_)H`(UlPmC>w~f8}hKI%Bx<30x$qUY8v<3VxE#MTZ^1zK3483T? ziVNfGpSgVFSDyr~opI;NuQxBSP1_sZdaeHYt2=kuv|2-5W z;qLMBOyT5?qq%uK=REMx@|D~@FaGpWZ~x)foAPWhddH71-q=@j*#igcD`uA-FC9AU zUy67Aa$h&@j4uWs-I_kI<P(7Twqxa=y9qyz(%GQg%zupv?@;Z&o13*XIm9f zPF5}+7@hL!@ts`l%Z28lwylM&%MTt7F79#SEwJ34f2Miso=@kE#am{6*}VGNrbDbe z@YMFB6Ao`}x%r2AHXGV`SF7TI-J7MB9;e;{--a9>z1ZI}^XO}fk4*TsdE=O)Q8dp=KVN!Db7tbn1=(@q4qiIsHyM$q8%kL_Np!KlONHN$kk# z%v~e*Fk|*#xM#zOant(^zSax*lE}(OKi#uu`iS^5-;ZiHd+w?3w`xaRH_kG7@|Xd) z?V_GI{PBX#`PUyT%(-l2_lbvk+@C&>4h+k@I%M;_>EEO_ZU1K8(-uptU z!gASJgCFU%`gYr#QS;6XNiT+w(xzvO_n$A{ch1`_cP+Sl(aiEmK4)mzFrs;Dp9PQio3!zhEvpY>uNI%_ zUU=%hjoy>Y#N9ia_g&NHlhOZ^aEaNgT81_sz5|}yr5`ToI{C!X4TNZdZD`PJQVdF#ye&ugt~JCFI`?7N!>=X8JVPz6wI z!@f+f+m}{F!@vwUb)b37iRCAw8@PKu`()DkCC8R7Kee?5+V@q5L2LUQ*{nMXD%c>< zKXLo+uSN|z0np&iN5&kTxiov-5Z~_2cWvI-yrzQ$} zOf!f6OnLL{ix$5k>|0U*J!`;hoj82Y`Z*UZKJsx()4n#0;nO~c|37>0x^r3;x~9iI z-^M@P`Qh>DZWo???PuKHk50r5YO2S5AI;IOi$6U4tmQuUNk?sP;I_4&H+Agz`jS%> z^nC1-vj^-0X2bz7Tn?7VH_=DthpHy!z5)uoZU$Q_9vB3pj= z>x}yzs@}PGY&klo&&yqRMP@zTySx6?kEh*x<9o(W7h1Zr>PKvS%kbXb^WL7D=gRk9 z@~pr4(v~}}I3EAx@wGGeG<5}A4(6?%zQplEgwV{{a^rq{$(?HeV19e>thERE<}Hp1 zO@G@nY|F-0#o6+kut)xA>!!+8udQQldl%X8&Gxa^bieNG4eQ*~I!xL%WB#CnA79H% zzkcwI@iX@y%cgp|_bhpvWj5Zmpz~JU8TSwB+GpF6^<9_lyuHh3V~ovr&3})(?J@rO zZ({wj8NtKwYij!`o-6p)=t$E)-ZtC7EclSL0JCFQdUZ-25 zK6a>8v7z|w(*0-s(D~Gk>gpv=o?6qi^9xXUamGO{=IiTt^}eO83hy}hcwj*Dx{c3| z9axTBF;l#uO8=XXA~byUlYna-v~o%h{$ zc<`2kg>S*~8-8m4^{2KR+V>Y>Ut9Z;eH~}khR^(8?7eqbQ~$dq8VjN*U;*h(P`XGH zrA1V_NbdqFy-V*zKtwXmt=l9IqdFGruGxz@H z&c8cP_TGfO*XMoLyVm;n{rU~^m2dO(^XUTKXVUX!sq@aAMb;gpCm4APy~tvGKx%y6 zPCSMB@1J46h+`Kiv0XmckeOzgnbv(sD?74Ty5)by*+(rAezl*r%E}iP($Z^^2w8ze z{rsHq_k7b|Zhh~3w-klAF9!s_k?`5c#;;x8r>wzh+%IiHfIG6=tPfd|dkrZyWv0{M zBOaC>CwqugxXbk87avf~i^|~SVzKKFUvO*l8_9nb8e+|hSeu}FyA>v?WylkN5{Z#d zkc%HS3gtZ7Ld8mN{mUTuQ`xs|6&%v2aE2f_O{?!g0kmtwQz3J3-8*!GxwtpOZ(J)? z3^l*U1kpT?oT84`FyJxJ%T0NHi}uq@;s-s0elOQ~`{n0D{x2pb(?MR}T-)s6Wmvxs zrm}X?Mb&%Rl&SP)xC`A-&;`y*Dc0ZXlTn=SkNUckLklfTH589HQer2-O~*6;03CiJkZ5Sn=hJ?C zT_Z1bOr{Mv5ItCN4Ym4DMc{M{V=a~#OCJObdiYN;Ete0>&tK8nup<2}!VOq9kF6M*^gRK57B zB@#l+5HUHydADQocs;vHVYb4QUwD@4a?sOk)Yk;=H3af# z6Ik>JX}onPk`XP~F57t7-<7Bv`1DocA?6hYV&pgIk?}~o^)Tww&@M})>(s%m2VmPy zj??tc-7rtHub)z#W?r81xeoO6XlSfDl#-jheg}2N1$9Mx`A;?7g^EhT!#GzZzF>IQ zDfQ)d+jsAIu)zs{sZ^mWeoJV_jvLk6Y_wx2#_fTQ~K`z23ZuZQxWc0IcK;` zv{B{#wBsSoEjk=U`Og#;)OG?<+wgf+?LeAf4LP2zQ#J%ICC@$ z&=xNLFr|#NJj9+hMdRX+*t7hlG&QvbgBMdbSTx&s3S)eh&t~keiE+MNrT0`%h%ui} zDk>;6e*Ohh)ybSWE>Pw0c7eunZUb?RevBd`weBXQ<(5gj>T`dUS(ap6%7}%UFSq%X z=aSjRTa<$pH^I5jj&`=!fGwNCGPAo^thqM@XXlk{ZupUND3b6~Q!{d_Dl4j7erNhQ zJ^kv`Eh-Xi5QWrowXnopQ=JF7__Ixedp&_7pnD%dj38Q&Z-@qAr}+Qp@bGw7{6`au zMd?gLAxwUfC`e^Gm$yMD5f2DhLV((YMe1p#l3c$9cx_q$zy}`UDMb4>$iXjbe?+cT z5NHy>fbKHJIxH&*(XlB6J#iwZXP&mq)N!{#VUOk+;Mwf2zi}s2q&ghNT!t!M`aHu=UoXw`#;^F4*Pa5F;F4 zjt?fs-Y&)PP$ep9t#0Jy*IOsfNfVm7EB7=|LZfj8Ttv<%+Jt65syl0=sj)bRmDH!7 zk!G+0edel1_*)On9F>AQX2PjWq+5fKLrtqE{X|`p!E!;c_V`L&qz7G11%!ntZ12ra zR^KwH8<2P%R=maPry>P5vHxoR+CnZ$=;!fus)(`=ufd1Yb70}4t28egX@rJ)hix^$ z@XG6lrM`_rW!}hAFVdK#y(QCRQ$rsu=2yB((T5QiT#Uu7oV{^TlarG=TB^Fbl2h7l zn#!8m+M2v0>YC~jHPxz;8uw?Mw3A^lK36-7zK9m*Dr z@!tsir#DzoCEgy{-(0Fobn^YRP+v+7@3C%FF5oN*otB+HH5j-tV&TBU+MSt@D%@c0 zG+S?}c0cTul%4Jv`<2g9ql==$6#cjMR3~Ezf(6g~yNfl?l)MMPZ=YIr)HSR(#iQd+ zZ_b=jeU1<5PvQycqH_9yfx+sbCDH#|v7Uc^$Nxa15(R%y{d#Dg7m_#o187v<7m`*J z`oRkkz4&R!>A@EBApc`q8I#ip8oZOYCINe{d){{vF0(n(jl-Y;3f1N}$cvefi9yqB z%(R{piV3SeCqXmKCK<-(=#z46GKqkb zR32dD7)Pwu5^wJ?raUkz4;H~mb@B+M8CVwB@F?D$_OwCVIb}0;XgV{hH!RGJtp(42 zh2cnNy+!7uhCjSnQ`0??1P?$S7F)1&#$5x_p*hjZa_LhiS}asZ7G-*!J2B@ zsBK7?x>+NdInITq*W5>^&OqPC^s(J^OwOJ7tb8~zQ=8#BZ;$KG?hyY)ySAps>OMT< zsO?C<=ajp>>(?MB9nIojTDI29k%)2kCd;w~7!av-MN^fo?cQa`-Ze(=8SH;qRRl{z zxAry;uh%yY=A0dd+G1h0j(!c5?yExANcf&Ib;suSK6r7TPkmrA$yduoRNG#Lo_mWe zMyD!EdfdeL1#E86?}POX1wFz?ouGBk3+ql}pQq{;93{RnXh)S!brsW}X@8-X4tr90 z2+DZaTFJs7PGgQY-ZaiX#m!VU?mf(#)3eWtC`u6xD;HFMaizIurLUJkQaJ&EYbXM? zgQI{p7R4%De=Dfz+?MLnmiVEbIIYX)uMMcFWI;HJMwZIGyG>Wd9IN#q_0tDDm)5Nh z1aVE;(4q9tXsbri|AQOw^nlWgH#FAvZc}IOsGGoGQz!{y@_w&_2l>;90Q54wP?@y6 zP4c2i-QZ@(LHl?(y7#Ocn{kS)Nxz13eTT8#nQ6sCfbzrZHz?v98|ZK3vpq9Y?gx;X z+W*q0pfBSau~X&c#^(pbV!{Sd483>_P%#vn0KLo#LHB$F?}%)^bGU=lX>IZOx3jl- za<0v^mil>Pkx-1uU)|5X>R`E_!5IeK#&1IVEi;X0)>99#5Z}lJ!)^H*)D4S8Ld(e(e-RaQR&MF_z-U zZ(|ZoPL?t8@iL3!UHrBcWc*lac6sb=RYus0;=^Cy1c-E~SOrX;=}$K-uZ#1<%m4I6 z;vf?&%uZ1{zd;ue=P4Qdb5Cs^SeZCrjOWdV9Pxd{2mRDcj{9WR+<-=oqd}?2%CSiM zM0rS}$}aUKJV>)kJNwf5##QNo)!(3(KtYlpgL5{vT{kpJc;rjRaYX}sLwSK+w{Pn* zzh{H`#>|){4(gjb-!_>4OZDA}+(cfdJtA*vp0*5l7>aLLt3Zt~Nj+(>3c)8uAQ2Vl z-0Xm&^Z0mRN@bGQl)eJ#qNMZsjDVoy7+gs|9?{S1wRNRT5n~EBS$;Qda|En6j{R@4 zL^1VRF;6}{-umJBRip=W{Oo!(p356&KRrEdR?Ktwl)fA*LYkLw>(DE(1iLj@fP;fMzZa#R5&iW02*_KiSh3^gocVVFL}09Zhv0PIk?L=qz{RW-+jzPv_;KVY-v4MMwr%^RbXJsO=i*jUz*=b7<`(x~Lu*Gk#g{m~#n){D(McCCPtEZZ+S;sv5?B@0 zg;@&E&U+fZd#!t|4n=lD=T%gXw%sB{!$dr_Z|e)Iz@tXz-nkxpnVqI>x+r_esJnj`yrM0i4A zHCxoDqN>`i#d$lVVx9CJ7r`f17AKdNagbMt`cMi9#u$0$_Q@)m}mdeDhcQq9E#Z5RkJgc0TVVUGT{Y)fPW^$<-mj( zz$V}(rifVp3IZeM8NBXa@btgr&SVAF>#!1#>?7J)3&ANgV;#HwOZ8H&eBnAv_P6AC zVYTn)XjjQ^rR2`Fx(R?bjwh9wd2?^>(6Ch;npr0ZkFIh8r|XAsD@|IZ7U*I$D`H?`D_SQA&@ z3>8pDO*;3kW|#Os!(fm%3@!&%y`Y-X3kN4d~#_~!_ctX+99EW{83MUmNyC2r4xzvcz=U#8Uu{&Vu z*M3gFLFz!OgWOIfw6%G%m;Y~fV7sB!$HLYkv9yx@!5i=11|b!0mTGnV;yX&wp8&qzKX)!Lj zEGN!}Cd4_CysT2SkpUKDNijl)POfb`lal*clGsnBaYjFvxm9CH0}kEWcepBbgQ`*(iHg8M2tFdp6yQZ8Rgo(+ww zTf#I}i|1(B6f3lLuicQZ8nLa*xb1~ambWwQR6Xtq?!yHGC$sYgpZ{=S39_^u*caP= z^~b)oFVSb zg#t$%F;TQMuj0H$l5*gE?Dml{uRE!}vV3BB5OpGuROmqFeHQrk(G#QMz>d9$+OXbV z?(2xIm5L`fm>(q2-_T*W9+DHn64q_XWj!wLE$J`*IsM5yVE!K|LY4`VTH%P1EF14r zR_Ka(t;op{yRE9fcSF#yqvBan)V*Nsive0>AkYsG*@abcckJtL{|86@FC5vf7u0_C z{vdlLj7*R?pAJ62N(^KaA-ABX-d(kE!I#zmGfET7Bm`)_-c)S}vY3fq6@8%jUWMmB>SVawAr?+NYr41mehDTyqW5e+?4QWV7 zeIu~H{idqQwRu1xr}7+W%Uq3xw|sO)a_ZZoL=ZL4^=*Tel^1{D$Fsqeq$ZN(>n?}! zL{t1a?%JYjGHIVZW8F#dJi)5FJwSZ$eYQP+w3G-pN5%MW5Pj!+O-^ySq0d!~7L(oK zjDc%u=it^wxo37Ct2hn4g*x6hKeA=2cADy+NCl-+n-IMG^uj*dAc&IUYg zI!1QlH~Qia^Sx=f6&Z{wk2dT{EI;Ed_8756)0ZQBzd@ z)YXL;)Ys>rswm*+2+Pc{LObr(ZK`x17gDhge_@HjfY~(15@u=~Qm6W==k3?(8^E7M z`JQ1!I-ol5K}S+{i3eiD+e?R+{N*DMZ4b{a9b9#8n=l?>kS4wIb4>AkvTu}S<3@UQ zRcFo(as_o13oQ{!TGUpfsb5yyvCVq&^aEg3I*n}hfM%b^1u+zs%(~nwX@E&7OPrmvvPejIV|=j353O?+)RM^`xg4iovPZ3?LQz?&^w}^x z(yuhc0r8N=sQHe#cabpV{DsZ34c|GtjE?5!+OTKV@(H4%=X3!(kz+W{gP9868S09r zqwgMdDr=FKd+nJgX5ld{>v2!RsB=0kZEfiFp9^YVcL%|@&=dAe5slmFi)JP|a+h!c zGo`TMgaf>U#Gt;}v7)KzId$l!%}IF9f2TV+Q`yxw@pPqf+aTN<|8a{yOE2aNeXOf{ zT^Ag~D;Ugkng5ERlp!MmM1n zoHIzDVN4S8iVFOQ$YIX2l-c9^{d6O`tkA;1n}->-Bi`6Br~b!dp5f9jQc{`qo{9ma zx&n`=Mh%FHmi|y*H-(&$=4mE-Im=yRUNZaX`|04$xu~neJKHabg_m8yM{<6iDRW!80%jHya zIITSt{j!`@NTFS4HDGV{ZTh+lb)#fVI*|3z%Se_UyI?f0*Wuu39?9UOR9zuUEkbR< zO7`u>>hZPTpz81Y8^puG6?r1mA0X3qi|3IDcrQL1$jkc438X%F^AKyCM+l5|klk#7 zKM@xH#TSrmE{-gRp)S8cPcn!BJILusKoyx20VTxA0+|{Z^oeULBT(S#`&t3)8G5RX z5buLU*{=}QA3`?n06`a0K4)Wt;A5vsKppj^ZBx2R=15?P@M?5I)3@YwPF-D{?}N-` zrj6;_!;<6mkzDV>q((J0yJ%1RB^dLu}o>mI5PO^dD+r?J)pW; z{u5duiUy3P@2q_>UbGrb ze@OArdHT?ue^H``zLZ?UjQAvjLVDgq;P~SXe?z`0V|%so`w*4oaRrmp2d{Gc;`bak zE4KveE3H3`SO^Nfs(qwpW>dIjGx|BQD&m1-RcUAlIkh>rq8hZQUV6}jLERi8nq610 z5*=9aYtVR_<$=K?9CO6u!D&1RP|&Fok)KWh_^PtJO{zfux&X-iupXZ_i-^~@+G zZp=3mOa)2Qg*Zx@LuMU~+uO?(F!0t5}E((+y{Iky+Fa6>-xBdT5SLj5nW z^e^dGaIp6gc{3mHW&36Dnbt66f2vXjtfU+J0zxcz)?S;m&)YWJXFn8QD~h2k%7d|p z?yOUyA2j}G{VW}IdcFYW@QCvv&1qb5c7MDfvS(H9zRrHaxA9CWk2HNb zo=uJla;Gvo8~WI}NODA1WgK~_Ek3_TfeUMS#E%=U_POITA+8OnYCE72OxQj1EMgnu zOZBKY*DQB>TCmh7DMY(n$hBQ@O-0f@oc{^yO>@-wFhjDS^FnwpV7He0b{Rj$_|^M1 zeNGs~cc$_q2Gz#sbz1fX3yywO@UEDLo~mzjLc*Iw#bG#z z@AcyLJu(sl@24LSMgL!QR|0K_+#Z~8(LO$b>_phR-NM-$e?TqUYCQTLPB7M zJzHW^j@YF}X>SH$UHEzQ*x21NAgUgr0TrIOrxIS14->`$djmLhCa zoTdO0o)eM?5pVV1+uJz|UzKJrIcr;4)0%<{=QbXdpEwujPQq;*J9fj#1?&?BjF8g~nxxKWW&VVVMZkeJu*oyp=UQ;0Cj zdhA%+ph>B-&~UXwo=k zyn>YJaP3T^^xgt^LTUP(%bx#x3WFKxewwIP5(Vq&CoNqC_F}pn$5X}MAdRsVYeN|E zjr+r;AxXBKw>v<3si7EyirSV73BS%pKf1AK(|Yysk)+7pBbtM5KUN7BROy`Qv3qop zm#sT6@SwY{x`8_utvqkG9|*!GtV#60I#n+M`O6M{BGgbHGnwv={JD;fj_JC8D$Z?| z!H1Vows6ZwxZHjHuy<;R%7Cu8u1@#8)=G9X`n*_GOO55o-oeivRXXZ;FsLvG$y`(2 zeLF?SDK{Z3K0bNwK0II+JFYj?jvwujhrc*+(`Z?kg8U4@@t9d1=`F7B7D=a2Paf;N z?$}wFiUnF4sGDfaz}*mrdUrW|x9;1zGKYM^y7FHdJHVsZJ-H!@(TJ*(c(Bv7on4@_hrDK}e?M zn!wR44t}_^vY^`gaJbJclE%)wYL+o?V}amND}4R};?M>6q7~K~dTq%@aZmzxJ&hk8 z)70)~22;P_h}G!1kuGI5__$SWwl3|5!{ZK}xQoEigkDoY^Q_`43tN-Xp5>C zX^m@XO{zSY&a}Rt?v=cjplj+pq{xY&83qXm8V6HdT>A}L=`E>Wy=%FeR`@NZr-wc^ z#)njXVvgoH>L0{$JZ*ffws@;k`3lH5OCNEh)9#JE2|^b3=+a=qB7D5@8)qz2b)CPc zjd1c@j#rNUdL)~?9EWaE%GIA0wl%v=30UyN-k5O3vL_Ez$28#ZNcvfN5Wag@f`(^~ z;DG&5kyRGIjFOLGEf$Mc;i@gZDLkEJBG|U1wt#6Zc0M;*3Nc!0RJP2J5~Vadc?_m$ zl`yU)8;CY5s9UYl#rz!eQMmTKAjFADz4+P>ewGGD%afiClG|t@kt46{+m+DhC+_Dm zs?{Y9>y8jHADFGbZh*ag{WbH8iqN5l?^P>8rI8QA^MN%=AY;Q8KmIFsaiL|FsW@rYgM}=hy2D8`uekWoEBWBo}{(NDsBf2Dk~==;4qhwsAIkWOK&m zmjT64w>*w;Wzp-aM*T!3VA`0!x9Mh%^{6GISZU}RS!e&m#lkwYXCk@9Vj;wD0bb%Z zp|;HSJoydkFOSk23U!1R4VijxJbhW5)tw#~?qI*Q&>XDNt#l@Hx8X8}s+*V^ve4cE zFiNn@1FtZtoDg9V=lPwjtyvfI?hfTFHKcQla~FN8LNn~}F&*ZM=fhcsE+#jUYr7_G zZe!(prMn9iB&#*l-nk^%_cxp@IJ~A)DpEF#@0_UQe^5}bYU#eXx(oXy(_%pNVgoMZ zmwPjpvQm%GLQ+l$Ur+m%+Nz7t(br|w-piE^(AS^LNEN4){IL1mUUh>6zXDDGn4#_| zFelvA5%O#Jx(=pyz`j;Je#qR33oZZH(C(!BavVc1SXHMoS|#pHkL}$fIgVRDCBxZD znzQ=xz4jPGi@5TYfPxFhR>NApGfJ?zQD-Eg+~w&#EY_W{tepSV!$$d5NAaVa3Tf5S z@$8zXWkYQsAczmrIg%2eB;C4z(ZJiHUsY?WYwSw*4Xv!sGvo_MMRSP*{fwmN&})mb zJPU=w%_^-~-*_$5Js@IUI)@&O4{5f`Dq~~Od@o#hVKQfGg2T6u57f_I2#+No*UGHT zsvE#BnnXd7I`m8}?1>jbe^CbtKGAk4=bdFQH2GW5#}DCe<;JKEzYbq8VfOhh#$ZQ| z)D9}^m!24UNGhdjSa(F_wJW7fp`;}FNwDSN>p*U@{>znpG|I)?tjYEgrPG7Hslf>u z1<#&M@^HzL%Sl4ZU;6eA!XYAiLUv7gZ8>p;;ta~#dO}fFo;RNaicdq7ypEiAYm#ehP?wdApKMU_C_Gla zFDXQq8RMSe`q0-c-0QRO1rY%z8IxoUy+_QhJk}lgSIE}EZ~0oIH`aU#gO^KRYQ~#8 zgUK3;s5tv7oZj5doV4~x>N0;!G~^EWX}W#zwB&&t>{e2@Zu@;vcQNjW!h~=(XlY&? zUkwJ>uc_IY9n`liv+9$qKK=4G8wu^jtG{IW*!Om5??)<_S(yXjjW>V%3M9YE-WL=! zQ*rt=@7Lh_{U?%s#)hJ)ePiz*ofci?C@kwY7t$6~H$6Dk8oQE^T~<|Q7Ac~pzwnVU z!{0-1v~TsY8W0I{i6f)BT$V9bp`z>iclQg=`Z-cSq{*C@N@t~kzO}=^`*XP2TMx>i z*UP)UBj&4IR)=&_;S+RHO?8j`$4b+0amnq|PPzptb^Sv|k zG;zZ|-^;f;@ie{rV^rj0^&`wUS#WvQBw&0@O!WH**BN`TZks%h`}p9AQZ^g$_HNlv z`GQg2jvP*Vs1sP9<{yS=|3I_<57+ek`)x&9g!?j7aoI>EPx*;6xtz1}z8;wZLd!ct zrS>m43_D8o_%B0n#QAi>Ho$6(4Fh6M{=P8aL~j0ncHtn0cB7`|O=l^Sn8MGJtt)U$1D~C9IWDhv@_9|| z=8qV&+&-$!p!ChpT${v9g~W*Z)p5KUc;|Coiq2=x zbmV-b7Aqih!hSyLt_y8BHA*Nuqc0>bVSQ z*&S7mT`2Eew=h~!mcq#N`++3HkMfsog77qF=aVraxx#CK0XQx)@cWNo0pn6e+nYQ| z8eK#?WtRD_jFHk>@DdLTbE4S%Z_s^dwKc2RiCv~NSfit*mQq$`s%Tpkrz@H)Ow(nh ztm9ps|Kq#wFYK?=USBA^b)H{uRa-Ydx-KsyHk+eFd9ECb23Rn#b?Z{?mKuOr<@$D~)ou%Fnb1ZyoYI1KKUWUoa%YhpM%{)GFGc zLlA`FB$ItgTaOUqR%k9+c;i6%zKEz(&-RG2+SK*S+mwO>lds}Uv;jP(7amn z#JBF(*zIdV@s(O@JY>A8>g*B56Y`e!jIB?eIZ&=VwehwQWre&{mycrNPjzWcN9fml zt$Q{p*HSs@6=%ThsQXqASGg&8Qk=`A2R-aMC20XCK?j&BCuX6`wEp$&r(zIy$3`pK z&Khx4VA=iBz?j0Nlz0z|ouses%rv@F+T3Ylg@r}>mzsy$ItSsV#!}>i?mcZ?8zJY~ z%Sb*a_N;QYL%$hu$Ai7fZ9^(_y38p%sfeU#TSw63WCYLTS&$pjc=u=CtPi-P5$(8g zlGiSn}!vc!3%rv>)bGg*%JHTpt^)- zIcu6q8BxprFLKX&w(P6ybs1WgzSlPMvwKHnH_9I;jcIwskeUd_)}N>1fG6+KaGt~+ zC#5Fz>W0%^X%WWh8c(I4TUGI4)R=q(eDe0K97ngGLt%1(%p7Uf28#L{35qNaBqgM^ z*05)8gzHxNtqwnQa?hHaj>{gA>ane${Fhz6K>#7eiU(FHewqEbwJZyewiom>UX&xf zH9bvW!q4&GsQ58h8FP20cNf*T(@b&~0jFhjeW#Hg83A@LtILE+s|v>#dGQqQSXvgA zm8KYkrmaQKuJEs|{akXzCfXS<%iRyf;Jh#VxbTtdiueeBxn1R4-DzD*ooLv=WwJ2I zk4K!tfW`Ky1xJh`XBK$gArbROqx8TdeQ;hXyw>HM!GG@?^Wj`mF`edTM5>|l3fIvS z#&w=bNjDL?S4O^?<2ss3ZQ8E)e%5N ze@U2j^hfizo`Dosch;f!mEWL55#(U`nyB3Le0X%}rtxf6Dbk2Nsjm+sD&kpRw49G1 zKw?VGHT9Eb>6GfT4{xKTA65l=84intCExGIcRdZUJuuH~`w|>QDSY~dQl6zU=)y0OL?s{Q+M678QsO_jLZqUOAN@_%798Q4AWzeqh(=9rXCq}> z`)P@Xy)H}L4y5MVuQ3dkB?s|im!OMMStrz5Q<>VlIHy@X^Uu}y!dGLIY%C(J-eHARNzxdM zw!M49pOa37goD28!}H7llwWNP{KQA#%G~YiPbOnmL6`Wt4n+DbI5LE%8Bq)gM?>)Z zDU*Kw#Vv_oW;;a@Em_)&d0}CIxpBu!V2ug?`D|p_g2B$Ccc6sgQjNcwj;6-CppPLZ z`CXs4u5XSwQ^*)qI3lz|7t(@d3RUE6WMk3;3tde?2Yv)Cw&7-sOOu42z1^7o$I*(; z!6Rv#kbckT1E+DJHh6|r$y0YOtpD4W^~k;(H^*{oAL` zcxm>%6FuC(I2P9E=0g37l1Yp7Wdzkz>Osa~+o1%1zPI<6)fAaLSspMK`9h=HF;A37 z5Fz!>kJtIv9~hMxBw2jFbfMd@-^@htN7$qG%o>)pJ9r>=q__85B;pi%r0ayD=-Q-f z?Io@8F=d_dA`m77%fbxk?GU?`188GdF7&y`D~o~m)-(7-6kDV?EA9H?^Pmqph{xJ` z=tBd+;MnPOrQe`~1W7BGQ7jzl>2RW}lA2<)J~vGLK-8M;@Ivz<+g75?{v@cy(Gmuu zAa(!sp()=Mh!KR^J4hN`adAZ5VDfvYdp)&d&H8<5k9p-∾|;KGP~Hm)wfsJI&5L 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�#Q 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 OKhN|Z_e|UV%>4_r*&p8k literal 0 HcmV?d00001 diff --git a/art/src_logo.gif b/art/src_logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..c63ff6dcd121d839fd6698942115a058dbd043e8 GIT binary patch literal 3348 zcmV+v4eRnpNk%w1VMhUB0OJq<`~3Xk>gd_z;l$O@&)wPR@9lo1n3A%nwadh%z_;`E z_3rcW`1<+w`1k++|NZ^_EC2ui07n5~000F3(8)=wy*TU5yZ>M)j$~<`XsWJk>%MR- zn@>R?Nv}Ja7Zi~k4Q%VaWFWa(C7gnSR}95tajUa5E9X__<2YW7~Hga%}y4Y z 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 eNh*D%hR3;_Gconfig.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.7.5, which was +It was created by sqlite $as_me 3.7.6.2, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -13942,7 +13942,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.7.5, which was +This file was extended by sqlite $as_me 3.7.6.2, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13995,7 +13995,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.7.5 +sqlite config.status 3.7.6.2 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/contrib/sqlitecon.tcl b/contrib/sqlitecon.tcl new file mode 100644 index 0000000..b5dbcaf --- /dev/null +++ b/contrib/sqlitecon.tcl @@ -0,0 +1,679 @@ +# A Tk console widget for SQLite. Invoke sqlitecon::create with a window name, +# a prompt string, a title to set a new top-level window, and the SQLite +# database handle. For example: +# +# sqlitecon::create .sqlcon {sql:- } {SQL Console} db +# +# A toplevel window is created that allows you to type in SQL commands to +# be processed on the spot. +# +# A limited set of dot-commands are supported: +# +# .table +# .schema ?TABLE? +# .mode list|column|multicolumn|line +# .exit +# +# In addition, a new SQL function named "edit()" is created. This function +# takes a single text argument and returns a text result. Whenever the +# the function is called, it pops up a new toplevel window containing a +# text editor screen initialized to the argument. When the "OK" button +# is pressed, whatever revised text is in the text editor is returned as +# the result of the edit() function. This allows text fields of SQL tables +# to be edited quickly and easily as follows: +# +# UPDATE table1 SET dscr = edit(dscr) WHERE rowid=15; +# + + +# Create a namespace to work in +# +namespace eval ::sqlitecon { + # do nothing +} + +# Create a console widget named $w. The prompt string is $prompt. +# The title at the top of the window is $title. The database connection +# object is $db +# +proc sqlitecon::create {w prompt title db} { + upvar #0 $w.t v + if {[winfo exists $w]} {destroy $w} + if {[info exists v]} {unset v} + toplevel $w + wm title $w $title + wm iconname $w $title + frame $w.mb -bd 2 -relief raised + pack $w.mb -side top -fill x + menubutton $w.mb.file -text File -menu $w.mb.file.m + menubutton $w.mb.edit -text Edit -menu $w.mb.edit.m + pack $w.mb.file $w.mb.edit -side left -padx 8 -pady 1 + set m [menu $w.mb.file.m -tearoff 0] + $m add command -label {Close} -command "destroy $w" + sqlitecon::create_child $w $prompt $w.mb.edit.m + set v(db) $db + $db function edit ::sqlitecon::_edit +} + +# This routine creates a console as a child window within a larger +# window. It also creates an edit menu named "$editmenu" if $editmenu!="". +# The calling function is responsible for posting the edit menu. +# +proc sqlitecon::create_child {w prompt editmenu} { + upvar #0 $w.t v + if {$editmenu!=""} { + set m [menu $editmenu -tearoff 0] + $m add command -label Cut -command "sqlitecon::Cut $w.t" + $m add command -label Copy -command "sqlitecon::Copy $w.t" + $m add command -label Paste -command "sqlitecon::Paste $w.t" + $m add command -label {Clear Screen} -command "sqlitecon::Clear $w.t" + $m add separator + $m add command -label {Save As...} -command "sqlitecon::SaveFile $w.t" + catch {$editmenu config -postcommand "sqlitecon::EnableEditMenu $w"} + } + scrollbar $w.sb -orient vertical -command "$w.t yview" + pack $w.sb -side right -fill y + text $w.t -font fixed -yscrollcommand "$w.sb set" + pack $w.t -side right -fill both -expand 1 + bindtags $w.t Sqlitecon + set v(editmenu) $editmenu + set v(history) 0 + set v(historycnt) 0 + set v(current) -1 + set v(prompt) $prompt + set v(prior) {} + set v(plength) [string length $v(prompt)] + set v(x) 0 + set v(y) 0 + set v(mode) column + set v(header) on + $w.t mark set insert end + $w.t tag config ok -foreground blue + $w.t tag config err -foreground red + $w.t insert end $v(prompt) + $w.t mark set out 1.0 + after idle "focus $w.t" +} + +bind Sqlitecon <1> {sqlitecon::Button1 %W %x %y} +bind Sqlitecon {sqlitecon::B1Motion %W %x %y} +bind Sqlitecon {sqlitecon::B1Leave %W %x %y} +bind Sqlitecon {sqlitecon::cancelMotor %W} +bind Sqlitecon {sqlitecon::cancelMotor %W} +bind Sqlitecon {sqlitecon::Insert %W %A} +bind Sqlitecon {sqlitecon::Left %W} +bind Sqlitecon {sqlitecon::Left %W} +bind Sqlitecon {sqlitecon::Right %W} +bind Sqlitecon {sqlitecon::Right %W} +bind Sqlitecon {sqlitecon::Backspace %W} +bind Sqlitecon {sqlitecon::Backspace %W} +bind Sqlitecon {sqlitecon::Delete %W} +bind Sqlitecon {sqlitecon::Delete %W} +bind Sqlitecon {sqlitecon::Home %W} +bind Sqlitecon {sqlitecon::Home %W} +bind Sqlitecon {sqlitecon::End %W} +bind Sqlitecon {sqlitecon::End %W} +bind Sqlitecon {sqlitecon::Enter %W} +bind Sqlitecon {sqlitecon::Enter %W} +bind Sqlitecon {sqlitecon::Prior %W} +bind Sqlitecon {sqlitecon::Prior %W} +bind Sqlitecon {sqlitecon::Next %W} +bind Sqlitecon {sqlitecon::Next %W} +bind Sqlitecon {sqlitecon::EraseEOL %W} +bind Sqlitecon <> {sqlitecon::Cut %W} +bind Sqlitecon <> {sqlitecon::Copy %W} +bind Sqlitecon <> {sqlitecon::Paste %W} +bind Sqlitecon <> {sqlitecon::Clear %W} + +# Insert a single character at the insertion cursor +# +proc sqlitecon::Insert {w a} { + $w insert insert $a + $w yview insert +} + +# Move the cursor one character to the left +# +proc sqlitecon::Left {w} { + upvar #0 $w v + scan [$w index insert] %d.%d row col + if {$col>$v(plength)} { + $w mark set insert "insert -1c" + } +} + +# Erase the character to the left of the cursor +# +proc sqlitecon::Backspace {w} { + upvar #0 $w v + scan [$w index insert] %d.%d row col + if {$col>$v(plength)} { + $w delete {insert -1c} + } +} + +# Erase to the end of the line +# +proc sqlitecon::EraseEOL {w} { + upvar #0 $w v + scan [$w index insert] %d.%d row col + if {$col>=$v(plength)} { + $w delete insert {insert lineend} + } +} + +# Move the cursor one character to the right +# +proc sqlitecon::Right {w} { + $w mark set insert "insert +1c" +} + +# Erase the character to the right of the cursor +# +proc sqlitecon::Delete w { + $w delete insert +} + +# Move the cursor to the beginning of the current line +# +proc sqlitecon::Home w { + upvar #0 $w v + scan [$w index insert] %d.%d row col + $w mark set insert $row.$v(plength) +} + +# Move the cursor to the end of the current line +# +proc sqlitecon::End w { + $w mark set insert {insert lineend} +} + +# Add a line to the history +# +proc sqlitecon::addHistory {w line} { + upvar #0 $w v + if {$v(historycnt)>0} { + set last [lindex $v(history) [expr $v(historycnt)-1]] + if {[string compare $last $line]} { + lappend v(history) $line + incr v(historycnt) + } + } else { + set v(history) [list $line] + set v(historycnt) 1 + } + set v(current) $v(historycnt) +} + +# Called when "Enter" is pressed. Do something with the line +# of text that was entered. +# +proc sqlitecon::Enter w { + upvar #0 $w v + scan [$w index insert] %d.%d row col + set start $row.$v(plength) + set line [$w get $start "$start lineend"] + $w insert end \n + $w mark set out end + if {$v(prior)==""} { + set cmd $line + } else { + set cmd $v(prior)\n$line + } + if {[string index $cmd 0]=="." || [$v(db) complete $cmd]} { + regsub -all {\n} [string trim $cmd] { } cmd2 + addHistory $w $cmd2 + set rc [catch {DoCommand $w $cmd} res] + if {![winfo exists $w]} return + if {$rc} { + $w insert end $res\n err + } elseif {[string length $res]>0} { + $w insert end $res\n ok + } + set v(prior) {} + $w insert end $v(prompt) + } else { + set v(prior) $cmd + regsub -all {[^ ]} $v(prompt) . x + $w insert end $x + } + $w mark set insert end + $w mark set out {insert linestart} + $w yview insert +} + +# Execute a single SQL command. Pay special attention to control +# directives that begin with "." +# +# The return value is the text output from the command, properly +# formatted. +# +proc sqlitecon::DoCommand {w cmd} { + upvar #0 $w v + set mode $v(mode) + set header $v(header) + if {[regexp {^(\.[a-z]+)} $cmd all word]} { + if {$word==".mode"} { + regexp {^.[a-z]+ +([a-z]+)} $cmd all v(mode) + return {} + } elseif {$word==".exit"} { + destroy [winfo toplevel $w] + return {} + } elseif {$word==".header"} { + regexp {^.[a-z]+ +([a-z]+)} $cmd all v(header) + return {} + } elseif {$word==".tables"} { + set mode multicolumn + set cmd {SELECT name FROM sqlite_master WHERE type='table' + UNION ALL + SELECT name FROM sqlite_temp_master WHERE type='table'} + $v(db) eval {PRAGMA database_list} { + if {$name!="temp" && $name!="main"} { + append cmd "UNION ALL SELECT name FROM $name.sqlite_master\ + WHERE type='table'" + } + } + append cmd { ORDER BY 1} + } elseif {$word==".fullschema"} { + set pattern % + regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern + set mode list + set header 0 + set cmd "SELECT sql FROM sqlite_master WHERE tbl_name LIKE '$pattern' + AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master + WHERE tbl_name LIKE '$pattern' AND sql NOT NULL" + $v(db) eval {PRAGMA database_list} { + if {$name!="temp" && $name!="main"} { + append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\ + WHERE tbl_name LIKE '$pattern' AND sql NOT NULL" + } + } + } elseif {$word==".schema"} { + set pattern % + regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern + set mode list + set header 0 + set cmd "SELECT sql FROM sqlite_master WHERE name LIKE '$pattern' + AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master + WHERE name LIKE '$pattern' AND sql NOT NULL" + $v(db) eval {PRAGMA database_list} { + if {$name!="temp" && $name!="main"} { + append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\ + WHERE name LIKE '$pattern' AND sql NOT NULL" + } + } + } else { + return \ + ".exit\n.mode line|list|column\n.schema ?TABLENAME?\n.tables" + } + } + set res {} + if {$mode=="list"} { + $v(db) eval $cmd x { + set sep {} + foreach col $x(*) { + append res $sep$x($col) + set sep | + } + append res \n + } + if {[info exists x(*)] && $header} { + set sep {} + set hdr {} + foreach col $x(*) { + append hdr $sep$col + set sep | + } + set res $hdr\n$res + } + } elseif {[string range $mode 0 2]=="col"} { + set y {} + $v(db) eval $cmd x { + foreach col $x(*) { + if {![info exists cw($col)] || $cw($col)<[string length $x($col)]} { + set cw($col) [string length $x($col)] + } + lappend y $x($col) + } + } + if {[info exists x(*)] && $header} { + set hdr {} + set ln {} + set dash --------------------------------------------------------------- + append dash ------------------------------------------------------------ + foreach col $x(*) { + if {![info exists cw($col)] || $cw($col)<[string length $col]} { + set cw($col) [string length $col] + } + lappend hdr $col + lappend ln [string range $dash 1 $cw($col)] + } + set y [concat $hdr $ln $y] + } + if {[info exists x(*)]} { + set format {} + set arglist {} + set arglist2 {} + set i 0 + foreach col $x(*) { + lappend arglist x$i + append arglist2 " \$x$i" + incr i + append format " %-$cw($col)s" + } + set format [string trimleft $format]\n + if {[llength $arglist]>0} { + foreach $arglist $y "append res \[format [list $format] $arglist2\]" + } + } + } elseif {$mode=="multicolumn"} { + set y [$v(db) eval $cmd] + set max 0 + foreach e $y { + if {$max<[string length $e]} {set max [string length $e]} + } + set ncol [expr {int(80/($max+2))}] + if {$ncol<1} {set ncol 1} + set nelem [llength $y] + set nrow [expr {($nelem+$ncol-1)/$ncol}] + set format "%-${max}s" + for {set i 0} {$i<$nrow} {incr i} { + set j $i + while 1 { + append res [format $format [lindex $y $j]] + incr j $nrow + if {$j>=$nelem} break + append res { } + } + append res \n + } + } else { + $v(db) eval $cmd x { + foreach col $x(*) {append res "$col = $x($col)\n"} + append res \n + } + } + return [string trimright $res] +} + +# Change the line to the previous line +# +proc sqlitecon::Prior w { + upvar #0 $w v + if {$v(current)<=0} return + incr v(current) -1 + set line [lindex $v(history) $v(current)] + sqlitecon::SetLine $w $line +} + +# Change the line to the next line +# +proc sqlitecon::Next w { + upvar #0 $w v + if {$v(current)>=$v(historycnt)} return + incr v(current) 1 + set line [lindex $v(history) $v(current)] + sqlitecon::SetLine $w $line +} + +# Change the contents of the entry line +# +proc sqlitecon::SetLine {w line} { + upvar #0 $w v + scan [$w index insert] %d.%d row col + set start $row.$v(plength) + $w delete $start end + $w insert end $line + $w mark set insert end + $w yview insert +} + +# Called when the mouse button is pressed at position $x,$y on +# the console widget. +# +proc sqlitecon::Button1 {w x y} { + global tkPriv + upvar #0 $w v + set v(mouseMoved) 0 + set v(pressX) $x + set p [sqlitecon::nearestBoundry $w $x $y] + scan [$w index insert] %d.%d ix iy + scan $p %d.%d px py + if {$px==$ix} { + $w mark set insert $p + } + $w mark set anchor $p + focus $w +} + +# Find the boundry between characters that is nearest +# to $x,$y +# +proc sqlitecon::nearestBoundry {w x y} { + set p [$w index @$x,$y] + set bb [$w bbox $p] + if {![string compare $bb ""]} {return $p} + if {($x-[lindex $bb 0])<([lindex $bb 2]/2)} {return $p} + $w index "$p + 1 char" +} + +# This routine extends the selection to the point specified by $x,$y +# +proc sqlitecon::SelectTo {w x y} { + upvar #0 $w v + set cur [sqlitecon::nearestBoundry $w $x $y] + if {[catch {$w index anchor}]} { + $w mark set anchor $cur + } + set anchor [$w index anchor] + if {[$w compare $cur != $anchor] || (abs($v(pressX) - $x) >= 3)} { + if {$v(mouseMoved)==0} { + $w tag remove sel 0.0 end + } + set v(mouseMoved) 1 + } + if {[$w compare $cur < anchor]} { + set first $cur + set last anchor + } else { + set first anchor + set last $cur + } + if {$v(mouseMoved)} { + $w tag remove sel 0.0 $first + $w tag add sel $first $last + $w tag remove sel $last end + update idletasks + } +} + +# Called whenever the mouse moves while button-1 is held down. +# +proc sqlitecon::B1Motion {w x y} { + upvar #0 $w v + set v(y) $y + set v(x) $x + sqlitecon::SelectTo $w $x $y +} + +# Called whenever the mouse leaves the boundries of the widget +# while button 1 is held down. +# +proc sqlitecon::B1Leave {w x y} { + upvar #0 $w v + set v(y) $y + set v(x) $x + sqlitecon::motor $w +} + +# This routine is called to automatically scroll the window when +# the mouse drags offscreen. +# +proc sqlitecon::motor w { + upvar #0 $w v + if {![winfo exists $w]} return + if {$v(y)>=[winfo height $w]} { + $w yview scroll 1 units + } elseif {$v(y)<0} { + $w yview scroll -1 units + } else { + return + } + sqlitecon::SelectTo $w $v(x) $v(y) + set v(timer) [after 50 sqlitecon::motor $w] +} + +# This routine cancels the scrolling motor if it is active +# +proc sqlitecon::cancelMotor w { + upvar #0 $w v + catch {after cancel $v(timer)} + catch {unset v(timer)} +} + +# Do a Copy operation on the stuff currently selected. +# +proc sqlitecon::Copy w { + if {![catch {set text [$w get sel.first sel.last]}]} { + clipboard clear -displayof $w + clipboard append -displayof $w $text + } +} + +# Return 1 if the selection exists and is contained +# entirely on the input line. Return 2 if the selection +# exists but is not entirely on the input line. Return 0 +# if the selection does not exist. +# +proc sqlitecon::canCut w { + set r [catch { + scan [$w index sel.first] %d.%d s1x s1y + scan [$w index sel.last] %d.%d s2x s2y + scan [$w index insert] %d.%d ix iy + }] + if {$r==1} {return 0} + if {$s1x==$ix && $s2x==$ix} {return 1} + return 2 +} + +# Do a Cut operation if possible. Cuts are only allowed +# if the current selection is entirely contained on the +# current input line. +# +proc sqlitecon::Cut w { + if {[sqlitecon::canCut $w]==1} { + sqlitecon::Copy $w + $w delete sel.first sel.last + } +} + +# Do a paste opeation. +# +proc sqlitecon::Paste w { + if {[sqlitecon::canCut $w]==1} { + $w delete sel.first sel.last + } + if {[catch {selection get -displayof $w -selection CLIPBOARD} topaste] + && [catch {selection get -displayof $w -selection PRIMARY} topaste]} { + return + } + if {[info exists ::$w]} { + set prior 0 + foreach line [split $topaste \n] { + if {$prior} { + sqlitecon::Enter $w + update + } + set prior 1 + $w insert insert $line + } + } else { + $w insert insert $topaste + } +} + +# Enable or disable entries in the Edit menu +# +proc sqlitecon::EnableEditMenu w { + upvar #0 $w.t v + set m $v(editmenu) + if {$m=="" || ![winfo exists $m]} return + switch [sqlitecon::canCut $w.t] { + 0 { + $m entryconf Copy -state disabled + $m entryconf Cut -state disabled + } + 1 { + $m entryconf Copy -state normal + $m entryconf Cut -state normal + } + 2 { + $m entryconf Copy -state normal + $m entryconf Cut -state disabled + } + } +} + +# Prompt the user for the name of a writable file. Then write the +# entire contents of the console screen to that file. +# +proc sqlitecon::SaveFile w { + set types { + {{Text Files} {.txt}} + {{All Files} *} + } + set f [tk_getSaveFile -filetypes $types -title "Write Screen To..."] + if {$f!=""} { + if {[catch {open $f w} fd]} { + tk_messageBox -type ok -icon error -message $fd + } else { + puts $fd [string trimright [$w get 1.0 end] \n] + close $fd + } + } +} + +# Erase everything from the console above the insertion line. +# +proc sqlitecon::Clear w { + $w delete 1.0 {insert linestart} +} + +# An in-line editor for SQL +# +proc sqlitecon::_edit {origtxt {title {}}} { + for {set i 0} {[winfo exists .ed$i]} {incr i} continue + set w .ed$i + toplevel $w + wm protocol $w WM_DELETE_WINDOW "$w.b.can invoke" + wm title $w {Inline SQL Editor} + frame $w.b + pack $w.b -side bottom -fill x + button $w.b.can -text Cancel -width 6 -command [list set ::$w 0] + button $w.b.ok -text OK -width 6 -command [list set ::$w 1] + button $w.b.cut -text Cut -width 6 -command [list ::sqlitecon::Cut $w.t] + button $w.b.copy -text Copy -width 6 -command [list ::sqlitecon::Copy $w.t] + button $w.b.paste -text Paste -width 6 -command [list ::sqlitecon::Paste $w.t] + set ::$w {} + pack $w.b.cut $w.b.copy $w.b.paste $w.b.can $w.b.ok\ + -side left -padx 5 -pady 5 -expand 1 + if {$title!=""} { + label $w.title -text $title + pack $w.title -side top -padx 5 -pady 5 + } + text $w.t -bg white -fg black -yscrollcommand [list $w.sb set] + pack $w.t -side left -fill both -expand 1 + scrollbar $w.sb -orient vertical -command [list $w.t yview] + pack $w.sb -side left -fill y + $w.t insert end $origtxt + + vwait ::$w + + if {[set ::$w]} { + set txt [string trimright [$w.t get 1.0 end]] + } else { + set txt $origtxt + } + destroy $w + return $txt +} diff --git a/doc/lemon.html b/doc/lemon.html new file mode 100644 index 0000000..6a4d6db --- /dev/null +++ b/doc/lemon.html @@ -0,0 +1,892 @@ + + +The Lemon Parser Generator + + +

The Lemon Parser Generator

+ +

Lemon is an LALR(1) parser generator for C or C++. +It does the same job as ``bison'' and ``yacc''. +But lemon is not another bison or yacc clone. It +uses a different grammar syntax which is designed to +reduce the number of coding errors. Lemon also uses a more +sophisticated parsing engine that is faster than yacc and +bison and which is both reentrant and thread-safe. +Furthermore, Lemon implements features that can be used +to eliminate resource leaks, making is suitable for use +in long-running programs such as graphical user interfaces +or embedded controllers.

+ +

This document is an introduction to the Lemon +parser generator.

+ +

Theory of Operation

+ +

The main goal of Lemon is to translate a context free grammar (CFG) +for a particular language into C code that implements a parser for +that language. +The program has two inputs: +

    +
  • The grammar specification. +
  • A parser template file. +
+Typically, only the grammar specification is supplied by the programmer. +Lemon comes with a default parser template which works fine for most +applications. But the user is free to substitute a different parser +template if desired.

+ +

Depending on command-line options, Lemon will generate between +one and three files of outputs. +

    +
  • C code to implement the parser. +
  • A header file defining an integer ID for each terminal symbol. +
  • An information file that describes the states of the generated parser + automaton. +
+By default, all three of these output files are generated. +The header file is suppressed if the ``-m'' command-line option is +used and the report file is omitted when ``-q'' is selected.

+ +

The grammar specification file uses a ``.y'' suffix, by convention. +In the examples used in this document, we'll assume the name of the +grammar file is ``gram.y''. A typical use of Lemon would be the +following command: +

+   lemon gram.y
+
+This command will generate three output files named ``gram.c'', +``gram.h'' and ``gram.out''. +The first is C code to implement the parser. The second +is the header file that defines numerical values for all +terminal symbols, and the last is the report that explains +the states used by the parser automaton.

+ +

Command Line Options

+ +

The behavior of Lemon can be modified using command-line options. +You can obtain a list of the available command-line options together +with a brief explanation of what each does by typing +

+   lemon -?
+
+As of this writing, the following command-line options are supported: +
    +
  • -b +
  • -c +
  • -g +
  • -m +
  • -q +
  • -s +
  • -x +
+The ``-b'' option reduces the amount of text in the report file by +printing only the basis of each parser state, rather than the full +configuration. +The ``-c'' option suppresses action table compression. Using -c +will make the parser a little larger and slower but it will detect +syntax errors sooner. +The ``-g'' option causes no output files to be generated at all. +Instead, the input grammar file is printed on standard output but +with all comments, actions and other extraneous text deleted. This +is a useful way to get a quick summary of a grammar. +The ``-m'' option causes the output C source file to be compatible +with the ``makeheaders'' program. +Makeheaders is a program that automatically generates header files +from C source code. When the ``-m'' option is used, the header +file is not output since the makeheaders program will take care +of generated all header files automatically. +The ``-q'' option suppresses the report file. +Using ``-s'' causes a brief summary of parser statistics to be +printed. Like this: +
+   Parser statistics: 74 terminals, 70 nonterminals, 179 rules
+                      340 states, 2026 parser table entries, 0 conflicts
+
+Finally, the ``-x'' option causes Lemon to print its version number +and then stops without attempting to read the grammar or generate a parser.

+ +

The Parser Interface

+ +

Lemon doesn't generate a complete, working program. It only generates +a few subroutines that implement a parser. This section describes +the interface to those subroutines. It is up to the programmer to +call these subroutines in an appropriate way in order to produce a +complete system.

+ +

Before a program begins using a Lemon-generated parser, the program +must first create the parser. +A new parser is created as follows: +

+   void *pParser = ParseAlloc( malloc );
+
+The ParseAlloc() routine allocates and initializes a new parser and +returns a pointer to it. +The actual data structure used to represent a parser is opaque -- +its internal structure is not visible or usable by the calling routine. +For this reason, the ParseAlloc() routine returns a pointer to void +rather than a pointer to some particular structure. +The sole argument to the ParseAlloc() routine is a pointer to the +subroutine used to allocate memory. Typically this means ``malloc()''.

+ +

After a program is finished using a parser, it can reclaim all +memory allocated by that parser by calling +

+   ParseFree(pParser, free);
+
+The first argument is the same pointer returned by ParseAlloc(). The +second argument is a pointer to the function used to release bulk +memory back to the system.

+ +

After a parser has been allocated using ParseAlloc(), the programmer +must supply the parser with a sequence of tokens (terminal symbols) to +be parsed. This is accomplished by calling the following function +once for each token: +

+   Parse(pParser, hTokenID, sTokenData, pArg);
+
+The first argument to the Parse() routine is the pointer returned by +ParseAlloc(). +The second argument is a small positive integer that tells the parse the +type of the next token in the data stream. +There is one token type for each terminal symbol in the grammar. +The gram.h file generated by Lemon contains #define statements that +map symbolic terminal symbol names into appropriate integer values. +(A value of 0 for the second argument is a special flag to the +parser to indicate that the end of input has been reached.) +The third argument is the value of the given token. By default, +the type of the third argument is integer, but the grammar will +usually redefine this type to be some kind of structure. +Typically the second argument will be a broad category of tokens +such as ``identifier'' or ``number'' and the third argument will +be the name of the identifier or the value of the number.

+ +

The Parse() function may have either three or four arguments, +depending on the grammar. If the grammar specification file request +it, the Parse() function will have a fourth parameter that can be +of any type chosen by the programmer. The parser doesn't do anything +with this argument except to pass it through to action routines. +This is a convenient mechanism for passing state information down +to the action routines without having to use global variables.

+ +

A typical use of a Lemon parser might look something like the +following: +

+   01 ParseTree *ParseFile(const char *zFilename){
+   02    Tokenizer *pTokenizer;
+   03    void *pParser;
+   04    Token sToken;
+   05    int hTokenId;
+   06    ParserState sState;
+   07
+   08    pTokenizer = TokenizerCreate(zFilename);
+   09    pParser = ParseAlloc( malloc );
+   10    InitParserState(&sState);
+   11    while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
+   12       Parse(pParser, hTokenId, sToken, &sState);
+   13    }
+   14    Parse(pParser, 0, sToken, &sState);
+   15    ParseFree(pParser, free );
+   16    TokenizerFree(pTokenizer);
+   17    return sState.treeRoot;
+   18 }
+
+This example shows a user-written routine that parses a file of +text and returns a pointer to the parse tree. +(We've omitted all error-handling from this example to keep it +simple.) +We assume the existence of some kind of tokenizer which is created +using TokenizerCreate() on line 8 and deleted by TokenizerFree() +on line 16. The GetNextToken() function on line 11 retrieves the +next token from the input file and puts its type in the +integer variable hTokenId. The sToken variable is assumed to be +some kind of structure that contains details about each token, +such as its complete text, what line it occurs on, etc.

+ +

This example also assumes the existence of structure of type +ParserState that holds state information about a particular parse. +An instance of such a structure is created on line 6 and initialized +on line 10. A pointer to this structure is passed into the Parse() +routine as the optional 4th argument. +The action routine specified by the grammar for the parser can use +the ParserState structure to hold whatever information is useful and +appropriate. In the example, we note that the treeRoot field of +the ParserState structure is left pointing to the root of the parse +tree.

+ +

The core of this example as it relates to Lemon is as follows: +

+   ParseFile(){
+      pParser = ParseAlloc( malloc );
+      while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
+         Parse(pParser, hTokenId, sToken);
+      }
+      Parse(pParser, 0, sToken);
+      ParseFree(pParser, free );
+   }
+
+Basically, what a program has to do to use a Lemon-generated parser +is first create the parser, then send it lots of tokens obtained by +tokenizing an input source. When the end of input is reached, the +Parse() routine should be called one last time with a token type +of 0. This step is necessary to inform the parser that the end of +input has been reached. Finally, we reclaim memory used by the +parser by calling ParseFree().

+ +

There is one other interface routine that should be mentioned +before we move on. +The ParseTrace() function can be used to generate debugging output +from the parser. A prototype for this routine is as follows: +

+   ParseTrace(FILE *stream, char *zPrefix);
+
+After this routine is called, a short (one-line) message is written +to the designated output stream every time the parser changes states +or calls an action routine. Each such message is prefaced using +the text given by zPrefix. This debugging output can be turned off +by calling ParseTrace() again with a first argument of NULL (0).

+ +

Differences With YACC and BISON

+ +

Programmers who have previously used the yacc or bison parser +generator will notice several important differences between yacc and/or +bison and Lemon. +

    +
  • In yacc and bison, the parser calls the tokenizer. In Lemon, + the tokenizer calls the parser. +
  • Lemon uses no global variables. Yacc and bison use global variables + to pass information between the tokenizer and parser. +
  • Lemon allows multiple parsers to be running simultaneously. Yacc + and bison do not. +
+These differences may cause some initial confusion for programmers +with prior yacc and bison experience. +But after years of experience using Lemon, I firmly +believe that the Lemon way of doing things is better.

+ +

Input File Syntax

+ +

The main purpose of the grammar specification file for Lemon is +to define the grammar for the parser. But the input file also +specifies additional information Lemon requires to do its job. +Most of the work in using Lemon is in writing an appropriate +grammar file.

+ +

The grammar file for lemon is, for the most part, free format. +It does not have sections or divisions like yacc or bison. Any +declaration can occur at any point in the file. +Lemon ignores whitespace (except where it is needed to separate +tokens) and it honors the same commenting conventions as C and C++.

+ +

Terminals and Nonterminals

+ +

A terminal symbol (token) is any string of alphanumeric +and underscore characters +that begins with an upper case letter. +A terminal can contain lower class letters after the first character, +but the usual convention is to make terminals all upper case. +A nonterminal, on the other hand, is any string of alphanumeric +and underscore characters than begins with a lower case letter. +Again, the usual convention is to make nonterminals use all lower +case letters.

+ +

In Lemon, terminal and nonterminal symbols do not need to +be declared or identified in a separate section of the grammar file. +Lemon is able to generate a list of all terminals and nonterminals +by examining the grammar rules, and it can always distinguish a +terminal from a nonterminal by checking the case of the first +character of the name.

+ +

Yacc and bison allow terminal symbols to have either alphanumeric +names or to be individual characters included in single quotes, like +this: ')' or '$'. Lemon does not allow this alternative form for +terminal symbols. With Lemon, all symbols, terminals and nonterminals, +must have alphanumeric names.

+ +

Grammar Rules

+ +

The main component of a Lemon grammar file is a sequence of grammar +rules. +Each grammar rule consists of a nonterminal symbol followed by +the special symbol ``::='' and then a list of terminals and/or nonterminals. +The rule is terminated by a period. +The list of terminals and nonterminals on the right-hand side of the +rule can be empty. +Rules can occur in any order, except that the left-hand side of the +first rule is assumed to be the start symbol for the grammar (unless +specified otherwise using the %start directive described below.) +A typical sequence of grammar rules might look something like this: +

+  expr ::= expr PLUS expr.
+  expr ::= expr TIMES expr.
+  expr ::= LPAREN expr RPAREN.
+  expr ::= VALUE.
+
+

+ +

There is one non-terminal in this example, ``expr'', and five +terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'', +``RPAREN'' and ``VALUE''.

+ +

Like yacc and bison, Lemon allows the grammar to specify a block +of C code that will be executed whenever a grammar rule is reduced +by the parser. +In Lemon, this action is specified by putting the C code (contained +within curly braces {...}) immediately after the +period that closes the rule. +For example: +

+  expr ::= expr PLUS expr.   { printf("Doing an addition...\n"); }
+
+

+ +

In order to be useful, grammar actions must normally be linked to +their associated grammar rules. +In yacc and bison, this is accomplished by embedding a ``$$'' in the +action to stand for the value of the left-hand side of the rule and +symbols ``$1'', ``$2'', and so forth to stand for the value of +the terminal or nonterminal at position 1, 2 and so forth on the +right-hand side of the rule. +This idea is very powerful, but it is also very error-prone. The +single most common source of errors in a yacc or bison grammar is +to miscount the number of symbols on the right-hand side of a grammar +rule and say ``$7'' when you really mean ``$8''.

+ +

Lemon avoids the need to count grammar symbols by assigning symbolic +names to each symbol in a grammar rule and then using those symbolic +names in the action. +In yacc or bison, one would write this: +

+  expr -> expr PLUS expr  { $$ = $1 + $3; };
+
+But in Lemon, the same rule becomes the following: +
+  expr(A) ::= expr(B) PLUS expr(C).  { A = B+C; }
+
+In the Lemon rule, any symbol in parentheses after a grammar rule +symbol becomes a place holder for that symbol in the grammar rule. +This place holder can then be used in the associated C action to +stand for the value of that symbol.

+ +

The Lemon notation for linking a grammar rule with its reduce +action is superior to yacc/bison on several counts. +First, as mentioned above, the Lemon method avoids the need to +count grammar symbols. +Secondly, if a terminal or nonterminal in a Lemon grammar rule +includes a linking symbol in parentheses but that linking symbol +is not actually used in the reduce action, then an error message +is generated. +For example, the rule +

+  expr(A) ::= expr(B) PLUS expr(C).  { A = B; }
+
+will generate an error because the linking symbol ``C'' is used +in the grammar rule but not in the reduce action.

+ +

The Lemon notation for linking grammar rules to reduce actions +also facilitates the use of destructors for reclaiming memory +allocated by the values of terminals and nonterminals on the +right-hand side of a rule.

+ +

Precedence Rules

+ +

Lemon resolves parsing ambiguities in exactly the same way as +yacc and bison. A shift-reduce conflict is resolved in favor +of the shift, and a reduce-reduce conflict is resolved by reducing +whichever rule comes first in the grammar file.

+ +

Just like in +yacc and bison, Lemon allows a measure of control +over the resolution of paring conflicts using precedence rules. +A precedence value can be assigned to any terminal symbol +using the %left, %right or %nonassoc directives. Terminal symbols +mentioned in earlier directives have a lower precedence that +terminal symbols mentioned in later directives. For example:

+ +

+   %left AND.
+   %left OR.
+   %nonassoc EQ NE GT GE LT LE.
+   %left PLUS MINUS.
+   %left TIMES DIVIDE MOD.
+   %right EXP NOT.
+

+ +

In the preceding sequence of directives, the AND operator is +defined to have the lowest precedence. The OR operator is one +precedence level higher. And so forth. Hence, the grammar would +attempt to group the ambiguous expression +

+     a AND b OR c
+
+like this +
+     a AND (b OR c).
+
+The associativity (left, right or nonassoc) is used to determine +the grouping when the precedence is the same. AND is left-associative +in our example, so +
+     a AND b AND c
+
+is parsed like this +
+     (a AND b) AND c.
+
+The EXP operator is right-associative, though, so +
+     a EXP b EXP c
+
+is parsed like this +
+     a EXP (b EXP c).
+
+The nonassoc precedence is used for non-associative operators. +So +
+     a EQ b EQ c
+
+is an error.

+ +

The precedence of non-terminals is transferred to rules as follows: +The precedence of a grammar rule is equal to the precedence of the +left-most terminal symbol in the rule for which a precedence is +defined. This is normally what you want, but in those cases where +you want to precedence of a grammar rule to be something different, +you can specify an alternative precedence symbol by putting the +symbol in square braces after the period at the end of the rule and +before any C-code. For example:

+ +

+   expr = MINUS expr.  [NOT]
+

+ +

This rule has a precedence equal to that of the NOT symbol, not the +MINUS symbol as would have been the case by default.

+ +

With the knowledge of how precedence is assigned to terminal +symbols and individual +grammar rules, we can now explain precisely how parsing conflicts +are resolved in Lemon. Shift-reduce conflicts are resolved +as follows: +

    +
  • If either the token to be shifted or the rule to be reduced + lacks precedence information, then resolve in favor of the + shift, but report a parsing conflict. +
  • If the precedence of the token to be shifted is greater than + the precedence of the rule to reduce, then resolve in favor + of the shift. No parsing conflict is reported. +
  • If the precedence of the token it be shifted is less than the + precedence of the rule to reduce, then resolve in favor of the + reduce action. No parsing conflict is reported. +
  • If the precedences are the same and the shift token is + right-associative, then resolve in favor of the shift. + No parsing conflict is reported. +
  • If the precedences are the same the the shift token is + left-associative, then resolve in favor of the reduce. + No parsing conflict is reported. +
  • Otherwise, resolve the conflict by doing the shift and + report the parsing conflict. +
+Reduce-reduce conflicts are resolved this way: +
    +
  • If either reduce rule + lacks precedence information, then resolve in favor of the + rule that appears first in the grammar and report a parsing + conflict. +
  • If both rules have precedence and the precedence is different + then resolve the dispute in favor of the rule with the highest + precedence and do not report a conflict. +
  • Otherwise, resolve the conflict by reducing by the rule that + appears first in the grammar and report a parsing conflict. +
+ +

Special Directives

+ +

The input grammar to Lemon consists of grammar rules and special +directives. We've described all the grammar rules, so now we'll +talk about the special directives.

+ +

Directives in lemon can occur in any order. You can put them before +the grammar rules, or after the grammar rules, or in the mist of the +grammar rules. It doesn't matter. The relative order of +directives used to assign precedence to terminals is important, but +other than that, the order of directives in Lemon is arbitrary.

+ +

Lemon supports the following special directives: +

    +
  • %code +
  • %default_destructor +
  • %default_type +
  • %destructor +
  • %extra_argument +
  • %include +
  • %left +
  • %name +
  • %nonassoc +
  • %parse_accept +
  • %parse_failure +
  • %right +
  • %stack_overflow +
  • %stack_size +
  • %start_symbol +
  • %syntax_error +
  • %token_destructor +
  • %token_prefix +
  • %token_type +
  • %type +
+Each of these directives will be described separately in the +following sections:

+ +

The %code directive

+ +

The %code directive is used to specify addition C/C++ code that +is added to the end of the main output file. This is similar to +the %include directive except that %include is inserted at the +beginning of the main output file.

+ +

%code is typically used to include some action routines or perhaps +a tokenizer as part of the output file.

+ +

The %default_destructor directive

+ +

The %default_destructor directive specifies a destructor to +use for non-terminals that do not have their own destructor +specified by a separate %destructor directive. See the documentation +on the %destructor directive below for additional information.

+ +

In some grammers, many different non-terminal symbols have the +same datatype and hence the same destructor. This directive is +a convenience way to specify the same destructor for all those +non-terminals using a single statement.

+ +

The %default_type directive

+ +

The %default_type directive specifies the datatype of non-terminal +symbols that do no have their own datatype defined using a separate +%type directive. See the documentation on %type below for addition +information.

+ +

The %destructor directive

+ +

The %destructor directive is used to specify a destructor for +a non-terminal symbol. +(See also the %token_destructor directive which is used to +specify a destructor for terminal symbols.)

+ +

A non-terminal's destructor is called to dispose of the +non-terminal's value whenever the non-terminal is popped from +the stack. This includes all of the following circumstances: +

    +
  • When a rule reduces and the value of a non-terminal on + the right-hand side is not linked to C code. +
  • When the stack is popped during error processing. +
  • When the ParseFree() function runs. +
+The destructor can do whatever it wants with the value of +the non-terminal, but its design is to deallocate memory +or other resources held by that non-terminal.

+ +

Consider an example: +

+   %type nt {void*}
+   %destructor nt { free($$); }
+   nt(A) ::= ID NUM.   { A = malloc( 100 ); }
+
+This example is a bit contrived but it serves to illustrate how +destructors work. The example shows a non-terminal named +``nt'' that holds values of type ``void*''. When the rule for +an ``nt'' reduces, it sets the value of the non-terminal to +space obtained from malloc(). Later, when the nt non-terminal +is popped from the stack, the destructor will fire and call +free() on this malloced space, thus avoiding a memory leak. +(Note that the symbol ``$$'' in the destructor code is replaced +by the value of the non-terminal.)

+ +

It is important to note that the value of a non-terminal is passed +to the destructor whenever the non-terminal is removed from the +stack, unless the non-terminal is used in a C-code action. If +the non-terminal is used by C-code, then it is assumed that the +C-code will take care of destroying it if it should really +be destroyed. More commonly, the value is used to build some +larger structure and we don't want to destroy it, which is why +the destructor is not called in this circumstance.

+ +

By appropriate use of destructors, it is possible to +build a parser using Lemon that can be used within a long-running +program, such as a GUI, that will not leak memory or other resources. +To do the same using yacc or bison is much more difficult.

+ +

The %extra_argument directive

+ +The %extra_argument directive instructs Lemon to add a 4th parameter +to the parameter list of the Parse() function it generates. Lemon +doesn't do anything itself with this extra argument, but it does +make the argument available to C-code action routines, destructors, +and so forth. For example, if the grammar file contains:

+ +

+    %extra_argument { MyStruct *pAbc }
+

+ +

Then the Parse() function generated will have an 4th parameter +of type ``MyStruct*'' and all action routines will have access to +a variable named ``pAbc'' that is the value of the 4th parameter +in the most recent call to Parse().

+ +

The %include directive

+ +

The %include directive specifies C code that is included at the +top of the generated parser. You can include any text you want -- +the Lemon parser generator copies it blindly. If you have multiple +%include directives in your grammar file the value of the last +%include directive overwrites all the others.The %include directive is very handy for getting some extra #include +preprocessor statements at the beginning of the generated parser. +For example:

+ +

+   %include {#include <unistd.h>}
+

+ +

This might be needed, for example, if some of the C actions in the +grammar call functions that are prototyed in unistd.h.

+ +

The %left directive

+ +The %left directive is used (along with the %right and +%nonassoc directives) to declare precedences of terminal +symbols. Every terminal symbol whose name appears after +a %left directive but before the next period (``.'') is +given the same left-associative precedence value. Subsequent +%left directives have higher precedence. For example:

+ +

+   %left AND.
+   %left OR.
+   %nonassoc EQ NE GT GE LT LE.
+   %left PLUS MINUS.
+   %left TIMES DIVIDE MOD.
+   %right EXP NOT.
+

+ +

Note the period that terminates each %left, %right or %nonassoc +directive.

+ +

LALR(1) grammars can get into a situation where they require +a large amount of stack space if you make heavy use or right-associative +operators. For this reason, it is recommended that you use %left +rather than %right whenever possible.

+ +

The %name directive

+ +

By default, the functions generated by Lemon all begin with the +five-character string ``Parse''. You can change this string to something +different using the %name directive. For instance:

+ +

+   %name Abcde
+

+ +

Putting this directive in the grammar file will cause Lemon to generate +functions named +

    +
  • AbcdeAlloc(), +
  • AbcdeFree(), +
  • AbcdeTrace(), and +
  • Abcde(). +
+The %name directive allows you to generator two or more different +parsers and link them all into the same executable. +

+ +

The %nonassoc directive

+ +

This directive is used to assign non-associative precedence to +one or more terminal symbols. See the section on precedence rules +or on the %left directive for additional information.

+ +

The %parse_accept directive

+ +

The %parse_accept directive specifies a block of C code that is +executed whenever the parser accepts its input string. To ``accept'' +an input string means that the parser was able to process all tokens +without error.

+ +

For example:

+ +

+   %parse_accept {
+      printf("parsing complete!\n");
+   }
+

+ + +

The %parse_failure directive

+ +

The %parse_failure directive specifies a block of C code that +is executed whenever the parser fails complete. This code is not +executed until the parser has tried and failed to resolve an input +error using is usual error recovery strategy. The routine is +only invoked when parsing is unable to continue.

+ +

+   %parse_failure {
+     fprintf(stderr,"Giving up.  Parser is hopelessly lost...\n");
+   }
+

+ +

The %right directive

+ +

This directive is used to assign right-associative precedence to +one or more terminal symbols. See the section on precedence rules +or on the %left directive for additional information.

+ +

The %stack_overflow directive

+ +

The %stack_overflow directive specifies a block of C code that +is executed if the parser's internal stack ever overflows. Typically +this just prints an error message. After a stack overflow, the parser +will be unable to continue and must be reset.

+ +

+   %stack_overflow {
+     fprintf(stderr,"Giving up.  Parser stack overflow\n");
+   }
+

+ +

You can help prevent parser stack overflows by avoiding the use +of right recursion and right-precedence operators in your grammar. +Use left recursion and and left-precedence operators instead, to +encourage rules to reduce sooner and keep the stack size down. +For example, do rules like this: +

+   list ::= list element.      // left-recursion.  Good!
+   list ::= .
+
+Not like this: +
+   list ::= element list.      // right-recursion.  Bad!
+   list ::= .
+
+ +

The %stack_size directive

+ +

If stack overflow is a problem and you can't resolve the trouble +by using left-recursion, then you might want to increase the size +of the parser's stack using this directive. Put an positive integer +after the %stack_size directive and Lemon will generate a parse +with a stack of the requested size. The default value is 100.

+ +

+   %stack_size 2000
+

+ +

The %start_symbol directive

+ +

By default, the start-symbol for the grammar that Lemon generates +is the first non-terminal that appears in the grammar file. But you +can choose a different start-symbol using the %start_symbol directive.

+ +

+   %start_symbol  prog
+

+ +

The %token_destructor directive

+ +

The %destructor directive assigns a destructor to a non-terminal +symbol. (See the description of the %destructor directive above.) +This directive does the same thing for all terminal symbols.

+ +

Unlike non-terminal symbols which may each have a different data type +for their values, terminals all use the same data type (defined by +the %token_type directive) and so they use a common destructor. Other +than that, the token destructor works just like the non-terminal +destructors.

+ +

The %token_prefix directive

+ +

Lemon generates #defines that assign small integer constants +to each terminal symbol in the grammar. If desired, Lemon will +add a prefix specified by this directive +to each of the #defines it generates. +So if the default output of Lemon looked like this: +

+    #define AND              1
+    #define MINUS            2
+    #define OR               3
+    #define PLUS             4
+
+You can insert a statement into the grammar like this: +
+    %token_prefix    TOKEN_
+
+to cause Lemon to produce these symbols instead: +
+    #define TOKEN_AND        1
+    #define TOKEN_MINUS      2
+    #define TOKEN_OR         3
+    #define TOKEN_PLUS       4
+
+ +

The %token_type and %type directives

+ +

These directives are used to specify the data types for values +on the parser's stack associated with terminal and non-terminal +symbols. The values of all terminal symbols must be of the same +type. This turns out to be the same data type as the 3rd parameter +to the Parse() function generated by Lemon. Typically, you will +make the value of a terminal symbol by a pointer to some kind of +token structure. Like this:

+ +

+   %token_type    {Token*}
+

+ +

If the data type of terminals is not specified, the default value +is ``int''.

+ +

Non-terminal symbols can each have their own data types. Typically +the data type of a non-terminal is a pointer to the root of a parse-tree +structure that contains all information about that non-terminal. +For example:

+ +

+   %type   expr  {Expr*}
+

+ +

Each entry on the parser's stack is actually a union containing +instances of all data types for every non-terminal and terminal symbol. +Lemon will automatically use the correct element of this union depending +on what the corresponding non-terminal or terminal symbol is. But +the grammar designer should keep in mind that the size of the union +will be the size of its largest element. So if you have a single +non-terminal whose data type requires 1K of storage, then your 100 +entry parser stack will require 100K of heap space. If you are willing +and able to pay that price, fine. You just need to know.

+ +

Error Processing

+ +

After extensive experimentation over several years, it has been +discovered that the error recovery strategy used by yacc is about +as good as it gets. And so that is what Lemon uses.

+ +

When a Lemon-generated parser encounters a syntax error, it +first invokes the code specified by the %syntax_error directive, if +any. It then enters its error recovery strategy. The error recovery +strategy is to begin popping the parsers stack until it enters a +state where it is permitted to shift a special non-terminal symbol +named ``error''. It then shifts this non-terminal and continues +parsing. But the %syntax_error routine will not be called again +until at least three new tokens have been successfully shifted.

+ +

If the parser pops its stack until the stack is empty, and it still +is unable to shift the error symbol, then the %parse_failed routine +is invoked and the parser resets itself to its start state, ready +to begin parsing a new file. This is what will happen at the very +first syntax error, of course, if there are no instances of the +``error'' non-terminal in your grammar.

+ + + diff --git a/doc/pager-invariants.txt b/doc/pager-invariants.txt new file mode 100644 index 0000000..c6deda7 --- /dev/null +++ b/doc/pager-invariants.txt @@ -0,0 +1,76 @@ + *** Throughout this document, a page is deemed to have been synced + automatically as soon as it is written when PRAGMA synchronous=OFF. + Otherwise, the page is not synced until the xSync method of the VFS + is called successfully on the file containing the page. + + *** Definition: A page of the database file is said to be "overwriteable" if + one or more of the following are true about the page: + + (a) The original content of the page as it was at the beginning of + the transaction has been written into the rollback journal and + synced. + + (b) The page was a freelist leaf page at the start of the transaction. + + (c) The page number is greater than the largest page that existed in + the database file at the start of the transaction. + + (1) A page of the database file is never overwritten unless one of the + following are true: + + (a) The page and all other pages on the same sector are overwriteable. + + (b) The atomic page write optimization is enabled, and the entire + transaction other than the update of the transaction sequence + number consists of a single page change. + + (2) The content of a page written into the rollback journal exactly matches + both the content in the database when the rollback journal was written + and the content in the database at the beginning of the current + transaction. + + (3) Writes to the database file are an integer multiple of the page size + in length and are aligned to a page boundary. + + (4) Reads from the database file are either aligned on a page boundary and + an integer multiple of the page size in length or are taken from the + first 100 bytes of the database file. + + (5) All writes to the database file are synced prior to the rollback journal + being deleted, truncated, or zeroed. + + (6) If a master journal file is used, then all writes to the database file + are synced prior to the master journal being deleted. + + *** Definition: Two databases (or the same database at two points it time) + are said to be "logically equivalent" if they give the same answer to + all queries. Note in particular the the content of freelist leaf + pages can be changed arbitarily without effecting the logical equivalence + of the database. + + (7) At any time, if any subset, including the empty set and the total set, + of the unsynced changes to a rollback journal are removed and the + journal is rolled back, the resulting database file will be logical + equivalent to the database file at the beginning of the transaction. + + (8) When a transaction is rolled back, the xTruncate method of the VFS + is called to restore the database file to the same size it was at + the beginning of the transaction. (In some VFSes, the xTruncate + method is a no-op, but that does not change the fact the SQLite will + invoke it.) + + (9) Whenever the database file is modified, at least one bit in the range + of bytes from 24 through 39 inclusive will be changed prior to releasing + the EXCLUSIVE lock. + +(10) The pattern of bits in bytes 24 through 39 shall not repeat in less + than one billion transactions. + +(11) A database file is well-formed at the beginning and at the conclusion + of every transaction. + +(12) An EXCLUSIVE lock must be held on the database file before making + any changes to the database file. + +(13) A SHARED lock must be held on the database file before reading any + content out of the database file. diff --git a/doc/vfs-shm.txt b/doc/vfs-shm.txt new file mode 100644 index 0000000..c1f125a --- /dev/null +++ b/doc/vfs-shm.txt @@ -0,0 +1,130 @@ +The 5 states of an historical rollback lock as implemented by the +xLock, xUnlock, and xCheckReservedLock methods of the sqlite3_io_methods +objec are: + + UNLOCKED + SHARED + RESERVED + PENDING + EXCLUSIVE + +The wal-index file has a similar locking hierarchy implemented using +the xShmLock method of the sqlite3_vfs object, but with 7 +states. Each connection to a wal-index file must be in one of +the following 7 states: + + UNLOCKED + READ + READ_FULL + WRITE + PENDING + CHECKPOINT + RECOVER + +These roughly correspond to the 5 states of a rollback lock except +that SHARED is split out into 2 states: READ and READ_FULL and +there is an extra RECOVER state used for wal-index reconstruction. + +The meanings of the various wal-index locking states is as follows: + + UNLOCKED - The wal-index is not in use. + + READ - Some prefix of the wal-index is being read. Additional + wal-index information can be appended at any time. The + newly appended content will be ignored by the holder of + the READ lock. + + READ_FULL - The entire wal-index is being read. No new information + can be added to the wal-index. The holder of a READ_FULL + lock promises never to read pages from the database file + that are available anywhere in the wal-index. + + WRITE - It is OK to append to the wal-index file and to adjust + the header to indicate the new "last valid frame". + + PENDING - Waiting on all READ locks to clear so that a + CHECKPOINT lock can be acquired. + + CHECKPOINT - It is OK to write any WAL data into the database file + and zero the last valid frame field of the wal-index + header. The wal-index file itself may not be changed + other than to zero the last valid frame field in the + header. + + RECOVER - Held during wal-index recovery. Used to prevent a + race if multiple clients try to recover a wal-index at + the same time. + + +A particular lock manager implementation may coalesce one or more of +the wal-index locking states, though with a reduction in concurrency. +For example, an implemention might implement only exclusive locking, +in which case all states would be equivalent to CHECKPOINT, meaning that +only one reader or one writer or one checkpointer could be active at a +time. Or, an implementation might combine READ and READ_FULL into +a single state equivalent to READ, meaning that a writer could +coexist with a reader, but no reader or writers could coexist with a +checkpointer. + +The lock manager must obey the following rules: + +(1) A READ cannot coexist with CHECKPOINT. +(2) A READ_FULL cannot coexist with WRITE. +(3) None of WRITE, PENDING, CHECKPOINT, or RECOVER can coexist. + +The SQLite core will obey the next set of rules. These rules are +assertions on the behavior of the SQLite core which might be verified +during testing using an instrumented lock manager. + +(5) No part of the wal-index will be read without holding either some + kind of SHM lock or an EXCLUSIVE lock on the original database. + The original database is the file named in the 2nd parameter to + the xShmOpen method. + +(6) A holder of a READ_FULL will never read any page of the database + file that is contained anywhere in the wal-index. + +(7) No part of the wal-index other than the header will be written nor + will the size of the wal-index grow without holding a WRITE or + an EXCLUSIVE on the original database file. + +(8) The wal-index header will not be written without holding one of + WRITE, CHECKPOINT, or RECOVER on the wal-index or an EXCLUSIVE on + the original database files. + +(9) A CHECKPOINT or RECOVER must be held on the wal-index, or an + EXCLUSIVE on the original database file, in order to reset the + last valid frame counter in the header of the wal-index back to zero. + +(10) A WRITE can only increase the last valid frame pointer in the header. + +The SQLite core will only ever send requests for UNLOCK, READ, WRITE, +CHECKPOINT, or RECOVER to the lock manager. The SQLite core will never +request a READ_FULL or PENDING lock though the lock manager may deliver +those locking states in response to READ and CHECKPOINT requests, +respectively, if and only if the requested READ or CHECKPOINT cannot +be delivered. + +The following are the allowed lock transitions: + + Original-State Request New-State + -------------- ---------- ---------- +(11a) UNLOCK READ READ +(11b) UNLOCK READ READ_FULL +(11c) UNLOCK CHECKPOINT PENDING +(11d) UNLOCK CHECKPOINT CHECKPOINT +(11e) READ UNLOCK UNLOCK +(11f) READ WRITE WRITE +(11g) READ RECOVER RECOVER +(11h) READ_FULL UNLOCK UNLOCK +(11i) READ_FULL WRITE WRITE +(11j) READ_FULL RECOVER RECOVER +(11k) WRITE READ READ +(11l) PENDING UNLOCK UNLOCK +(11m) PENDING CHECKPOINT CHECKPOINT +(11n) CHECKPOINT UNLOCK UNLOCK +(11o) RECOVER READ READ + +These 15 transitions are all that needs to be supported. The lock +manager implementation can assert that fact. The other 27 possible +transitions among the 7 locking states will never occur. diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 072e12e..20da051 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -448,6 +448,8 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ sqlite3_finalize(p->aStmt[i]); } sqlite3_free(p->zSegmentsTbl); + sqlite3_free(p->zReadExprlist); + sqlite3_free(p->zWriteExprlist); /* Invoke the tokenizer destructor to free the tokenizer. */ p->pTokenizer->pModule->xDestroy(p->pTokenizer); @@ -665,6 +667,141 @@ static int fts3IsSpecialColumn( return 1; } +/* +** Append the output of a printf() style formatting to an existing string. +*/ +static void fts3Appendf( + int *pRc, /* IN/OUT: Error code */ + char **pz, /* IN/OUT: Pointer to string buffer */ + const char *zFormat, /* Printf format string to append */ + ... /* Arguments for printf format string */ +){ + if( *pRc==SQLITE_OK ){ + va_list ap; + char *z; + va_start(ap, zFormat); + z = sqlite3_vmprintf(zFormat, ap); + if( z && *pz ){ + char *z2 = sqlite3_mprintf("%s%s", *pz, z); + sqlite3_free(z); + z = z2; + } + if( z==0 ) *pRc = SQLITE_NOMEM; + sqlite3_free(*pz); + *pz = z; + } +} + +/* +** Return a copy of input string zInput enclosed in double-quotes (") and +** with all double quote characters escaped. For example: +** +** fts3QuoteId("un \"zip\"") -> "un \"\"zip\"\"" +** +** The pointer returned points to memory obtained from sqlite3_malloc(). It +** is the callers responsibility to call sqlite3_free() to release this +** memory. +*/ +static char *fts3QuoteId(char const *zInput){ + int nRet; + char *zRet; + nRet = 2 + strlen(zInput)*2 + 1; + zRet = sqlite3_malloc(nRet); + if( zRet ){ + int i; + char *z = zRet; + *(z++) = '"'; + for(i=0; zInput[i]; i++){ + if( zInput[i]=='"' ) *(z++) = '"'; + *(z++) = zInput[i]; + } + *(z++) = '"'; + *(z++) = '\0'; + } + return zRet; +} + +/* +** Return a list of comma separated SQL expressions that could be used +** in a SELECT statement such as the following: +** +** SELECT FROM %_content AS x ... +** +** to return the docid, followed by each column of text data in order +** from left to write. If parameter zFunc is not NULL, then instead of +** being returned directly each column of text data is passed to an SQL +** function named zFunc first. For example, if zFunc is "unzip" and the +** table has the three user-defined columns "a", "b", and "c", the following +** string is returned: +** +** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')" +** +** The pointer returned points to a buffer allocated by sqlite3_malloc(). It +** is the responsibility of the caller to eventually free it. +** +** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and +** a NULL pointer is returned). Otherwise, if an OOM error is encountered +** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If +** no error occurs, *pRc is left unmodified. +*/ +static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){ + char *zRet = 0; + char *zFree = 0; + char *zFunction; + int i; + + if( !zFunc ){ + zFunction = ""; + }else{ + zFree = zFunction = fts3QuoteId(zFunc); + } + fts3Appendf(pRc, &zRet, "docid"); + for(i=0; inColumn; i++){ + fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); + } + sqlite3_free(zFree); + return zRet; +} + +/* +** Return a list of N comma separated question marks, where N is the number +** of columns in the %_content table (one for the docid plus one for each +** user-defined text column). +** +** If argument zFunc is not NULL, then all but the first question mark +** is preceded by zFunc and an open bracket, and followed by a closed +** bracket. For example, if zFunc is "zip" and the FTS3 table has three +** user-defined text columns, the following string is returned: +** +** "?, zip(?), zip(?), zip(?)" +** +** The pointer returned points to a buffer allocated by sqlite3_malloc(). It +** is the responsibility of the caller to eventually free it. +** +** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and +** a NULL pointer is returned). Otherwise, if an OOM error is encountered +** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If +** no error occurs, *pRc is left unmodified. +*/ +static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ + char *zRet = 0; + char *zFree = 0; + char *zFunction; + int i; + + if( !zFunc ){ + zFunction = ""; + }else{ + zFree = zFunction = fts3QuoteId(zFunc); + } + fts3Appendf(pRc, &zRet, "?"); + for(i=0; inColumn; i++){ + fts3Appendf(pRc, &zRet, ",%s(?)", zFunction); + } + sqlite3_free(zFree); + return zRet; +} + /* ** This function is the implementation of both the xConnect and xCreate ** methods of the FTS3 virtual table. @@ -701,6 +838,9 @@ static int fts3InitVtab( const char **aCol; /* Array of column names */ sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ + char *zCompress = 0; + char *zUncompress = 0; + assert( strlen(argv[0])==4 ); assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4) @@ -751,6 +891,12 @@ static int fts3InitVtab( *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); rc = SQLITE_ERROR; } + }else if( nKey==8 && 0==sqlite3_strnicmp(z, "compress", 8) ){ + zCompress = zVal; + zVal = 0; + }else if( nKey==10 && 0==sqlite3_strnicmp(z, "uncompress", 10) ){ + zUncompress = zVal; + zVal = 0; }else{ *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); rc = SQLITE_ERROR; @@ -825,6 +971,15 @@ static int fts3InitVtab( assert( zCsr <= &((char *)p)[nByte] ); } + if( (zCompress==0)!=(zUncompress==0) ){ + char const *zMiss = (zCompress==0 ? "compress" : "uncompress"); + rc = SQLITE_ERROR; + *pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss); + } + p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc); + p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc); + if( rc!=SQLITE_OK ) goto fts3_init_out; + /* If this is an xCreate call, create the underlying tables in the ** database. TODO: For xConnect(), it could verify that said tables exist. */ @@ -842,7 +997,8 @@ static int fts3InitVtab( fts3DeclareVtab(&rc, p); fts3_init_out: - + sqlite3_free(zCompress); + sqlite3_free(zUncompress); sqlite3_free((void *)aCol); if( rc!=SQLITE_OK ){ if( p ){ @@ -1935,132 +2091,135 @@ static int fts3DeferredTermSelect( return SQLITE_OK; } -/* -** An Fts3SegReaderArray is used to store an array of Fts3SegReader objects. -** Elements are added to the array using fts3SegReaderArrayAdd(). -*/ -struct Fts3SegReaderArray { - int nSegment; /* Number of valid entries in apSegment[] */ - int nAlloc; /* Allocated size of apSegment[] */ - int nCost; /* The cost of executing SegReaderIterate() */ - Fts3SegReader *apSegment[1]; /* Array of seg-reader objects */ -}; - - -/* -** Free an Fts3SegReaderArray object. Also free all seg-readers in the -** array (using sqlite3Fts3SegReaderFree()). -*/ -static void fts3SegReaderArrayFree(Fts3SegReaderArray *pArray){ - if( pArray ){ - int i; - for(i=0; inSegment; i++){ - sqlite3Fts3SegReaderFree(pArray->apSegment[i]); - } - sqlite3_free(pArray); - } -} - -static int fts3SegReaderArrayAdd( - Fts3SegReaderArray **ppArray, - Fts3SegReader *pNew +int sqlite3Fts3SegReaderCursor( + Fts3Table *p, /* FTS3 table handle */ + int iLevel, /* Level of segments to scan */ + const char *zTerm, /* Term to query for */ + int nTerm, /* Size of zTerm in bytes */ + int isPrefix, /* True for a prefix search */ + int isScan, /* True to scan from zTerm to EOF */ + Fts3SegReaderCursor *pCsr /* Cursor object to populate */ ){ - Fts3SegReaderArray *pArray = *ppArray; + int rc = SQLITE_OK; + int rc2; + int iAge = 0; + sqlite3_stmt *pStmt = 0; + Fts3SegReader *pPending = 0; - if( !pArray || pArray->nAlloc==pArray->nSegment ){ - int nNew = (pArray ? pArray->nAlloc+16 : 16); - pArray = (Fts3SegReaderArray *)sqlite3_realloc(pArray, - sizeof(Fts3SegReaderArray) + (nNew-1) * sizeof(Fts3SegReader*) - ); - if( !pArray ){ - sqlite3Fts3SegReaderFree(pNew); - return SQLITE_NOMEM; + assert( iLevel==FTS3_SEGCURSOR_ALL + || iLevel==FTS3_SEGCURSOR_PENDING + || iLevel>=0 + ); + assert( FTS3_SEGCURSOR_PENDING<0 ); + assert( FTS3_SEGCURSOR_ALL<0 ); + assert( iLevel==FTS3_SEGCURSOR_ALL || (zTerm==0 && isPrefix==1) ); + assert( isPrefix==0 || isScan==0 ); + + + memset(pCsr, 0, sizeof(Fts3SegReaderCursor)); + + /* If iLevel is less than 0, include a seg-reader for the pending-terms. */ + assert( isScan==0 || fts3HashCount(&p->pendingTerms)==0 ); + if( iLevel<0 && isScan==0 ){ + rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pPending); + if( rc==SQLITE_OK && pPending ){ + int nByte = (sizeof(Fts3SegReader *) * 16); + pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte); + if( pCsr->apSegment==0 ){ + rc = SQLITE_NOMEM; + }else{ + pCsr->apSegment[0] = pPending; + pCsr->nSegment = 1; + pPending = 0; + } } - if( nNew==16 ){ - pArray->nSegment = 0; - pArray->nCost = 0; - } - pArray->nAlloc = nNew; - *ppArray = pArray; } - pArray->apSegment[pArray->nSegment++] = pNew; - return SQLITE_OK; + if( iLevel!=FTS3_SEGCURSOR_PENDING ){ + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3AllSegdirs(p, iLevel, &pStmt); + } + while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ + + /* Read the values returned by the SELECT into local variables. */ + sqlite3_int64 iStartBlock = sqlite3_column_int64(pStmt, 1); + sqlite3_int64 iLeavesEndBlock = sqlite3_column_int64(pStmt, 2); + sqlite3_int64 iEndBlock = sqlite3_column_int64(pStmt, 3); + int nRoot = sqlite3_column_bytes(pStmt, 4); + char const *zRoot = sqlite3_column_blob(pStmt, 4); + + /* If nSegment is a multiple of 16 the array needs to be extended. */ + if( (pCsr->nSegment%16)==0 ){ + Fts3SegReader **apNew; + int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); + apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte); + if( !apNew ){ + rc = SQLITE_NOMEM; + goto finished; + } + pCsr->apSegment = apNew; + } + + /* If zTerm is not NULL, and this segment is not stored entirely on its + ** root node, the range of leaves scanned can be reduced. Do this. */ + if( iStartBlock && zTerm ){ + sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0); + rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi); + if( rc!=SQLITE_OK ) goto finished; + if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock; + } + + rc = sqlite3Fts3SegReaderNew(iAge, iStartBlock, iLeavesEndBlock, + iEndBlock, zRoot, nRoot, &pCsr->apSegment[pCsr->nSegment] + ); + if( rc!=SQLITE_OK ) goto finished; + pCsr->nSegment++; + iAge++; + } + } + + finished: + rc2 = sqlite3_reset(pStmt); + if( rc==SQLITE_DONE ) rc = rc2; + sqlite3Fts3SegReaderFree(pPending); + + return rc; } -static int fts3TermSegReaderArray( + +static int fts3TermSegReaderCursor( Fts3Cursor *pCsr, /* Virtual table cursor handle */ const char *zTerm, /* Term to query for */ int nTerm, /* Size of zTerm in bytes */ int isPrefix, /* True for a prefix search */ - Fts3SegReaderArray **ppArray /* OUT: Allocated seg-reader array */ + Fts3SegReaderCursor **ppSegcsr /* OUT: Allocated seg-reader cursor */ ){ - Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; - int rc; /* Return code */ - Fts3SegReaderArray *pArray = 0; /* Array object to build */ - Fts3SegReader *pReader = 0; /* Seg-reader to add to pArray */ - sqlite3_stmt *pStmt = 0; /* SQL statement to scan %_segdir table */ - int iAge = 0; /* Used to assign ages to segments */ + Fts3SegReaderCursor *pSegcsr; /* Object to allocate and return */ + int rc = SQLITE_NOMEM; /* Return code */ - /* Allocate a seg-reader to scan the pending terms, if any. */ - rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pReader); - if( rc==SQLITE_OK && pReader ) { - rc = fts3SegReaderArrayAdd(&pArray, pReader); - } - - /* Loop through the entire %_segdir table. For each segment, create a - ** Fts3SegReader to iterate through the subset of the segment leaves - ** that may contain a term that matches zTerm/nTerm. For non-prefix - ** searches, this is always a single leaf. For prefix searches, this - ** may be a contiguous block of leaves. - */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3AllSegdirs(p, &pStmt); - } - while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ - Fts3SegReader *pNew = 0; - int nRoot = sqlite3_column_bytes(pStmt, 4); - char const *zRoot = sqlite3_column_blob(pStmt, 4); - if( sqlite3_column_int64(pStmt, 1)==0 ){ - /* The entire segment is stored on the root node (which must be a - ** leaf). Do not bother inspecting any data in this case, just - ** create a Fts3SegReader to scan the single leaf. - */ - rc = sqlite3Fts3SegReaderNew(iAge, 0, 0, 0, zRoot, nRoot, &pNew); - }else{ - sqlite3_int64 i1; /* First leaf that may contain zTerm */ - sqlite3_int64 i2; /* Final leaf that may contain zTerm */ - rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1, (isPrefix?&i2:0)); - if( isPrefix==0 ) i2 = i1; - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3SegReaderNew(iAge, i1, i2, 0, 0, 0, &pNew); - } + pSegcsr = sqlite3_malloc(sizeof(Fts3SegReaderCursor)); + if( pSegcsr ){ + Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; + int i; + int nCost = 0; + rc = sqlite3Fts3SegReaderCursor( + p, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr); + + for(i=0; rc==SQLITE_OK && inSegment; i++){ + rc = sqlite3Fts3SegReaderCost(pCsr, pSegcsr->apSegment[i], &nCost); } - assert( (pNew==0)==(rc!=SQLITE_OK) ); - - /* If a new Fts3SegReader was allocated, add it to the array. */ - if( rc==SQLITE_OK ){ - rc = fts3SegReaderArrayAdd(&pArray, pNew); - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3SegReaderCost(pCsr, pNew, &pArray->nCost); - } - iAge++; + pSegcsr->nCost = nCost; } - if( rc==SQLITE_DONE ){ - rc = sqlite3_reset(pStmt); - }else{ - sqlite3_reset(pStmt); - } - if( rc!=SQLITE_OK ){ - fts3SegReaderArrayFree(pArray); - pArray = 0; - } - *ppArray = pArray; + *ppSegcsr = pSegcsr; return rc; } +static void fts3SegReaderCursorFree(Fts3SegReaderCursor *pSegcsr){ + sqlite3Fts3SegReaderFinish(pSegcsr); + sqlite3_free(pSegcsr); +} + /* ** This function retreives the doclist for the specified term (or term ** prefix) from the database. @@ -2081,11 +2240,11 @@ static int fts3TermSelect( char **ppOut /* OUT: Malloced result buffer */ ){ int rc; /* Return code */ - Fts3SegReaderArray *pArray; /* Seg-reader array for this term */ - TermSelect tsc; /* Context object for fts3TermSelectCb() */ - Fts3SegFilter filter; /* Segment term filter configuration */ + Fts3SegReaderCursor *pSegcsr; /* Seg-reader cursor for this term */ + TermSelect tsc; /* Context object for fts3TermSelectCb() */ + Fts3SegFilter filter; /* Segment term filter configuration */ - pArray = pTok->pArray; + pSegcsr = pTok->pSegcsr; memset(&tsc, 0, sizeof(TermSelect)); tsc.isReqPos = isReqPos; @@ -2097,13 +2256,18 @@ static int fts3TermSelect( filter.zTerm = pTok->z; filter.nTerm = pTok->n; - rc = sqlite3Fts3SegReaderIterate(p, pArray->apSegment, pArray->nSegment, - &filter, fts3TermSelectCb, (void *)&tsc - ); + rc = sqlite3Fts3SegReaderStart(p, pSegcsr, &filter); + while( SQLITE_OK==rc + && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr)) + ){ + rc = fts3TermSelectCb(p, (void *)&tsc, + pSegcsr->zTerm, pSegcsr->nTerm, pSegcsr->aDoclist, pSegcsr->nDoclist + ); + } + if( rc==SQLITE_OK ){ rc = fts3TermSelectMerge(&tsc); } - if( rc==SQLITE_OK ){ *ppOut = tsc.aaOutput[0]; *pnOut = tsc.anOutput[0]; @@ -2114,8 +2278,8 @@ static int fts3TermSelect( } } - fts3SegReaderArrayFree(pArray); - pTok->pArray = 0; + fts3SegReaderCursorFree(pSegcsr); + pTok->pSegcsr = 0; return rc; } @@ -2238,13 +2402,13 @@ static int fts3PhraseSelect( */ for(ii=0; iinToken; ii++){ Fts3PhraseToken *pTok = &pPhrase->aToken[ii]; - if( pTok->pArray==0 ){ + if( pTok->pSegcsr==0 ){ if( (pCsr->eEvalmode==FTS3_EVAL_FILTER) || (pCsr->eEvalmode==FTS3_EVAL_NEXT && pCsr->pDeferred==0) || (pCsr->eEvalmode==FTS3_EVAL_MATCHINFO && pTok->bFulltext) ){ - rc = fts3TermSegReaderArray( - pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray + rc = fts3TermSegReaderCursor( + pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr ); if( rc!=SQLITE_OK ) return rc; } @@ -2275,10 +2439,10 @@ static int fts3PhraseSelect( /* Find the remaining token with the lowest cost. */ for(jj=0; jjnToken; jj++){ - Fts3SegReaderArray *pArray = pPhrase->aToken[jj].pArray; - if( pArray && pArray->nCostaToken[jj].pSegcsr; + if( pSegcsr && pSegcsr->nCostnCost; + nMinCost = pSegcsr->nCost; } } pTok = &pPhrase->aToken[iTok]; @@ -2297,12 +2461,12 @@ static int fts3PhraseSelect( if( pCsr->eEvalmode==FTS3_EVAL_NEXT && pTok->pDeferred ){ rc = fts3DeferredTermSelect(pTok->pDeferred, isTermPos, &nList, &pList); }else{ - if( pTok->pArray ){ + if( pTok->pSegcsr ){ rc = fts3TermSelect(p, pTok, iCol, isTermPos, &nList, &pList); } pTok->bFulltext = 1; } - assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pArray==0 ); + assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pSegcsr==0 ); if( rc!=SQLITE_OK ) break; if( isFirst ){ @@ -2480,9 +2644,9 @@ static int fts3ExprAllocateSegReaders( for(ii=0; rc==SQLITE_OK && iinToken; ii++){ Fts3PhraseToken *pTok = &pPhrase->aToken[ii]; - if( pTok->pArray==0 ){ - rc = fts3TermSegReaderArray( - pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray + if( pTok->pSegcsr==0 ){ + rc = fts3TermSegReaderCursor( + pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr ); } } @@ -2506,8 +2670,8 @@ static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){ if( pPhrase ){ int kk; for(kk=0; kknToken; kk++){ - fts3SegReaderArrayFree(pPhrase->aToken[kk].pArray); - pPhrase->aToken[kk].pArray = 0; + fts3SegReaderCursorFree(pPhrase->aToken[kk].pSegcsr); + pPhrase->aToken[kk].pSegcsr = 0; } } fts3ExprFreeSegReaders(pExpr->pLeft); @@ -2527,10 +2691,8 @@ static int fts3ExprCost(Fts3Expr *pExpr){ int ii; nCost = 0; for(ii=0; iinToken; ii++){ - Fts3SegReaderArray *pArray = pPhrase->aToken[ii].pArray; - if( pArray ){ - nCost += pPhrase->aToken[ii].pArray->nCost; - } + Fts3SegReaderCursor *pSegcsr = pPhrase->aToken[ii].pSegcsr; + if( pSegcsr ) nCost += pSegcsr->nCost; } }else{ nCost = fts3ExprCost(pExpr->pLeft) + fts3ExprCost(pExpr->pRight); @@ -2872,8 +3034,8 @@ static int fts3FilterMethod( sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ const char *azSql[] = { - "SELECT * FROM %Q.'%q_content' WHERE docid = ?", /* non-full-table-scan */ - "SELECT * FROM %Q.'%q_content'", /* full-table-scan */ + "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?", /* non-full-scan */ + "SELECT %s FROM %Q.'%q_content' AS x ", /* full-scan */ }; int rc; /* Return code */ char *zSql; /* SQL statement used to access %_content */ @@ -2928,7 +3090,8 @@ static int fts3FilterMethod( ** full-text query or docid lookup, the statement retrieves a single ** row by docid. */ - zSql = sqlite3_mprintf(azSql[idxNum==FTS3_FULLSCAN_SEARCH], p->zDb, p->zName); + zSql = (char *)azSql[idxNum==FTS3_FULLSCAN_SEARCH]; + zSql = sqlite3_mprintf(zSql, p->zReadExprlist, p->zDb, p->zName); if( !zSql ){ rc = SQLITE_NOMEM; }else{ @@ -3446,6 +3609,9 @@ int sqlite3Fts3Init(sqlite3 *db){ sqlite3Fts3IcuTokenizerModule(&pIcu); #endif + rc = sqlite3Fts3InitAux(db); + if( rc!=SQLITE_OK ) return rc; + sqlite3Fts3SimpleTokenizerModule(&pSimple); sqlite3Fts3PorterTokenizerModule(&pPorter); diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 0875443..b3f1ab5 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -107,7 +107,7 @@ typedef struct Fts3PhraseToken Fts3PhraseToken; typedef struct Fts3SegFilter Fts3SegFilter; typedef struct Fts3DeferredToken Fts3DeferredToken; typedef struct Fts3SegReader Fts3SegReader; -typedef struct Fts3SegReaderArray Fts3SegReaderArray; +typedef struct Fts3SegReaderCursor Fts3SegReaderCursor; /* ** A connection to a fulltext index is an instance of the following @@ -130,6 +130,9 @@ struct Fts3Table { */ sqlite3_stmt *aStmt[24]; + char *zReadExprlist; + char *zWriteExprlist; + int nNodeSize; /* Soft limit for node size */ u8 bHasStat; /* True if %_stat table exists */ u8 bHasDocsize; /* True if %_docsize table exists */ @@ -217,7 +220,7 @@ struct Fts3PhraseToken { int n; /* Number of bytes in buffer z */ int isPrefix; /* True if token ends with a "*" character */ int bFulltext; /* True if full-text index was used */ - Fts3SegReaderArray *pArray; /* Segment-reader for this token */ + Fts3SegReaderCursor *pSegcsr; /* Segment-reader for this token */ Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ }; @@ -285,12 +288,8 @@ int sqlite3Fts3SegReaderNew(int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); void sqlite3Fts3SegReaderFree(Fts3SegReader *); -int sqlite3Fts3SegReaderIterate( - Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, - int (*)(Fts3Table *, void *, char *, int, char *, int), void * -); int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *); -int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); +int sqlite3Fts3AllSegdirs(Fts3Table*, int, sqlite3_stmt **); int sqlite3Fts3ReadLock(Fts3Table *); int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*); @@ -302,14 +301,23 @@ int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int); int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *); void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *); char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *); - void sqlite3Fts3SegmentsClose(Fts3Table *); +#define FTS3_SEGCURSOR_PENDING -1 +#define FTS3_SEGCURSOR_ALL -2 + +int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3SegReaderCursor*, Fts3SegFilter*); +int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *); +void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *); +int sqlite3Fts3SegReaderCursor( + Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *); + /* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ #define FTS3_SEGMENT_REQUIRE_POS 0x00000001 #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 #define FTS3_SEGMENT_PREFIX 0x00000008 +#define FTS3_SEGMENT_SCAN 0x00000010 /* Type passed as 4th argument to SegmentReaderIterate() */ struct Fts3SegFilter { @@ -319,6 +327,25 @@ struct Fts3SegFilter { int flags; }; +struct Fts3SegReaderCursor { + /* Used internally by sqlite3Fts3SegReaderXXX() calls */ + Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */ + int nSegment; /* Size of apSegment array */ + int nAdvance; /* How many seg-readers to advance */ + Fts3SegFilter *pFilter; /* Pointer to filter object */ + char *aBuffer; /* Buffer to merge doclists in */ + int nBuffer; /* Allocated size of aBuffer[] in bytes */ + + /* Cost of running this iterator. Used by fts3.c only. */ + int nCost; + + /* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */ + char *zTerm; /* Pointer to term buffer */ + int nTerm; /* Size of zTerm in bytes */ + char *aDoclist; /* Pointer to doclist buffer */ + int nDoclist; /* Size of aDoclist[] in bytes */ +}; + /* fts3.c */ int sqlite3Fts3PutVarint(char *, sqlite3_int64); int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); @@ -355,4 +382,7 @@ void sqlite3Fts3ExprFree(Fts3Expr *); int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); #endif +/* fts3_aux.c */ +int sqlite3Fts3InitAux(sqlite3 *db); + #endif /* _FTSINT_H */ diff --git a/ext/fts3/fts3_aux.c b/ext/fts3/fts3_aux.c new file mode 100644 index 0000000..6108689 --- /dev/null +++ b/ext/fts3/fts3_aux.c @@ -0,0 +1,470 @@ +/* +** 2011 Jan 27 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +*/ + +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +#include "fts3Int.h" +#include +#include + +typedef struct Fts3auxTable Fts3auxTable; +typedef struct Fts3auxCursor Fts3auxCursor; + +struct Fts3auxTable { + sqlite3_vtab base; /* Base class used by SQLite core */ + Fts3Table *pFts3Tab; +}; + +struct Fts3auxCursor { + sqlite3_vtab_cursor base; /* Base class used by SQLite core */ + Fts3SegReaderCursor csr; /* Must be right after "base" */ + Fts3SegFilter filter; + char *zStop; + int nStop; /* Byte-length of string zStop */ + int isEof; /* True if cursor is at EOF */ + sqlite3_int64 iRowid; /* Current rowid */ + + int iCol; /* Current value of 'col' column */ + int nStat; /* Size of aStat[] array */ + struct Fts3auxColstats { + sqlite3_int64 nDoc; /* 'documents' values for current csr row */ + sqlite3_int64 nOcc; /* 'occurrences' values for current csr row */ + } *aStat; +}; + +/* +** Schema of the terms table. +*/ +#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)" + +/* +** This function does all the work for both the xConnect and xCreate methods. +** These tables have no persistent representation of their own, so xConnect +** and xCreate are identical operations. +*/ +static int fts3auxConnectMethod( + sqlite3 *db, /* Database connection */ + void *pUnused, /* Unused */ + int argc, /* Number of elements in argv array */ + const char * const *argv, /* xCreate/xConnect argument array */ + sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ + char **pzErr /* OUT: sqlite3_malloc'd error message */ +){ + char const *zDb; /* Name of database (e.g. "main") */ + char const *zFts3; /* Name of fts3 table */ + int nDb; /* Result of strlen(zDb) */ + int nFts3; /* Result of strlen(zFts3) */ + int nByte; /* Bytes of space to allocate here */ + int rc; /* value returned by declare_vtab() */ + Fts3auxTable *p; /* Virtual table object to return */ + + UNUSED_PARAMETER(pUnused); + + /* The user should specify a single argument - the name of an fts3 table. */ + if( argc!=4 ){ + *pzErr = sqlite3_mprintf( + "wrong number of arguments to fts4aux constructor" + ); + return SQLITE_ERROR; + } + + zDb = argv[1]; + nDb = strlen(zDb); + zFts3 = argv[3]; + nFts3 = strlen(zFts3); + + rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); + if( rc!=SQLITE_OK ) return rc; + + nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; + p = (Fts3auxTable *)sqlite3_malloc(nByte); + if( !p ) return SQLITE_NOMEM; + memset(p, 0, nByte); + + p->pFts3Tab = (Fts3Table *)&p[1]; + p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; + p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; + p->pFts3Tab->db = db; + + memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); + memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); + sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); + + *ppVtab = (sqlite3_vtab *)p; + return SQLITE_OK; +} + +/* +** This function does the work for both the xDisconnect and xDestroy methods. +** These tables have no persistent representation of their own, so xDisconnect +** and xDestroy are identical operations. +*/ +static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){ + Fts3auxTable *p = (Fts3auxTable *)pVtab; + Fts3Table *pFts3 = p->pFts3Tab; + int i; + + /* Free any prepared statements held */ + for(i=0; iaStmt); i++){ + sqlite3_finalize(pFts3->aStmt[i]); + } + sqlite3_free(pFts3->zSegmentsTbl); + sqlite3_free(p); + return SQLITE_OK; +} + +#define FTS4AUX_EQ_CONSTRAINT 1 +#define FTS4AUX_GE_CONSTRAINT 2 +#define FTS4AUX_LE_CONSTRAINT 4 + +/* +** xBestIndex - Analyze a WHERE and ORDER BY clause. +*/ +static int fts3auxBestIndexMethod( + sqlite3_vtab *pVTab, + sqlite3_index_info *pInfo +){ + int i; + int iEq = -1; + int iGe = -1; + int iLe = -1; + + UNUSED_PARAMETER(pVTab); + + /* This vtab delivers always results in "ORDER BY term ASC" order. */ + if( pInfo->nOrderBy==1 + && pInfo->aOrderBy[0].iColumn==0 + && pInfo->aOrderBy[0].desc==0 + ){ + pInfo->orderByConsumed = 1; + } + + /* Search for equality and range constraints on the "term" column. */ + for(i=0; inConstraint; i++){ + if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){ + int op = pInfo->aConstraint[i].op; + if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i; + if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i; + if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i; + if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i; + if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i; + } + } + + if( iEq>=0 ){ + pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT; + pInfo->aConstraintUsage[iEq].argvIndex = 1; + pInfo->estimatedCost = 5; + }else{ + pInfo->idxNum = 0; + pInfo->estimatedCost = 20000; + if( iGe>=0 ){ + pInfo->idxNum += FTS4AUX_GE_CONSTRAINT; + pInfo->aConstraintUsage[iGe].argvIndex = 1; + pInfo->estimatedCost /= 2; + } + if( iLe>=0 ){ + pInfo->idxNum += FTS4AUX_LE_CONSTRAINT; + pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0); + pInfo->estimatedCost /= 2; + } + } + + return SQLITE_OK; +} + +/* +** xOpen - Open a cursor. +*/ +static int fts3auxOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ + Fts3auxCursor *pCsr; /* Pointer to cursor object to return */ + + UNUSED_PARAMETER(pVTab); + + pCsr = (Fts3auxCursor *)sqlite3_malloc(sizeof(Fts3auxCursor)); + if( !pCsr ) return SQLITE_NOMEM; + memset(pCsr, 0, sizeof(Fts3auxCursor)); + + *ppCsr = (sqlite3_vtab_cursor *)pCsr; + return SQLITE_OK; +} + +/* +** xClose - Close a cursor. +*/ +static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){ + Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + + sqlite3Fts3SegmentsClose(pFts3); + sqlite3Fts3SegReaderFinish(&pCsr->csr); + sqlite3_free((void *)pCsr->filter.zTerm); + sqlite3_free(pCsr->zStop); + sqlite3_free(pCsr->aStat); + sqlite3_free(pCsr); + return SQLITE_OK; +} + +static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){ + if( nSize>pCsr->nStat ){ + struct Fts3auxColstats *aNew; + aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, + sizeof(struct Fts3auxColstats) * nSize + ); + if( aNew==0 ) return SQLITE_NOMEM; + memset(&aNew[pCsr->nStat], 0, + sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat) + ); + pCsr->aStat = aNew; + pCsr->nStat = nSize; + } + return SQLITE_OK; +} + +/* +** xNext - Advance the cursor to the next row, if any. +*/ +static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; + int rc; + + /* Increment our pretend rowid value. */ + pCsr->iRowid++; + + for(pCsr->iCol++; pCsr->iColnStat; pCsr->iCol++){ + if( pCsr->aStat[pCsr->iCol].nDoc>0 ) return SQLITE_OK; + } + + rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr); + if( rc==SQLITE_ROW ){ + int i = 0; + int nDoclist = pCsr->csr.nDoclist; + char *aDoclist = pCsr->csr.aDoclist; + int iCol; + + int eState = 0; + + if( pCsr->zStop ){ + int n = (pCsr->nStopcsr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm; + int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n); + if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){ + pCsr->isEof = 1; + return SQLITE_OK; + } + } + + if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; + memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); + iCol = 0; + + while( iaStat[0].nDoc++; + eState = 1; + iCol = 0; + break; + + /* State 1. In this state we are expecting either a 1, indicating + ** that the following integer will be a column number, or the + ** start of a position list for column 0. + ** + ** The only difference between state 1 and state 2 is that if the + ** integer encountered in state 1 is not 0 or 1, then we need to + ** increment the column 0 "nDoc" count for this term. + */ + case 1: + assert( iCol==0 ); + if( v>1 ){ + pCsr->aStat[1].nDoc++; + } + eState = 2; + /* fall through */ + + case 2: + if( v==0 ){ /* 0x00. Next integer will be a docid. */ + eState = 0; + }else if( v==1 ){ /* 0x01. Next integer will be a column number. */ + eState = 3; + }else{ /* 2 or greater. A position. */ + pCsr->aStat[iCol+1].nOcc++; + pCsr->aStat[0].nOcc++; + } + break; + + /* State 3. The integer just read is a column number. */ + default: assert( eState==3 ); + iCol = (int)v; + if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM; + pCsr->aStat[iCol+1].nDoc++; + eState = 2; + break; + } + } + + pCsr->iCol = 0; + rc = SQLITE_OK; + }else{ + pCsr->isEof = 1; + } + return rc; +} + +/* +** xFilter - Initialize a cursor to point at the start of its data. +*/ +static int fts3auxFilterMethod( + sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ + int idxNum, /* Strategy index */ + const char *idxStr, /* Unused */ + int nVal, /* Number of elements in apVal */ + sqlite3_value **apVal /* Arguments for the indexing scheme */ +){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; + int rc; + int isScan; + + UNUSED_PARAMETER(nVal); + + assert( idxStr==0 ); + assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0 + || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT + || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) + ); + isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT); + + /* In case this cursor is being reused, close and zero it. */ + testcase(pCsr->filter.zTerm); + sqlite3Fts3SegReaderFinish(&pCsr->csr); + sqlite3_free((void *)pCsr->filter.zTerm); + sqlite3_free(pCsr->aStat); + memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); + + pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; + if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN; + + if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){ + const unsigned char *zStr = sqlite3_value_text(apVal[0]); + if( zStr ){ + pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr); + pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]); + if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM; + } + } + if( idxNum&FTS4AUX_LE_CONSTRAINT ){ + int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0; + pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx])); + pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]); + if( pCsr->zStop==0 ) return SQLITE_NOMEM; + } + + rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL, + pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr + ); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter); + } + + if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor); + return rc; +} + +/* +** xEof - Return true if the cursor is at EOF, or false otherwise. +*/ +static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + return pCsr->isEof; +} + +/* +** xColumn - Return a column value. +*/ +static int fts3auxColumnMethod( + sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ + sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */ + int iCol /* Index of column to read value from */ +){ + Fts3auxCursor *p = (Fts3auxCursor *)pCursor; + + assert( p->isEof==0 ); + if( iCol==0 ){ /* Column "term" */ + sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT); + }else if( iCol==1 ){ /* Column "col" */ + if( p->iCol ){ + sqlite3_result_int(pContext, p->iCol-1); + }else{ + sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC); + } + }else if( iCol==2 ){ /* Column "documents" */ + sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc); + }else{ /* Column "occurrences" */ + sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc); + } + + return SQLITE_OK; +} + +/* +** xRowid - Return the current rowid for the cursor. +*/ +static int fts3auxRowidMethod( + sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ + sqlite_int64 *pRowid /* OUT: Rowid value */ +){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + *pRowid = pCsr->iRowid; + return SQLITE_OK; +} + +/* +** Register the fts3aux module with database connection db. Return SQLITE_OK +** if successful or an error code if sqlite3_create_module() fails. +*/ +int sqlite3Fts3InitAux(sqlite3 *db){ + static const sqlite3_module fts3aux_module = { + 0, /* iVersion */ + fts3auxConnectMethod, /* xCreate */ + fts3auxConnectMethod, /* xConnect */ + fts3auxBestIndexMethod, /* xBestIndex */ + fts3auxDisconnectMethod, /* xDisconnect */ + fts3auxDisconnectMethod, /* xDestroy */ + fts3auxOpenMethod, /* xOpen */ + fts3auxCloseMethod, /* xClose */ + fts3auxFilterMethod, /* xFilter */ + fts3auxNextMethod, /* xNext */ + fts3auxEofMethod, /* xEof */ + fts3auxColumnMethod, /* xColumn */ + fts3auxRowidMethod, /* xRowid */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindFunction */ + 0 /* xRename */ + }; + int rc; /* Return code */ + + rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0); + return rc; +} + +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c index acf1786..6b74535 100644 --- a/ext/fts3/fts3_snippet.c +++ b/ext/fts3/fts3_snippet.c @@ -880,13 +880,13 @@ static int fts3ExprLocalHitsCb( void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; + int iStart = iPhrase * p->nCol * 3; + int i; + + for(i=0; inCol; i++) p->aMatchinfo[iStart+i*3] = 0; if( pExpr->aDoclist ){ char *pCsr; - int iStart = iPhrase * p->nCol * 3; - int i; - - for(i=0; inCol; i++) p->aMatchinfo[iStart+i*3] = 0; pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1); if( pCsr ){ @@ -960,6 +960,7 @@ static int fts3MatchinfoSelectDoctotal( a = sqlite3_column_blob(pStmt, 0); a += sqlite3Fts3GetVarint(a, &nDoc); + if( nDoc==0 ) return SQLITE_CORRUPT; *pnDoc = (u32)nDoc; if( paLen ) *paLen = a; @@ -1166,9 +1167,11 @@ static int fts3MatchinfoValues( if( rc==SQLITE_OK ){ int iCol; for(iCol=0; iColnCol; iCol++){ + u32 iVal; sqlite3_int64 nToken; a += sqlite3Fts3GetVarint(a, &nToken); - pInfo->aMatchinfo[iCol] = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc); + iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc); + pInfo->aMatchinfo[iCol] = iVal; } } } diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 61def99..1e71874 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -212,7 +212,7 @@ static int fts3SqlStmt( /* 4 */ "DELETE FROM %Q.'%q_segdir'", /* 5 */ "DELETE FROM %Q.'%q_docsize'", /* 6 */ "DELETE FROM %Q.'%q_stat'", -/* 7 */ "SELECT * FROM %Q.'%q_content' WHERE rowid=?", +/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?", /* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1", /* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)", /* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)", @@ -229,7 +229,7 @@ static int fts3SqlStmt( /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", -/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", +/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?", /* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?", @@ -246,20 +246,9 @@ static int fts3SqlStmt( if( !pStmt ){ char *zSql; if( eStmt==SQL_CONTENT_INSERT ){ - int i; /* Iterator variable */ - char *zVarlist; /* The "?, ?, ..." string */ - zVarlist = (char *)sqlite3_malloc(2*p->nColumn+2); - if( !zVarlist ){ - *pp = 0; - return SQLITE_NOMEM; - } - zVarlist[0] = '?'; - zVarlist[p->nColumn*2+1] = '\0'; - for(i=1; i<=p->nColumn; i++){ - zVarlist[i*2-1] = ','; - zVarlist[i*2] = '?'; - } - zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, zVarlist); + zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); + }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ + zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); } @@ -300,7 +289,7 @@ static int fts3SelectDocsize( sqlite3_bind_int64(pStmt, 1, iDocid); } rc = sqlite3_step(pStmt); - if( rc!=SQLITE_ROW ){ + if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ rc = sqlite3_reset(pStmt); if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT; pStmt = 0; @@ -401,8 +390,17 @@ int sqlite3Fts3ReadLock(Fts3Table *p){ ** 3: end_block ** 4: root */ -int sqlite3Fts3AllSegdirs(Fts3Table *p, sqlite3_stmt **ppStmt){ - return fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, ppStmt, 0); +int sqlite3Fts3AllSegdirs(Fts3Table *p, int iLevel, sqlite3_stmt **ppStmt){ + int rc; + sqlite3_stmt *pStmt = 0; + if( iLevel<0 ){ + rc = fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, &pStmt, 0); + }else{ + rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0); + if( rc==SQLITE_OK ) sqlite3_bind_int(pStmt, 1, iLevel); + } + *ppStmt = pStmt; + return rc; } @@ -1104,16 +1102,18 @@ int sqlite3Fts3SegReaderCost( sqlite3_stmt *pStmt; sqlite3_int64 nDoc = 0; sqlite3_int64 nByte = 0; + const char *pEnd; const char *a; + rc = sqlite3Fts3SelectDoctotal(p, &pStmt); - if( rc ) return rc; + if( rc!=SQLITE_OK ) return rc; a = sqlite3_column_blob(pStmt, 0); - if( a ){ - const char *pEnd = &a[sqlite3_column_bytes(pStmt, 0)]; - a += sqlite3Fts3GetVarint(a, &nDoc); - while( a=0 ); - rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_COUNT, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - sqlite3_bind_int(pStmt, 1, iLevel); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *pnSegment = sqlite3_column_int(pStmt, 0); - } - return sqlite3_reset(pStmt); -} - /* ** Set *pnSegment to the total number of segments in the database. Set ** *pnMax to the largest segment level in the database (segment levels @@ -2020,15 +1965,18 @@ static int fts3DeleteSegdir( return rc; } - if( iLevel>=0 ){ + if( iLevel==FTS3_SEGCURSOR_ALL ){ + fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); + }else if( iLevel==FTS3_SEGCURSOR_PENDING ){ + sqlite3Fts3PendingTermsClear(p); + }else{ + assert( iLevel>=0 ); rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0); if( rc==SQLITE_OK ){ sqlite3_bind_int(pDelete, 1, iLevel); sqlite3_step(pDelete); rc = sqlite3_reset(pDelete); } - }else{ - fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); } return rc; @@ -2077,85 +2025,15 @@ static void fts3ColumnFilter( *pnList = nList; } -/* -** sqlite3Fts3SegReaderIterate() callback used when merging multiple -** segments to create a single, larger segment. -*/ -static int fts3MergeCallback( - Fts3Table *p, /* FTS3 Virtual table handle */ - void *pContext, /* Pointer to SegmentWriter* to write with */ - char *zTerm, /* Term to write to the db */ - int nTerm, /* Number of bytes in zTerm */ - char *aDoclist, /* Doclist associated with zTerm */ - int nDoclist /* Number of bytes in doclist */ -){ - SegmentWriter **ppW = (SegmentWriter **)pContext; - return fts3SegWriterAdd(p, ppW, 1, zTerm, nTerm, aDoclist, nDoclist); -} - -/* -** sqlite3Fts3SegReaderIterate() callback used when flushing the contents -** of the pending-terms hash table to the database. -*/ -static int fts3FlushCallback( - Fts3Table *p, /* FTS3 Virtual table handle */ - void *pContext, /* Pointer to SegmentWriter* to write with */ - char *zTerm, /* Term to write to the db */ - int nTerm, /* Number of bytes in zTerm */ - char *aDoclist, /* Doclist associated with zTerm */ - int nDoclist /* Number of bytes in doclist */ -){ - SegmentWriter **ppW = (SegmentWriter **)pContext; - return fts3SegWriterAdd(p, ppW, 0, zTerm, nTerm, aDoclist, nDoclist); -} - -/* -** This function is used to iterate through a contiguous set of terms -** stored in the full-text index. It merges data contained in one or -** more segments to support this. -** -** The second argument is passed an array of pointers to SegReader objects -** allocated with sqlite3Fts3SegReaderNew(). This function merges the range -** of terms selected by each SegReader. If a single term is present in -** more than one segment, the associated doclists are merged. For each -** term and (possibly merged) doclist in the merged range, the callback -** function xFunc is invoked with its arguments set as follows. -** -** arg 0: Copy of 'p' parameter passed to this function -** arg 1: Copy of 'pContext' parameter passed to this function -** arg 2: Pointer to buffer containing term -** arg 3: Size of arg 2 buffer in bytes -** arg 4: Pointer to buffer containing doclist -** arg 5: Size of arg 2 buffer in bytes -** -** The 4th argument to this function is a pointer to a structure of type -** Fts3SegFilter, defined in fts3Int.h. The contents of this structure -** further restrict the range of terms that callbacks are made for and -** modify the behaviour of this function. See comments above structure -** definition for details. -*/ -int sqlite3Fts3SegReaderIterate( +int sqlite3Fts3SegReaderStart( Fts3Table *p, /* Virtual table handle */ - Fts3SegReader **apSegment, /* Array of Fts3SegReader objects */ - int nSegment, /* Size of apSegment array */ - Fts3SegFilter *pFilter, /* Restrictions on range of iteration */ - int (*xFunc)(Fts3Table *, void *, char *, int, char *, int), /* Callback */ - void *pContext /* Callback context (2nd argument) */ + Fts3SegReaderCursor *pCsr, /* Cursor object */ + Fts3SegFilter *pFilter /* Restrictions on range of iteration */ ){ - int i; /* Iterator variable */ - char *aBuffer = 0; /* Buffer to merge doclists in */ - int nAlloc = 0; /* Allocated size of aBuffer buffer */ - int rc = SQLITE_OK; /* Return code */ + int i; - int isIgnoreEmpty = (pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); - int isRequirePos = (pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); - int isColFilter = (pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); - int isPrefix = (pFilter->flags & FTS3_SEGMENT_PREFIX); - - /* If there are zero segments, this function is a no-op. This scenario - ** comes about only when reading from an empty database. - */ - if( nSegment==0 ) goto finished; + /* Initialize the cursor object */ + pCsr->pFilter = pFilter; /* If the Fts3SegFilter defines a specific term (or term prefix) to search ** for, then advance each segment iterator until it points to a term of @@ -2163,21 +2041,59 @@ int sqlite3Fts3SegReaderIterate( ** unnecessary merge/sort operations for the case where single segment ** b-tree leaf nodes contain more than one term. */ - for(i=0; inSegment; i++){ int nTerm = pFilter->nTerm; const char *zTerm = pFilter->zTerm; - Fts3SegReader *pSeg = apSegment[i]; + Fts3SegReader *pSeg = pCsr->apSegment[i]; do { - rc = fts3SegReaderNext(p, pSeg); - if( rc!=SQLITE_OK ) goto finished; + int rc = fts3SegReaderNext(p, pSeg); + if( rc!=SQLITE_OK ) return rc; }while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 ); } + fts3SegReaderSort( + pCsr->apSegment, pCsr->nSegment, pCsr->nSegment, fts3SegReaderCmp); - fts3SegReaderSort(apSegment, nSegment, nSegment, fts3SegReaderCmp); - while( apSegment[0]->aNode ){ - int nTerm = apSegment[0]->nTerm; - char *zTerm = apSegment[0]->zTerm; - int nMerge = 1; + return SQLITE_OK; +} + +int sqlite3Fts3SegReaderStep( + Fts3Table *p, /* Virtual table handle */ + Fts3SegReaderCursor *pCsr /* Cursor object */ +){ + int rc = SQLITE_OK; + + int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); + int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); + int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); + int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX); + int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN); + + Fts3SegReader **apSegment = pCsr->apSegment; + int nSegment = pCsr->nSegment; + Fts3SegFilter *pFilter = pCsr->pFilter; + + if( pCsr->nSegment==0 ) return SQLITE_OK; + + do { + int nMerge; + int i; + + /* Advance the first pCsr->nAdvance entries in the apSegment[] array + ** forward. Then sort the list in order of current term again. + */ + for(i=0; inAdvance; i++){ + rc = fts3SegReaderNext(p, apSegment[i]); + if( rc!=SQLITE_OK ) return rc; + } + fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp); + pCsr->nAdvance = 0; + + /* If all the seg-readers are at EOF, we're finished. return SQLITE_OK. */ + assert( rc==SQLITE_OK ); + if( apSegment[0]->aNode==0 ) break; + + pCsr->nTerm = apSegment[0]->nTerm; + pCsr->zTerm = apSegment[0]->zTerm; /* If this is a prefix-search, and if the term that apSegment[0] points ** to does not share a suffix with pFilter->zTerm/nTerm, then all @@ -2186,35 +2102,36 @@ int sqlite3Fts3SegReaderIterate( ** Similarly, if this is a search for an exact match, and the first term ** of segment apSegment[0] is not a match, exit early. */ - if( pFilter->zTerm ){ - if( nTermnTerm - || (!isPrefix && nTerm>pFilter->nTerm) - || memcmp(zTerm, pFilter->zTerm, pFilter->nTerm) - ){ - goto finished; + if( pFilter->zTerm && !isScan ){ + if( pCsr->nTermnTerm + || (!isPrefix && pCsr->nTerm>pFilter->nTerm) + || memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm) + ){ + break; } } + nMerge = 1; while( nMergeaNode - && apSegment[nMerge]->nTerm==nTerm - && 0==memcmp(zTerm, apSegment[nMerge]->zTerm, nTerm) + && apSegment[nMerge]->nTerm==pCsr->nTerm + && 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm) ){ nMerge++; } assert( isIgnoreEmpty || (isRequirePos && !isColFilter) ); if( nMerge==1 && !isIgnoreEmpty ){ - Fts3SegReader *p0 = apSegment[0]; - rc = xFunc(p, pContext, zTerm, nTerm, p0->aDoclist, p0->nDoclist); - if( rc!=SQLITE_OK ) goto finished; + pCsr->aDoclist = apSegment[0]->aDoclist; + pCsr->nDoclist = apSegment[0]->nDoclist; + rc = SQLITE_ROW; }else{ int nDoclist = 0; /* Size of doclist */ sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */ /* The current term of the first nMerge entries in the array ** of Fts3SegReader objects is the same. The doclists must be merged - ** and a single term added to the new segment. + ** and a single term returned with the merged doclist. */ for(i=0; i0 ){ nByte = sqlite3Fts3VarintLen(iDocid-iPrev) + (isRequirePos?nList+1:0); - if( nDoclist+nByte>nAlloc ){ + if( nDoclist+nByte>pCsr->nBuffer ){ char *aNew; - nAlloc = (nDoclist+nByte)*2; - aNew = sqlite3_realloc(aBuffer, nAlloc); + pCsr->nBuffer = (nDoclist+nByte)*2; + aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); if( !aNew ){ - rc = SQLITE_NOMEM; - goto finished; + return SQLITE_NOMEM; } - aBuffer = aNew; + pCsr->aBuffer = aNew; } - nDoclist += sqlite3Fts3PutVarint(&aBuffer[nDoclist], iDocid-iPrev); + nDoclist += sqlite3Fts3PutVarint( + &pCsr->aBuffer[nDoclist], iDocid-iPrev + ); iPrev = iDocid; if( isRequirePos ){ - memcpy(&aBuffer[nDoclist], pList, nList); + memcpy(&pCsr->aBuffer[nDoclist], pList, nList); nDoclist += nList; - aBuffer[nDoclist++] = '\0'; + pCsr->aBuffer[nDoclist++] = '\0'; } } fts3SegReaderSort(apSegment, nMerge, j, fts3SegReaderDoclistCmp); } - if( nDoclist>0 ){ - rc = xFunc(p, pContext, zTerm, nTerm, aBuffer, nDoclist); - if( rc!=SQLITE_OK ) goto finished; + pCsr->aDoclist = pCsr->aBuffer; + pCsr->nDoclist = nDoclist; + rc = SQLITE_ROW; } } + pCsr->nAdvance = nMerge; + }while( rc==SQLITE_OK ); - /* If there is a term specified to filter on, and this is not a prefix - ** search, return now. The callback that corresponds to the required - ** term (if such a term exists in the index) has already been made. - */ - if( pFilter->zTerm && !isPrefix ){ - goto finished; - } - - for(i=0; inSegment; i++){ + sqlite3Fts3SegReaderFree(pCsr->apSegment[i]); + } + sqlite3_free(pCsr->apSegment); + sqlite3_free(pCsr->aBuffer); + + pCsr->nSegment = 0; + pCsr->apSegment = 0; + pCsr->aBuffer = 0; + } +} + /* ** Merge all level iLevel segments in the database into a single ** iLevel+1 segment. Or, if iLevel<0, merge all segments into a @@ -2302,100 +2222,60 @@ int sqlite3Fts3SegReaderIterate( ** an SQLite error code is returned. */ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ - int i; /* Iterator variable */ int rc; /* Return code */ - int iIdx; /* Index of new segment */ + int iIdx = 0; /* Index of new segment */ int iNewLevel = 0; /* Level to create new segment at */ - sqlite3_stmt *pStmt = 0; - SegmentWriter *pWriter = 0; - int nSegment = 0; /* Number of segments being merged */ - Fts3SegReader **apSegment = 0; /* Array of Segment iterators */ - Fts3SegReader *pPending = 0; /* Iterator for pending-terms */ + SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */ Fts3SegFilter filter; /* Segment term filter condition */ + Fts3SegReaderCursor csr; /* Cursor to iterate through level(s) */ - if( iLevel<0 ){ + rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, 0, &csr); + if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished; + + if( iLevel==FTS3_SEGCURSOR_ALL ){ /* This call is to merge all segments in the database to a single ** segment. The level of the new segment is equal to the the numerically ** greatest segment level currently present in the database. The index - ** of the new segment is always 0. - */ - iIdx = 0; - rc = sqlite3Fts3SegReaderPending(p, 0, 0, 1, &pPending); - if( rc!=SQLITE_OK ) goto finished; - rc = fts3SegmentCountMax(p, &nSegment, &iNewLevel); - if( rc!=SQLITE_OK ) goto finished; - nSegment += (pPending!=0); - if( nSegment<=1 ){ - return SQLITE_DONE; + ** of the new segment is always 0. */ + int nDummy; /* TODO: Remove this */ + if( csr.nSegment==1 ){ + rc = SQLITE_DONE; + goto finished; } + rc = fts3SegmentCountMax(p, &nDummy, &iNewLevel); }else{ /* This call is to merge all segments at level iLevel. Find the next ** available segment index at level iLevel+1. The call to ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to - ** a single iLevel+2 segment if necessary. - */ + ** a single iLevel+2 segment if necessary. */ iNewLevel = iLevel+1; rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx); - if( rc!=SQLITE_OK ) goto finished; - rc = fts3SegmentCount(p, iLevel, &nSegment); - if( rc!=SQLITE_OK ) goto finished; } - assert( nSegment>0 ); + if( rc!=SQLITE_OK ) goto finished; + assert( csr.nSegment>0 ); assert( iNewLevel>=0 ); - /* Allocate space for an array of pointers to segment iterators. */ - apSegment = (Fts3SegReader**)sqlite3_malloc(sizeof(Fts3SegReader *)*nSegment); - if( !apSegment ){ - rc = SQLITE_NOMEM; - goto finished; - } - memset(apSegment, 0, sizeof(Fts3SegReader *)*nSegment); - - /* Allocate a Fts3SegReader structure for each segment being merged. A - ** Fts3SegReader stores the state data required to iterate through all - ** entries on all leaves of a single segment. - */ - assert( SQL_SELECT_LEVEL+1==SQL_SELECT_ALL_LEVEL); - rc = fts3SqlStmt(p, SQL_SELECT_LEVEL+(iLevel<0), &pStmt, 0); - if( rc!=SQLITE_OK ) goto finished; - sqlite3_bind_int(pStmt, 1, iLevel); - for(i=0; SQLITE_ROW==(sqlite3_step(pStmt)); i++){ - rc = fts3SegReaderNew(pStmt, i, &apSegment[i]); - if( rc!=SQLITE_OK ){ - goto finished; - } - } - rc = sqlite3_reset(pStmt); - if( pPending ){ - apSegment[i] = pPending; - pPending = 0; - } - pStmt = 0; - if( rc!=SQLITE_OK ) goto finished; - memset(&filter, 0, sizeof(Fts3SegFilter)); filter.flags = FTS3_SEGMENT_REQUIRE_POS; - filter.flags |= (iLevel<0 ? FTS3_SEGMENT_IGNORE_EMPTY : 0); - rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, - &filter, fts3MergeCallback, (void *)&pWriter - ); - if( rc!=SQLITE_OK ) goto finished; + filter.flags |= (iLevel==FTS3_SEGCURSOR_ALL ? FTS3_SEGMENT_IGNORE_EMPTY : 0); - rc = fts3DeleteSegdir(p, iLevel, apSegment, nSegment); - if( rc==SQLITE_OK ){ - rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx); + rc = sqlite3Fts3SegReaderStart(p, &csr, &filter); + while( SQLITE_OK==rc ){ + rc = sqlite3Fts3SegReaderStep(p, &csr); + if( rc!=SQLITE_ROW ) break; + rc = fts3SegWriterAdd(p, &pWriter, 1, + csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist); } + if( rc!=SQLITE_OK ) goto finished; + assert( pWriter ); + + rc = fts3DeleteSegdir(p, iLevel, csr.apSegment, csr.nSegment); + if( rc!=SQLITE_OK ) goto finished; + rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx); finished: fts3SegWriterFree(pWriter); - if( apSegment ){ - for(i=0; idb, "SAVEPOINT fts3", 0, 0, 0); if( rc==SQLITE_OK ){ - rc = fts3SegmentMerge(p, -1); + rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL); if( rc==SQLITE_OK ){ rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); if( rc==SQLITE_OK ){ diff --git a/ext/icu/icu.c b/ext/icu/icu.c index e99f142..ae28d70 100644 --- a/ext/icu/icu.c +++ b/ext/icu/icu.c @@ -247,6 +247,8 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){ UBool res; const UChar *zString = sqlite3_value_text16(apArg[1]); + (void)nArg; /* Unused parameter */ + /* If the left hand side of the regexp operator is NULL, ** then the result is also NULL. */ @@ -475,7 +477,7 @@ int sqlite3IcuInit(sqlite3 *db){ int rc = SQLITE_OK; int i; - for(i=0; rc==SQLITE_OK && i<(sizeof(scalars)/sizeof(struct IcuScalar)); i++){ + for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){ struct IcuScalar *p = &scalars[i]; rc = sqlite3_create_function( db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0 diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 5665e6a..ebf430a 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -1268,7 +1268,7 @@ static int rtreeFilter( rc = SQLITE_NOMEM; }else{ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); - assert( (idxStr==0 && argc==0) || strlen(idxStr)==argc*2 ); + assert( (idxStr==0 && argc==0) || (int)strlen(idxStr)==argc*2 ); for(ii=0; iiaConstraint[ii]; p->op = idxStr[ii*2]; @@ -1353,7 +1353,7 @@ static int rtreeFilter( */ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int rc = SQLITE_OK; - int ii, cCol; + int ii; int iIdx = 0; char zIdxStr[RTREE_MAX_DIMENSIONS*8+1]; @@ -1361,7 +1361,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ UNUSED_PARAMETER(tab); assert( pIdxInfo->idxStr==0 ); - for(ii=0; iinConstraint; ii++){ + for(ii=0; iinConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ @@ -1385,9 +1385,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ } if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){ - int j, opmsk; - static const unsigned char compatible[] = { 0, 0, 1, 1, 2, 2 }; - u8 op = 0; + u8 op; switch( p->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; @@ -1399,37 +1397,10 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ op = RTREE_MATCH; break; } - assert( op!=0 ); - - /* Make sure this particular constraint has not been used before. - ** If it has been used before, ignore it. - ** - ** A <= or < can be used if there is a prior >= or >. - ** A >= or > can be used if there is a prior < or <=. - ** A <= or < is disqualified if there is a prior <=, <, or ==. - ** A >= or > is disqualified if there is a prior >=, >, or ==. - ** A == is disqualifed if there is any prior constraint. - */ - assert( compatible[RTREE_EQ & 7]==0 ); - assert( compatible[RTREE_LT & 7]==1 ); - assert( compatible[RTREE_LE & 7]==1 ); - assert( compatible[RTREE_GT & 7]==2 ); - assert( compatible[RTREE_GE & 7]==2 ); - cCol = p->iColumn - 1 + 'a'; - opmsk = compatible[op & 7]; - for(j=0; jaConstraintUsage[ii].argvIndex = (iIdx/2); - pIdxInfo->aConstraintUsage[ii].omit = 1; - } + zIdxStr[iIdx++] = op; + zIdxStr[iIdx++] = p->iColumn - 1 + 'a'; + pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); + pIdxInfo->aConstraintUsage[ii].omit = 1; } } @@ -3133,7 +3104,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ int jj; nodeGetCell(&tree, &node, ii, &cell); - sqlite3_snprintf(512-nCell,&zCell[nCell],"%d", cell.iRowid); + sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid); nCell = strlen(zCell); for(jj=0; jj0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5; +} {1 1.0 1.0 2.0 2.0} + +do_test rtree6.3.2 { + rtree_strategy { + SELECT * FROM t3 WHERE + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 + } +} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa} +do_test rtree6.3.3 { + rtree_strategy { + SELECT * FROM t3 WHERE + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 + } +} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa} + +do_execsql_test rtree6-3.4 { + SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1 +} {} +do_execsql_test rtree6-3.5 { + SELECT * FROM t3 WHERE + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND + x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>1.1 +} {} + + finish_test diff --git a/ext/rtree/rtreeB.test b/ext/rtree/rtreeB.test new file mode 100644 index 0000000..2756fce --- /dev/null +++ b/ext/rtree/rtreeB.test @@ -0,0 +1,34 @@ +# 2011 March 2 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# Make sure the rtreenode() testing function can handle entries with +# 64-bit rowids. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source $testdir/tester.tcl +ifcapable !rtree { finish_test ; return } + +do_test rtreeB-1.1 { + db eval { + CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1); + INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0); + INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0); + INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0); + INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0); + INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400); + SELECT rtreenode(2, data) FROM t1_node; + } +} {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}} + + +finish_test diff --git a/main.mk b/main.mk index 974bbe4..e1df7a7 100644 --- a/main.mk +++ b/main.mk @@ -53,7 +53,7 @@ TCCX += -I$(TOP)/ext/async LIBOBJ+= alter.o analyze.o attach.o auth.o \ backup.o bitvec.o btmutex.o btree.o build.o \ callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \ - fts3.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ + fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o fts3_write.o \ func.o global.o hash.o \ icu.o insert.o journal.o legacy.o loadext.o \ @@ -187,6 +187,7 @@ SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ + $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_hash.c \ $(TOP)/ext/fts3/fts3_hash.h \ @@ -236,6 +237,7 @@ TESTSRC = \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ + $(TOP)/src/test_fuzzer.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ @@ -252,9 +254,11 @@ TESTSRC = \ $(TOP)/src/test_server.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_superlock.c \ + $(TOP)/src/test_syscall.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ + $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c @@ -293,6 +297,7 @@ TESTSRC2 = \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ + $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ @@ -462,6 +467,9 @@ fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c +fts3_aux.o: $(TOP)/ext/fts3/fts3_aux.c $(HDR) $(EXTHDR) + $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_aux.c + fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c @@ -531,8 +539,8 @@ test: testfixture$(EXE) sqlite3$(EXE) # threadtest runs a few thread-safety tests that are implemented in C. This # target is invoked by the releasetest.tcl script. # -threadtest3$(EXE): sqlite3.c $(TOP)/test/threadtest3.c - $(TCCX) -O2 sqlite3.c $(TOP)/test/threadtest3.c \ +threadtest3$(EXE): sqlite3.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoint.c + $(TCCX) -O2 sqlite3.o $(TOP)/test/threadtest3.c \ -o threadtest3$(EXE) $(THREADLIB) threadtest: threadtest3$(EXE) diff --git a/manifest b/manifest index 0411312..eaac4d6 100644 --- a/manifest +++ b/manifest @@ -1,14 +1,11 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA1 - -C SQLite\sversion\s3.7.5\srelease\scandidate\s2 -D 2011-01-28T17:03:50.592 +C Version\s3.7.6.2 +D 2011-04-17T17:25:17.173 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f -F Makefile.in de6498556d536ae60bb8bb10e8c1ba011448658c +F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 -F VERSION de8d3477dbf0d6cc226ccc6e046273627eb55fc5 +F VERSION 7fbd89ecdebc63a53a5cedadb822b74f5ae42a24 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248 @@ -25,7 +22,7 @@ F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 868fdb48c028421a203470e15c69ada15b9ba673 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure 0eb10c03a6536d8e5ce52ab70fda0a152d8a3262 x +F configure eb34ce3c85e7658b2eab3439f0e5b5c10b166b13 x F configure.ac 87a3c71bbe9c925381c154413eea7f3cdc397244 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 @@ -64,37 +61,39 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 28ada7d1c700e57b072b2c95d70565b05925fa46 +F ext/fts3/fts3.c 5653c5654ac9b65bf3646af7e1d695c7e9b991a0 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h a6c69c1c5e2c8c19172ddff42d262c087dcd7337 +F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e +F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93 F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295 F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2 -F ext/fts3/fts3_snippet.c 196c5e6cde57bfc1907c2d60e9c29590e4f93fb6 +F ext/fts3/fts3_snippet.c e857c6a89d81d3b89df59f3b44b35c68d8ed5c62 F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3 F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d -F ext/fts3/fts3_write.c 3eea26b9ca4219e1711b0db74fd5a9d448a6afbb +F ext/fts3/fts3_write.c 813495ed106eb9461044e3c0374f4db69b37eb09 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9 -F ext/icu/icu.c 850e9a36567bbcce6bd85a4b68243cad8e3c2de2 +F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/rtree.c 05b293c85403cf39bb5af0e7c010b0cafeab5e47 +F ext/rtree/rtree.c f5fa951eba03c41d292958064604a033021acdee F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test dbd4250ac0ad367a262eb9676f7e3080b0368206 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af F ext/rtree/rtree5.test ce3d7ccae2cfd9d2e1052b462424964c9bdcda12 -F ext/rtree/rtree6.test 309806a2a27ef5897d4dd6aee2e8006bf754cc22 +F ext/rtree/rtree6.test 0b380bd9af93f3bc496eef42502a336f58949c1b F ext/rtree/rtree7.test bcb647b42920b3b5d025846689147778485cc318 F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d F ext/rtree/rtree9.test df9843d1a9195249c8d3b4ea6aedda2d5c73e9c2 F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf +F ext/rtree/rtreeB.test b1916a9cecb86b02529c4cc5a546e8d6e7ff10da F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0 @@ -102,7 +101,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 -F main.mk 05d0f3475dd331896bd607cfb45c5e21b94589ad +F main.mk bd4e376deea4704b2bd9c77a4e6f0fa3de25c495 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac @@ -115,157 +114,166 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad -F src/alter.c 6a0c176e64a34929a4436048066a84ef4f1445b3 -F src/analyze.c a038162344265ac21dfb24b3fcc06c666ebb9c07 -F src/attach.c 252c4f7e36cc219349451ed63e278c60e80b26f3 +F src/alter.c 280f5c04b11b492703a342222b3de0a999445280 +F src/analyze.c a425d62e8fa9ebcb4359ab84ff0c62c6563d2e2a +F src/attach.c 7f97ca76ef2453440170929531a9c778267c0830 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 -F src/backup.c 6728d6d48d55b449af76a3e51c0808849cb32a2e +F src/backup.c 986c15232757f2873dff35ee3b35cbf935fc573c F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef -F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff -F src/btree.c 9004c98fc576306eee4fc0562ffeb362ef53912c -F src/btree.h 10f9296bf4edf034f5adce921b7b4383a56a1c90 -F src/btreeInt.h 20f73dc93b1eeb83afd7259fbc6bd7dcf2df7fe4 -F src/build.c 00a327120d81ace6267e714ae8010c997d55de5d -F src/callback.c a1d1b1c9c85415dff013af033e2fed9c8382d33b +F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 +F src/btree.c 6a9164af8a2ef4612ee30b253635a9bd8e5e1b1b +F src/btree.h 11753dd46597a20702bca8746cb4caa4486a82b5 +F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3 +F src/build.c f09c46c66a1e7668c6ee25c9a2518aaa6842044c +F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 7deec4534f3b5a0c3b4a4cbadf809d321f64f9c4 F src/date.c 1548fdac51377e4e7833251de878b4058c148e1b -F src/delete.c 7ed8a8c8b5f748ece92df173d7e0f7810c899ebd -F src/expr.c 1810f3056b11de99cc10e24629edf00e5fbd3a75 +F src/delete.c 7a24fcc9a31664d145acb97ce56b6d9f249a25e4 +F src/expr.c e3cf0957c6b8faaaf7386a3bc69e53c0dc9705be F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c 17950a28f28b23e8ad3feaac5fc88c324d2f600a -F src/func.c cb41f614edc43b00bfeb030f9768e80eaff47edd +F src/fkey.c a43ba8a005fb5efd1deeee06853e3a6120d46a91 +F src/func.c 3a8cb2fb2de3e3aed7f39106daf4878d9d17fcce F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3 F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c a4995747c062256582a90b4f87f716e11b067050 +F src/insert.c acfb89fe4a73d703e425e167bfcc72985f4299ae F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e -F src/loadext.c 8af9fcc75708d60b88636ccba38b4a7b3c155c3e -F src/main.c 6653e46db7ecb5a7449d8a12900147192f748b97 -F src/malloc.c 92d59a007d7a42857d4e9454aa25b6b703286be1 +F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85 +F src/main.c a8571665d43ff18f89a49d47a281605ce5ea825e +F src/malloc.c 788f2ed928786dfe305b6783d551d6b1a9080976 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf F src/mem3.c 9b237d911ba9904142a804be727cc6664873f8a3 -F src/mem5.c 6fe00f46997bebb690397cb029719f711e7640e3 +F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f F src/memjournal.c 0ebce851677a7ac035ba1512a7e65851b34530c6 F src/mutex.c 6949180803ff05a7d0e2b9334a95b4fb5a00e23f F src/mutex.h fe2ef5e1c4dae531d5a544f9241f19c56d26803d F src/mutex_noop.c d5cfbca87168c661a0b118cd8e329a908e453151 -F src/mutex_os2.c 6a62583e374ba3ac1a3fcc0da2bfdac7d3942689 +F src/mutex_os2.c 882d735098c07c8c6a5472b8dd66e19675fe117f F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579 -F src/mutex_w32.c 3ade5ae71449d1d023f0ebb3184c2ae6aa9307e4 +F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33 F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f -F src/os_os2.c 2e452c9f2ca507623ad351c33a8a8b27849b1863 -F src/os_unix.c 1be46a35bad4bec5171e4de88aaff817260eb378 -F src/os_win.c 9abdcdd925416d854eabb0996c96debd92abfef5 -F src/pager.c b0fcbe3038fd08b111e1cf1deddd5f42418004d8 -F src/pager.h 0ea59db2a33bc6c2c02cae34de33367e1effdf76 +F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 +F src/os_unix.c 4389231d079f8e323d9e24db6658bfd31f6d27a2 +F src/os_win.c 24d72407a90551969744cf9bcbb1b4c72c5fa845 +F src/pager.c 055239dcdfe12b3f5d97f6f01f85da01e2d6d912 +F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 09d38c44ab275db581f7a2f6ff8b9bc7f8c0faaa F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 F src/pcache1.c d548e31beafa792d1994b663a29a5303569efc4e -F src/pragma.c 8a6cd3c787f882fa44f6490d2411fc26839ce8f3 -F src/prepare.c 395b3fab1b93f45b6aa194b23ebc201221c47b99 -F src/printf.c df2ff3bb5409e8958136933342c46464fbd017e7 +F src/pragma.c 49c90ab27a4339d4b5bc0b03c08cbcf20ed8d454 +F src/prepare.c e64261559a3187698a3e7e6c8b001a4f4f98dab4 +F src/printf.c 585a36b6a963df832cfb69505afa3a34ed5ef8a1 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 -F src/select.c 8a7ba246b0b4bb45df7fbc52681728a0e3deaaa7 -F src/shell.c 83c6f0cc5a79a081c7b9ddfe4f557b47e0bad976 -F src/sqlite.h.in 76955fcd1c5371268ecc8afe0ce6c49ea750ae38 +F src/select.c 649a6f10f7eb7b52a5a28847773cb9968a828ae8 +F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79 +F src/sqlite.h.in 4d28db70c37a1b17942820308eb59f211140da43 F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 -F src/sqliteInt.h 45926deaf59b1ce3f55d21d5f91a8cecb6a7eb4c -F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44 -F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc +F src/sqliteInt.h ac8f3f5846275c634f6649969304a9e97f6f9854 +F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d +F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c 549859dc2c143f3deb6a92636a2d27973652c164 -F src/test1.c 771407a49ae199241f0efb7055634e4a1899c026 +F src/tclsqlite.c 501c9a200fd998a268be475be5858febc90b725b +F src/test1.c 9ca440e80e16e53920904a0a5ac7feffb9b2c9a1 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc -F src/test4.c 0528360b5025688002a5feb6be906ddce52eaaee +F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7 F src/test5.c e1a19845625144caf038031234a12185e40d315c F src/test6.c c7256cc21d2409486d094277d5b017e8eced44ba -F src/test7.c d67f65d82815eb0b57699b122ca0145dbea55bbb +F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843 F src/test8.c 6b1d12912a04fe6fca8c45bb9c3ea022f4352228 F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2 -F src/test_config.c 9f025a7f3686c94e82dc6d6bd3cbf0f89cd67487 -F src/test_demovfs.c 0aed671636735116fc872c5b03706fd5612488b5 +F src/test_config.c d536042f27226b4639f0f87d4795fd37428a9ddf +F src/test_demovfs.c 31050680fa6925b4f677cfd4fa965b5f19195e50 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc -F src/test_func.c 13b582345fb1185a93e46c53310fae8547dcce20 -F src/test_hexio.c 1237f000ec7a491009b1233f5c626ea71bce1ea2 +F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5 +F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254 +F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a F src/test_init.c 5d624ffd0409d424cf9adbfe1f056b200270077c F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99 F src/test_intarray.h 489edb9068bb926583445cb02589344961054207 F src/test_journal.c 785edd54f963aefb3c1628124170a56697c68c70 F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e F src/test_malloc.c fd6188b1501c0010fb4241ddc9f0d5ac402c688d -F src/test_multiplex.c 5990431a852aa21c9a67da748f23d2cf1e21f8fc +F src/test_multiplex.c fdabd793ee7a9642c5a8a470def2347144c46d05 +F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec -F src/test_osinst.c f408c6a181f2fb04c56273afd5c3e1e82f60392c +F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32 F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8 F src/test_quota.c b5576f17d701af461effd7ca1e71f0d100071192 F src/test_rtree.c 30c981837445a4e187ee850a49c4760d9642f7c3 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 -F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6 +F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd +F src/test_syscall.c 162c4ec0137a549c009bb9ecab550527743cfc5d F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa -F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0 +F src/test_thread.c 361ae0a0f1cbf5a28ad0388a258b104017a370c0 F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86 +F src/test_vfstrace.c 2265c9895f350c8d3c39b079998fbe7481505cc1 +F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080 -F src/trigger.c b8bedb9c0084ceb51a40f54fcca2ce048c8de852 -F src/update.c 227e6cd512108b84f69421fc6c7aa1b83d60d6e0 -F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685 -F src/util.c ab1c92426494f499f42b9e307537b03e923d75c1 -F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f -F src/vdbe.c 5d310eaf1a4d8383602126fa82e01291ab7d3cf3 -F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2 -F src/vdbeInt.h 6e6f28e9bccc6c703dca1372fd661c57b5c15fb0 -F src/vdbeapi.c 8e9324fd35eb70d0b5904bd1af40f2598744dc4d -F src/vdbeaux.c 33448d23b857654dd69ed2103611f5c733606f68 -F src/vdbeblob.c 18955f0ee6b133cd08e1592010cb9a6b11e9984c -F src/vdbemem.c 411649a35686f54268ccabeda175322c4697f5a6 -F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5 -F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30 -F src/wal.c dbca424f71678f663a286ab2a98f947af1d412a7 -F src/wal.h c1aac6593a0b02b15dc625987e619edeab39292e +F src/trigger.c 144cc18bb701f3286484aae4292a9531f09278c8 +F src/update.c 81911be16ece3c3e7716aa18565b4814ec41f8b9 +F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60 +F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a +F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e +F src/vdbe.c 05deeec6659f2579674a5e6510b3ada2a442f8d5 +F src/vdbe.h 8a675fefdf7119441fe817c800a9a52440c2e797 +F src/vdbeInt.h fe8f58d305e629fff02f61f655aca1d299f1f6ae +F src/vdbeapi.c e0e2672e0a96ae3f8575c8ecd02912a3e8a554a1 +F src/vdbeaux.c 9ae5074b19bdff2d8806a278533956fb281510d5 +F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562 +F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b +F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 +F src/vtab.c b0abc931f95af94c9ffdf9f747eb191cda953123 +F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794 +F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c af069e6b53234118014dabfece96a9515b69d76b +F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 51756962d522e474338e9b2ebb26e7364d4aa125 -F test/alter.test 15f9224868b290d6bf7a63f31437f31aee070636 +F test/alter.test 4e47fb9ea59348b88fce4e8bb49de530128b104c F test/alter2.test 75f731508f1bf27ba09a6075c66cd02216ba464b F test/alter3.test 8677e48d95536f7a6ed86a1a774744dadcc22b07 F test/alter4.test 1e5dd6b951e9f65ca66422edff02e56df82dd403 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc -F test/analyze.test c1eb87067fc16ece7c07e823d6395fd831b270c5 -F test/analyze2.test 3bde8f0879d9c1f2df3af21fcf42e706d8ee1e43 -F test/analyze3.test 820ddfb7591b49607fbaf77240c7955ac3cabb04 +F test/analyze.test c8cb89e8736336f1f0646c8123e6028a14c7b55e +F test/analyze2.test 8f2b1534d43f5547ce9a6b736c021d4192c75be3 +F test/analyze3.test d61f55d8b472fc6e713160b1e577f7a68e63f38b F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045 +F test/analyze5.test 1de8d66b11aae5a1453aa042d62e834a476bac9c +F test/analyze6.test c125622a813325bba1b4999040ddc213773c2290 +F test/analyze7.test 5508e7828164ea0b518ed219bed7320a481863d4 F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3 F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a F test/async5.test f3592d79c84d6e83a5f50d3fd500445f7d97dfdf -F test/attach.test ce9660e51768fab93cf129787be886c5d6c4fd81 +F test/attach.test 2bb09073d7d5499127db00f50780766dcea913e1 F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437 F test/attach3.test bd9830bc3a0d22ed1310c9bff6896927937017dc +F test/attach4.test 31f9eb0ca7bdbc393cc4657b877903a226a83d4b F test/attachmalloc.test 1d5b821a676f7bf0b00d87cc106b78966789ba57 -F test/auth.test 26cc6f219580191539bf335abe03e55e49310846 +F test/auth.test b047105c32da7db70b842fd24056723125ecc2ff F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882 F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/autoinc.test 85ef3180a737e6580086a018c09c6f1a52759b46 @@ -273,12 +281,13 @@ F test/autoindex1.test 860fc83f4fefb0c68ad062afc3ff43faa1534fc4 F test/autovacuum.test bb7c0885e6f8f1d633045de48f2b66082162766d F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6 F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 -F test/backcompat.test 541314d69ec9db3e03630b7616696ddc5048efb1 +F test/backcompat.test 0f23ff8d516acdf42f3d866a66d85306de2d02bc F test/backup.test 004d3b78bffd990741ab50133ed4347c25c172b1 F test/backup2.test b7c69f937c912e85ac8a5dbd1e1cf290302b2d49 F test/backup_ioerr.test 1f012e692f42c0442ae652443258f70e9f20fa38 F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f +F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070 F test/bigfile.test a8ec8073a20207456dab01a29ad9cde42b0dd103 F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747 @@ -297,11 +306,11 @@ F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0 F test/cache.test 754baab2f18089fc9bcba7afaeb4dc907c6c6de2 F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360 -F test/capi3.test 1945a2ba75e3f4c49d5beb8fc092115b6292d471 +F test/capi3.test 5c1ea6c940f2d7c4d5af8ef1ec2f92a267d2e37a F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 F test/capi3c.test bea67403a5e37a4b33230ee4723e315a2ffb31e7 F test/capi3d.test cd36571f014f34bdc4421967f6453cbb597d5d16 -F test/capi3e.test 4fda47388ddfbfe807987aa62f46fcbceec9327f +F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde F test/cast.test 166951664a0b0a2e0f8fb5997a152490c6363932 F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3 F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91 @@ -320,17 +329,17 @@ F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b F test/conflict.test cabc41f7616675df71b4fddabca3bd5d9221915a F test/corrupt.test 1a5bef8b2d178859af69814ecedcd37219a89968 F test/corrupt2.test 808a28d0ca3b97e9aa8c91cd2b485ea2700b76d1 -F test/corrupt3.test a399dacccb91c732f6b071c913e70d195af8c058 +F test/corrupt3.test e3006aaf579d2ed7f1b94bf4cc695d3c784fa5af F test/corrupt4.test b963f9e01e0f92d15c76fb0747876fd4b96dc30a F test/corrupt5.test c23da7bfb20917cc7fdbb13ee25c7cc4e9fffeff F test/corrupt6.test 4e4161aef1f30b9f34582bb4142334b7f47eacae F test/corrupt7.test a90caf89c7d7cb7893ea4d92529bd0c129317ee4 F test/corrupt8.test 48eb37ffb9a03bceada62219e2bd4c92f4b0cb75 F test/corrupt9.test fad0bc26a5c972580a8d763c62f24094f4e8ef25 -F test/corruptA.test 38b4f81c16099f6d8fa8b37e188fde76b8243994 +F test/corruptA.test 856ea7a2eb5c1c767abbdf02679ac6cb158e4643 F test/corruptB.test 44133515cf46c4d7bba691e3bcfa478080413af0 F test/corruptC.test 483aa35dadfd96bdf549e38d75ffc2942576477e -F test/corruptD.test 3ae6e2dc6e2226c6935a8a40d4b5ee3eba75f8c0 +F test/corruptD.test 63a449a3146b460231535d04a409d14ce71795ad F test/corruptE.test 7290b61145d954be549340e462ca84826d8a31a3 F test/count.test 454e1ce985c94d13efeac405ce54439f49336163 F test/crash.test 1b6ac8410689ff78028887f445062dc897c9ac89 @@ -356,11 +365,11 @@ F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f F test/diskfull.test 0cede7ef9d8f415d9d3944005c76be7589bb5ebb F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 -F test/e_createtable.test b8f5286879315d5b7f4cc5ead1afda4846f0c0bb +F test/e_createtable.test b40fc61bc4f1ad2a3c84590bd1d711507263d921 F test/e_delete.test 55d868b647acc091c261a10b9b0cb0ab660a6acb F test/e_droptrigger.test ddd4b28ed8a3d81bd5153fa0ab7559529a2ca03a F test/e_dropview.test b347bab30fc8de67b131594b3cd6f3d3bdaa753d -F test/e_expr.test 4e004d1f5187d4bbc9ca3d55660a8d164dd59f4e +F test/e_expr.test 9e8b9790803df4de23c2d68d566959934a6179d4 F test/e_fkey.test 38039b840ab19331000b0f0eb1d82baa7208a67a F test/e_fts3.test 75bb0aee26384ef586165e21018a17f7cd843469 F test/e_insert.test 7390c2da39f16a134dc9a439144768c727757d2c @@ -369,23 +378,25 @@ F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6 F test/e_select.test bf385ae3aa0f014c4933ae66fd3e1302138493eb F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92 F test/e_update.test 963d6876064e65f318d1c93aaed36a02b9b389bf -F test/e_vacuum.test 057cc29445746fc1d2542984ff0253d511a234bd +F test/e_vacuum.test 6c09c2af7f2f140518f371c5342100118f779dcf F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398 F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041 F test/enc4.test 4b575ef09e0eff896e73bd24076f96c2aa6a42de -F test/eqp.test 69670e7919030f21de29fb99bf1d68f97aedcbdb +F test/eqp.test f14fadd76da53405e9885e2431cacf7191d83cdb F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3 F test/exclusive.test 53e1841b422e554cecf0160f937c473d6d0e3062 -F test/exclusive2.test b65264c3e76e1db6c6eda15c02000a40743f6541 +F test/exclusive2.test 343d55130c12c67b8bf10407acec043a6c26c86b F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 -F test/expr.test 620a636cf7b7d4e5834a0b9d83a4da372e24a7b7 +F test/exists.test 5e2d64b4eb5a9d08876599bdae2e1213d2d12e2a +F test/expr.test 19e8ac40313e2282a47b586d11c4892040990d3a F test/fallocate.test 43dc34b8c24be6baffadc3b4401ee15710ce83c6 F test/filectrl.test 97003734290887566e01dded09dc9e99cb937e9e -F test/filefmt.test f77c92141960b7933bc6691631d2ad62257ef40a +F test/filefmt.test f178cfc29501a14565954c961b226e61877dd32c F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f F test/fkey3.test 42f88d6048d8dc079e2a8cf7baad1cc1483a7620 +F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49 F test/fkey_malloc.test a5ede29bd2f6e56dea78c3d43fb86dd696c068c8 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7 @@ -440,18 +451,21 @@ F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8 F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18 F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9 F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa +F test/fts3aux1.test 719c35cbbcc04dde8e5a54a6f69851a0af9ed1f2 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 -F test/fts3corrupt.test d874ba27975aa8e5514bf58bf97b473404de0dbb +F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c +F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 F test/fts3d.test 95fb3c862cbc4297c93fceb9a635543744e9ef52 F test/fts3defer.test d6cb0db9b5997ecf863d96ff419f83f8f2c87f4f -F test/fts3defer2.test da840efaedebfdd54293d04b36098e2d9872caa6 +F test/fts3defer2.test 288bef6de15557319b8c12d476ebdc83688ef96c F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851 F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a F test/fts3fault.test f83e556465bb69dc8bc676339eca408dce4ca246 +F test/fts3fault2.test dc96203af6ba31ce20163fc35460e1556e8edf4d F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844 @@ -468,6 +482,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test dd7001ac86d09c154a7dff064f4739c60e2b312c +F test/fuzzer1.test 3105b5a89a6cb0d475f0877debec942fe4143462 F test/hook.test f04c3412463f8ec117c1c704c74ca0f627ce733a F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d @@ -480,12 +495,12 @@ F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7 F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0 F test/incrvacuum.test 453d1e490d8f5ad2c9b3a54282a0690d6ae56462 -F test/incrvacuum2.test 9e22a794899c91b7d8c8e12eaacac8df249faafe +F test/incrvacuum2.test ae04573b73ad52179f56e194fff0fbe43b509d23 F test/incrvacuum_ioerr.test 57d2f5777ab13fa03b87b262a4ea1bad5cfc0291 -F test/index.test df7c00c6edd9504ab71c83a9514f1c5ca0fa54d8 +F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097 F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6 F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7 -F test/indexedby.test d7367c5a0e8ed8db642824a68126753e0808c706 +F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908 F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435 @@ -516,7 +531,7 @@ F test/jrnlmode3.test c6522b276ba315fd1416198de6fc1da9e72409fb F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05 F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 -F test/like.test 0f64aeaed50b6e3ebaef3af0b3b8f894aed5acca +F test/like.test a47f52692aac96ba82508efba74819214cdebc17 F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da F test/limit.test 2db7b3b34fb925b8e847d583d2eb67531d0ce67e F test/loadext.test 0393ce12d9616aa87597dd0ec88181de181f6db0 @@ -532,7 +547,7 @@ F test/lock_common.tcl d279887a0ab16cdb6d935c1203e64113c5a000e9 F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2 F test/main.test 9d7bbfcc1b52c88ba7b2ba6554068ecf9939f252 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9 -F test/malloc.test 927e6c8668a1d48c23aa6189bda02aff5a1b83de +F test/malloc.test e56c9c3358da2c18385aea15a42dc970913986c2 F test/malloc3.test 4128b1e6ffa506103b278ad97af89174f310c7ca F test/malloc4.test 957337613002b7058a85116493a262f679f3a261 F test/malloc5.test 4d16d1bb26d2deddd7c4f480deec341f9b2d0e22 @@ -552,15 +567,16 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6 F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9 -F test/malloc_common.tcl 660b82ab528521cc4a48ff6df05ca3b6a00d47c5 +F test/malloc_common.tcl 50d0ed21eed0ae9548b58935bd29ac89a05a54fa F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c -F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498 +F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f +F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6 F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memsubsys1.test 679db68394a5692791737b150852173b3e2fea10 F test/memsubsys2.test 72a731225997ad5e8df89fdbeae9224616b6aecc F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0 -F test/minmax3.test 66a60eb0f20281b0753249d347c5de0766954cee +F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/misc1.test e56baf44656dd68d6475a4b44521045a60241e9b F test/misc2.test a628db7b03e18973e5d446c67696b03de718c9fd F test/misc3.test 72c5dc87a78e7865c5ec7a969fc572913dbe96b6 @@ -569,7 +585,7 @@ F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 F test/misc7.test 29032efcd3d826fbd409e2a7af873e7939f4a4e3 F test/misuse.test 30b3a458e5a70c31e74c291937b6c82204c59f33 -F test/multiplex.test 92a4839213fd8cba8b59f86d42b7a1da1857db39 +F test/multiplex.test a88f3e2c16e567e72be7296195c59fbdd6a8d3d4 F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41 F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660 F test/nan.test a44e04df1486fcfb02d32468cbcd3c8e1e433723 @@ -579,17 +595,18 @@ F test/notify3.test d60923e186e0900f4812a845fcdfd8eea096e33a F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec -F test/pager1.test 7006a8b5dd3df1fe0d51d7da014333d7dc099778 +F test/oserror.test 498d8337e9d15543eb7b004fef8594bf204ff43c +F test/pager1.test d8672fd0af5f4f9b99b06283d00f01547809bebe F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f F test/pagerfault.test 9de4d3e0c59970b4c6cb8dac511fa242f335d8a7 F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401 -F test/pagerfault3.test 9b413f48a3e9a9a8c26968118f8db19fd7bfb8c7 +F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7 F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806 F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16 F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16 -F test/permutations.test c0ce0f3b741dd92a6d4c2671dbacba4b92dd81eb +F test/permutations.test 5b2a4cb756ffb2407cb4743163668d1d769febb6 F test/pragma.test fdfc09067ea104a0c247a1a79d8093b56656f850 F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47 F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea @@ -603,7 +620,7 @@ F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a -F test/releasetest.tcl 627ccd04a113a193c375594bd5d6d051d8220658 +F test/releasetest.tcl c0c0865f1dff08dde08a964ef49e83217ebedbf8 F test/rollback.test 1a83118ea6db4e7d8c10eaa63871b5e90502ffdc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test e58e0acef38b527ed1b0b70d3ada588f804af287 @@ -659,26 +676,28 @@ F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a -F test/superlock.test 8468e057d8a5531ff99e504e77fcc585a0291bf2 +F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082 F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3 +F test/syscall.test 707c95e4ab7863e13f1293c6b0c76bead30249b3 +F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f F test/table.test 04ba066432430657712d167ebf28080fe878d305 -F test/tableapi.test 7262a8cbaa9965d429f1cbd2747edc185fa56516 +F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3 F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05 -F test/tester.tcl dafe0d30279f6d380d5d2a535781dda91b8cfc3f +F test/tester.tcl 6fa3d2f581b479a3a088b1b5b0d145e548ebe662 F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca F test/thread004.test f51dfc3936184aaf73ee85f315224baad272a87f F test/thread005.test bf5c374ca65dd89fd56c8fe511ccfb46875bda5e -F test/thread1.test 862dd006d189e8b0946935db17399dcac2f8ef91 -F test/thread2.test e08034b83fe9693ade77049732518e5b3d2d700d -F test/thread_common.tcl 2aa6f2fdcd4d6e461169c3e5ca098eebf643b863 +F test/thread1.test df115faa10a4ba1d456e9d4d9ec165016903eae4 +F test/thread2.test f35d2106452b77523b3a2b7d1dcde2e5ee8f9e46 +F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9 -F test/threadtest3.c d6d209190c7110f9a7e6a8154bdc3260efdbf8b7 +F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef F test/tkt-02a8e81d44.test 58494de77be2cf249228ada3f313fa399821c6ab F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660 F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28 @@ -691,6 +710,7 @@ F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e F test/tkt-5d863f876e.test 884072c2de496ddbb90c387c9ebc0d4f44a91b8e F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84 F test/tkt-5ee23731f.test 3581260f2a71e51db94e1506ba6b0f7311d002a9 +F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7 F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7 @@ -698,11 +718,13 @@ F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c F test/tkt-94c04eaadb.test be5ea61cb04dfdc047d19b5c5a9e75fa3da67a7f F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 +F test/tkt-b72787b1.test e6b62b2b2785c04d0d698d6a603507e384165049 F test/tkt-cbd054fa6b.test f14f97ea43662e6f70c9e63287081e8be5d9d589 F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7 F test/tkt-d82e3f3721.test 731359dfdcdb36fea0559cd33fec39dd0ceae8e6 F test/tkt-f3e5abed55.test 19fb59268da6f20a69a181b9c14154132d1c65e3 F test/tkt-f777251dc7a.test 6f24c053bc5cdb7e1e19be9a72c8887cf41d5e87 +F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7 F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4 F test/tkt-fc62af4523.test 72825d3febdedcd5593a27989fc05accdbfc2bb4 F test/tkt1435.test f8c52c41de6e5ca02f1845f3a46e18e25cadac00 @@ -774,7 +796,7 @@ F test/tkt3773.test 430b06567ce40285dfd2c4834a2a61816403efeb F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267 F test/tkt3793.test 754b73f0e6a9349c70dc57e522cf3247272ecd5d F test/tkt3810.test 90fa0635dfa7da9680c8cd3513350a49b3a8ae12 -F test/tkt3824.test 3da2f5c81b057e3ff355f5dfc9aa0cf0a92e0206 +F test/tkt3824.test 150aa00bb6220672e5f0eb14dc8eaa36750425f0 F test/tkt3832.test 2300d10d57562b89875b72148338ac3e14f8847d F test/tkt3838.test f956f0719b5f805b12dd1dbf19f19d298bacebc3 F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f @@ -790,7 +812,7 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1 F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7 F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5 -F test/trace2.test 092bc2c5776272700450d60a36919921095bdc21 +F test/trace2.test 0ce11265c83333d8f5beeca19e71ed93a88d386c F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6 F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22 F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97 @@ -807,14 +829,17 @@ F test/triggerA.test eaf11a29db2a11967d2d4b49d37f92bce598194e F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe F test/triggerC.test 8a691ff6dd47df2e57395bbec4b62101fac0f363 F test/triggerD.test c6add3817351451e419f6ff9e9a259b02b6e2de7 +F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150 F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2 +F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e +F test/unordered.test c479d3027f9c4db05b44b83010735c6708abcc91 F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172 F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae F test/vacuum.test 29b60e8cc9e573b39676df6c4a75fe9e02d04a09 -F test/vacuum2.test 2165164ed2463816e8c4648d0a779a863ce1a76c +F test/vacuum2.test 91a84c9b08adfc4472097d2e8deb0150214e0e76 F test/vacuum3.test f39ad1428347c5808cd2da7578c470f186a4d0ce F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 @@ -837,18 +862,19 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61 F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d -F test/wal.test 70227190e713b3e7eb2a7d5ec3510b66db01f327 -F test/wal2.test 3de797854de175323e7351b5f2514a30d1ee1410 -F test/wal3.test ac51126c36814bce334f66a0a4dbbfa56d429733 +F test/wal.test 5617ad308bfdb8a8885220d8a261a6096a8d7e57 +F test/wal2.test e561a8c6fdd1c2cd1876f3e39757934e7b7361f8 +F test/wal3.test 5c396cc22497244d627306f4c1d360167353f8dd F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30 +F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b F test/wal6.test 07aa31ca8892d0527f2c5c5a9a2a87aa421dfaa8 -F test/wal_common.tcl 895d76138043b86bdccf36494054bdabcf65837b +F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4 F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0 F test/walcksum.test a37b36375c595e61bdb7e1ec49b5f0979b6fc7ce F test/walcrash.test e763841551d6b23677ccb419797c1589dcbdbaf5 F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142 -F test/walfault.test 81ed760def1c1573151d416b0d09178cf006f9fd +F test/walfault.test 58fce626359c9376fe35101b5c0f2df8040aa839 F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483 F test/walmode.test 22ddccd073c817ac9ead62b88ac446e8dedc7d2c F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317 @@ -857,14 +883,14 @@ F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933 F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c F test/where.test de337a3fe0a459ec7c93db16a519657a90552330 F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554 -F test/where3.test 8ebedae552e13fc7f2b4e8df6cbe72a095347400 +F test/where3.test 8e1175c7ef710c70502858fc4fb08d784b3620b9 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where7.test aa4cfcd6f66e2a4ef87b6717327325bf4d547502 F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 -F test/where9.test 7ee38c3fd67e76789a6ec769f62f6433d3d4a5cf +F test/where9.test 24f19ad14bb1b831564ced5273e681e495662848 F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 @@ -878,13 +904,14 @@ F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 -F tool/mksqlite3c.tcl e0db70c2c52b0e3d0867ca931229e5b90ffe7837 +F tool/mksqlite3c.tcl 623e26cc8c83322e4151d3ad85ac69d41221bae8 F tool/mksqlite3h.tcl d76c226a5e8e1f3b5f6593bcabe5e98b3b1ec9ff F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87 -F tool/omittest.tcl 27d6f6e3b1e95aeb26a1c140e6eb57771c6d794a +F tool/omittest.tcl b1dd290c1596e0f31fd335160a74ec5dfea3df4a F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a -F tool/shell1.test c31b0814a9c543db51ca0cc63edb5e77ea532303 +F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 +F tool/shell1.test 7a389c6aaad05621be39501d6f8db410da464dcd F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3 F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836 F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce @@ -900,15 +927,9 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e +F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 682fe41efd3578e8c9abc7138b61f361c3adbe95 -R 7fccaf9f7c013ddef8926255f6aeb773 +P c429edf30accac12d5deef10e59cd31146036f11 +R 050fbfdb8675b7eaafb0b1677e5953e7 U drh -Z 4223173e58f25d45d236e232b9c90989 ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.6 (GNU/Linux) - -iD8DBQFNQvb6oxKgR168RlERAtOTAJ9BaewewKXL3RGZUy5ycaHwjeEmJACeOPbw -/JKqOugR+37RH7HnLCo9DBk= -=kgPx ------END PGP SIGNATURE----- +Z 289c52539a2a59a211a989409cbc5426 diff --git a/manifest.uuid b/manifest.uuid index af0bce6..e541108 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ed759d5a9edb3bba5f48f243df47be29e3fe8cd7 +154ddbc17120be2915eb03edc52af1225eb7cb5e diff --git a/src/alter.c b/src/alter.c index 1534fdf..aa3fa92 100644 --- a/src/alter.c +++ b/src/alter.c @@ -370,6 +370,22 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ #endif } +/* +** Parameter zName is the name of a table that is about to be altered +** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). +** If the table is a system table, this function leaves an error message +** in pParse->zErr (system tables may not be altered) and returns non-zero. +** +** Or, if zName is not a system table, zero is returned. +*/ +static int isSystemTable(Parse *pParse, const char *zName){ + if( sqlite3Strlen30(zName)>6 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ + sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); + return 1; + } + return 0; +} + /* ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" ** command. @@ -420,14 +436,11 @@ void sqlite3AlterRenameTable( /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ - if( sqlite3Strlen30(pTab->zName)>6 - && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) - ){ - sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); + if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ goto exit_rename_table; } - if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - goto exit_rename_table; + if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto + exit_rename_table; } #ifndef SQLITE_OMIT_VIEW @@ -759,6 +772,9 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } + if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + goto exit_begin_add_column; + } assert( pTab->addColOffset>0 ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); diff --git a/src/analyze.c b/src/analyze.c index 0a8339b..17c1de8 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -34,7 +34,8 @@ static void openStatTable( Parse *pParse, /* Parsing context */ int iDb, /* The database we are looking in */ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ - const char *zWhere /* Delete entries associated with this table */ + const char *zWhere, /* Delete entries for this table or index */ + const char *zWhereType /* Either "tbl" or "idx" */ ){ static const struct { const char *zName; @@ -79,7 +80,7 @@ static void openStatTable( sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); if( zWhere ){ sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE tbl=%Q", pDb->zName, zTab, zWhere + "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere ); }else{ /* The sqlite_stat[12] table already exists. Delete all rows. */ @@ -103,6 +104,7 @@ static void openStatTable( static void analyzeOneTable( Parse *pParse, /* Parser context */ Table *pTab, /* Table whose indices are to be analyzed */ + Index *pOnlyIdx, /* If not NULL, only analyze this one index */ int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */ int iMem /* Available memory locations begin here */ ){ @@ -113,8 +115,7 @@ static void analyzeOneTable( int i; /* Loop counter */ int topOfLoop; /* The top of the loop */ int endOfLoop; /* The end of the loop */ - int addr = 0; /* The address of an instruction */ - int jZeroRows = 0; /* Jump from here if number of rows is zero */ + int jZeroRows = -1; /* Jump from here if number of rows is zero */ int iDb; /* Index of database containing pTab */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ @@ -125,6 +126,7 @@ static void analyzeOneTable( int regRowid = iMem++; /* Rowid for the inserted record */ #ifdef SQLITE_ENABLE_STAT2 + int addr = 0; /* Instruction address */ int regTemp2 = iMem++; /* Temporary use register */ int regSamplerecno = iMem++; /* Index of next sample to record */ int regRecno = iMem++; /* Current sample index */ @@ -147,6 +149,7 @@ static void analyzeOneTable( assert( sqlite3BtreeHoldsAllMutexes(db) ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ @@ -160,9 +163,12 @@ static void analyzeOneTable( iIdxCur = pParse->nTab++; sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int nCol = pIdx->nColumn; - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + int nCol; + KeyInfo *pKey; + if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; + nCol = pIdx->nColumn; + pKey = sqlite3IndexKeyinfo(pParse, pIdx); if( iMem+1+(nCol*2)>pParse->nMem ){ pParse->nMem = iMem+1+(nCol*2); } @@ -319,7 +325,7 @@ static void analyzeOneTable( ** is never possible. */ sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); - if( jZeroRows==0 ){ + if( jZeroRows<0 ){ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); } for(i=0; izName)); sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno); sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); + jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno); }else{ - assert( jZeroRows>0 ); - addr = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, jZeroRows); + jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto); } sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); @@ -356,9 +362,7 @@ static void analyzeOneTable( sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); if( pParse->nMemnMem = regRec; - if( jZeroRows ){ - sqlite3VdbeJumpHere(v, addr); - } + sqlite3VdbeJumpHere(v, jZeroRows); } /* @@ -385,20 +389,22 @@ static void analyzeDatabase(Parse *pParse, int iDb){ sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 2; - openStatTable(pParse, iDb, iStatCur, 0); + openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); - analyzeOneTable(pParse, pTab, iStatCur, iMem); + analyzeOneTable(pParse, pTab, 0, iStatCur, iMem); } loadAnalysis(pParse, iDb); } /* ** Generate code that will do an analysis of a single table in -** a database. +** a database. If pOnlyIdx is not NULL then it is a single index +** in pTab that should be analyzed. */ -static void analyzeTable(Parse *pParse, Table *pTab){ +static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){ int iDb; int iStatCur; @@ -408,8 +414,12 @@ static void analyzeTable(Parse *pParse, Table *pTab){ sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 2; - openStatTable(pParse, iDb, iStatCur, pTab->zName); - analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1); + if( pOnlyIdx ){ + openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx"); + }else{ + openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl"); + } + analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1); loadAnalysis(pParse, iDb); } @@ -431,6 +441,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ int i; char *z, *zDb; Table *pTab; + Index *pIdx; Token *pTableName; /* Read the database schema. If an error occurs, leave an error message @@ -455,11 +466,12 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ }else{ z = sqlite3NameFromToken(db, pName1); if( z ){ - pTab = sqlite3LocateTable(pParse, 0, z, 0); - sqlite3DbFree(db, z); - if( pTab ){ - analyzeTable(pParse, pTab); + if( (pIdx = sqlite3FindIndex(db, z, 0))!=0 ){ + analyzeTable(pParse, pIdx->pTable, pIdx); + }else if( (pTab = sqlite3LocateTable(pParse, 0, z, 0))!=0 ){ + analyzeTable(pParse, pTab, 0); } + sqlite3DbFree(db, z); } } }else{ @@ -469,11 +481,12 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ zDb = db->aDb[iDb].zName; z = sqlite3NameFromToken(db, pTableName); if( z ){ - pTab = sqlite3LocateTable(pParse, 0, z, zDb); - sqlite3DbFree(db, z); - if( pTab ){ - analyzeTable(pParse, pTab); + if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){ + analyzeTable(pParse, pIdx->pTable, pIdx); + }else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){ + analyzeTable(pParse, pTab, 0); } + sqlite3DbFree(db, z); } } } @@ -535,6 +548,10 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ if( pIndex==0 ) break; pIndex->aiRowEst[i] = v; if( *z==' ' ) z++; + if( memcmp(z, "unordered", 10)==0 ){ + pIndex->bUnordered = 1; + break; + } } return 0; } @@ -589,9 +606,9 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ assert( iDb>=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); - assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); /* Clear any prior statistics */ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); diff --git a/src/attach.c b/src/attach.c index e3dc49d..bda1c87 100644 --- a/src/attach.c +++ b/src/attach.c @@ -176,7 +176,9 @@ static void attachFunc( case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){ + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + } break; } } @@ -200,7 +202,7 @@ static void attachFunc( db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; @@ -272,7 +274,7 @@ static void detachFunc( sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); return; detach_error: @@ -312,9 +314,11 @@ static void codeAttach( #ifndef SQLITE_OMIT_AUTHORIZATION if( pAuthArg ){ - char *zAuthArg = pAuthArg->u.zToken; - if( NEVER(zAuthArg==0) ){ - goto attach_end; + char *zAuthArg; + if( pAuthArg->op==TK_STRING ){ + zAuthArg = pAuthArg->u.zToken; + }else{ + zAuthArg = 0; } rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0); if(rc!=SQLITE_OK ){ diff --git a/src/backup.c b/src/backup.c index 5d8ea7f..4d7ae31 100644 --- a/src/backup.c +++ b/src/backup.c @@ -219,6 +219,10 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); const int nCopy = MIN(nSrcPgsz, nDestPgsz); const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; +#ifdef SQLITE_HAS_CODEC + int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc); + int nDestReserve = sqlite3BtreeGetReserve(p->pDest); +#endif int rc = SQLITE_OK; i64 iOff; @@ -237,11 +241,22 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ #ifdef SQLITE_HAS_CODEC /* Backup is not possible if the page size of the destination is changing - ** a a codec is in use. + ** and a codec is in use. */ if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){ rc = SQLITE_READONLY; } + + /* Backup is not possible if the number of bytes of reserve space differ + ** between source and destination. If there is a difference, try to + ** fix the destination to agree with the source. If that is not possible, + ** then the backup cannot proceed. + */ + if( nSrcReserve!=nDestReserve ){ + u32 newPgsz = nSrcPgsz; + rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); + if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY; + } #endif /* This loop runs once for each destination page spanned by the source @@ -401,7 +416,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int nDestTruncate; if( p->pDestDb ){ - sqlite3ResetInternalSchema(p->pDestDb, 0); + sqlite3ResetInternalSchema(p->pDestDb, -1); } /* Set nDestTruncate to the final number of pages in the destination @@ -488,7 +503,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc - && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest)) + && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0)) ){ rc = SQLITE_DONE; } @@ -502,7 +517,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ if( bCloseTrans ){ TESTONLY( int rc2 ); TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); - TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc); + TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); assert( rc2==SQLITE_OK ); } @@ -607,7 +622,11 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ ** has been modified by a transaction on the source pager. Copy ** the new data into the backup. */ - int rc = backupOnePage(p, iPage, aData); + int rc; + assert( p->pDestDb ); + sqlite3_mutex_enter(p->pDestDb->mutex); + rc = backupOnePage(p, iPage, aData); + sqlite3_mutex_leave(p->pDestDb->mutex); assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); if( rc!=SQLITE_OK ){ p->rc = rc; diff --git a/src/btmutex.c b/src/btmutex.c index 201291a..d87d4d5 100644 --- a/src/btmutex.c +++ b/src/btmutex.c @@ -39,12 +39,13 @@ static void lockBtreeMutex(Btree *p){ ** clear the p->locked boolean. */ static void unlockBtreeMutex(Btree *p){ + BtShared *pBt = p->pBt; assert( p->locked==1 ); - assert( sqlite3_mutex_held(p->pBt->mutex) ); + assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3_mutex_held(p->db->mutex) ); - assert( p->db==p->pBt->db ); + assert( p->db==pBt->db ); - sqlite3_mutex_leave(p->pBt->mutex); + sqlite3_mutex_leave(pBt->mutex); p->locked = 0; } @@ -185,30 +186,11 @@ void sqlite3BtreeLeaveCursor(BtCursor *pCur){ */ void sqlite3BtreeEnterAll(sqlite3 *db){ int i; - Btree *p, *pLater; + Btree *p; assert( sqlite3_mutex_held(db->mutex) ); for(i=0; inDb; i++){ p = db->aDb[i].pBt; - assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db ); - if( p && p->sharable ){ - p->wantToLock++; - if( !p->locked ){ - assert( p->wantToLock==1 ); - while( p->pPrev ) p = p->pPrev; - /* Reason for ALWAYS: There must be at least on unlocked Btree in - ** the chain. Otherwise the !p->locked test above would have failed */ - while( p->locked && ALWAYS(p->pNext) ) p = p->pNext; - for(pLater = p->pNext; pLater; pLater=pLater->pNext){ - if( pLater->locked ){ - unlockBtreeMutex(pLater); - } - } - while( p ){ - lockBtreeMutex(p); - p = p->pNext; - } - } - } + if( p ) sqlite3BtreeEnter(p); } } void sqlite3BtreeLeaveAll(sqlite3 *db){ @@ -217,16 +199,18 @@ void sqlite3BtreeLeaveAll(sqlite3 *db){ assert( sqlite3_mutex_held(db->mutex) ); for(i=0; inDb; i++){ p = db->aDb[i].pBt; - if( p && p->sharable ){ - assert( p->wantToLock>0 ); - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } + if( p ) sqlite3BtreeLeave(p); } } +/* +** Return true if a particular Btree requires a lock. Return FALSE if +** no lock is ever required since it is not sharable. +*/ +int sqlite3BtreeSharable(Btree *p){ + return p->sharable; +} + #ifndef NDEBUG /* ** Return true if the current thread holds the database connection @@ -251,97 +235,42 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ } #endif /* NDEBUG */ -/* -** Add a new Btree pointer to a BtreeMutexArray. -** if the pointer can possibly be shared with -** another database connection. -** -** The pointers are kept in sorted order by pBtree->pBt. That -** way when we go to enter all the mutexes, we can enter them -** in order without every having to backup and retry and without -** worrying about deadlock. -** -** The number of shared btrees will always be small (usually 0 or 1) -** so an insertion sort is an adequate algorithm here. -*/ -void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){ - int i, j; - BtShared *pBt; - if( pBtree==0 || pBtree->sharable==0 ) return; #ifndef NDEBUG - { - for(i=0; inMutex; i++){ - assert( pArray->aBtree[i]!=pBtree ); - } - } -#endif - assert( pArray->nMutex>=0 ); - assert( pArray->nMutexaBtree)-1 ); - pBt = pBtree->pBt; - for(i=0; inMutex; i++){ - assert( pArray->aBtree[i]!=pBtree ); - if( pArray->aBtree[i]->pBt>pBt ){ - for(j=pArray->nMutex; j>i; j--){ - pArray->aBtree[j] = pArray->aBtree[j-1]; - } - pArray->aBtree[i] = pBtree; - pArray->nMutex++; - return; - } - } - pArray->aBtree[pArray->nMutex++] = pBtree; -} - /* -** Enter the mutex of every btree in the array. This routine is -** called at the beginning of sqlite3VdbeExec(). The mutexes are -** exited at the end of the same function. +** Return true if the correct mutexes are held for accessing the +** db->aDb[iDb].pSchema structure. The mutexes required for schema +** access are: +** +** (1) The mutex on db +** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt. +** +** If pSchema is not NULL, then iDb is computed from pSchema and +** db using sqlite3SchemaToIndex(). */ -void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){ - int i; - for(i=0; inMutex; i++){ - Btree *p = pArray->aBtree[i]; - /* Some basic sanity checking */ - assert( i==0 || pArray->aBtree[i-1]->pBtpBt ); - assert( !p->locked || p->wantToLock>0 ); - - /* We should already hold a lock on the database connection */ - assert( sqlite3_mutex_held(p->db->mutex) ); - - /* The Btree is sharable because only sharable Btrees are entered - ** into the array in the first place. */ - assert( p->sharable ); - - p->wantToLock++; - if( !p->locked ){ - lockBtreeMutex(p); - } - } +int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){ + Btree *p; + assert( db!=0 ); + if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema); + assert( iDb>=0 && iDbnDb ); + if( !sqlite3_mutex_held(db->mutex) ) return 0; + if( iDb==1 ) return 1; + p = db->aDb[iDb].pBt; + assert( p!=0 ); + return p->sharable==0 || p->locked==1; } +#endif /* NDEBUG */ +#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ /* -** Leave the mutex of every btree in the group. +** The following are special cases for mutex enter routines for use +** in single threaded applications that use shared cache. Except for +** these two routines, all mutex operations are no-ops in that case and +** are null #defines in btree.h. +** +** If shared cache is disabled, then all btree mutex routines, including +** the ones below, are no-ops and are null #defines in btree.h. */ -void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){ - int i; - for(i=0; inMutex; i++){ - Btree *p = pArray->aBtree[i]; - /* Some basic sanity checking */ - assert( i==0 || pArray->aBtree[i-1]->pBtpBt ); - assert( p->locked ); - assert( p->wantToLock>0 ); - /* We should already hold a lock on the database connection */ - assert( sqlite3_mutex_held(p->db->mutex) ); - - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } -} - -#else void sqlite3BtreeEnter(Btree *p){ p->pBt->db = p->db; } diff --git a/src/btree.c b/src/btree.c index aa12015..103a1f3 100644 --- a/src/btree.c +++ b/src/btree.c @@ -1228,7 +1228,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ */ top -= nByte; put2byte(&data[hdr+5], top); - assert( top+nByte <= pPage->pBt->usableSize ); + assert( top+nByte <= (int)pPage->pBt->usableSize ); *pIdx = top; return SQLITE_OK; } @@ -1249,7 +1249,7 @@ static int freeSpace(MemPage *pPage, int start, int size){ assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( start>=pPage->hdrOffset+6+pPage->childPtrSize ); - assert( (start + size)<=pPage->pBt->usableSize ); + assert( (start + size) <= (int)pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( size>=0 ); /* Minimum cell size is 4 */ @@ -1292,7 +1292,7 @@ static int freeSpace(MemPage *pPage, int start, int size){ while( (pbegin = get2byte(&data[addr]))>0 ){ int pnext, psize, x; assert( pbegin>addr ); - assert( pbegin<=pPage->pBt->usableSize-4 ); + assert( pbegin <= (int)pPage->pBt->usableSize-4 ); pnext = get2byte(&data[pbegin]); psize = get2byte(&data[pbegin+2]); if( pbegin + psize + 3 >= pnext && pnext>0 ){ @@ -2127,7 +2127,6 @@ int sqlite3BtreeSyncDisabled(Btree *p){ return rc; } -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Change the default pages size and the number of reserved bytes per page. ** Or, if the page size has already been fixed, return SQLITE_READONLY @@ -2182,6 +2181,7 @@ int sqlite3BtreeGetPageSize(Btree *p){ return p->pBt->pageSize; } +#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Return the number of bytes of space at the end of every page that ** are intentually left unused. This is the "reserved" space that is @@ -2381,7 +2381,7 @@ static int lockBtree(BtShared *pBt){ pageSize-usableSize); return rc; } - if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPageHeader>nPageFile ){ + if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPage>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } @@ -3160,10 +3160,21 @@ static void btreeEndTransaction(Btree *p){ ** the rollback journal (which causes the transaction to commit) and ** drop locks. ** +** Normally, if an error occurs while the pager layer is attempting to +** finalize the underlying journal file, this function returns an error and +** the upper layer will attempt a rollback. However, if the second argument +** is non-zero then this b-tree transaction is part of a multi-file +** transaction. In this case, the transaction has already been committed +** (by deleting a master journal file) and the caller will ignore this +** functions return code. So, even if an error occurs in the pager layer, +** reset the b-tree objects internal state to indicate that the write +** transaction has been closed. This is quite safe, as the pager will have +** transitioned to the error state. +** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ -int sqlite3BtreeCommitPhaseTwo(Btree *p){ +int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ if( p->inTrans==TRANS_NONE ) return SQLITE_OK; sqlite3BtreeEnter(p); @@ -3178,7 +3189,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){ assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->nTransaction>0 ); rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); - if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_OK && bCleanup==0 ){ sqlite3BtreeLeave(p); return rc; } @@ -3198,7 +3209,7 @@ int sqlite3BtreeCommit(Btree *p){ sqlite3BtreeEnter(p); rc = sqlite3BtreeCommitPhaseOne(p, 0); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeCommitPhaseTwo(p); + rc = sqlite3BtreeCommitPhaseTwo(p, 0); } sqlite3BtreeLeave(p); return rc; @@ -4808,7 +4819,7 @@ static int allocateBtreePage( goto end_allocate_page; } - k = get4byte(&pTrunk->aData[4]); + k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */ if( k==0 && !searchList ){ /* The trunk has no leaves and the list is not being searched. ** So extract the trunk page itself and use it as the newly @@ -4893,19 +4904,13 @@ static int allocateBtreePage( u32 closest; Pgno iPage; unsigned char *aData = pTrunk->aData; - rc = sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } if( nearby>0 ){ u32 i; int dist; closest = 0; - dist = get4byte(&aData[8]) - nearby; - if( dist<0 ) dist = -dist; + dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby); for(i=1; ipgno, n-1)); + rc = sqlite3PagerWrite(pTrunk->pDbPage); + if( rc ) goto end_allocate_page; if( closestpDbPage) ); noContent = !btreeGetHasContent(pBt, *pPgno); rc = btreeGetPage(pBt, *pPgno, ppPage, noContent); if( rc==SQLITE_OK ){ @@ -5001,6 +5007,7 @@ end_allocate_page: }else{ *ppPage = 0; } + assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) ); return rc; } @@ -5490,7 +5497,7 @@ static void insertCell( /* The allocateSpace() routine guarantees the following two properties ** if it returns success */ assert( idx >= end+2 ); - assert( idx+sz <= pPage->pBt->usableSize ); + assert( idx+sz <= (int)pPage->pBt->usableSize ); pPage->nCell++; pPage->nFree -= (u16)(2 + sz); memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); @@ -5533,7 +5540,8 @@ static void assemblePage( assert( pPage->nOverflow==0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921); + assert( nCell>=0 && nCell<=(int)MX_CELL(pPage->pBt) + && (int)MX_CELL(pPage->pBt)<=10921); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); /* Check that the page has just been zeroed by zeroPage() */ @@ -5747,7 +5755,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ assert( pFrom->isInit ); assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); + assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize ); /* Copy the b-tree node content from page pFrom to page pTo. */ iData = get2byte(&aFrom[iFromHdr+5]); @@ -6014,7 +6022,7 @@ static int balance_nonroot( pTemp = &aSpace1[iSpace1]; iSpace1 += sz; assert( sz<=pBt->maxLocal+23 ); - assert( iSpace1<=pBt->pageSize ); + assert( iSpace1 <= (int)pBt->pageSize ); memcpy(pTemp, apDiv[i], sz); apCell[nCell] = pTemp+leafCorrection; assert( leafCorrection==0 || leafCorrection==4 ); @@ -6179,9 +6187,7 @@ static int balance_nonroot( } } if( minI>i ){ - int t; MemPage *pT; - t = apNew[i]->pgno; pT = apNew[i]; apNew[i] = apNew[minI]; apNew[minI] = pT; @@ -6260,7 +6266,7 @@ static int balance_nonroot( } iOvflSpace += sz; assert( sz<=pBt->maxLocal+23 ); - assert( iOvflSpace<=pBt->pageSize ); + assert( iOvflSpace <= (int)pBt->pageSize ); insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc); if( rc!=SQLITE_OK ) goto balance_cleanup; assert( sqlite3PagerIswriteable(pParent->pDbPage) ); @@ -6705,7 +6711,7 @@ int sqlite3BtreeInsert( rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew); if( rc ) goto end_insert; assert( szNew==cellSizePtr(pPage, newCell) ); - assert( szNew<=MX_CELL_SIZE(pBt) ); + assert( szNew <= MX_CELL_SIZE(pBt) ); idx = pCur->aiIdx[pCur->iPage]; if( loc==0 ){ u16 szOld; @@ -6845,7 +6851,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){ pCell = findCell(pLeaf, pLeaf->nCell-1); nCell = cellSizePtr(pLeaf, pCell); - assert( MX_CELL_SIZE(pBt)>=nCell ); + assert( MX_CELL_SIZE(pBt) >= nCell ); allocateTempSpace(pBt); pTmp = pBt->pTmpSpace; @@ -7932,8 +7938,10 @@ int sqlite3BtreeIsInTrans(Btree *p){ ** ** Return SQLITE_LOCKED if this or any other connection has an open ** transaction on the shared-cache the argument Btree is connected to. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ -int sqlite3BtreeCheckpoint(Btree *p){ +int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; if( p ){ BtShared *pBt = p->pBt; @@ -7941,7 +7949,7 @@ int sqlite3BtreeCheckpoint(Btree *p){ if( pBt->inTransaction!=TRANS_NONE ){ rc = SQLITE_LOCKED; }else{ - rc = sqlite3PagerCheckpoint(pBt->pPager); + rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt); } sqlite3BtreeLeave(p); } @@ -7981,7 +7989,7 @@ int sqlite3BtreeIsInBackup(Btree *p){ ** ** Just before the shared-btree is closed, the function passed as the ** xFree argument when the memory allocation was made is invoked on the -** blob of allocated memory. This function should not call sqlite3_free() +** blob of allocated memory. The xFree function should not call sqlite3_free() ** on the memory, the btree layer does that. */ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ diff --git a/src/btree.h b/src/btree.h index 90fa7a2..c6f6aec 100644 --- a/src/btree.h +++ b/src/btree.h @@ -39,18 +39,6 @@ typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; -typedef struct BtreeMutexArray BtreeMutexArray; - -/* -** This structure records all of the Btrees that need to hold -** a mutex before we enter sqlite3VdbeExec(). The Btrees are -** are placed in aBtree[] in order of aBtree[]->pBt. That way, -** we can always lock and unlock them all quickly. -*/ -struct BtreeMutexArray { - int nMutex; - Btree *aBtree[SQLITE_MAX_ATTACHED+1]; -}; int sqlite3BtreeOpen( @@ -87,7 +75,7 @@ int sqlite3BtreeSetAutoVacuum(Btree *, int); int sqlite3BtreeGetAutoVacuum(Btree *); int sqlite3BtreeBeginTrans(Btree*,int); int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); -int sqlite3BtreeCommitPhaseTwo(Btree*); +int sqlite3BtreeCommitPhaseTwo(Btree*, int); int sqlite3BtreeCommit(Btree*); int sqlite3BtreeRollback(Btree*); int sqlite3BtreeBeginStmt(Btree*,int); @@ -207,7 +195,7 @@ void sqlite3BtreeCursorList(Btree*); #endif #ifndef SQLITE_OMIT_WAL - int sqlite3BtreeCheckpoint(Btree*); + int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); #endif /* @@ -224,30 +212,28 @@ void sqlite3BtreeCursorList(Btree*); #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE + int sqlite3BtreeSharable(Btree*); void sqlite3BtreeLeave(Btree*); void sqlite3BtreeEnterCursor(BtCursor*); void sqlite3BtreeLeaveCursor(BtCursor*); void sqlite3BtreeLeaveAll(sqlite3*); - void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); - void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); - void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); #ifndef NDEBUG /* These routines are used inside assert() statements only. */ int sqlite3BtreeHoldsMutex(Btree*); int sqlite3BtreeHoldsAllMutexes(sqlite3*); + int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); #endif #else +# define sqlite3BtreeSharable(X) 0 # define sqlite3BtreeLeave(X) # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) # define sqlite3BtreeLeaveAll(X) -# define sqlite3BtreeMutexArrayEnter(X) -# define sqlite3BtreeMutexArrayLeave(X) -# define sqlite3BtreeMutexArrayInsert(X,Y) # define sqlite3BtreeHoldsMutex(X) 1 # define sqlite3BtreeHoldsAllMutexes(X) 1 +# define sqlite3SchemaMutexHeld(X,Y,Z) 1 #endif diff --git a/src/btreeInt.h b/src/btreeInt.h index 0e71195..55469cf 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -218,7 +218,7 @@ /* The following value is the maximum cell size assuming a maximum page ** size give above. */ -#define MX_CELL_SIZE(pBt) (pBt->pageSize-8) +#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8)) /* The maximum number of cells on a single page of the database. This ** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself @@ -336,7 +336,7 @@ struct BtLock { ** All fields in this structure are accessed under sqlite3.mutex. ** The pBt pointer itself may not be changed while there exists cursors ** in the referenced BtShared that point back to this Btree since those -** cursors have to do go through this Btree to find their BtShared and +** cursors have to go through this Btree to find their BtShared and ** they often do so without holding sqlite3.mutex. */ struct Btree { @@ -426,7 +426,7 @@ struct BtShared { u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ - sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ + sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ diff --git a/src/build.c b/src/build.c index 2cfb1f4..83a1db8 100644 --- a/src/build.c +++ b/src/build.c @@ -148,7 +148,7 @@ void sqlite3FinishCoding(Parse *pParse){ ** on each used database. */ if( pParse->cookieGoto>0 ){ - u32 mask; + yDbMask mask; int iDb; sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDbnDb; mask<<=1, iDb++){ @@ -156,7 +156,10 @@ void sqlite3FinishCoding(Parse *pParse){ sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0); if( db->init.busy==0 ){ - sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + sqlite3VdbeAddOp3(v, OP_VerifyCookie, + iDb, pParse->cookieValue[iDb], + db->aDb[iDb].pSchema->iGeneration); } } #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -269,9 +272,12 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){ int nName; assert( zName!=0 ); nName = sqlite3Strlen30(zName); + /* All mutexes are required for schema access. Make sure we hold them. */ + assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, nName); if( p ) break; } @@ -331,11 +337,14 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ Index *p = 0; int i; int nName = sqlite3Strlen30(zName); + /* All mutexes are required for schema access. Make sure we hold them. */ + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ Schema *pSchema = db->aDb[j].pSchema; assert( pSchema ); if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); p = sqlite3HashFind(&pSchema->idxHash, zName, nName); if( p ) break; } @@ -362,11 +371,13 @@ static void freeIndex(sqlite3 *db, Index *p){ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ Index *pIndex; int len; - Hash *pHash = &db->aDb[iDb].pSchema->idxHash; + Hash *pHash; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pHash = &db->aDb[iDb].pSchema->idxHash; len = sqlite3Strlen30(zIdxName); pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0); - if( pIndex ){ + if( ALWAYS(pIndex) ){ if( pIndex->pTable->pIndex==pIndex ){ pIndex->pTable->pIndex = pIndex->pNext; }else{ @@ -391,26 +402,42 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ ** if there were schema changes during the transaction or if a ** schema-cookie mismatch occurs. ** -** If iDb==0 then reset the internal schema tables for all database -** files. If iDb>=1 then reset the internal schema for only the +** If iDb<0 then reset the internal schema tables for all database +** files. If iDb>=0 then reset the internal schema for only the ** single file indicated. */ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ int i, j; - assert( iDb>=0 && iDbnDb ); + assert( iDbnDb ); - if( iDb==0 ){ - sqlite3BtreeEnterAll(db); + if( iDb>=0 ){ + /* Case 1: Reset the single schema identified by iDb */ + Db *pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + assert( pDb->pSchema!=0 ); + sqlite3SchemaClear(pDb->pSchema); + + /* If any database other than TEMP is reset, then also reset TEMP + ** since TEMP might be holding triggers that reference tables in the + ** other database. + */ + if( iDb!=1 ){ + pDb = &db->aDb[1]; + assert( pDb->pSchema!=0 ); + sqlite3SchemaClear(pDb->pSchema); + } + return; } - for(i=iDb; inDb; i++){ + /* Case 2 (from here to the end): Reset all schemas for all attached + ** databases. */ + assert( iDb<0 ); + sqlite3BtreeEnterAll(db); + for(i=0; inDb; i++){ Db *pDb = &db->aDb[i]; if( pDb->pSchema ){ - assert(i==1 || (pDb->pBt && sqlite3BtreeHoldsMutex(pDb->pBt))); - sqlite3SchemaFree(pDb->pSchema); + sqlite3SchemaClear(pDb->pSchema); } - if( iDb>0 ) return; } - assert( iDb==0 ); db->flags &= ~SQLITE_InternChanges; sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); @@ -496,6 +523,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ TESTONLY ( Index *pOld = ) sqlite3HashInsert( &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0 ); + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); assert( pOld==pIndex || pOld==0 ); } freeIndex(db, pIndex); @@ -530,6 +558,7 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ assert( db!=0 ); assert( iDb>=0 && iDbnDb ); assert( zTabName ); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ pDb = &db->aDb[iDb]; p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, @@ -784,6 +813,9 @@ void sqlite3StartTable( if( pTable ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "table %T already exists", pName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto begin_table_error; } @@ -814,6 +846,7 @@ void sqlite3StartTable( */ #ifndef SQLITE_OMIT_AUTOINCREMENT if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTable->pSchema->pSeqTab = pTable; } #endif @@ -1274,6 +1307,7 @@ void sqlite3ChangeCookie(Parse *pParse, int iDb){ int r1 = sqlite3GetTempReg(pParse); sqlite3 *db = pParse->db; Vdbe *v = pParse->pVdbe; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1); sqlite3ReleaseTempReg(pParse, r1); @@ -1381,7 +1415,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){ zSep = zSep2; identPut(zStmt, &k, pCol->zName); assert( pCol->affinity-SQLITE_AFF_TEXT >= 0 ); - assert( pCol->affinity-SQLITE_AFF_TEXT < sizeof(azType)/sizeof(azType[0]) ); + assert( pCol->affinity-SQLITE_AFF_TEXT < ArraySize(azType) ); testcase( pCol->affinity==SQLITE_AFF_TEXT ); testcase( pCol->affinity==SQLITE_AFF_NONE ); testcase( pCol->affinity==SQLITE_AFF_NUMERIC ); @@ -1576,6 +1610,7 @@ void sqlite3EndTable( */ if( p->tabFlags & TF_Autoincrement ){ Db *pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->pSeqTab==0 ){ sqlite3NestedParse(pParse, "CREATE TABLE %Q.sqlite_sequence(name,seq)", @@ -1596,6 +1631,7 @@ void sqlite3EndTable( if( db->init.busy ){ Table *pOld; Schema *pSchema = p->pSchema; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, sqlite3Strlen30(p->zName),p); if( pOld ){ @@ -1780,6 +1816,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ pSelTab->nCol = 0; pSelTab->aCol = 0; sqlite3DeleteTable(db, pSelTab); + assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); pTable->pSchema->flags |= DB_UnresetViews; }else{ pTable->nCol = 0; @@ -1800,6 +1837,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ */ static void sqliteViewResetAll(sqlite3 *db, int idx){ HashElem *i; + assert( sqlite3SchemaMutexHeld(db, idx, 0) ); if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); @@ -1833,10 +1871,13 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ ** in order to be certain that we got the right one. */ #ifndef SQLITE_OMIT_AUTOVACUUM -void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){ +void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){ HashElem *pElem; Hash *pHash; + Db *pDb; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pDb = &db->aDb[iDb]; pHash = &pDb->pSchema->tblHash; for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){ Table *pTab = sqliteHashData(pElem); @@ -1962,6 +2003,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ if( noErr ) db->suppressErr--; if( pTab==0 ){ + if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); goto exit_drop_table; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -2210,6 +2252,7 @@ void sqlite3CreateForeignKey( pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */ pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */ + assert( sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash, pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey ); @@ -2479,6 +2522,9 @@ Index *sqlite3CreateIndex( if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){ if( !ifNotExist ){ sqlite3ErrorMsg(pParse, "index %s already exists", zName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto exit_create_index; } @@ -2565,6 +2611,7 @@ Index *sqlite3CreateIndex( pIndex->onError = (u8)onError; pIndex->autoIndex = (u8)(pName==0); pIndex->pSchema = db->aDb[iDb].pSchema; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); /* Check to see if we should honor DESC requests on index columns */ @@ -2694,6 +2741,7 @@ Index *sqlite3CreateIndex( */ if( db->init.busy ){ Index *p; + assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, sqlite3Strlen30(pIndex->zName), pIndex); @@ -2870,6 +2918,8 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ if( pIndex==0 ){ if( !ifExists ){ sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); } pParse->checkSchema = 1; goto exit_drop_index; @@ -3442,12 +3492,13 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ } if( iDb>=0 ){ sqlite3 *db = pToplevel->db; - int mask; + yDbMask mask; assert( iDbnDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDbcookieMask & mask)==0 ){ pToplevel->cookieMask |= mask; pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; @@ -3458,6 +3509,21 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ } } +/* +** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each +** attached database. Otherwise, invoke it for the database named zDb only. +*/ +void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){ + sqlite3 *db = pParse->db; + int i; + for(i=0; inDb; i++){ + Db *pDb = &db->aDb[i]; + if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){ + sqlite3CodeVerifySchema(pParse, i); + } + } +} + /* ** Generate VDBE code that prepares for doing an operation that ** might change the database. @@ -3474,7 +3540,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); - pToplevel->writeMask |= 1<writeMask |= ((yDbMask)1)<isMultiWrite |= setStatement; } @@ -3574,6 +3640,7 @@ static void reindexDatabases(Parse *pParse, char const *zColl){ HashElem *k; /* For looping over tables in pDb */ Table *pTab; /* A table in the database */ + assert( sqlite3BtreeHoldsAllMutexes(db) ); /* Needed for schema access */ for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ assert( pDb!=0 ); for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ diff --git a/src/callback.c b/src/callback.c index eaff6d0..ce84908 100644 --- a/src/callback.c +++ b/src/callback.c @@ -400,12 +400,12 @@ FuncDef *sqlite3FindFunction( /* ** Free all resources held by the schema structure. The void* argument points ** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the -** pointer itself, it just cleans up subsiduary resources (i.e. the contents +** pointer itself, it just cleans up subsidiary resources (i.e. the contents ** of the schema hash tables). ** ** The Schema.cache_size variable is not cleared. */ -void sqlite3SchemaFree(void *p){ +void sqlite3SchemaClear(void *p){ Hash temp1; Hash temp2; HashElem *pElem; @@ -427,7 +427,10 @@ void sqlite3SchemaFree(void *p){ sqlite3HashClear(&temp1); sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; - pSchema->flags &= ~DB_SchemaLoaded; + if( pSchema->flags & DB_SchemaLoaded ){ + pSchema->iGeneration++; + pSchema->flags &= ~DB_SchemaLoaded; + } } /* @@ -437,7 +440,7 @@ void sqlite3SchemaFree(void *p){ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ Schema * p; if( pBt ){ - p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaFree); + p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear); }else{ p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema)); } diff --git a/src/delete.c b/src/delete.c index bd7ac3d..e5389e2 100644 --- a/src/delete.c +++ b/src/delete.c @@ -15,9 +15,18 @@ #include "sqliteInt.h" /* -** Look up every table that is named in pSrc. If any table is not found, -** add an error message to pParse->zErrMsg and return NULL. If all tables -** are found, return a pointer to the last table. +** While a SrcList can in general represent multiple tables and subqueries +** (as in the FROM clause of a SELECT statement) in this case it contains +** the name of a single table, as one might find in an INSERT, DELETE, +** or UPDATE statement. Look up that table in the symbol table and +** return a pointer. Set an error message and return NULL if the table +** name is not found or if any other error occurs. +** +** The following fields are initialized appropriate in pSrc: +** +** pSrc->a[0].pTab Pointer to the Table object +** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one +** */ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ struct SrcList_item *pItem = pSrc->a; @@ -536,7 +545,7 @@ void sqlite3GenerateRowDelete( sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); if( count ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } } @@ -627,7 +636,7 @@ int sqlite3GenerateIndexKey( } if( doMakeRec ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT); } sqlite3ReleaseTempRange(pParse, regBase, nCol+1); return regBase; diff --git a/src/expr.c b/src/expr.c index b902f45..c0e9ba6 100644 --- a/src/expr.c +++ b/src/expr.c @@ -92,7 +92,7 @@ Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr *pExpr, Token *pCollName){ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ CollSeq *pColl = 0; Expr *p = pExpr; - while( ALWAYS(p) ){ + while( p ){ int op; pColl = p->pColl; if( pColl ) break; @@ -389,6 +389,7 @@ Expr *sqlite3ExprAlloc( if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ nExtra = pToken->n+1; + assert( iValue>=0 ); } } pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); @@ -614,6 +615,8 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ */ void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p==0 ) return; + /* Sanity check: Assert that the IntValue is non-negative if it exists */ + assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ sqlite3ExprDelete(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); @@ -1198,16 +1201,17 @@ int sqlite3ExprIsConstantOrFunction(Expr *p){ */ int sqlite3ExprIsInteger(Expr *p, int *pValue){ int rc = 0; + + /* If an expression is an integer literal that fits in a signed 32-bit + ** integer, then the EP_IntValue flag will have already been set */ + assert( p->op!=TK_INTEGER || (p->flags & EP_IntValue)!=0 + || sqlite3GetInt32(p->u.zToken, &rc)==0 ); + if( p->flags & EP_IntValue ){ *pValue = p->u.iValue; return 1; } switch( p->op ){ - case TK_INTEGER: { - rc = sqlite3GetInt32(p->u.zToken, pValue); - assert( rc==0 ); - break; - } case TK_UPLUS: { rc = sqlite3ExprIsInteger(p->pLeft, pValue); break; @@ -1222,13 +1226,6 @@ int sqlite3ExprIsInteger(Expr *p, int *pValue){ } default: break; } - if( rc ){ - assert( ExprHasAnyProperty(p, EP_Reduced|EP_TokenOnly) - || (p->flags2 & EP2_MallocedToken)==0 ); - p->op = TK_INTEGER; - p->flags |= EP_IntValue; - p->u.iValue = *pValue; - } return rc; } @@ -1953,6 +1950,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ Vdbe *v = pParse->pVdbe; if( pExpr->flags & EP_IntValue ){ int i = pExpr->u.iValue; + assert( i>=0 ); if( negFlag ) i = -i; sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); }else{ @@ -1963,7 +1961,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); if( c==0 || (c==2 && negFlag) ){ char *zV; - if( negFlag ){ value = -value; } + if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; } zV = dup8bytes(v, (char*)&value); sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); }else{ @@ -2347,7 +2345,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ assert( pExpr->u.zToken[0]!=0 ); sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); if( pExpr->u.zToken[1]!=0 ){ - sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, 0); + sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, P4_TRANSIENT); } break; } @@ -3251,6 +3249,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull); break; } +#ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { int destIfFalse = sqlite3VdbeMakeLabel(v); int destIfNull = jumpIfNull ? dest : destIfFalse; @@ -3259,6 +3258,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ sqlite3VdbeResolveLabel(v, destIfFalse); break; } +#endif default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); @@ -3392,6 +3392,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull); break; } +#ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { if( jumpIfNull ){ sqlite3ExprCodeIN(pParse, pExpr, dest, dest); @@ -3402,6 +3403,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ } break; } +#endif default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); diff --git a/src/fkey.c b/src/fkey.c index a385b81..34fdfda 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -398,7 +398,7 @@ static void fkLookupParent( } sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); sqlite3ReleaseTempReg(pParse, regRec); @@ -687,7 +687,6 @@ void sqlite3FkCheck( int regNew /* New row data is stored here */ ){ sqlite3 *db = pParse->db; /* Database handle */ - Vdbe *v; /* VM to write code to */ FKey *pFKey; /* Used to iterate through FKs */ int iDb; /* Index of database containing pTab */ const char *zDb; /* Name of database containing pTab */ @@ -699,7 +698,6 @@ void sqlite3FkCheck( /* If foreign-keys are disabled, this function is a no-op. */ if( (db->flags&SQLITE_ForeignKeys)==0 ) return; - v = sqlite3GetVdbe(pParse); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zName; @@ -1156,6 +1154,7 @@ void sqlite3FkDelete(sqlite3 *db, Table *pTab){ FKey *pFKey; /* Iterator variable */ FKey *pNext; /* Copy of pFKey->pNextFrom */ + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ /* Remove the FK from the fkeyHash hash table. */ diff --git a/src/func.c b/src/func.c index 19c6d22..6a4f7c0 100644 --- a/src/func.c +++ b/src/func.c @@ -1239,13 +1239,8 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ if( type==SQLITE_INTEGER ){ i64 v = sqlite3_value_int64(argv[0]); p->rSum += v; - if( (p->approx|p->overflow)==0 ){ - i64 iNewSum = p->iSum + v; - int s1 = (int)(p->iSum >> (sizeof(i64)*8-1)); - int s2 = (int)(v >> (sizeof(i64)*8-1)); - int s3 = (int)(iNewSum >> (sizeof(i64)*8-1)); - p->overflow = ((s1&s2&~s3) | (~s1&~s2&s3))?1:0; - p->iSum = iNewSum; + if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ + p->overflow = 1; } }else{ p->rSum += sqlite3_value_double(argv[0]); diff --git a/src/insert.c b/src/insert.c index adf6ef2..588a84f 100644 --- a/src/insert.c +++ b/src/insert.c @@ -123,7 +123,7 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ pTab->zColAff = zColAff; } - sqlite3VdbeChangeP4(v, -1, pTab->zColAff, 0); + sqlite3VdbeChangeP4(v, -1, pTab->zColAff, P4_TRANSIENT); } /* @@ -237,6 +237,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){ for(p = pParse->pAinc; p; p = p->pNext){ pDb = &db->aDb[p->iDb]; memId = p->regCtr; + assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); @@ -287,6 +288,7 @@ void sqlite3AutoincrementEnd(Parse *pParse){ int memId = p->regCtr; iRec = sqlite3GetTempReg(pParse); + assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); j2 = sqlite3VdbeAddOp0(v, OP_Rewind); @@ -465,7 +467,6 @@ void sqlite3Insert( int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ - int regRecord; /* Holds the assemblied row record */ int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ @@ -794,7 +795,6 @@ void sqlite3Insert( /* Allocate registers for holding the rowid of the new row, ** the content of the new row, and the assemblied row record. */ - regRecord = ++pParse->nMem; regRowid = regIns = pParse->nMem+1; pParse->nMem += pTab->nCol + 1; if( IsVirtual(pTab) ){ @@ -1188,7 +1188,7 @@ void sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Fail: { char *zMsg; - j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull, + sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT, onError, regData+i); zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", pTab->zName, pTab->aCol[i].zName); @@ -1328,7 +1328,7 @@ void sqlite3GenerateConstraintChecks( } sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); /* Find out what action to take in case there is an indexing conflict */ @@ -1468,7 +1468,7 @@ void sqlite3CompleteInsertion( } sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid); if( !pParse->nested ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } sqlite3VdbeChangeP5(v, pik_flags); } diff --git a/src/loadext.c b/src/loadext.c index da2eec8..c832e68 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -71,6 +71,11 @@ # define sqlite3_complete16 0 #endif +#ifdef SQLITE_OMIT_DECLTYPE +# define sqlite3_column_decltype16 0 +# define sqlite3_column_decltype 0 +#endif + #ifdef SQLITE_OMIT_PROGRESS_CALLBACK # define sqlite3_progress_handler 0 #endif diff --git a/src/main.c b/src/main.c index 88dfcf0..4aaa618 100644 --- a/src/main.c +++ b/src/main.c @@ -375,6 +375,13 @@ int sqlite3_config(int op, ...){ sqlite3GlobalConfig.nHeap = va_arg(ap, int); sqlite3GlobalConfig.mnReq = va_arg(ap, int); + if( sqlite3GlobalConfig.mnReq<1 ){ + sqlite3GlobalConfig.mnReq = 1; + }else if( sqlite3GlobalConfig.mnReq>(1<<12) ){ + /* cap min request size at 2^12 */ + sqlite3GlobalConfig.mnReq = (1<<12); + } + if( sqlite3GlobalConfig.pHeap==0 ){ /* If the heap pointer is NULL, then restore the malloc implementation ** back to NULL pointers too. This will cause the malloc to go @@ -508,14 +515,42 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){ va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_LOOKASIDE: { - void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */ + void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */ int sz = va_arg(ap, int); /* IMP: R-47871-25994 */ int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */ rc = setupLookaside(db, pBuf, sz, cnt); break; } default: { + static const struct { + int op; /* The opcode */ + u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ + } aFlagOp[] = { + { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, + { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, + }; + unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ + for(i=0; iflags; + if( onoff>0 ){ + db->flags |= aFlagOp[i].mask; + }else if( onoff==0 ){ + db->flags &= ~aFlagOp[i].mask; + } + if( oldFlags!=db->flags ){ + sqlite3ExpirePreparedStatements(db); + } + if( pRes ){ + *pRes = (db->flags & aFlagOp[i].mask)!=0; + } + rc = SQLITE_OK; + break; + } + } break; } } @@ -652,7 +687,8 @@ int sqlite3_close(sqlite3 *db){ } sqlite3_mutex_enter(db->mutex); - sqlite3ResetInternalSchema(db, 0); + /* Force xDestroy calls on all virtual tables */ + sqlite3ResetInternalSchema(db, -1); /* If a transaction is open, the ResetInternalSchema() call above ** will not have called the xDisconnect() method on any virtual @@ -695,7 +731,7 @@ int sqlite3_close(sqlite3 *db){ } } } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); /* Tell the code in notify.c that the connection no longer holds any ** locks and does not require any further unlock-notify callbacks. @@ -786,7 +822,7 @@ void sqlite3RollbackAll(sqlite3 *db){ if( db->flags&SQLITE_InternChanges ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } /* Any deferred constraint violations have now been resolved. */ @@ -855,7 +891,7 @@ static int sqliteDefaultBusyCallback( { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; static const u8 totals[] = { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; -# define NDELAY (sizeof(delays)/sizeof(delays[0])) +# define NDELAY ArraySize(delays) sqlite3 *db = (sqlite3 *)ptr; int timeout = db->busyTimeout; int delay, prior; @@ -1340,19 +1376,33 @@ void *sqlite3_wal_hook( #endif } - /* -** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points -** to contains a zero-length string, all attached databases are -** checkpointed. +** Checkpoint database zDb. */ -int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ +int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +){ #ifdef SQLITE_OMIT_WAL return SQLITE_OK; #else int rc; /* Return code */ int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ + /* Initialize the output variables to -1 in case an error occurs. */ + if( pnLog ) *pnLog = -1; + if( pnCkpt ) *pnCkpt = -1; + + assert( SQLITE_CHECKPOINT_FULL>SQLITE_CHECKPOINT_PASSIVE ); + assert( SQLITE_CHECKPOINT_FULLSQLITE_CHECKPOINT_RESTART ){ + return SQLITE_MISUSE; + } + sqlite3_mutex_enter(db->mutex); if( zDb && zDb[0] ){ iDb = sqlite3FindDbName(db, zDb); @@ -1361,7 +1411,7 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb); }else{ - rc = sqlite3Checkpoint(db, iDb); + rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); sqlite3Error(db, rc, 0); } rc = sqlite3ApiExit(db, rc); @@ -1370,6 +1420,16 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ #endif } + +/* +** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points +** to contains a zero-length string, all attached databases are +** checkpointed. +*/ +int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ + return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0); +} + #ifndef SQLITE_OMIT_WAL /* ** Run a checkpoint on database iDb. This is a no-op if database iDb is @@ -1387,20 +1447,31 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ -int sqlite3Checkpoint(sqlite3 *db, int iDb){ +int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; /* Return code */ int i; /* Used to iterate through attached dbs */ + int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ assert( sqlite3_mutex_held(db->mutex) ); + assert( !pnLog || *pnLog==-1 ); + assert( !pnCkpt || *pnCkpt==-1 ); for(i=0; inDb && rc==SQLITE_OK; i++){ if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ - rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt); + rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); + pnLog = 0; + pnCkpt = 0; + if( rc==SQLITE_BUSY ){ + bBusy = 1; + rc = SQLITE_OK; + } } } - return rc; + return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc; } #endif /* SQLITE_OMIT_WAL */ @@ -1653,8 +1724,8 @@ static const int aHardLimit[] = { #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000 # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000 #endif -#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>30 -# error SQLITE_MAX_ATTACHED must be between 0 and 30 +#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62 +# error SQLITE_MAX_ATTACHED must be between 0 and 62 #endif #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 # error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 @@ -1773,7 +1844,8 @@ static int openDatabase( ** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were ** dealt with in the previous code block. Besides these, the only ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, - ** SQLITE_OPEN_READWRITE, and SQLITE_OPEN_CREATE. Silently mask + ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, + ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask ** off all other flags. */ flags &= ~( SQLITE_OPEN_DELETEONCLOSE | @@ -1812,7 +1884,7 @@ static int openDatabase( db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; - db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex + db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif diff --git a/src/malloc.c b/src/malloc.c index a3a9e0f..50fdf52 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -404,7 +404,7 @@ void sqlite3ScratchFree(void *p){ pSlot->pNext = mem0.pScratchFree; mem0.pScratchFree = pSlot; mem0.nScratchFree++; - assert( mem0.nScratchFree<=sqlite3GlobalConfig.nScratch ); + assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch ); sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); sqlite3_mutex_leave(mem0.mutex); }else{ diff --git a/src/mem5.c b/src/mem5.c index 2fdfac1..783cef6 100644 --- a/src/mem5.c +++ b/src/mem5.c @@ -127,7 +127,7 @@ static SQLITE_WSD struct Mem5Global { */ u8 *aCtrl; -} mem5 = { 0 }; +} mem5; /* ** Access the static variable through a macro for SQLITE_OMIT_WSD @@ -442,7 +442,7 @@ static int memsys5Roundup(int n){ */ static int memsys5Log(int iValue){ int iLog; - for(iLog=0; (1<mem5.szAtom ){ diff --git a/src/mutex_os2.c b/src/mutex_os2.c index 4438c09..ce650d9 100644 --- a/src/mutex_os2.c +++ b/src/mutex_os2.c @@ -31,11 +31,16 @@ struct sqlite3_mutex { HMTX mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ - int nRef; /* Number of references */ - TID owner; /* Thread holding this mutex */ +#ifdef SQLITE_DEBUG + int trace; /* True to trace changes */ +#endif }; -#define OS2_MUTEX_INITIALIZER 0,0,0,0 +#ifdef SQLITE_DEBUG +#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 } +#else +#define SQLITE3_MUTEX_INITIALIZER { 0, 0 } +#endif /* ** Initialize and deinitialize the mutex subsystem. @@ -51,11 +56,14 @@ static int os2MutexEnd(void){ return SQLITE_OK; } ** to sqlite3_mutex_alloc() is one of these integer constants: ** **
    -**
  • SQLITE_MUTEX_FAST 0 -**
  • SQLITE_MUTEX_RECURSIVE 1 -**
  • SQLITE_MUTEX_STATIC_MASTER 2 -**
  • SQLITE_MUTEX_STATIC_MEM 3 -**
  • SQLITE_MUTEX_STATIC_PRNG 4 +**
  • SQLITE_MUTEX_FAST +**
  • SQLITE_MUTEX_RECURSIVE +**
  • SQLITE_MUTEX_STATIC_MASTER +**
  • SQLITE_MUTEX_STATIC_MEM +**
  • SQLITE_MUTEX_STATIC_MEM2 +**
  • SQLITE_MUTEX_STATIC_PRNG +**
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 **
** ** The first two constants cause sqlite3_mutex_alloc() to create @@ -69,7 +77,7 @@ static int os2MutexEnd(void){ return SQLITE_OK; } ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Three static mutexes are +** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should @@ -99,13 +107,13 @@ static sqlite3_mutex *os2MutexAlloc(int iType){ } default: { static volatile int isInit = 0; - static sqlite3_mutex staticMutexes[] = { - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, + static sqlite3_mutex staticMutexes[6] = { + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, }; if ( !isInit ){ APIRET rc; @@ -151,9 +159,14 @@ static sqlite3_mutex *os2MutexAlloc(int iType){ ** SQLite is careful to deallocate every mutex that it allocates. */ static void os2MutexFree(sqlite3_mutex *p){ - if( p==0 ) return; - assert( p->nRef==0 ); +#ifdef SQLITE_DEBUG + TID tid; + PID pid; + ULONG ulCount; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + assert( ulCount==0 ); assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); +#endif DosCloseMutexSem( p->mutex ); sqlite3_free( p ); } @@ -168,26 +181,29 @@ static int os2MutexHeld(sqlite3_mutex *p){ PID pid; ULONG ulCount; PTIB ptib; - if( p!=0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || (p->nRef!=0 && p->owner==tid); + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) ) + return 0; + DosGetInfoBlocks(&ptib, NULL); + return tid==ptib->tib_ptib2->tib2_ultid; } static int os2MutexNotheld(sqlite3_mutex *p){ TID tid; PID pid; ULONG ulCount; PTIB ptib; - if( p!= 0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || p->nRef==0 || p->owner!=tid; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + if( ulCount==0 ) + return 1; + DosGetInfoBlocks(&ptib, NULL); + return tid!=ptib->tib_ptib2->tib2_ultid; +} +static void os2MutexTrace(sqlite3_mutex *p, char *pAction){ + TID tid; + PID pid; + ULONG ulCount; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount); } #endif @@ -203,32 +219,21 @@ static int os2MutexNotheld(sqlite3_mutex *p){ ** more than once, the behavior is undefined. */ static void os2MutexEnter(sqlite3_mutex *p){ - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return; assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT); - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - p->owner = tid; - p->nRef++; +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "enter"); +#endif } static int os2MutexTry(sqlite3_mutex *p){ - int rc; - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return SQLITE_OK; + int rc = SQLITE_BUSY; assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); - if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR) { - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - p->owner = tid; - p->nRef++; + if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) { rc = SQLITE_OK; - } else { - rc = SQLITE_BUSY; +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "try"); +#endif } - return rc; } @@ -239,16 +244,11 @@ static int os2MutexTry(sqlite3_mutex *p){ ** is not currently allocated. SQLite will never do either. */ static void os2MutexLeave(sqlite3_mutex *p){ - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return; - assert( p->nRef>0 ); - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - assert( p->owner==tid ); - p->nRef--; - assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); + assert( os2MutexHeld(p) ); DosReleaseMutexSem(p->mutex); +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "leave"); +#endif } sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ @@ -263,6 +263,9 @@ sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ #ifdef SQLITE_DEBUG os2MutexHeld, os2MutexNotheld +#else + 0, + 0 #endif }; diff --git a/src/mutex_w32.c b/src/mutex_w32.c index 8e257a9..bfd9dac 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -280,7 +280,7 @@ static int winMutexTry(sqlite3_mutex *p){ #endif #ifdef SQLITE_DEBUG if( rc==SQLITE_OK && p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); + printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif return rc; diff --git a/src/os_os2.c b/src/os_os2.c index df5ad10..487ac3c 100644 --- a/src/os_os2.c +++ b/src/os_os2.c @@ -55,20 +55,35 @@ */ #include "os_common.h" +/* Forward references */ +typedef struct os2File os2File; /* The file structure */ +typedef struct os2ShmNode os2ShmNode; /* A shared descritive memory node */ +typedef struct os2ShmLink os2ShmLink; /* A connection to shared-memory */ + /* ** The os2File structure is subclass of sqlite3_file specific for the OS/2 ** protability layer. */ -typedef struct os2File os2File; struct os2File { const sqlite3_io_methods *pMethod; /* Always the first entry */ HFILE h; /* Handle for accessing the file */ - char* pathToDel; /* Name of file to delete on close, NULL if not */ - unsigned char locktype; /* Type of lock currently held on this file */ + int flags; /* Flags provided to os2Open() */ + int locktype; /* Type of lock currently held on this file */ + int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ + char *zFullPathCp; /* Full path name of this file */ + os2ShmLink *pShmLink; /* Instance of shared memory on this file */ }; #define LOCK_TIMEOUT 10L /* the default locking timeout */ +/* +** Missing from some versions of the OS/2 toolkit - +** used to allocate from high memory if possible +*/ +#ifndef OBJ_ANY +# define OBJ_ANY 0x00000400 +#endif + /***************************************************************************** ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. @@ -78,21 +93,24 @@ struct os2File { ** Close a file. */ static int os2Close( sqlite3_file *id ){ - APIRET rc = NO_ERROR; - os2File *pFile; - if( id && (pFile = (os2File*)id) != 0 ){ - OSTRACE(( "CLOSE %d\n", pFile->h )); - rc = DosClose( pFile->h ); - pFile->locktype = NO_LOCK; - if( pFile->pathToDel != NULL ){ - rc = DosForceDelete( (PSZ)pFile->pathToDel ); - free( pFile->pathToDel ); - pFile->pathToDel = NULL; - } - id = 0; - OpenCounter( -1 ); - } + APIRET rc; + os2File *pFile = (os2File*)id; + assert( id!=0 ); + OSTRACE(( "CLOSE %d (%s)\n", pFile->h, pFile->zFullPathCp )); + + rc = DosClose( pFile->h ); + + if( pFile->flags & SQLITE_OPEN_DELETEONCLOSE ) + DosForceDelete( (PSZ)pFile->zFullPathCp ); + + free( pFile->zFullPathCp ); + pFile->zFullPathCp = NULL; + pFile->locktype = NO_LOCK; + pFile->h = (HFILE)-1; + pFile->flags = 0; + + OpenCounter( -1 ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR; } @@ -165,10 +183,21 @@ static int os2Write( ** Truncate an open file to a specified size */ static int os2Truncate( sqlite3_file *id, i64 nByte ){ - APIRET rc = NO_ERROR; + APIRET rc; os2File *pFile = (os2File*)id; + assert( id!=0 ); OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte )); SimulateIOError( return SQLITE_IOERR_TRUNCATE ); + + /* If the user has configured a chunk-size for this file, truncate the + ** file so that it consists of an integer number of chunks (i.e. the + ** actual file size after the operation may be larger than the requested + ** size). + */ + if( pFile->szChunk ){ + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; + } + rc = DosSetFileSize( pFile->h, nByte ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; } @@ -532,6 +561,20 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ ((os2File*)id)->h, ((os2File*)id)->locktype )); return SQLITE_OK; } + case SQLITE_FCNTL_CHUNK_SIZE: { + ((os2File*)id)->szChunk = *(int*)pArg; + return SQLITE_OK; + } + case SQLITE_FCNTL_SIZE_HINT: { + sqlite3_int64 sz = *(sqlite3_int64*)pArg; + SimulateIOErrorBenign(1); + os2Truncate(id, sz); + SimulateIOErrorBenign(0); + return SQLITE_OK; + } + case SQLITE_FCNTL_SYNC_OMITTED: { + return SQLITE_OK; + } } return SQLITE_NOTFOUND; } @@ -547,6 +590,7 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ ** same for both. */ static int os2SectorSize(sqlite3_file *id){ + UNUSED_PARAMETER(id); return SQLITE_DEFAULT_SECTOR_SIZE; } @@ -554,7 +598,8 @@ static int os2SectorSize(sqlite3_file *id){ ** Return a vector of device characteristics. */ static int os2DeviceCharacteristics(sqlite3_file *id){ - return 0; + UNUSED_PARAMETER(id); + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } @@ -641,26 +686,682 @@ char *convertCpPathToUtf8( const char *in ){ return out; } + +#ifndef SQLITE_OMIT_WAL + +/* +** Use main database file for interprocess locking. If un-defined +** a separate file is created for this purpose. The file will be +** used only to set file locks. There will be no data written to it. +*/ +#define SQLITE_OS2_NO_WAL_LOCK_FILE + +#if 0 +static void _ERR_TRACE( const char *fmt, ... ) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fflush(stderr); +} +#define ERR_TRACE(rc, msg) \ + if( (rc) != SQLITE_OK ) _ERR_TRACE msg; +#else +#define ERR_TRACE(rc, msg) +#endif + +/* +** Helper functions to obtain and relinquish the global mutex. The +** global mutex is used to protect os2ShmNodeList. +** +** Function os2ShmMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() +** statements. e.g. +** +** os2ShmEnterMutex() +** assert( os2ShmMutexHeld() ); +** os2ShmLeaveMutex() +*/ +static void os2ShmEnterMutex(void){ + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +static void os2ShmLeaveMutex(void){ + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +#ifdef SQLITE_DEBUG +static int os2ShmMutexHeld(void) { + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +int GetCurrentProcessId(void) { + PPIB pib; + DosGetInfoBlocks(NULL, &pib); + return (int)pib->pib_ulpid; +} +#endif + +/* +** Object used to represent a the shared memory area for a single log file. +** When multiple threads all reference the same log-summary, each thread has +** its own os2File object, but they all point to a single instance of this +** object. In other words, each log-summary is opened only once per process. +** +** os2ShmMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: +** +** nRef +** pNext +** +** The following fields are read-only after the object is created: +** +** szRegion +** hLockFile +** shmBaseName +** +** Either os2ShmNode.mutex must be held or os2ShmNode.nRef==0 and +** os2ShmMutexHeld() is true when reading or writing any other field +** in this structure. +** +*/ +struct os2ShmNode { + sqlite3_mutex *mutex; /* Mutex to access this object */ + os2ShmNode *pNext; /* Next in list of all os2ShmNode objects */ + + int szRegion; /* Size of shared-memory regions */ + + int nRegion; /* Size of array apRegion */ + void **apRegion; /* Array of pointers to shared-memory regions */ + + int nRef; /* Number of os2ShmLink objects pointing to this */ + os2ShmLink *pFirst; /* First os2ShmLink object pointing to this */ + + HFILE hLockFile; /* File used for inter-process memory locking */ + char shmBaseName[1]; /* Name of the memory object !!! must last !!! */ +}; + + +/* +** Structure used internally by this VFS to record the state of an +** open shared memory connection. +** +** The following fields are initialized when this object is created and +** are read-only thereafter: +** +** os2Shm.pShmNode +** os2Shm.id +** +** All other fields are read/write. The os2Shm.pShmNode->mutex must be held +** while accessing any read/write fields. +*/ +struct os2ShmLink { + os2ShmNode *pShmNode; /* The underlying os2ShmNode object */ + os2ShmLink *pNext; /* Next os2Shm with the same os2ShmNode */ + u32 sharedMask; /* Mask of shared locks held */ + u32 exclMask; /* Mask of exclusive locks held */ +#ifdef SQLITE_DEBUG + u8 id; /* Id of this connection with its os2ShmNode */ +#endif +}; + + +/* +** A global list of all os2ShmNode objects. +** +** The os2ShmMutexHeld() must be true while reading or writing this list. +*/ +static os2ShmNode *os2ShmNodeList = NULL; + +/* +** Constants used for locking +*/ +#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE +#define OS2_SHM_BASE (PENDING_BYTE + 0x10000) /* first lock byte */ +#else +#define OS2_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ +#endif + +#define OS2_SHM_DMS (OS2_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ + +/* +** Apply advisory locks for all n bytes beginning at ofst. +*/ +#define _SHM_UNLCK 1 /* no lock */ +#define _SHM_RDLCK 2 /* shared lock, no wait */ +#define _SHM_WRLCK 3 /* exlusive lock, no wait */ +#define _SHM_WRLCK_WAIT 4 /* exclusive lock, wait */ +static int os2ShmSystemLock( + os2ShmNode *pNode, /* Apply locks to this open shared-memory segment */ + int lockType, /* _SHM_UNLCK, _SHM_RDLCK, _SHM_WRLCK or _SHM_WRLCK_WAIT */ + int ofst, /* Offset to first byte to be locked/unlocked */ + int nByte /* Number of bytes to lock or unlock */ +){ + APIRET rc; + FILELOCK area; + ULONG mode, timeout; + + /* Access to the os2ShmNode object is serialized by the caller */ + assert( sqlite3_mutex_held(pNode->mutex) || pNode->nRef==0 ); + + mode = 1; /* shared lock */ + timeout = 0; /* no wait */ + area.lOffset = ofst; + area.lRange = nByte; + + switch( lockType ) { + case _SHM_WRLCK_WAIT: + timeout = (ULONG)-1; /* wait forever */ + case _SHM_WRLCK: + mode = 0; /* exclusive lock */ + case _SHM_RDLCK: + rc = DosSetFileLocks(pNode->hLockFile, + NULL, &area, timeout, mode); + break; + /* case _SHM_UNLCK: */ + default: + rc = DosSetFileLocks(pNode->hLockFile, + &area, NULL, 0, 0); + break; + } + + OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n", + pNode->hLockFile, + rc==SQLITE_OK ? "ok" : "failed", + lockType==_SHM_UNLCK ? "Unlock" : "Lock", + rc)); + + ERR_TRACE(rc, ("os2ShmSystemLock: %d %s\n", rc, pNode->shmBaseName)) + + return ( rc == 0 ) ? SQLITE_OK : SQLITE_BUSY; +} + +/* +** Find an os2ShmNode in global list or allocate a new one, if not found. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static int os2OpenSharedMemory( os2File *fd, int szRegion ) { + os2ShmLink *pLink; + os2ShmNode *pNode; + int cbShmName, rc = SQLITE_OK; + char shmName[CCHMAXPATH + 30]; +#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE + ULONG action; +#endif + + /* We need some additional space at the end to append the region number */ + cbShmName = sprintf(shmName, "\\SHAREMEM\\%s", fd->zFullPathCp ); + if( cbShmName >= CCHMAXPATH-8 ) + return SQLITE_IOERR_SHMOPEN; + + /* Replace colon in file name to form a valid shared memory name */ + shmName[10+1] = '!'; + + /* Allocate link object (we free it later in case of failure) */ + pLink = sqlite3_malloc( sizeof(*pLink) ); + if( !pLink ) + return SQLITE_NOMEM; + + /* Access node list */ + os2ShmEnterMutex(); + + /* Find node by it's shared memory base name */ + for( pNode = os2ShmNodeList; + pNode && stricmp(shmName, pNode->shmBaseName) != 0; + pNode = pNode->pNext ) ; + + /* Not found: allocate a new node */ + if( !pNode ) { + pNode = sqlite3_malloc( sizeof(*pNode) + cbShmName ); + if( pNode ) { + memset(pNode, 0, sizeof(*pNode) ); + pNode->szRegion = szRegion; + pNode->hLockFile = (HFILE)-1; + strcpy(pNode->shmBaseName, shmName); + +#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE + if( DosDupHandle(fd->h, &pNode->hLockFile) != 0 ) { +#else + sprintf(shmName, "%s-lck", fd->zFullPathCp); + if( DosOpen((PSZ)shmName, &pNode->hLockFile, &action, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW, + OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | + OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR, + NULL) != 0 ) { +#endif + sqlite3_free(pNode); + rc = SQLITE_IOERR; + } else { + pNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( !pNode->mutex ) { + sqlite3_free(pNode); + rc = SQLITE_NOMEM; + } + } + } else { + rc = SQLITE_NOMEM; + } + + if( rc == SQLITE_OK ) { + pNode->pNext = os2ShmNodeList; + os2ShmNodeList = pNode; + } else { + pNode = NULL; + } + } else if( pNode->szRegion != szRegion ) { + rc = SQLITE_IOERR_SHMSIZE; + pNode = NULL; + } + + if( pNode ) { + sqlite3_mutex_enter(pNode->mutex); + + memset(pLink, 0, sizeof(*pLink)); + + pLink->pShmNode = pNode; + pLink->pNext = pNode->pFirst; + pNode->pFirst = pLink; + pNode->nRef++; + + fd->pShmLink = pLink; + + sqlite3_mutex_leave(pNode->mutex); + + } else { + /* Error occured. Free our link object. */ + sqlite3_free(pLink); + } + + os2ShmLeaveMutex(); + + ERR_TRACE(rc, ("os2OpenSharedMemory: %d %s\n", rc, fd->zFullPathCp)) + + return rc; +} + +/* +** Purge the os2ShmNodeList list of all entries with nRef==0. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static void os2PurgeShmNodes( int deleteFlag ) { + os2ShmNode *pNode; + os2ShmNode **ppNode; + + os2ShmEnterMutex(); + + ppNode = &os2ShmNodeList; + + while( *ppNode ) { + pNode = *ppNode; + + if( pNode->nRef == 0 ) { + *ppNode = pNode->pNext; + + if( pNode->apRegion ) { + /* Prevent other processes from resizing the shared memory */ + os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1); + + while( pNode->nRegion-- ) { +#ifdef SQLITE_DEBUG + int rc = +#endif + DosFreeMem(pNode->apRegion[pNode->nRegion]); + + OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", + (int)GetCurrentProcessId(), pNode->nRegion, + rc == 0 ? "ok" : "failed")); + } + + /* Allow other processes to resize the shared memory */ + os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1); + + sqlite3_free(pNode->apRegion); + } + + DosClose(pNode->hLockFile); + +#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE + if( deleteFlag ) { + char fileName[CCHMAXPATH]; + /* Skip "\\SHAREMEM\\" */ + sprintf(fileName, "%s-lck", pNode->shmBaseName + 10); + /* restore colon */ + fileName[1] = ':'; + + DosForceDelete(fileName); + } +#endif + + sqlite3_mutex_free(pNode->mutex); + + sqlite3_free(pNode); + + } else { + ppNode = &pNode->pNext; + } + } + + os2ShmLeaveMutex(); +} + +/* +** This function is called to obtain a pointer to region iRegion of the +** shared-memory associated with the database file id. Shared-memory regions +** are numbered starting from zero. Each shared-memory region is szRegion +** bytes in size. +** +** If an error occurs, an error code is returned and *pp is set to NULL. +** +** Otherwise, if the bExtend parameter is 0 and the requested shared-memory +** region has not been allocated (by any client, including one running in a +** separate process), then *pp is set to NULL and SQLITE_OK returned. If +** bExtend is non-zero and the requested shared-memory region has not yet +** been allocated, it is allocated by this function. +** +** If the shared-memory region has already been allocated or is allocated by +** this call as described above, then it is mapped into this processes +** address space (if it is not already), *pp is set to point to the mapped +** memory and SQLITE_OK returned. +*/ +static int os2ShmMap( + sqlite3_file *id, /* Handle open on database file */ + int iRegion, /* Region to retrieve */ + int szRegion, /* Size of regions */ + int bExtend, /* True to extend block if necessary */ + void volatile **pp /* OUT: Mapped memory */ +){ + PVOID pvTemp; + void **apRegion; + os2ShmNode *pNode; + int n, rc = SQLITE_OK; + char shmName[CCHMAXPATH]; + os2File *pFile = (os2File*)id; + + *pp = NULL; + + if( !pFile->pShmLink ) + rc = os2OpenSharedMemory( pFile, szRegion ); + + if( rc == SQLITE_OK ) { + pNode = pFile->pShmLink->pShmNode ; + + sqlite3_mutex_enter(pNode->mutex); + + assert( szRegion==pNode->szRegion ); + + /* Unmapped region ? */ + if( iRegion >= pNode->nRegion ) { + /* Prevent other processes from resizing the shared memory */ + os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1); + + apRegion = sqlite3_realloc( + pNode->apRegion, (iRegion + 1) * sizeof(apRegion[0])); + + if( apRegion ) { + pNode->apRegion = apRegion; + + while( pNode->nRegion <= iRegion ) { + sprintf(shmName, "%s-%u", + pNode->shmBaseName, pNode->nRegion); + + if( DosGetNamedSharedMem(&pvTemp, (PSZ)shmName, + PAG_READ | PAG_WRITE) != NO_ERROR ) { + if( !bExtend ) + break; + + if( DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion, + PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_ANY) != NO_ERROR && + DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion, + PAG_READ | PAG_WRITE | PAG_COMMIT) != NO_ERROR ) { + rc = SQLITE_NOMEM; + break; + } + } + + apRegion[pNode->nRegion++] = pvTemp; + } + + /* zero out remaining entries */ + for( n = pNode->nRegion; n <= iRegion; n++ ) + pNode->apRegion[n] = NULL; + + /* Return this region (maybe zero) */ + *pp = pNode->apRegion[iRegion]; + } else { + rc = SQLITE_NOMEM; + } + + /* Allow other processes to resize the shared memory */ + os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1); + + } else { + /* Region has been mapped previously */ + *pp = pNode->apRegion[iRegion]; + } + + sqlite3_mutex_leave(pNode->mutex); + } + + ERR_TRACE(rc, ("os2ShmMap: %s iRgn = %d, szRgn = %d, bExt = %d : %d\n", + pFile->zFullPathCp, iRegion, szRegion, bExtend, rc)) + + return rc; +} + +/* +** Close a connection to shared-memory. Delete the underlying +** storage if deleteFlag is true. +** +** If there is no shared memory associated with the connection then this +** routine is a harmless no-op. +*/ +static int os2ShmUnmap( + sqlite3_file *id, /* The underlying database file */ + int deleteFlag /* Delete shared-memory if true */ +){ + os2File *pFile = (os2File*)id; + os2ShmLink *pLink = pFile->pShmLink; + + if( pLink ) { + int nRef = -1; + os2ShmLink **ppLink; + os2ShmNode *pNode = pLink->pShmNode; + + sqlite3_mutex_enter(pNode->mutex); + + for( ppLink = &pNode->pFirst; + *ppLink && *ppLink != pLink; + ppLink = &(*ppLink)->pNext ) ; + + assert(*ppLink); + + if( *ppLink ) { + *ppLink = pLink->pNext; + nRef = --pNode->nRef; + } else { + ERR_TRACE(1, ("os2ShmUnmap: link not found ! %s\n", + pNode->shmBaseName)) + } + + pFile->pShmLink = NULL; + sqlite3_free(pLink); + + sqlite3_mutex_leave(pNode->mutex); + + if( nRef == 0 ) + os2PurgeShmNodes( deleteFlag ); + } + + return SQLITE_OK; +} + +/* +** Change the lock state for a shared-memory segment. +** +** Note that the relationship between SHAREd and EXCLUSIVE locks is a little +** different here than in posix. In xShmLock(), one can go from unlocked +** to shared and back or from unlocked to exclusive and back. But one may +** not go from shared to exclusive or from exclusive to shared. +*/ +static int os2ShmLock( + sqlite3_file *id, /* Database file holding the shared memory */ + int ofst, /* First lock to acquire or release */ + int n, /* Number of locks to acquire or release */ + int flags /* What to do with the lock */ +){ + u32 mask; /* Mask of locks to take or release */ + int rc = SQLITE_OK; /* Result code */ + os2File *pFile = (os2File*)id; + os2ShmLink *p = pFile->pShmLink; /* The shared memory being locked */ + os2ShmLink *pX; /* For looping over all siblings */ + os2ShmNode *pShmNode = p->pShmNode; /* Our node */ + + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); + assert( n>=1 ); + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + + mask = (u32)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex); + + if( flags & SQLITE_SHM_UNLOCK ){ + u32 allMask = 0; /* Mask of locks held by siblings */ + + /* See if any siblings hold this same lock */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); + allMask |= pX->sharedMask; + } + + /* Unlock the system-level locks */ + if( (mask & allMask)==0 ){ + rc = os2ShmSystemLock(pShmNode, _SHM_UNLCK, ofst+OS2_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } + + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~mask; + p->sharedMask &= ~mask; + } + }else if( flags & SQLITE_SHM_SHARED ){ + u32 allShared = 0; /* Union of locks held by connections other than "p" */ + + /* Find out which shared locks are already held by sibling connections. + ** If any sibling already holds an exclusive lock, go ahead and return + ** SQLITE_BUSY. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + allShared |= pX->sharedMask; + } + + /* Get shared locks at the system level, if necessary */ + if( rc==SQLITE_OK ){ + if( (allShared & mask)==0 ){ + rc = os2ShmSystemLock(pShmNode, _SHM_RDLCK, ofst+OS2_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } + } + + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= mask; + } + }else{ + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + } + + /* Get the exclusive locks at the system level. Then if successful + ** also mark the local connection as being locked. + */ + if( rc==SQLITE_OK ){ + rc = os2ShmSystemLock(pShmNode, _SHM_WRLCK, ofst+OS2_SHM_BASE, n); + if( rc==SQLITE_OK ){ + assert( (p->sharedMask & mask)==0 ); + p->exclMask |= mask; + } + } + } + + sqlite3_mutex_leave(pShmNode->mutex); + + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", + p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask, + rc ? "failed" : "ok")); + + ERR_TRACE(rc, ("os2ShmLock: ofst = %d, n = %d, flags = 0x%x -> %d \n", + ofst, n, flags, rc)) + + return rc; +} + +/* +** Implement a memory barrier or memory fence on shared memory. +** +** All loads and stores begun before the barrier must complete before +** any load or store begun after the barrier. +*/ +static void os2ShmBarrier( + sqlite3_file *id /* Database file holding the shared memory */ +){ + UNUSED_PARAMETER(id); + os2ShmEnterMutex(); + os2ShmLeaveMutex(); +} + +#else +# define os2ShmMap 0 +# define os2ShmLock 0 +# define os2ShmBarrier 0 +# define os2ShmUnmap 0 +#endif /* #ifndef SQLITE_OMIT_WAL */ + + /* ** This vector defines all the methods that can operate on an ** sqlite3_file for os2. */ static const sqlite3_io_methods os2IoMethod = { - 1, /* iVersion */ - os2Close, - os2Read, - os2Write, - os2Truncate, - os2Sync, - os2FileSize, - os2Lock, - os2Unlock, - os2CheckReservedLock, - os2FileControl, - os2SectorSize, - os2DeviceCharacteristics + 2, /* iVersion */ + os2Close, /* xClose */ + os2Read, /* xRead */ + os2Write, /* xWrite */ + os2Truncate, /* xTruncate */ + os2Sync, /* xSync */ + os2FileSize, /* xFileSize */ + os2Lock, /* xLock */ + os2Unlock, /* xUnlock */ + os2CheckReservedLock, /* xCheckReservedLock */ + os2FileControl, /* xFileControl */ + os2SectorSize, /* xSectorSize */ + os2DeviceCharacteristics, /* xDeviceCharacteristics */ + os2ShmMap, /* xShmMap */ + os2ShmLock, /* xShmLock */ + os2ShmBarrier, /* xShmBarrier */ + os2ShmUnmap /* xShmUnmap */ }; + /*************************************************************************** ** Here ends the I/O methods that form the sqlite3_io_methods object. ** @@ -672,50 +1373,57 @@ static const sqlite3_io_methods os2IoMethod = { ** hold at pVfs->mxPathname characters. */ static int getTempname(int nBuf, char *zBuf ){ - static const unsigned char zChars[] = + static const char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; - char zTempPathBuf[3]; - PSZ zTempPath = (PSZ)&zTempPathBuf; - if( sqlite3_temp_directory ){ - zTempPath = sqlite3_temp_directory; - }else{ - if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){ - if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){ - if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){ - ULONG ulDriveNum = 0, ulDriveMap = 0; - DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); - sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) ); - } - } - } + PSZ zTempPathCp; + char zTempPath[CCHMAXPATH]; + ULONG ulDriveNum, ulDriveMap; + + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. + */ + SimulateIOError( return SQLITE_IOERR ); + + if( sqlite3_temp_directory ) { + sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", sqlite3_temp_directory); + } else if( DosScanEnv( (PSZ)"TEMP", &zTempPathCp ) == NO_ERROR || + DosScanEnv( (PSZ)"TMP", &zTempPathCp ) == NO_ERROR || + DosScanEnv( (PSZ)"TMPDIR", &zTempPathCp ) == NO_ERROR ) { + char *zTempPathUTF = convertCpPathToUtf8( (char *)zTempPathCp ); + sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", zTempPathUTF); + free( zTempPathUTF ); + } else if( DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ) == NO_ERROR ) { + zTempPath[0] = (char)('A' + ulDriveNum - 1); + zTempPath[1] = ':'; + zTempPath[2] = '\0'; + } else { + zTempPath[0] = '\0'; } + /* Strip off a trailing slashes or backslashes, otherwise we would get * * multiple (back)slashes which causes DosOpen() to fail. * * Trailing spaces are not allowed, either. */ j = sqlite3Strlen30(zTempPath); - while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' - || zTempPath[j-1] == ' ' ) ){ + while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' || + zTempPath[j-1] == ' ' ) ){ j--; } zTempPath[j] = '\0'; - if( !sqlite3_temp_directory ){ - char *zTempPathUTF = convertCpPathToUtf8( zTempPath ); - sqlite3_snprintf( nBuf-30, zBuf, - "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPathUTF ); - free( zTempPathUTF ); - }else{ - sqlite3_snprintf( nBuf-30, zBuf, - "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath ); - } - j = sqlite3Strlen30( zBuf ); + + /* We use 20 bytes to randomize the name */ + sqlite3_snprintf(nBuf-22, zBuf, + "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); + j = sqlite3Strlen30(zBuf); sqlite3_randomness( 20, &zBuf[j] ); for( i = 0; i < 20; i++, j++ ){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; + OSTRACE(( "TEMP FILENAME: %s\n", zBuf )); return SQLITE_OK; } @@ -735,8 +1443,8 @@ static int os2FullPathname( char *zRelativeCp = convertUtf8PathToCp( zRelative ); char zFullCp[CCHMAXPATH] = "\0"; char *zFullUTF; - APIRET rc = DosQueryPathInfo( zRelativeCp, FIL_QUERYFULLNAME, zFullCp, - CCHMAXPATH ); + APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME, + zFullCp, CCHMAXPATH ); free( zRelativeCp ); zFullUTF = convertCpPathToUtf8( zFullCp ); sqlite3_snprintf( nFull, zFull, zFullUTF ); @@ -750,100 +1458,127 @@ static int os2FullPathname( */ static int os2Open( sqlite3_vfs *pVfs, /* Not used */ - const char *zName, /* Name of the file */ + const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ int *pOutFlags /* Status return flags */ ){ HFILE h; - ULONG ulFileAttribute = FILE_NORMAL; ULONG ulOpenFlags = 0; ULONG ulOpenMode = 0; + ULONG ulAction = 0; + ULONG rc; os2File *pFile = (os2File*)id; - APIRET rc = NO_ERROR; - ULONG ulAction; + const char *zUtf8Name = zName; char *zNameCp; - char zTmpname[CCHMAXPATH+1]; /* Buffer to hold name of temp file */ + char zTmpname[CCHMAXPATH]; + + int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); + int isCreate = (flags & SQLITE_OPEN_CREATE); + int isReadWrite = (flags & SQLITE_OPEN_READWRITE); +#ifndef NDEBUG + int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); + int isReadonly = (flags & SQLITE_OPEN_READONLY); + int eType = (flags & 0xFFFFFF00); + int isOpenJournal = (isCreate && ( + eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_MAIN_JOURNAL + || eType==SQLITE_OPEN_WAL + )); +#endif + + UNUSED_PARAMETER(pVfs); + assert( id!=0 ); + + /* Check the following statements are true: + ** + ** (a) Exactly one of the READWRITE and READONLY flags must be set, and + ** (b) if CREATE is set, then READWRITE must also be set, and + ** (c) if EXCLUSIVE is set, then CREATE must also be set. + ** (d) if DELETEONCLOSE is set, then CREATE must also be set. + */ + assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); + assert(isCreate==0 || isReadWrite); + assert(isExclusive==0 || isCreate); + assert(isDelete==0 || isCreate); + + /* The main DB, main journal, WAL file and master journal are never + ** automatically deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); + + /* Assert that the upper layer has set one of the "file-type" flags. */ + assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB + || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL + ); + + memset( pFile, 0, sizeof(*pFile) ); + pFile->h = (HFILE)-1; /* If the second argument to this function is NULL, generate a ** temporary file name to use */ - if( !zName ){ - int rc = getTempname(CCHMAXPATH+1, zTmpname); + if( !zUtf8Name ){ + assert(isDelete && !isOpenJournal); + rc = getTempname(CCHMAXPATH, zTmpname); if( rc!=SQLITE_OK ){ return rc; } - zName = zTmpname; + zUtf8Name = zTmpname; } - - memset( pFile, 0, sizeof(*pFile) ); - - OSTRACE(( "OPEN want %d\n", flags )); - - if( flags & SQLITE_OPEN_READWRITE ){ + if( isReadWrite ){ ulOpenMode |= OPEN_ACCESS_READWRITE; - OSTRACE(( "OPEN read/write\n" )); }else{ ulOpenMode |= OPEN_ACCESS_READONLY; - OSTRACE(( "OPEN read only\n" )); } - if( flags & SQLITE_OPEN_CREATE ){ - ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW; - OSTRACE(( "OPEN open new/create\n" )); + /* Open in random access mode for possibly better speed. Allow full + ** sharing because file locks will provide exclusive access when needed. + ** The handle should not be inherited by child processes and we don't + ** want popups from the critical error handler. + */ + ulOpenMode |= OPEN_FLAGS_RANDOM | OPEN_SHARE_DENYNONE | + OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR; + + /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is + ** created. SQLite doesn't use it to indicate "exclusive access" + ** as it is usually understood. + */ + if( isExclusive ){ + /* Creates a new file, only if it does not already exist. */ + /* If the file exists, it fails. */ + ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS; + }else if( isCreate ){ + /* Open existing file, or create if it doesn't exist */ + ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; }else{ - ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW; - OSTRACE(( "OPEN open existing\n" )); + /* Opens a file, only if it exists. */ + ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; } - if( flags & SQLITE_OPEN_MAIN_DB ){ - ulOpenMode |= OPEN_SHARE_DENYNONE; - OSTRACE(( "OPEN share read/write\n" )); - }else{ - ulOpenMode |= OPEN_SHARE_DENYWRITE; - OSTRACE(( "OPEN share read only\n" )); - } - - if( flags & SQLITE_OPEN_DELETEONCLOSE ){ - char pathUtf8[CCHMAXPATH]; -#ifdef NDEBUG /* when debugging we want to make sure it is deleted */ - ulFileAttribute = FILE_HIDDEN; -#endif - os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 ); - pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); - OSTRACE(( "OPEN hidden/delete on close file attributes\n" )); - }else{ - pFile->pathToDel = NULL; - OSTRACE(( "OPEN normal file attribute\n" )); - } - - /* always open in random access mode for possibly better speed */ - ulOpenMode |= OPEN_FLAGS_RANDOM; - ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; - ulOpenMode |= OPEN_FLAGS_NOINHERIT; - - zNameCp = convertUtf8PathToCp( zName ); + zNameCp = convertUtf8PathToCp( zUtf8Name ); rc = DosOpen( (PSZ)zNameCp, &h, &ulAction, 0L, - ulFileAttribute, + FILE_NORMAL, ulOpenFlags, ulOpenMode, (PEAOP2)NULL ); free( zNameCp ); + if( rc != NO_ERROR ){ - OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n", - rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode )); - if( pFile->pathToDel ) - free( pFile->pathToDel ); - pFile->pathToDel = NULL; - if( flags & SQLITE_OPEN_READWRITE ){ - OSTRACE(( "OPEN %d Invalid handle\n", - ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) )); + OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n", + rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode )); + + if( isReadWrite ){ return os2Open( pVfs, zName, id, - ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE), + ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags ); }else{ return SQLITE_CANTOPEN; @@ -851,11 +1586,15 @@ static int os2Open( } if( pOutFlags ){ - *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; + *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; } + os2FullPathname( pVfs, zUtf8Name, sizeof( zTmpname ), zTmpname ); + pFile->zFullPathCp = convertUtf8PathToCp( zTmpname ); pFile->pMethod = &os2IoMethod; + pFile->flags = flags; pFile->h = h; + OpenCounter(+1); OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); return SQLITE_OK; @@ -869,13 +1608,16 @@ static int os2Delete( const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on os2 */ ){ - APIRET rc = NO_ERROR; - char *zFilenameCp = convertUtf8PathToCp( zFilename ); + APIRET rc; + char *zFilenameCp; SimulateIOError( return SQLITE_IOERR_DELETE ); + zFilenameCp = convertUtf8PathToCp( zFilename ); rc = DosDelete( (PSZ)zFilenameCp ); free( zFilenameCp ); OSTRACE(( "DELETE \"%s\"\n", zFilename )); - return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE; + return (rc == NO_ERROR || + rc == ERROR_FILE_NOT_FOUND || + rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* @@ -887,30 +1629,42 @@ static int os2Access( int flags, /* Type of test to make on this file */ int *pOut /* Write results here */ ){ + APIRET rc; FILESTATUS3 fsts3ConfigInfo; - APIRET rc = NO_ERROR; - char *zFilenameCp = convertUtf8PathToCp( zFilename ); + char *zFilenameCp; - memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) ); + UNUSED_PARAMETER(pVfs); + SimulateIOError( return SQLITE_IOERR_ACCESS; ); + + zFilenameCp = convertUtf8PathToCp( zFilename ); rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD, &fsts3ConfigInfo, sizeof(FILESTATUS3) ); free( zFilenameCp ); OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n", fsts3ConfigInfo.attrFile, flags, rc )); + switch( flags ){ - case SQLITE_ACCESS_READ: case SQLITE_ACCESS_EXISTS: - rc = (rc == NO_ERROR); - OSTRACE(( "ACCESS %s access of read and exists rc=%d\n", zFilename, rc)); + /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file + ** as if it does not exist. + */ + if( fsts3ConfigInfo.cbFile == 0 ) + rc = ERROR_FILE_NOT_FOUND; + break; + case SQLITE_ACCESS_READ: break; case SQLITE_ACCESS_READWRITE: - rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 ); - OSTRACE(( "ACCESS %s access of read/write rc=%d\n", zFilename, rc )); + if( fsts3ConfigInfo.attrFile & FILE_READONLY ) + rc = ERROR_ACCESS_DENIED; break; default: + rc = ERROR_FILE_NOT_FOUND; assert( !"Invalid flags argument" ); } - *pOut = rc; + + *pOut = (rc == NO_ERROR); + OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut )); + return SQLITE_OK; } @@ -925,11 +1679,10 @@ static int os2Access( ** within the shared library, and closing the shared library. */ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ - UCHAR loadErr[256]; HMODULE hmod; APIRET rc; char *zFilenameCp = convertUtf8PathToCp(zFilename); - rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilenameCp, &hmod); + rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod); free(zFilenameCp); return rc != NO_ERROR ? 0 : (void*)hmod; } @@ -940,19 +1693,19 @@ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ /* no-op */ } -static void *os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){ +static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ PFN pfn; APIRET rc; - rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn); + rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)zSymbol, &pfn); if( rc != NO_ERROR ){ /* if the symbol itself was not found, search again for the same * symbol with an extra underscore, that might be needed depending * on the calling convention */ char _zSymbol[256] = "_"; - strncat(_zSymbol, zSymbol, 255); - rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn); + strncat(_zSymbol, zSymbol, 254); + rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn); } - return rc != NO_ERROR ? 0 : (void*)pfn; + return rc != NO_ERROR ? 0 : (void(*)(void))pfn; } static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){ DosFreeModule((HMODULE)pHandle); @@ -974,54 +1727,39 @@ static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){ n = nBuf; memset(zBuf, 0, nBuf); #else - int sizeofULong = sizeof(ULONG); - if( (int)sizeof(DATETIME) <= nBuf - n ){ - DATETIME x; - DosGetDateTime(&x); - memcpy(&zBuf[n], &x, sizeof(x)); - n += sizeof(x); + int i; + PPIB ppib; + PTIB ptib; + DATETIME dt; + static unsigned c = 0; + /* Ordered by variation probability */ + static ULONG svIdx[6] = { QSV_MS_COUNT, QSV_TIME_LOW, + QSV_MAXPRMEM, QSV_MAXSHMEM, + QSV_TOTAVAILMEM, QSV_TOTRESMEM }; + + /* 8 bytes; timezone and weekday don't increase the randomness much */ + if( (int)sizeof(dt)-3 <= nBuf - n ){ + c += 0x0100; + DosGetDateTime(&dt); + dt.year = (USHORT)((dt.year - 1900) | c); + memcpy(&zBuf[n], &dt, sizeof(dt)-3); + n += sizeof(dt)-3; } - if( sizeofULong <= nBuf - n ){ - PPIB ppib; - DosGetInfoBlocks(NULL, &ppib); - memcpy(&zBuf[n], &ppib->pib_ulpid, sizeofULong); - n += sizeofULong; + /* 4 bytes; PIDs and TIDs are 16 bit internally, so combine them */ + if( (int)sizeof(ULONG) <= nBuf - n ){ + DosGetInfoBlocks(&ptib, &ppib); + *(PULONG)&zBuf[n] = MAKELONG(ppib->pib_ulpid, + ptib->tib_ptib2->tib2_ultid); + n += sizeof(ULONG); } - if( sizeofULong <= nBuf - n ){ - PTIB ptib; - DosGetInfoBlocks(&ptib, NULL); - memcpy(&zBuf[n], &ptib->tib_ptib2->tib2_ultid, sizeofULong); - n += sizeofULong; - } - - /* if we still haven't filled the buffer yet the following will */ - /* grab everything once instead of making several calls for a single item */ - if( sizeofULong <= nBuf - n ){ - ULONG ulSysInfo[QSV_MAX]; - DosQuerySysInfo(1L, QSV_MAX, ulSysInfo, sizeofULong * QSV_MAX); - - memcpy(&zBuf[n], &ulSysInfo[QSV_MS_COUNT - 1], sizeofULong); - n += sizeofULong; - - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIMER_INTERVAL - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_LOW - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_HIGH - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TOTAVAILMEM - 1], sizeofULong); - n += sizeofULong; - } - } + /* Up to 6 * 4 bytes; variables depend on the system state */ + for( i = 0; i < 6 && (int)sizeof(ULONG) <= nBuf - n; i++ ){ + DosQuerySysInfo(svIdx[i], svIdx[i], + (PULONG)&zBuf[n], sizeof(ULONG)); + n += sizeof(ULONG); + } #endif return n; @@ -1049,46 +1787,98 @@ int sqlite3_current_time = 0; #endif /* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. +** Find the current time (in Universal Coordinated Time). Write into *piNow +** the current time and date as a Julian Day number times 86_400_000. In +** other words, write into *piNow the number of milliseconds since the Julian +** epoch of noon in Greenwich on November 24, 4714 B.C according to the +** proleptic Gregorian calendar. +** +** On success, return 0. Return 1 if the time and date cannot be found. */ -int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ - double now; - SHORT minute; /* needs to be able to cope with negative timezone offset */ - USHORT second, hour, - day, month, year; +static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ +#ifdef SQLITE_TEST + static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; +#endif + int year, month, datepart, timepart; + DATETIME dt; DosGetDateTime( &dt ); - second = (USHORT)dt.seconds; - minute = (SHORT)dt.minutes + dt.timezone; - hour = (USHORT)dt.hours; - day = (USHORT)dt.day; - month = (USHORT)dt.month; - year = (USHORT)dt.year; + + year = dt.year; + month = dt.month; /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html - http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c */ - /* Calculate the Julian days */ - now = day - 32076 + + ** http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c + ** Calculate the Julian days + */ + datepart = (int)dt.day - 32076 + 1461*(year + 4800 + (month - 14)/12)/4 + 367*(month - 2 - (month - 14)/12*12)/12 - 3*((year + 4900 + (month - 14)/12)/100)/4; - /* Add the fractional hours, mins and seconds */ - now += (hour + 12.0)/24.0; - now += minute/1440.0; - now += second/86400.0; - *prNow = now; + /* Time in milliseconds, hours to noon added */ + timepart = 12*3600*1000 + dt.hundredths*10 + dt.seconds*1000 + + ((int)dt.minutes + dt.timezone)*60*1000 + dt.hours*3600*1000; + + *piNow = (sqlite3_int64)datepart*86400*1000 + timepart; + #ifdef SQLITE_TEST if( sqlite3_current_time ){ - *prNow = sqlite3_current_time/86400.0 + 2440587.5; + *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } #endif + + UNUSED_PARAMETER(pVfs); return 0; } +/* +** Find the current time (in Universal Coordinated Time). Write the +** current time and date as a Julian Day number into *prNow and +** return 0. Return 1 if the time and date cannot be found. +*/ +static int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ + int rc; + sqlite3_int64 i; + rc = os2CurrentTimeInt64(pVfs, &i); + if( !rc ){ + *prNow = i/86400000.0; + } + return rc; +} + +/* +** The idea is that this function works like a combination of +** GetLastError() and FormatMessage() on windows (or errno and +** strerror_r() on unix). After an error is returned by an OS +** function, SQLite calls this function with zBuf pointing to +** a buffer of nBuf bytes. The OS layer should populate the +** buffer with a nul-terminated UTF-8 encoded error message +** describing the last IO error to have occurred within the calling +** thread. +** +** If the error message is too large for the supplied buffer, +** it should be truncated. The return value of xGetLastError +** is zero if the error message fits in the buffer, or non-zero +** otherwise (if the message was truncated). If non-zero is returned, +** then it is not necessary to include the nul-terminator character +** in the output buffer. +** +** Not supplying an error message will have no adverse effect +** on SQLite. It is fine to have an implementation that never +** returns an error message: +** +** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ +** assert(zBuf[0]=='\0'); +** return 0; +** } +** +** However if an error message is supplied, it will be incorporated +** by sqlite into the error message available to the user using +** sqlite3_errmsg(), possibly making IO errors easier to debug. +*/ static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ + assert(zBuf[0]=='\0'); return 0; } @@ -1097,7 +1887,7 @@ static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ */ int sqlite3_os_init(void){ static sqlite3_vfs os2Vfs = { - 1, /* iVersion */ + 3, /* iVersion */ sizeof(os2File), /* szOsFile */ CCHMAXPATH, /* mxPathname */ 0, /* pNext */ @@ -1116,9 +1906,14 @@ int sqlite3_os_init(void){ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ os2GetLastError, /* xGetLastError */ + os2CurrentTimeInt64, /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0 /* xNextSystemCall */ }; sqlite3_vfs_register(&os2Vfs, 1); initUconvObjects(); +/* sqlite3OSTrace = 1; */ return SQLITE_OK; } int sqlite3_os_end(void){ diff --git a/src/os_unix.c b/src/os_unix.c index fa200ae..2626ab4 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -206,10 +206,10 @@ struct unixFile { int h; /* The file descriptor */ int dirfd; /* File descriptor for the directory */ unsigned char eFileLock; /* The type of lock held on this fd */ + unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ - int fileFlags; /* Miscellanous flags */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ @@ -244,9 +244,10 @@ struct unixFile { }; /* -** The following macros define bits in unixFile.fileFlags +** Allowed values for the unixFile.ctrlFlags bitmask: */ -#define SQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */ +#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ +#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ /* ** Include code that is common to all os_*.c files @@ -270,16 +271,6 @@ struct unixFile { # define O_BINARY 0 #endif -/* -** The DJGPP compiler environment looks mostly like Unix, but it -** lacks the fcntl() system call. So redefine fcntl() to be something -** that always succeeds. This means that locking does not occur under -** DJGPP. But it is DOS - what did you expect? -*/ -#ifdef __DJGPP__ -# define fcntl(A,B,C) 0 -#endif - /* ** The threadid macro resolves to the thread-id or to 0. Used for ** testing and debugging only. @@ -290,6 +281,195 @@ struct unixFile { #define threadid 0 #endif +/* +** Many system calls are accessed through pointer-to-functions so that +** they may be overridden at runtime to facilitate fault injection during +** testing and sandboxing. The following array holds the names and pointers +** to all overrideable system calls. +*/ +static struct unix_syscall { + const char *zName; /* Name of the sytem call */ + sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ + sqlite3_syscall_ptr pDefault; /* Default value */ +} aSyscall[] = { + { "open", (sqlite3_syscall_ptr)open, 0 }, +#define osOpen ((int(*)(const char*,int,...))aSyscall[0].pCurrent) + + { "close", (sqlite3_syscall_ptr)close, 0 }, +#define osClose ((int(*)(int))aSyscall[1].pCurrent) + + { "access", (sqlite3_syscall_ptr)access, 0 }, +#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) + + { "getcwd", (sqlite3_syscall_ptr)getcwd, 0 }, +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) + + { "stat", (sqlite3_syscall_ptr)stat, 0 }, +#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) + +/* +** The DJGPP compiler environment looks mostly like Unix, but it +** lacks the fcntl() system call. So redefine fcntl() to be something +** that always succeeds. This means that locking does not occur under +** DJGPP. But it is DOS - what did you expect? +*/ +#ifdef __DJGPP__ + { "fstat", 0, 0 }, +#define osFstat(a,b,c) 0 +#else + { "fstat", (sqlite3_syscall_ptr)fstat, 0 }, +#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) +#endif + + { "ftruncate", (sqlite3_syscall_ptr)ftruncate, 0 }, +#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) + + { "fcntl", (sqlite3_syscall_ptr)fcntl, 0 }, +#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) + + { "read", (sqlite3_syscall_ptr)read, 0 }, +#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) + +#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) + { "pread", (sqlite3_syscall_ptr)pread, 0 }, +#else + { "pread", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) + +#if defined(USE_PREAD64) + { "pread64", (sqlite3_syscall_ptr)pread64, 0 }, +#else + { "pread64", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent) + + { "write", (sqlite3_syscall_ptr)write, 0 }, +#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) + +#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) + { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, +#else + { "pwrite", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[12].pCurrent) + +#if defined(USE_PREAD64) + { "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 }, +#else + { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[13].pCurrent) + +#if SQLITE_ENABLE_LOCKING_STYLE + { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, +#else + { "fchmod", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) + +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE + { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, +#else + { "fallocate", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent) + +}; /* End of the overrideable system calls */ + +/* +** This is the xSetSystemCall() method of sqlite3_vfs for all of the +** "unix" VFSes. Return SQLITE_OK opon successfully updating the +** system call pointer, or SQLITE_NOTFOUND if there is no configurable +** system call named zName. +*/ +static int unixSetSystemCall( + sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ + const char *zName, /* Name of system call to override */ + sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ +){ + unsigned int i; + int rc = SQLITE_NOTFOUND; + + UNUSED_PARAMETER(pNotUsed); + if( zName==0 ){ + /* If no zName is given, restore all system calls to their default + ** settings and return NULL + */ + rc = SQLITE_OK; + for(i=0; il_whence==SEEK_SET ); - s = fcntl(fd, op, p); + s = osFcntl(fd, op, p); savedErrno = errno; sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, @@ -376,7 +556,7 @@ static int lockTrace(int fd, int op, struct flock *p){ if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ struct flock l2; l2 = *p; - fcntl(fd, F_GETLK, &l2); + osFcntl(fd, F_GETLK, &l2); if( l2.l_type==F_RDLCK ){ zType = "RDLCK"; }else if( l2.l_type==F_WRLCK ){ @@ -392,10 +572,18 @@ static int lockTrace(int fd, int op, struct flock *p){ errno = savedErrno; return s; } -#define fcntl lockTrace +#undef osFcntl +#define osFcntl lockTrace #endif /* SQLITE_LOCK_TRACE */ - +/* +** Retry ftruncate() calls that fail due to EINTR +*/ +static int robust_ftruncate(int h, sqlite3_int64 sz){ + int rc; + do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); + return rc; +} /* ** This routine translates a standard POSIX errno code into something @@ -409,9 +597,22 @@ static int lockTrace(int fd, int op, struct flock *p){ */ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { switch (posixError) { +#if 0 + /* At one point this code was not commented out. In theory, this branch + ** should never be hit, as this function should only be called after + ** a locking-related function (i.e. fcntl()) has returned non-zero with + ** the value of errno as the first argument. Since a system call has failed, + ** errno should be non-zero. + ** + ** Despite this, if errno really is zero, we still don't want to return + ** SQLITE_OK. The system call failed, and *some* SQLite error should be + ** propagated back to the caller. Commenting this branch out means errno==0 + ** will be handled by the "default:" case below. + */ case 0: return SQLITE_OK; - +#endif + case EAGAIN: case ETIMEDOUT: case EBUSY: @@ -433,8 +634,15 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { case EPERM: return SQLITE_PERM; + /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And + ** this module never makes such a call. And the code in SQLite itself + ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons + ** this case is also commented out. If the system does set errno to EDEADLK, + ** the default SQLITE_IOERR_XXX code will be returned. */ +#if 0 case EDEADLK: return SQLITE_IOERR_BLOCKED; +#endif #if EOPNOTSUPP!=ENOTSUP case EOPNOTSUPP: @@ -717,7 +925,8 @@ struct unixFileId { struct unixInodeInfo { struct unixFileId fileId; /* The lookup key */ int nShared; /* Number of SHARED locks held */ - int eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + unsigned char bProcessLock; /* An exclusive process lock is held */ int nRef; /* Number of pointers to this structure */ unixShmNode *pShmNode; /* Shared memory associated with this inode */ int nLock; /* Number of outstanding file locks */ @@ -739,33 +948,108 @@ struct unixInodeInfo { static unixInodeInfo *inodeList = 0; /* -** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. -** If all such file descriptors are closed without error, the list is -** cleared and SQLITE_OK returned. ** -** Otherwise, if an error occurs, then successfully closed file descriptor -** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. -** not deleted and SQLITE_IOERR_CLOSE returned. +** This function - unixLogError_x(), is only ever called via the macro +** unixLogError(). +** +** It is invoked after an error occurs in an OS function and errno has been +** set. It logs a message using sqlite3_log() containing the current value of +** errno and, if possible, the human-readable equivalent from strerror() or +** strerror_r(). +** +** The first argument passed to the macro should be the error code that +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). +** The two subsequent arguments should be the name of the OS function that +** failed (e.g. "unlink", "open") and the the associated file-system path, +** if any. +*/ +#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) +static int unixLogErrorAtLine( + int errcode, /* SQLite error code */ + const char *zFunc, /* Name of OS function that failed */ + const char *zPath, /* File path associated with error */ + int iLine /* Source line number where error occurred */ +){ + char *zErr; /* Message from strerror() or equivalent */ + int iErrno = errno; /* Saved syscall error number */ + + /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use + ** the strerror() function to obtain the human-readable error message + ** equivalent to errno. Otherwise, use strerror_r(). + */ +#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R) + char aErr[80]; + memset(aErr, 0, sizeof(aErr)); + zErr = aErr; + + /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, + ** assume that the system provides the the GNU version of strerror_r() that + ** returns a pointer to a buffer containing the error message. That pointer + ** may point to aErr[], or it may point to some static storage somewhere. + ** Otherwise, assume that the system provides the POSIX version of + ** strerror_r(), which always writes an error message into aErr[]. + ** + ** If the code incorrectly assumes that it is the POSIX version that is + ** available, the error message will often be an empty string. Not a + ** huge problem. Incorrectly concluding that the GNU version is available + ** could lead to a segfault though. + */ +#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) + zErr = +# endif + strerror_r(iErrno, aErr, sizeof(aErr)-1); + +#elif SQLITE_THREADSAFE + /* This is a threadsafe build, but strerror_r() is not available. */ + zErr = ""; +#else + /* Non-threadsafe build, use strerror(). */ + zErr = strerror(iErrno); +#endif + + assert( errcode!=SQLITE_OK ); + if( zPath==0 ) zPath = ""; + sqlite3_log(errcode, + "os_unix.c:%d: (%d) %s(%s) - %s", + iLine, iErrno, zFunc, zPath, zErr + ); + + return errcode; +} + +/* +** Close a file descriptor. +** +** We assume that close() almost always works, since it is only in a +** very sick application or on a very sick platform that it might fail. +** If it does fail, simply leak the file descriptor, but do log the +** error. +** +** Note that it is not safe to retry close() after EINTR since the +** file descriptor might have already been reused by another thread. +** So we don't even try to recover from an EINTR. Just log the error +** and move on. +*/ +static void robust_close(unixFile *pFile, int h, int lineno){ + if( osClose(h) ){ + unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", + pFile ? pFile->zPath : 0, lineno); + } +} + +/* +** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. */ -static int closePendingFds(unixFile *pFile){ - int rc = SQLITE_OK; +static void closePendingFds(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *pError = 0; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; - if( close(p->fd) ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - p->pNext = pError; - pError = p; - }else{ - sqlite3_free(p); - } + robust_close(pFile, p->fd, __LINE__); + sqlite3_free(p); } - pInode->pUnused = pError; - return rc; + pInode->pUnused = 0; } /* @@ -777,7 +1061,7 @@ static int closePendingFds(unixFile *pFile){ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); - if( pInode ){ + if( ALWAYS(pInode) ){ pInode->nRef--; if( pInode->nRef==0 ){ assert( pInode->pShmNode==0 ); @@ -824,7 +1108,7 @@ static int findInodeInfo( ** create a unique name for the file. */ fd = pFile->h; - rc = fstat(fd, &statbuf); + rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; #ifdef EOVERFLOW @@ -845,12 +1129,12 @@ static int findInodeInfo( ** the first page of the database, no damage is done. */ if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ - rc = write(fd, "S", 1); + do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); if( rc!=1 ){ pFile->lastErrno = errno; return SQLITE_IOERR; } - rc = fstat(fd, &statbuf); + rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; return SQLITE_IOERR; @@ -913,16 +1197,15 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ /* Otherwise see if some other process holds it. */ #ifndef __DJGPP__ - if( !reserved ){ + if( !reserved && !pFile->pInode->bProcessLock ){ struct flock lock; lock.l_whence = SEEK_SET; lock.l_start = RESERVED_BYTE; lock.l_len = 1; lock.l_type = F_WRLCK; - if (-1 == fcntl(pFile->h, F_GETLK, &lock)) { - int tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - pFile->lastErrno = tErrno; + if( osFcntl(pFile->h, F_GETLK, &lock) ){ + rc = SQLITE_IOERR_CHECKRESERVEDLOCK; + pFile->lastErrno = errno; } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } @@ -936,6 +1219,53 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ return rc; } +/* +** Attempt to set a system-lock on the file pFile. The lock is +** described by pLock. +** +** If the pFile was opened read/write from unix-excl, then the only lock +** ever obtained is an exclusive lock, and it is obtained exactly once +** the first time any lock is attempted. All subsequent system locking +** operations become no-ops. Locking operations still happen internally, +** in order to coordinate access between separate database connections +** within this process, but all of that is handled in memory and the +** operating system does not participate. +** +** This function is a pass-through to fcntl(F_SETLK) if pFile is using +** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" +** and is read-only. +** +** Zero is returned if the call completes successfully, or -1 if a call +** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). +*/ +static int unixFileLock(unixFile *pFile, struct flock *pLock){ + int rc; + unixInodeInfo *pInode = pFile->pInode; + assert( unixMutexHeld() ); + assert( pInode!=0 ); + if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock) + && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0) + ){ + if( pInode->bProcessLock==0 ){ + struct flock lock; + assert( pInode->nLock==0 ); + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; + lock.l_type = F_WRLCK; + rc = osFcntl(pFile->h, F_SETLK, &lock); + if( rc<0 ) return rc; + pInode->bProcessLock = 1; + pInode->nLock++; + }else{ + rc = 0; + } + }else{ + rc = osFcntl(pFile->h, F_SETLK, pLock); + } + return rc; +} + /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: @@ -1003,7 +1333,6 @@ static int unixLock(sqlite3_file *id, int eFileLock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode = pFile->pInode; struct flock lock; - int s = 0; int tErrno = 0; assert( pFile ); @@ -1072,11 +1401,10 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; - s = fcntl(pFile->h, F_SETLK, &lock); - if( s==(-1) ){ + if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } goto end_lock; @@ -1090,33 +1418,31 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( eFileLock==SHARED_LOCK ){ assert( pInode->nShared==0 ); assert( pInode->eFileLock==0 ); + assert( rc==SQLITE_OK ); /* Now get the read-lock */ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ + if( unixFileLock(pFile, &lock) ){ tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); } + /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; lock.l_type = F_UNLCK; - if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){ - if( s != -1 ){ - /* This could happen with a network mount */ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_lock; - } + if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ + /* This could happen with a network mount */ + tErrno = errno; + rc = SQLITE_IOERR_UNLOCK; } - if( s==(-1) ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + + if( rc ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } + goto end_lock; }else{ pFile->eFileLock = SHARED_LOCK; pInode->nLock++; @@ -1133,22 +1459,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){ */ assert( 0!=pFile->eFileLock ); lock.l_type = F_WRLCK; - switch( eFileLock ){ - case RESERVED_LOCK: - lock.l_start = RESERVED_BYTE; - break; - case EXCLUSIVE_LOCK: - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - break; - default: - assert(0); + + assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); + if( eFileLock==RESERVED_LOCK ){ + lock.l_start = RESERVED_BYTE; + lock.l_len = 1L; + }else{ + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; } - s = fcntl(pFile->h, F_SETLK, &lock); - if( s==(-1) ){ + + if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } } @@ -1213,13 +1537,12 @@ static void setPendingFd(unixFile *pFile){ ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to ** remove the write lock on a region when a read lock is set. */ -static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ +static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; struct flock lock; int rc = SQLITE_OK; int h; - int tErrno; /* Error code from system call errors */ assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, @@ -1267,16 +1590,23 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ ** 4: [RRRR.] */ if( eFileLock==SHARED_LOCK ){ + +#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE + (void)handleNFSUnlock; + assert( handleNFSUnlock==0 ); +#endif +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE if( handleNFSUnlock ){ + int tErrno; /* Error code from system call errors */ off_t divSize = SHARED_SIZE - 1; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } @@ -1286,7 +1616,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ @@ -1298,25 +1628,30 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST+divSize; lock.l_len = SHARED_SIZE-divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } - }else{ + }else +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ + { lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } + if( unixFileLock(pFile, &lock) ){ + /* In theory, the call to unixFileLock() cannot fail because another + ** process is holding an incompatible lock. If it does, this + ** indicates that the other process is not following the locking + ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning + ** SQLITE_BUSY would confuse the upper layer (in practice it causes + ** an assert to fail). */ + rc = SQLITE_IOERR_RDLOCK; + pFile->lastErrno = errno; goto end_unlock; } } @@ -1325,14 +1660,11 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = SHARED_LOCK; }else{ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } + rc = SQLITE_IOERR_UNLOCK; + pFile->lastErrno = errno; goto end_unlock; } } @@ -1349,14 +1681,11 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = NO_LOCK; }else{ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } + rc = SQLITE_IOERR_UNLOCK; + pFile->lastErrno = errno; pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } @@ -1369,10 +1698,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ - int rc2 = closePendingFds(pFile); - if( rc==SQLITE_OK ){ - rc = rc2; - } + closePendingFds(pFile); } } @@ -1390,7 +1716,7 @@ end_unlock: ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ - return _posixUnlock(id, eFileLock, 0); + return posixUnlock(id, eFileLock, 0); } /* @@ -1405,37 +1731,27 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; - if( pFile ){ - if( pFile->dirfd>=0 ){ - int err = close(pFile->dirfd); - if( err ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_DIR_CLOSE; - }else{ - pFile->dirfd=-1; - } - } - if( pFile->h>=0 ){ - int err = close(pFile->h); - if( err ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_CLOSE; - } - } -#if OS_VXWORKS - if( pFile->pId ){ - if( pFile->isDelete ){ - unlink(pFile->pId->zCanonicalName); - } - vxworksReleaseFileId(pFile->pId); - pFile->pId = 0; - } -#endif - OSTRACE(("CLOSE %-3d\n", pFile->h)); - OpenCounter(-1); - sqlite3_free(pFile->pUnused); - memset(pFile, 0, sizeof(unixFile)); + if( pFile->dirfd>=0 ){ + robust_close(pFile, pFile->dirfd, __LINE__); + pFile->dirfd=-1; } + if( pFile->h>=0 ){ + robust_close(pFile, pFile->h, __LINE__); + pFile->h = -1; + } +#if OS_VXWORKS + if( pFile->pId ){ + if( pFile->isDelete ){ + unlink(pFile->pId->zCanonicalName); + } + vxworksReleaseFileId(pFile->pId); + pFile->pId = 0; + } +#endif + OSTRACE(("CLOSE %-3d\n", pFile->h)); + OpenCounter(-1); + sqlite3_free(pFile->pUnused); + memset(pFile, 0, sizeof(unixFile)); return SQLITE_OK; } @@ -1444,22 +1760,25 @@ static int closeUnixFile(sqlite3_file *id){ */ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile *)id; - unixUnlock(id, NO_LOCK); - unixEnterMutex(); - if( pFile->pInode && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed - ** when the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); - rc = closeUnixFile(id); - unixLeaveMutex(); + unixFile *pFile = (unixFile *)id; + unixUnlock(id, NO_LOCK); + unixEnterMutex(); + + /* unixFile.pInode is always valid here. Otherwise, a different close + ** routine (e.g. nolockClose()) would be called instead. + */ + assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); + if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->pUnused list. It will be automatically closed + ** when the last lock is cleared. + */ + setPendingFd(pFile); } + releaseInodeInfo(pFile); + rc = closeUnixFile(id); + unixLeaveMutex(); return rc; } @@ -1562,7 +1881,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { }else{ /* The lock is held if and only if the lockfile exists */ const char *zLockFile = (const char*)pFile->lockingContext; - reserved = access(zLockFile, 0)==0; + reserved = osAccess(zLockFile, 0)==0; } OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); *pResOut = reserved; @@ -1616,7 +1935,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } /* grab an exclusive lock */ - fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); + fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); if( fd<0 ){ /* failed to open/create the file, someone else may have stolen the lock */ int tErrno = errno; @@ -1630,10 +1949,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } return rc; } - if( close(fd) ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - } + robust_close(pFile, fd, __LINE__); /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; @@ -1677,7 +1993,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { int rc = 0; int tErrno = errno; if( ENOENT != tErrno ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + rc = SQLITE_IOERR_UNLOCK; } if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -1721,6 +2037,20 @@ static int dotlockClose(sqlite3_file *id) { */ #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS +/* +** Retry flock() calls that fail with EINTR +*/ +#ifdef EINTR +static int robust_flock(int fd, int op){ + int rc; + do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR ); + return rc; +} +#else +# define robust_flock(a,b) flock(a,b) +#endif + + /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, set *pResOut @@ -1744,14 +2074,14 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ /* Otherwise see if some other process holds it. */ if( !reserved ){ /* attempt to get the lock */ - int lrc = flock(pFile->h, LOCK_EX | LOCK_NB); + int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); if( !lrc ){ /* got the lock, unlock it */ - lrc = flock(pFile->h, LOCK_UN); + lrc = robust_flock(pFile->h, LOCK_UN); if ( lrc ) { int tErrno = errno; /* unlock failed with an error */ - lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + lrc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(lrc) ){ pFile->lastErrno = tErrno; rc = lrc; @@ -1824,7 +2154,7 @@ static int flockLock(sqlite3_file *id, int eFileLock) { /* grab an exclusive lock */ - if (flock(pFile->h, LOCK_EX | LOCK_NB)) { + if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) { int tErrno = errno; /* didn't get, must be busy */ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); @@ -1873,21 +2203,12 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { } /* no, really, unlock. */ - int rc = flock(pFile->h, LOCK_UN); - if (rc) { - int r, tErrno = errno; - r = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(r) ){ - pFile->lastErrno = tErrno; - } + if( robust_flock(pFile->h, LOCK_UN) ){ #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - if( (r & SQLITE_IOERR) == SQLITE_IOERR ){ - r = SQLITE_BUSY; - } + return SQLITE_OK; #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - - return r; - } else { + return SQLITE_IOERR_UNLOCK; + }else{ pFile->eFileLock = NO_LOCK; return SQLITE_OK; } @@ -2511,7 +2832,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ - rc = closePendingFds(pFile); + closePendingFds(pFile); } } } @@ -2568,7 +2889,7 @@ static int afpClose(sqlite3_file *id) { ** the requested locking level, this routine is a no-op. */ static int nfsUnlock(sqlite3_file *id, int eFileLock){ - return _posixUnlock(id, eFileLock, 1); + return posixUnlock(id, eFileLock, 1); } #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ @@ -2610,10 +2931,10 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ #endif TIMER_START; #if defined(USE_PREAD) - got = pread(id->h, pBuf, cnt, offset); + do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); SimulateIOError( got = -1 ); #elif defined(USE_PREAD64) - got = pread64(id->h, pBuf, cnt, offset); + do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR); SimulateIOError( got = -1 ); #else newOffset = lseek(id->h, offset, SEEK_SET); @@ -2626,7 +2947,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ } return -1; } - got = read(id->h, pBuf, cnt); + do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ @@ -2688,11 +3009,12 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ #endif TIMER_START; #if defined(USE_PREAD) - got = pwrite(id->h, pBuf, cnt, offset); + do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); #elif defined(USE_PREAD64) - got = pwrite64(id->h, pBuf, cnt, offset); + do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); #else newOffset = lseek(id->h, offset, SEEK_SET); + SimulateIOError( newOffset-- ); if( newOffset!=offset ){ if( newOffset == -1 ){ ((unixFile*)id)->lastErrno = errno; @@ -2701,7 +3023,7 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ } return -1; } - got = write(id->h, pBuf, cnt); + do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ @@ -2869,7 +3191,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ rc = SQLITE_OK; #elif HAVE_FULLFSYNC if( fullSync ){ - rc = fcntl(fd, F_FULLFSYNC, 0); + rc = osFcntl(fd, F_FULLFSYNC, 0); }else{ rc = 1; } @@ -2941,10 +3263,9 @@ static int unixSync(sqlite3_file *id, int flags){ SimulateIOError( rc=1 ); if( rc ){ pFile->lastErrno = errno; - return SQLITE_IOERR_FSYNC; + return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); } if( pFile->dirfd>=0 ){ - int err; OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd, HAVE_FULLFSYNC, isFullsync)); #ifndef SQLITE_DISABLE_DIRSYNC @@ -2963,13 +3284,9 @@ static int unixSync(sqlite3_file *id, int flags){ /* return SQLITE_IOERR; */ } #endif - err = close(pFile->dirfd); /* Only need to sync once, so close the */ - if( err==0 ){ /* directory when we are done */ - pFile->dirfd = -1; - }else{ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_DIR_CLOSE; - } + /* Only need to sync once, so close the directory when we are done */ + robust_close(pFile, pFile->dirfd, __LINE__); + pFile->dirfd = -1; } return rc; } @@ -2992,10 +3309,10 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } - rc = ftruncate(pFile->h, (off_t)nByte); + rc = robust_ftruncate(pFile->h, (off_t)nByte); if( rc ){ pFile->lastErrno = errno; - return SQLITE_IOERR_TRUNCATE; + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); }else{ #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to @@ -3021,7 +3338,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){ int rc; struct stat buf; assert( id ); - rc = fstat(((unixFile*)id)->h, &buf); + rc = osFstat(((unixFile*)id)->h, &buf); SimulateIOError( rc=1 ); if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; @@ -3062,14 +3379,20 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ - if( fstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; + if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; if( nSize>(i64)buf.st_size ){ + #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE - if( posix_fallocate(pFile->h, buf.st_size, nSize-buf.st_size) ){ - return SQLITE_IOERR_WRITE; - } + /* The code below is handling the return value of osFallocate() + ** correctly. posix_fallocate() is defined to "returns zero on success, + ** or an error number on failure". See the manpage for details. */ + int err; + do{ + err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); + }while( err==EINTR ); + if( err ) return SQLITE_IOERR_WRITE; #else /* If the OS does not have posix_fallocate(), fake it. First use ** ftruncate() to set the file size, then write a single byte to @@ -3079,18 +3402,17 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ */ int nBlk = buf.st_blksize; /* File-system block size */ i64 iWrite; /* Next offset to write to */ - int nWrite; /* Return value from seekAndWrite() */ - if( ftruncate(pFile->h, nSize) ){ + if( robust_ftruncate(pFile->h, nSize) ){ pFile->lastErrno = errno; - return SQLITE_IOERR_TRUNCATE; + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); } iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; - do { - nWrite = seekAndWrite(pFile, iWrite, "", 1); + while( iWrite=1 && nh>=0 ){ + /* Initialize the locking parameters */ + memset(&f, 0, sizeof(f)); + f.l_type = lockType; + f.l_whence = SEEK_SET; + f.l_start = ofst; + f.l_len = n; - rc = fcntl(pShmNode->h, F_SETLK, &f); - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + rc = osFcntl(pShmNode->h, F_SETLK, &f); + rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + } /* Update the global lock state and do debug tracing */ #ifdef SQLITE_DEBUG @@ -3331,10 +3655,17 @@ static void unixShmPurge(unixFile *pFd){ assert( p->pInode==pFd->pInode ); if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; inRegion; i++){ - munmap(p->apRegion[i], p->szRegion); + if( p->h>=0 ){ + munmap(p->apRegion[i], p->szRegion); + }else{ + sqlite3_free(p->apRegion[i]); + } } sqlite3_free(p->apRegion); - if( p->h>=0 ) close(p->h); + if( p->h>=0 ){ + robust_close(pFd, p->h, __LINE__); + p->h = -1; + } p->pInode->pShmNode = 0; sqlite3_free(p); } @@ -3368,6 +3699,12 @@ static void unixShmPurge(unixFile *pFd){ ** When opening a new shared-memory file, if no other instances of that ** file are currently open, in this process or in other processes, then ** the file must be truncated to zero length or have its header cleared. +** +** If the original database file (pDbFd) is using the "unix-excl" VFS +** that means that an exclusive lock is held on the database file and +** that no other processes are able to read or write the database. In +** that case, we do not really need shared memory. No shared memory +** file is created. The shared memory will be simulated with heap memory. */ static int unixOpenSharedMemory(unixFile *pDbFd){ struct unixShm *p = 0; /* The connection to be opened */ @@ -3397,7 +3734,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** with the same permissions. The actual permissions the file is created ** with are subject to the current umask setting. */ - if( fstat(pDbFd->h, &sStat) ){ + if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ rc = SQLITE_IOERR_FSTAT; goto shm_open_err; } @@ -3430,25 +3767,28 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ goto shm_open_err; } - pShmNode->h = open(zShmFilename, O_RDWR|O_CREAT, (sStat.st_mode & 0777)); - if( pShmNode->h<0 ){ - rc = SQLITE_CANTOPEN_BKPT; - goto shm_open_err; - } - - /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ - if( ftruncate(pShmNode->h, 0) ){ - rc = SQLITE_IOERR_SHMOPEN; + if( pInode->bProcessLock==0 ){ + pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, + (sStat.st_mode & 0777)); + if( pShmNode->h<0 ){ + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); + goto shm_open_err; } + + /* Check to see if another process is holding the dead-man switch. + ** If not, truncate the file to zero length. + */ + rc = SQLITE_OK; + if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ + if( robust_ftruncate(pShmNode->h, 0) ){ + rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); + } + } + if( rc==SQLITE_OK ){ + rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); + } + if( rc ) goto shm_open_err; } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); - } - if( rc ) goto shm_open_err; } /* Make the new connection a child of the unixShmNode */ @@ -3522,6 +3862,9 @@ static int unixShmMap( pShmNode = p->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); + assert( pShmNode->pInode==pDbFd->pInode ); + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); if( pShmNode->nRegion<=iRegion ){ char **apNew; /* New apRegion[] array */ @@ -3530,27 +3873,30 @@ static int unixShmMap( pShmNode->szRegion = szRegion; - /* The requested region is not mapped into this processes address space. - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ - if( fstat(pShmNode->h, &sStat) ){ - rc = SQLITE_IOERR_SHMSIZE; - goto shmpage_out; - } - - if( sStat.st_sizeh>=0 ){ + /* The requested region is not mapped into this processes address space. + ** Check to see if it has been allocated (i.e. if the wal-index file is + ** large enough to contain the requested region). */ - if( !bExtend ) goto shmpage_out; - if( ftruncate(pShmNode->h, nByte) ){ + if( osFstat(pShmNode->h, &sStat) ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } + + if( sStat.st_sizeh, nByte) ){ + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate", + pShmNode->zFilename); + goto shmpage_out; + } + } } /* Map the requested memory region into this processes address space. */ @@ -3563,12 +3909,22 @@ static int unixShmMap( } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ - void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, - MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion - ); - if( pMem==MAP_FAILED ){ - rc = SQLITE_IOERR; - goto shmpage_out; + void *pMem; + if( pShmNode->h>=0 ){ + pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, + MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion + ); + if( pMem==MAP_FAILED ){ + rc = SQLITE_IOERR; + goto shmpage_out; + } + }else{ + pMem = sqlite3_malloc(szRegion); + if( pMem==0 ){ + rc = SQLITE_NOMEM; + goto shmpage_out; + } + memset(pMem, 0, szRegion); } pShmNode->apRegion[pShmNode->nRegion] = pMem; pShmNode->nRegion++; @@ -3615,6 +3971,8 @@ static int unixShmLock( || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); mask = (1<<(ofst+n)) - (1<1 || mask==(1<nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ - if( deleteFlag ) unlink(pShmNode->zFilename); + if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename); unixShmPurge(pDbFd); } unixLeaveMutex(); @@ -3993,7 +4351,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl( lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ return &nfsIoMethods; } else { @@ -4035,7 +4393,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl( lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { return &posixIoMethods; }else{ return &semIoMethods; @@ -4069,7 +4427,8 @@ static int fillInUnixFile( sqlite3_file *pId, /* Write to the unixFile structure here */ const char *zFilename, /* Name of the file being opened */ int noLock, /* Omit locking if true */ - int isDelete /* Delete on close if true */ + int isDelete, /* Delete on close if true */ + int isReadOnly /* True if the file is opened read-only */ ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; @@ -4096,8 +4455,15 @@ static int fillInUnixFile( OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; - pNew->fileFlags = 0; pNew->zPath = zFilename; + if( memcmp(pVfs->zName,"unix-excl",10)==0 ){ + pNew->ctrlFlags = UNIXFILE_EXCL; + }else{ + pNew->ctrlFlags = 0; + } + if( isReadOnly ){ + pNew->ctrlFlags |= UNIXFILE_RDONLY; + } #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); @@ -4145,7 +4511,7 @@ static int fillInUnixFile( ** implicit assumption here is that if fstat() fails, things are in ** such bad shape that dropping a lock or two doesn't matter much. */ - close(h); + robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); @@ -4171,7 +4537,7 @@ static int fillInUnixFile( rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ sqlite3_free(pNew->lockingContext); - close(h); + robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); @@ -4222,7 +4588,7 @@ static int fillInUnixFile( pNew->lastErrno = 0; #if OS_VXWORKS if( rc!=SQLITE_OK ){ - if( h>=0 ) close(h); + if( h>=0 ) robust_close(pNew, h, __LINE__); h = -1; unlink(zFilename); isDelete = 0; @@ -4230,8 +4596,8 @@ static int fillInUnixFile( pNew->isDelete = isDelete; #endif if( rc!=SQLITE_OK ){ - if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ - if( h>=0 ) close(h); + if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__); + if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); @@ -4258,16 +4624,16 @@ static int openDirectory(const char *zFilename, int *pFd){ for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); if( ii>0 ){ zDirname[ii] = '\0'; - fd = open(zDirname, O_RDONLY|O_BINARY, 0); + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ #ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); } } *pFd = fd; - return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT); + return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname)); } /* @@ -4291,9 +4657,9 @@ static const char *unixTempFileDir(void){ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); for(i=0; ilastErrno = errno; - if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ - close(fd); /* silently leak if fail, in error */ + if( dirfd>=0 ) robust_close(p, dirfd, __LINE__); + robust_close(p, fd, __LINE__); return SQLITE_IOERR_ACCESS; } if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { @@ -4692,16 +5059,17 @@ static int unixOpen( ** the same file are working. */ p->lastErrno = errno; if( dirfd>=0 ){ - close(dirfd); /* silently leak if fail, in error */ + robust_close(p, dirfd, __LINE__); } - close(fd); /* silently leak if fail, in error */ + robust_close(p, fd, __LINE__); rc = SQLITE_IOERR_ACCESS; goto open_finished; } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, + isDelete, isReadonly); if( rc==SQLITE_OK ){ rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); if( rc!=SQLITE_OK ){ @@ -4718,7 +5086,8 @@ static int unixOpen( } #endif - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, + isDelete, isReadonly); open_finished: if( rc!=SQLITE_OK ){ sqlite3_free(p->pUnused); @@ -4740,7 +5109,7 @@ static int unixDelete( UNUSED_PARAMETER(NotUsed); SimulateIOError(return SQLITE_IOERR_DELETE); if( unlink(zPath)==(-1) && errno!=ENOENT ){ - return SQLITE_IOERR_DELETE; + return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); } #ifndef SQLITE_DISABLE_DIRSYNC if( dirSync ){ @@ -4753,11 +5122,9 @@ static int unixDelete( if( fsync(fd) ) #endif { - rc = SQLITE_IOERR_DIR_FSYNC; - } - if( close(fd)&&!rc ){ - rc = SQLITE_IOERR_DIR_CLOSE; + rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); } + robust_close(0, fd, __LINE__); } } #endif @@ -4797,7 +5164,7 @@ static int unixAccess( default: assert(!"Invalid flags argument"); } - *pResOut = (access(zPath, amode)==0); + *pResOut = (osAccess(zPath, amode)==0); if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){ struct stat buf; if( 0==stat(zPath, &buf) && buf.st_size==0 ){ @@ -4839,8 +5206,8 @@ static int unixFullPathname( sqlite3_snprintf(nOut, zOut, "%s", zPath); }else{ int nCwd; - if( getcwd(zOut, nOut-1)==0 ){ - return SQLITE_CANTOPEN_BKPT; + if( osGetcwd(zOut, nOut-1)==0 ){ + return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); } nCwd = (int)strlen(zOut); sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); @@ -4934,7 +5301,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ #if !defined(SQLITE_TEST) { int pid, fd; - fd = open("/dev/urandom", O_RDONLY); + fd = robust_open("/dev/urandom", O_RDONLY, 0); if( fd<0 ){ time_t t; time(&t); @@ -4944,8 +5311,8 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); nBuf = sizeof(t) + sizeof(pid); }else{ - nBuf = read(fd, zBuf, nBuf); - close(fd); + do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR ); + robust_close(0, fd, __LINE__); } } #endif @@ -5343,17 +5710,17 @@ static int proxyCreateUnixFile( } } if( fd<0 ){ - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; if( fd<0 && errno==ENOENT && islockfile ){ if( proxyCreateLockPath(path) == SQLITE_OK ){ - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); } } } if( fd<0 ){ openFlags = O_RDONLY; - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; } if( fd<0 ){ @@ -5377,18 +5744,20 @@ static int proxyCreateUnixFile( } memset(pNew, 0, sizeof(unixFile)); pNew->openFlags = openFlags; + memset(&dummyVfs, 0, sizeof(dummyVfs)); dummyVfs.pAppData = (void*)&autolockIoFinder; + dummyVfs.zName = "dummy"; pUnused->fd = fd; pUnused->flags = openFlags; pNew->pUnused = pUnused; - rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0); + rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0); if( rc==SQLITE_OK ){ *ppFile = pNew; return SQLITE_OK; } end_create_proxy: - close(fd); /* silently leak fd if error, we're already in error */ + robust_close(pNew, fd, __LINE__); sqlite3_free(pNew); sqlite3_free(pUnused); return rc; @@ -5467,18 +5836,19 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ goto end_breaklock; } /* read the conch content */ - readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); + readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); if( readLenh); + robust_close(pFile, conchFile->h, __LINE__); conchFile->h = fd; conchFile->openFlags = O_RDWR | O_CREAT; @@ -5496,7 +5866,7 @@ end_breaklock: if( rc ){ if( fd>=0 ){ unlink(tPath); - close(fd); + robust_close(pFile, fd, __LINE__); } fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); } @@ -5524,7 +5894,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ * 3rd try: break the lock unless the mod time has changed. */ struct stat buf; - if( fstat(conchFile->h, &buf) ){ + if( osFstat(conchFile->h, &buf) ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; } @@ -5543,7 +5913,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ if( nTries==2 ){ char tBuf[PROXY_MAXCONCHLEN]; - int len = pread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); + int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); if( len<0 ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; @@ -5705,7 +6075,7 @@ static int proxyTakeConch(unixFile *pFile){ strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); } writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); - ftruncate(conchFile->h, writeSize); + robust_ftruncate(conchFile->h, writeSize); rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0); fsync(conchFile->h); /* If we created a new conch file (not just updated the contents of a @@ -5713,15 +6083,18 @@ static int proxyTakeConch(unixFile *pFile){ */ if( rc==SQLITE_OK && createConch ){ struct stat buf; - int err = fstat(pFile->h, &buf); + int err = osFstat(pFile->h, &buf); if( err==0 ){ mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); /* try to match the database file R/W permissions, ignore failure */ #ifndef SQLITE_PROXY_DEBUG - fchmod(conchFile->h, cmode); + osFchmod(conchFile->h, cmode); #else - if( fchmod(conchFile->h, cmode)!=0 ){ + do{ + rc = osFchmod(conchFile->h, cmode); + }while( rc==(-1) && errno==EINTR ); + if( rc!=0 ){ int code = errno; fprintf(stderr, "fchmod %o FAILED with %d %s\n", cmode, code, strerror(code)); @@ -5742,17 +6115,10 @@ static int proxyTakeConch(unixFile *pFile){ OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); if( rc==SQLITE_OK && pFile->openFlags ){ if( pFile->h>=0 ){ -#ifdef STRICT_CLOSE_ERROR - if( close(pFile->h) ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_CLOSE; - } -#else - close(pFile->h); /* silently leak fd if fail */ -#endif + robust_close(pFile, pFile->h, __LINE__); } pFile->h = -1; - int fd = open(pCtx->dbPath, pFile->openFlags, + int fd = robust_open(pCtx->dbPath, pFile->openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); if( fd>=0 ){ @@ -5978,7 +6344,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { struct stat conchInfo; int goLockless = 0; - if( stat(pCtx->conchFilePath, &conchInfo) == -1 ) { + if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { int err = errno; if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; @@ -6263,7 +6629,7 @@ int sqlite3_os_init(void){ ** that filesystem time. */ #define UNIXVFS(VFSNAME, FINDER) { \ - 2, /* iVersion */ \ + 3, /* iVersion */ \ sizeof(unixFile), /* szOsFile */ \ MAX_PATHNAME, /* mxPathname */ \ 0, /* pNext */ \ @@ -6282,6 +6648,9 @@ int sqlite3_os_init(void){ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError, /* xGetLastError */ \ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ + unixSetSystemCall, /* xSetSystemCall */ \ + unixGetSystemCall, /* xGetSystemCall */ \ + unixNextSystemCall, /* xNextSystemCall */ \ } /* @@ -6299,6 +6668,7 @@ int sqlite3_os_init(void){ #endif UNIXVFS("unix-none", nolockIoFinder ), UNIXVFS("unix-dotfile", dotlockIoFinder ), + UNIXVFS("unix-excl", posixIoFinder ), #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif @@ -6316,6 +6686,10 @@ int sqlite3_os_init(void){ }; unsigned int i; /* Loop counter */ + /* Double-check that the aSyscall[] array has been constructed + ** correctly. See ticket [bb3a86e890c8e96ab] */ + assert( ArraySize(aSyscall)==16 ); + /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); diff --git a/src/os_win.c b/src/os_win.c index 7042517..c876833 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -2746,7 +2746,7 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { - 2, /* iVersion */ + 3, /* iVersion */ sizeof(winFile), /* szOsFile */ MAX_PATH, /* mxPathname */ 0, /* pNext */ @@ -2765,6 +2765,9 @@ int sqlite3_os_init(void){ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0, /* xNextSystemCall */ }; #ifndef SQLITE_OMIT_WAL diff --git a/src/pager.c b/src/pager.c index 80f3b0e..94f647d 100644 --- a/src/pager.c +++ b/src/pager.c @@ -2851,6 +2851,28 @@ static int readDbPage(PgHdr *pPg){ return rc; } +/* +** Update the value of the change-counter at offsets 24 and 92 in +** the header and the sqlite version number at offset 96. +** +** This is an unconditional update. See also the pager_incr_changecounter() +** routine which only updates the change-counter if the update is actually +** needed, as determined by the pPager->changeCountDone state variable. +*/ +static void pager_write_changecounter(PgHdr *pPg){ + u32 change_counter; + + /* Increment the value just read and write it back to byte 24. */ + change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; + put32bits(((char*)pPg->pData)+24, change_counter); + + /* Also store the SQLite version number in bytes 96..99 and in + ** bytes 92..95 store the change counter for which the version number + ** is valid. */ + put32bits(((char*)pPg->pData)+92, change_counter); + put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER); +} + #ifndef SQLITE_OMIT_WAL /* ** This function is invoked once for each page that has already been @@ -2921,34 +2943,11 @@ static int pagerRollbackWal(Pager *pPager){ return rc; } - -/* -** Update the value of the change-counter at offsets 24 and 92 in -** the header and the sqlite version number at offset 96. -** -** This is an unconditional update. See also the pager_incr_changecounter() -** routine which only updates the change-counter if the update is actually -** needed, as determined by the pPager->changeCountDone state variable. -*/ -static void pager_write_changecounter(PgHdr *pPg){ - u32 change_counter; - - /* Increment the value just read and write it back to byte 24. */ - change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; - put32bits(((char*)pPg->pData)+24, change_counter); - - /* Also store the SQLite version number in bytes 96..99 and in - ** bytes 92..95 store the change counter for which the version number - ** is valid. */ - put32bits(((char*)pPg->pData)+92, change_counter); - put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER); -} - /* ** This function is a wrapper around sqlite3WalFrames(). As well as logging ** the contents of the list of pages headed by pList (connected by pDirty), ** this function notifies any active backup processes that the pages have -** changed. +** changed. ** ** The list of pages passed into this routine is always sorted by page number. ** Hence, if page 1 appears anywhere on the list, it will be the first page. @@ -2973,6 +2972,19 @@ static int pagerWalFrames( } #endif + if( isCommit ){ + /* If a WAL transaction is being committed, there is no point in writing + ** any pages with page numbers greater than nTruncate into the WAL file. + ** They will never be read by any client. So remove them from the pDirty + ** list here. */ + PgHdr *p; + PgHdr **ppNext = &pList; + for(p=pList; (*ppNext = p); p=p->pDirty){ + if( p->pgno<=nTruncate ) ppNext = &p->pDirty; + } + assert( pList ); + } + if( pList->pgno==1 ) pager_write_changecounter(pList); rc = sqlite3WalFrames(pPager->pWal, pPager->pageSize, pList, nTruncate, isCommit, syncFlags @@ -2985,6 +2997,7 @@ static int pagerWalFrames( } #ifdef SQLITE_CHECK_PAGES + pList = sqlite3PcacheDirtyList(pPager->pPCache); for(p=pList; p; p=p->pDirty){ pager_set_pagehash(p); } @@ -6600,14 +6613,20 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ #ifndef SQLITE_OMIT_WAL /* -** This function is called when the user invokes "PRAGMA checkpoint". +** This function is called when the user invokes "PRAGMA wal_checkpoint", +** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() +** or wal_blocking_checkpoint() API functions. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ -int sqlite3PagerCheckpoint(Pager *pPager){ +int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; if( pPager->pWal ){ - u8 *zBuf = (u8 *)pPager->pTmpSpace; - rc = sqlite3WalCheckpoint(pPager->pWal, pPager->ckptSyncFlags, - pPager->pageSize, zBuf); + rc = sqlite3WalCheckpoint(pPager->pWal, eMode, + pPager->xBusyHandler, pPager->pBusyHandlerArg, + pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, + pnLog, pnCkpt + ); } return rc; } @@ -6635,8 +6654,8 @@ static int pagerExclusiveLock(Pager *pPager){ assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ - /* If the attempt to grab the pending lock failed, release the - ** exclusive lock that may have been obtained instead. */ + /* If the attempt to grab the exclusive lock failed, release the + ** pending lock that may have been obtained instead. */ pagerUnlockDb(pPager, SHARED_LOCK); } diff --git a/src/pager.h b/src/pager.h index e775b0c..eab7dda 100644 --- a/src/pager.h +++ b/src/pager.h @@ -138,7 +138,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); -int sqlite3PagerCheckpoint(Pager *pPager); +int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*); int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); diff --git a/src/pragma.c b/src/pragma.c index 3198543..75ab26d 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -115,7 +115,7 @@ static int invalidateTempStorage(Parse *pParse){ } sqlite3BtreeClose(db->aDb[1].pBt); db->aDb[1].pBt = 0; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } return SQLITE_OK; } @@ -384,11 +384,11 @@ void sqlite3Pragma( sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ - int size = sqlite3Atoi(zRight); - if( size<0 ) size = -size; + int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } @@ -691,11 +691,11 @@ void sqlite3Pragma( */ if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); }else{ - int size = sqlite3Atoi(zRight); - if( size<0 ) size = -size; + int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } @@ -1112,6 +1112,7 @@ void sqlite3Pragma( ** Begin by filling registers 2, 3, ... with the root pages numbers ** for all tables and indices in the database. */ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTbls = &db->aDb[i].pSchema->tblHash; for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); @@ -1177,7 +1178,7 @@ void sqlite3Pragma( addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr); sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC); sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT); sqlite3VdbeJumpHere(v, addr+9); sqlite3VdbeJumpHere(v, jmp2); } @@ -1207,7 +1208,7 @@ void sqlite3Pragma( sqlite3VdbeJumpHere(v, addr+4); sqlite3VdbeChangeP4(v, addr+6, "wrong # of entries in index ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT); } } } @@ -1386,13 +1387,29 @@ void sqlite3Pragma( #ifndef SQLITE_OMIT_WAL /* - ** PRAGMA [database.]wal_checkpoint + ** PRAGMA [database.]wal_checkpoint = passive|full|restart ** ** Checkpoint the database. */ if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){ + int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); + int eMode = SQLITE_CHECKPOINT_PASSIVE; + if( zRight ){ + if( sqlite3StrICmp(zRight, "full")==0 ){ + eMode = SQLITE_CHECKPOINT_FULL; + }else if( sqlite3StrICmp(zRight, "restart")==0 ){ + eMode = SQLITE_CHECKPOINT_RESTART; + } + } if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeAddOp3(v, OP_Checkpoint, pId2->z?iDb:SQLITE_MAX_ATTACHED, 0, 0); + sqlite3VdbeSetNumCols(v, 3); + pParse->nMem = 3; + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC); + + sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1); + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); }else /* diff --git a/src/prepare.c b/src/prepare.c index 62a33b6..fc45b8e 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -34,7 +34,7 @@ static void corruptSchema( "%s - %s", *pData->pzErrMsg, zExtra); } } - pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT; + pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT; } /* @@ -141,7 +141,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ int meta[5]; InitData initData; char const *zMasterSchema; - char const *zMasterName = SCHEMA_TABLE(iDb); + char const *zMasterName; int openedTransaction = 0; /* @@ -278,9 +278,8 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ pDb->pSchema->enc = ENC(db); if( pDb->pSchema->cache_size==0 ){ - size = meta[BTREE_DEFAULT_CACHE_SIZE-1]; + size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]); if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } - if( size<0 ) size = -size; pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } @@ -339,7 +338,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ } if( db->mallocFailed ){ rc = SQLITE_NOMEM; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider @@ -471,7 +470,9 @@ static void schemaIsValid(Parse *pParse){ ** value stored as part of the in-memory schema representation, ** set Parse.rc to SQLITE_SCHEMA. */ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ + sqlite3ResetInternalSchema(db, iDb); pParse->rc = SQLITE_SCHEMA; } @@ -613,9 +614,6 @@ static int sqlite3Prepare( if( pParse->checkSchema ){ schemaIsValid(pParse); } - if( pParse->rc==SQLITE_SCHEMA ){ - sqlite3ResetInternalSchema(db, 0); - } if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } diff --git a/src/printf.c b/src/printf.c index c88bb30..2a3dd81 100644 --- a/src/printf.c +++ b/src/printf.c @@ -400,7 +400,11 @@ void sqlite3VXPrintf( v = va_arg(ap,int); } if( v<0 ){ - longvalue = -v; + if( v==SMALLEST_INT64 ){ + longvalue = ((u64)1)<<63; + }else{ + longvalue = -v; + } prefix = '-'; }else{ longvalue = v; diff --git a/src/select.c b/src/select.c index 6f64df8..3a4a881 100644 --- a/src/select.c +++ b/src/select.c @@ -806,6 +806,22 @@ static void explainTempTable(Parse *pParse, const char *zUsage){ } } +/* +** Assign expression b to lvalue a. A second, no-op, version of this macro +** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code +** in sqlite3Select() to assign values to structure member variables that +** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the +** code with #ifndef directives. +*/ +# define explainSetInteger(a, b) a = b + +#else +/* No-op versions of the explainXXX() functions and macros. */ +# define explainTempTable(y,z) +# define explainSetInteger(y,z) +#endif + +#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT) /* ** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function ** is a no-op. Otherwise, it adds a single row of output to the EQP result, @@ -837,21 +853,9 @@ static void explainComposite( sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } } - -/* -** Assign expression b to lvalue a. A second, no-op, version of this macro -** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code -** in sqlite3Select() to assign values to structure member variables that -** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the -** code with #ifndef directives. -*/ -# define explainSetInteger(a, b) a = b - #else /* No-op versions of the explainXXX() functions and macros. */ -# define explainTempTable(y,z) # define explainComposite(v,w,x,y,z) -# define explainSetInteger(y,z) #endif /* @@ -2652,6 +2656,9 @@ static void substSelect( ** appear as unmodified result columns in the outer query. But ** have other optimizations in mind to deal with that case. ** +** (21) The subquery does not use LIMIT or the outer query is not +** DISTINCT. (See ticket [752e1646fc]). +** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. @@ -2720,6 +2727,9 @@ static int flattenSubquery( } if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */ if( pSub->pLimit && p->pWhere ) return 0; /* Restriction (19) */ + if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){ + return 0; /* Restriction (21) */ + } /* OBSOLETE COMMENT 1: ** Restriction 3: If the subquery is a join, make sure the subquery is @@ -3612,6 +3622,32 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ sqlite3ExprCacheClear(pParse); } +/* +** Add a single OP_Explain instruction to the VDBE to explain a simple +** count(*) query ("SELECT count(*) FROM pTab"). +*/ +#ifndef SQLITE_OMIT_EXPLAIN +static void explainSimpleCount( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being queried */ + Index *pIdx /* Index used to optimize scan, or NULL */ +){ + if( pParse->explain==2 ){ + char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)", + pTab->zName, + pIdx ? "USING COVERING INDEX " : "", + pIdx ? pIdx->zName : "", + pTab->nRowEst + ); + sqlite3VdbeAddOp4( + pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC + ); + } +} +#else +# define explainSimpleCount(a,b,c) +#endif + /* ** Generate code for the SELECT statement given in the p argument. ** @@ -4223,6 +4259,7 @@ int sqlite3Select( } sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); + explainSimpleCount(pParse, pTab, pBest); }else #endif /* SQLITE_OMIT_BTREECOUNT */ { diff --git a/src/shell.c b/src/shell.c index 33dc1ac..aab70b2 100644 --- a/src/shell.c +++ b/src/shell.c @@ -71,6 +71,9 @@ extern int isatty(); #define isatty(x) 1 #endif +/* True if the timer is enabled */ +static int enableTimer = 0; + #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL) #include #include @@ -78,9 +81,6 @@ extern int isatty(); /* Saved resource information for the beginning of an operation */ static struct rusage sBegin; -/* True if the timer is enabled */ -static int enableTimer = 0; - /* ** Begin timing an operation */ @@ -124,9 +124,6 @@ static FILETIME ftUserBegin; typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); static GETPROCTIMES getProcessTimesAddr = NULL; -/* True if the timer is enabled */ -static int enableTimer = 0; - /* ** Check to see if we have timer support. Return 1 if necessary ** support found (or found previously). @@ -419,6 +416,7 @@ struct callback_data { ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ + const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ }; @@ -1850,7 +1848,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ }else #endif - if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=1 ){ + if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){ const char *zFile = azArg[1]; if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){ fclose(p->pLog); @@ -2171,12 +2169,136 @@ static int do_meta_command(char *zLine, struct callback_data *p){ sqlite3_free_table(azResult); }else + if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ + static const struct { + const char *zCtrlName; /* Name of a test-control option */ + int ctrlCode; /* Integer code for that option */ + } aCtrl[] = { + { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE }, + { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE }, + { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET }, + { "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST }, + { "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL }, + { "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS }, + { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, + { "assert", SQLITE_TESTCTRL_ASSERT }, + { "always", SQLITE_TESTCTRL_ALWAYS }, + { "reserve", SQLITE_TESTCTRL_RESERVE }, + { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, + { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, + { "pghdrsz", SQLITE_TESTCTRL_PGHDRSZ }, + { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, + }; + int testctrl = -1; + int rc = 0; + int i, n; + open_db(p); + + /* convert testctrl text option to value. allow any unique prefix + ** of the option name, or a numerical value. */ + n = strlen30(azArg[1]); + for(i=0; i<(int)(sizeof(aCtrl)/sizeof(aCtrl[0])); i++){ + if( strncmp(azArg[1], aCtrl[i].zCtrlName, n)==0 ){ + if( testctrl<0 ){ + testctrl = aCtrl[i].ctrlCode; + }else{ + fprintf(stderr, "ambiguous option name: \"%s\"\n", azArg[i]); + testctrl = -1; + break; + } + } + } + if( testctrl<0 ) testctrl = atoi(azArg[1]); + if( (testctrlSQLITE_TESTCTRL_LAST) ){ + fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]); + }else{ + switch(testctrl){ + + /* sqlite3_test_control(int, db, int) */ + case SQLITE_TESTCTRL_OPTIMIZATIONS: + case SQLITE_TESTCTRL_RESERVE: + if( nArg==3 ){ + int opt = (int)strtol(azArg[2], 0, 0); + rc = sqlite3_test_control(testctrl, p->db, opt); + printf("%d (0x%08x)\n", rc, rc); + } else { + fprintf(stderr,"Error: testctrl %s takes a single int option\n", + azArg[1]); + } + break; + + /* sqlite3_test_control(int) */ + case SQLITE_TESTCTRL_PRNG_SAVE: + case SQLITE_TESTCTRL_PRNG_RESTORE: + case SQLITE_TESTCTRL_PRNG_RESET: + case SQLITE_TESTCTRL_PGHDRSZ: + if( nArg==2 ){ + rc = sqlite3_test_control(testctrl); + printf("%d (0x%08x)\n", rc, rc); + } else { + fprintf(stderr,"Error: testctrl %s takes no options\n", azArg[1]); + } + break; + + /* sqlite3_test_control(int, uint) */ + case SQLITE_TESTCTRL_PENDING_BYTE: + if( nArg==3 ){ + unsigned int opt = (unsigned int)atoi(azArg[2]); + rc = sqlite3_test_control(testctrl, opt); + printf("%d (0x%08x)\n", rc, rc); + } else { + fprintf(stderr,"Error: testctrl %s takes a single unsigned" + " int option\n", azArg[1]); + } + break; + + /* sqlite3_test_control(int, int) */ + case SQLITE_TESTCTRL_ASSERT: + case SQLITE_TESTCTRL_ALWAYS: + if( nArg==3 ){ + int opt = atoi(azArg[2]); + rc = sqlite3_test_control(testctrl, opt); + printf("%d (0x%08x)\n", rc, rc); + } else { + fprintf(stderr,"Error: testctrl %s takes a single int option\n", + azArg[1]); + } + break; + + /* sqlite3_test_control(int, char *) */ +#ifdef SQLITE_N_KEYWORD + case SQLITE_TESTCTRL_ISKEYWORD: + if( nArg==3 ){ + const char *opt = azArg[2]; + rc = sqlite3_test_control(testctrl, opt); + printf("%d (0x%08x)\n", rc, rc); + } else { + fprintf(stderr,"Error: testctrl %s takes a single char * option\n", + azArg[1]); + } + break; +#endif + + case SQLITE_TESTCTRL_BITVEC_TEST: + case SQLITE_TESTCTRL_FAULT_INSTALL: + case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: + case SQLITE_TESTCTRL_SCRATCHMALLOC: + default: + fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", + azArg[1]); + break; + } + } + }else + if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){ open_db(p); sqlite3_busy_timeout(p->db, atoi(azArg[1])); }else - if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg==2 ){ + if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 + && nArg==2 + ){ enableTimer = booleanValue(azArg[1]); }else @@ -2363,7 +2485,9 @@ static int process_input(struct callback_data *p, FILE *in){ } } if( zSql ){ - if( !_all_whitespace(zSql) ) fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); + if( !_all_whitespace(zSql) ){ + fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); + } free(zSql); } free(zLine); @@ -2499,6 +2623,10 @@ static const char zOptions[] = " -stats print memory stats before each finalize\n" " -nullvalue 'text' set text string for NULL values\n" " -version show SQLite version\n" + " -vfs NAME use NAME as the default VFS\n" +#ifdef SQLITE_ENABLE_VFSTRACE + " -vfstrace enable tracing of all VFS calls\n" +#endif ; static void usage(int showDetail){ fprintf(stderr, @@ -2583,6 +2711,25 @@ int main(int argc, char **argv){ #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #endif +#ifdef SQLITE_ENABLE_VFSTRACE + }else if( strcmp(argv[i],"-vfstrace")==0 ){ + extern int vfstrace_register( + const char *zTraceName, + const char *zOldVfsName, + int (*xOut)(const char*,void*), + void *pOutArg, + int makeDefault + ); + vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); +#endif + }else if( strcmp(argv[i],"-vfs")==0 ){ + sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]); + if( pVfs ){ + sqlite3_vfs_register(pVfs, 1); + }else{ + fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]); + exit(1); + } } } if( iSQLITE_CONFIG_SCRATCH **
^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte -** aligned memory buffer from which the scrach allocations will be +** aligned memory buffer from which the scratch allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. @@ -1339,7 +1356,9 @@ struct sqlite3_mem_methods { ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte -** boundary or subsequent behavior of SQLite will be undefined.
+** boundary or subsequent behavior of SQLite will be undefined. +** The minimum allocation size is capped at 2^12. Reasonable values +** for the minimum allocation size are 2^5 through 2^8. ** **
SQLITE_CONFIG_MUTEX
**
^(This option takes a single argument which is a pointer to an @@ -1442,7 +1461,7 @@ struct sqlite3_mem_methods { **
^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an memory buffer to use for lookaside memory. +** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the @@ -1460,9 +1479,31 @@ struct sqlite3_mem_methods { ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^
** +**
SQLITE_DBCONFIG_ENABLE_FKEY
+**
^This option is used to enable or disable the enforcement of +** [foreign key constraints]. There should be two additional arguments. +** The first argument is an integer which is 0 to disable FK enforcement, +** positive to enable FK enforcement or negative to leave FK enforcement +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether FK enforcement is off or on +** following this call. The second parameter may be a NULL pointer, in +** which case the FK enforcement setting is not reported back.
+** +**
SQLITE_DBCONFIG_ENABLE_TRIGGER
+**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. +** There should be two additional arguments. +** The first argument is an integer which is 0 to disable triggers, +** positive to enable triggers or negative to leave the setting unchanged. +** The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether triggers are disabled or enabled +** following this call. The second parameter may be a NULL pointer, in +** which case the trigger setting is not reported back.
+** ** */ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ /* @@ -2064,7 +2105,7 @@ void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** -** ^This routine registers a authorizer callback with a particular +** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], @@ -2666,7 +2707,7 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database ** -** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if +** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if ** and only if the [prepared statement] X makes no direct changes to ** the content of the database file. ** @@ -2710,7 +2751,7 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); ** whether or not it requires a protected sqlite3_value. ** ** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. A internal mutex is held for a protected +** a mutex is held. An internal mutex is held for a protected ** sqlite3_value object but no mutex is held for an unprotected ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) @@ -2934,7 +2975,9 @@ int sqlite3_column_count(sqlite3_stmt *pStmt); ** column number. ^The leftmost column is number 0. ** ** ^The returned string pointer is valid until either the [prepared statement] -** is destroyed by [sqlite3_finalize()] or until the next call to +** is destroyed by [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** ** ^If sqlite3_malloc() fails during the processing of either routine @@ -2960,7 +3003,9 @@ const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed -** using [sqlite3_finalize()] or until the same information is requested +** using [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the same information is requested ** again in a different encoding. ** ** ^The names returned are the original un-aliased names of the @@ -3392,7 +3437,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt); ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only differences between ** these routines are the text encoding expected for -** the the second parameter (the name of the function being created) +** the second parameter (the name of the function being created) ** and the presence or absence of a destructor callback for ** the application data pointer. ** @@ -3437,7 +3482,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt); ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL poiners for all three function +** SQL function or aggregate, pass NULL pointers for all three function ** callbacks. ** ** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, @@ -3871,7 +3916,7 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n); ** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin ** on an even byte address. ** -** ^The fourth argument, pArg, is a application data pointer that is passed +** ^The fourth argument, pArg, is an application data pointer that is passed ** through as the first argument to the collating function callback. ** ** ^The fifth argument, xCallback, is a pointer to the collating function. @@ -3887,7 +3932,7 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n); ** by the eTextRep argument. The collating function must return an ** integer that is negative, zero, or positive ** if the first string is less than, equal to, or greater than the second, -** respectively. A collating function must alway return the same answer +** respectively. A collating function must always return the same answer ** given the same inputs. If two or more collating functions are registered ** to the same collation name (using different eTextRep values) then all ** must give an equivalent answer when invoked with equivalent strings. @@ -4299,7 +4344,7 @@ int sqlite3_release_memory(int); **
  • Memory accounting is disabled using a combination of the ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. -**
  • An alternative page cache implementation is specifed using +**
  • An alternative page cache implementation is specified using ** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). **
  • The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than @@ -4520,7 +4565,7 @@ typedef struct sqlite3_module sqlite3_module; ** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** -** This structure, sometimes called a a "virtual table module", +** This structure, sometimes called a "virtual table module", ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** @@ -4832,7 +4877,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. +** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ @@ -5534,24 +5579,21 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); ** ^(
    SQLITE_DBSTATUS_LOOKASIDE_HIT
    **
    This parameter returns the number malloc attempts that were ** satisfied using lookaside memory. Only the high-water value is meaningful; -** the current value is always zero. -** checked out.
    )^ +** the current value is always zero.)^ ** ** ^(
    SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
    **
    This parameter returns the number malloc attempts that might have ** been satisfied using lookaside memory but failed due to the amount of ** memory requested being larger than the lookaside slot size. ** Only the high-water value is meaningful; -** the current value is always zero. -** checked out.
    )^ +** the current value is always zero.)^ ** ** ^(
    SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
    **
    This parameter returns the number malloc attempts that might have ** been satisfied using lookaside memory but failed due to all lookaside ** memory already being in use. ** Only the high-water value is meaningful; -** the current value is always zero. -** checked out.
    )^ +** the current value is always zero.)^ ** ** ^(
    SQLITE_DBSTATUS_CACHE_USED
    **
    This parameter returns the approximate number of of bytes of heap @@ -6250,9 +6292,102 @@ int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); ** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] can be used to cause this interface to be ** run whenever the WAL reaches a certain size threshold. +** +** See also: [sqlite3_wal_checkpoint_v2()] */ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); +/* +** CAPI3REF: Checkpoint a database +** +** Run a checkpoint operation on WAL database zDb attached to database +** handle db. The specific operation is determined by the value of the +** eMode parameter: +** +**
    +**
    SQLITE_CHECKPOINT_PASSIVE
    +** Checkpoint as many frames as possible without waiting for any database +** readers or writers to finish. Sync the db file if all frames in the log +** are checkpointed. This mode is the same as calling +** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. +** +**
    SQLITE_CHECKPOINT_FULL
    +** This mode blocks (calls the busy-handler callback) until there is no +** database writer and all readers are reading from the most recent database +** snapshot. It then checkpoints all frames in the log file and syncs the +** database file. This call blocks database writers while it is running, +** but not database readers. +** +**
    SQLITE_CHECKPOINT_RESTART
    +** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after +** checkpointing the log file it blocks (calls the busy-handler callback) +** until all readers are reading from the database file only. This ensures +** that the next client to write to the database file restarts the log file +** from the beginning. This call blocks database writers while it is running, +** but not database readers. +**
    +** +** If pnLog is not NULL, then *pnLog is set to the total number of frames in +** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to +** the total number of checkpointed frames (including any that were already +** checkpointed when this function is called). *pnLog and *pnCkpt may be +** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. +** If no values are available because of an error, they are both set to -1 +** before returning to communicate this to the caller. +** +** All calls obtain an exclusive "checkpoint" lock on the database file. If +** any other process is running a checkpoint operation at the same time, the +** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a +** busy-handler configured, it will not be invoked in this case. +** +** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive +** "writer" lock on the database file. If the writer lock cannot be obtained +** immediately, and a busy-handler is configured, it is invoked and the writer +** lock retried until either the busy-handler returns 0 or the lock is +** successfully obtained. The busy-handler is also invoked while waiting for +** database readers as described above. If the busy-handler returns 0 before +** the writer lock is obtained or while waiting for database readers, the +** checkpoint operation proceeds from that point in the same way as +** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible +** without blocking any further. SQLITE_BUSY is returned in this case. +** +** If parameter zDb is NULL or points to a zero length string, then the +** specified operation is attempted on all WAL databases. In this case the +** values written to output parameters *pnLog and *pnCkpt are undefined. If +** an SQLITE_BUSY error is encountered when processing one or more of the +** attached WAL databases, the operation is still attempted on any remaining +** attached databases and SQLITE_BUSY is returned to the caller. If any other +** error occurs while processing an attached database, processing is abandoned +** and the error code returned to the caller immediately. If no error +** (SQLITE_BUSY or otherwise) is encountered while processing the attached +** databases, SQLITE_OK is returned. +** +** If database zDb is the name of an attached database that is not in WAL +** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If +** zDb is not NULL (or a zero length string) and is not the name of any +** attached database, SQLITE_ERROR is returned to the caller. +*/ +int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +); + +/* +** CAPI3REF: Checkpoint operation parameters +** +** These constants can be used as the 3rd parameter to +** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] +** documentation for additional information about the meaning and use of +** each of these values. +*/ +#define SQLITE_CHECKPOINT_PASSIVE 0 +#define SQLITE_CHECKPOINT_FULL 1 +#define SQLITE_CHECKPOINT_RESTART 2 + + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 41c46fa..ea0925e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -668,9 +668,24 @@ struct Db { /* ** An instance of the following structure stores a database schema. +** +** Most Schema objects are associated with a Btree. The exception is +** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** In shared cache mode, a single Schema object can be shared by multiple +** Btrees that refer to the same underlying BtShared object. +** +** Schema objects are automatically deallocated when the last Btree that +** references them is destroyed. The TEMP Schema is manually freed by +** sqlite3_close(). +* +** A thread must be holding a mutex on the corresponding Btree in order +** to access Schema content. This implies that the thread must also be +** holding a mutex on the sqlite3 connection pointer that owns the Btree. +** For a TEMP Schema, on the connection mutex is required. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ + int iGeneration; /* Generation counter. Incremented with each change */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ @@ -924,6 +939,7 @@ struct sqlite3 { #define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */ #define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */ #define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */ +#define SQLITE_EnableTrigger 0x40000000 /* True to enable triggers */ /* ** Bits of the sqlite3.flags field that are used by the @@ -1182,7 +1198,7 @@ struct CollSeq { ** schema is shared, as the implementation often stores the database ** connection handle passed to it via the xConnect() or xCreate() method ** during initialization internally. This database connection handle may -** then used by the virtual table implementation to access real tables +** then be used by the virtual table implementation to access real tables ** within the database. So that they appear as part of the callers ** transaction, these accesses need to be made via the same database ** connection as that used to execute SQL operations on the virtual table. @@ -1460,6 +1476,7 @@ struct Index { int tnum; /* Page containing root of this index in database file */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 bUnordered; /* Use this index for == or IN queries only */ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ @@ -1623,7 +1640,7 @@ struct Expr { u16 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ - int iValue; /* Integer value if EP_IntValue */ + int iValue; /* Non-negative integer value if EP_IntValue */ } u; /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no @@ -2123,6 +2140,15 @@ struct TriggerPrg { TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ }; +/* +** The yDbMask datatype for the bitmask of all attached databases. +*/ +#if SQLITE_MAX_ATTACHED>30 + typedef sqlite3_uint64 yDbMask; +#else + typedef unsigned int yDbMask; +#endif + /* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to @@ -2171,8 +2197,8 @@ struct Parse { int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ - u32 writeMask; /* Start a write transaction on these databases */ - u32 cookieMask; /* Bitmask of schema verified databases */ + yDbMask writeMask; /* Start a write transaction on these databases */ + yDbMask cookieMask; /* Bitmask of schema verified databases */ u8 isMultiWrite; /* True if statement may affect/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ @@ -2742,6 +2768,7 @@ void sqlite3PrngRestoreState(void); void sqlite3PrngResetState(void); void sqlite3RollbackAll(sqlite3*); void sqlite3CodeVerifySchema(Parse*, int); +void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); void sqlite3BeginTransaction(Parse*, int); void sqlite3CommitTransaction(Parse*); void sqlite3RollbackTransaction(Parse*); @@ -2903,6 +2930,10 @@ Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*); int sqlite3CheckCollSeq(Parse *, CollSeq *); int sqlite3CheckObjectName(Parse *, const char *); void sqlite3VdbeSetChanges(sqlite3 *, int); +int sqlite3AddInt64(i64*,i64); +int sqlite3SubInt64(i64*,i64); +int sqlite3MulInt64(i64*,i64); +int sqlite3AbsInt32(int); const void *sqlite3ValueText(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8); @@ -2927,7 +2958,7 @@ extern SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; extern int sqlite3PendingByte; #endif #endif -void sqlite3RootPageMoved(Db*, int, int); +void sqlite3RootPageMoved(sqlite3*, int, int, int); void sqlite3Reindex(Parse*, Token*, Token*); void sqlite3AlterFunctions(void); void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); @@ -2954,7 +2985,7 @@ void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); void sqlite3MinimumFileFormat(Parse*, int, int); -void sqlite3SchemaFree(void *); +void sqlite3SchemaClear(void *); Schema *sqlite3SchemaGet(sqlite3 *, Btree *); int sqlite3SchemaToIndex(sqlite3 *db, Schema *); KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); @@ -3041,7 +3072,7 @@ CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); VTable *sqlite3GetVTable(sqlite3*, Table*); const char *sqlite3JournalModename(int); -int sqlite3Checkpoint(sqlite3*, int); +int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); /* Declarations for functions in fkey.c. All of these are replaced by diff --git a/src/sqliteLimit.h b/src/sqliteLimit.h index 62772cf..c7aee53 100644 --- a/src/sqliteLimit.h +++ b/src/sqliteLimit.h @@ -118,7 +118,7 @@ /* ** The maximum number of attached databases. This must be between 0 -** and 30. The upper bound on 30 is because a 32-bit integer bitmap +** and 62. The upper bound on 62 is because a 64-bit integer bitmap ** is used internally to track attached databases. */ #ifndef SQLITE_MAX_ATTACHED diff --git a/src/status.c b/src/status.c index 96759fd..b8c1d58 100644 --- a/src/status.c +++ b/src/status.c @@ -163,6 +163,7 @@ int sqlite3_db_status( int i; /* Used to iterate through schemas */ int nByte = 0; /* Used to accumulate return value */ + sqlite3BtreeEnterAll(db); db->pnBytesFreed = &nByte; for(i=0; inDb; i++){ Schema *pSchema = db->aDb[i].pSchema; @@ -189,6 +190,7 @@ int sqlite3_db_status( } } db->pnBytesFreed = 0; + sqlite3BtreeLeaveAll(db); *pHighwater = 0; *pCurrent = nByte; diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 99939c1..575651d 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2468,7 +2468,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else{ pDb->zProfile = 0; } -#ifndef SQLITE_OMIT_TRACE +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) if( pDb->zProfile ){ pDb->interp = interp; sqlite3_profile(pDb->db, DbProfileHandler, pDb); @@ -2652,7 +2652,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else{ pDb->zTrace = 0; } -#ifndef SQLITE_OMIT_TRACE +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) if( pDb->zTrace ){ pDb->interp = interp; sqlite3_trace(pDb->db, DbTraceHandler, pDb); @@ -3581,6 +3581,9 @@ static void init_all(Tcl_Interp *interp){ extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); + extern int SqlitetestSyscall_Init(Tcl_Interp*); + extern int Sqlitetestfuzzer_Init(Tcl_Interp*); + extern int Sqlitetestwholenumber_Init(Tcl_Interp*); #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); @@ -3618,6 +3621,9 @@ static void init_all(Tcl_Interp *interp){ Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); + SqlitetestSyscall_Init(interp); + Sqlitetestfuzzer_Init(interp); + Sqlitetestwholenumber_Init(interp); Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0); diff --git a/src/test1.c b/src/test1.c index cf7e06b..8a0d09a 100644 --- a/src/test1.c +++ b/src/test1.c @@ -4888,6 +4888,44 @@ static int file_control_chunksize_test( return TCL_OK; } +/* +** tclcmd: file_control_sizehint_test DB DBNAME SIZE +** +** This TCL command runs the sqlite3_file_control interface and +** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and +** SQLITE_SET_LOCKPROXYFILE verbs. +*/ +static int file_control_sizehint_test( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3_int64 nSize; /* Hinted size */ + char *zDb; /* Db name ("main", "temp" etc.) */ + sqlite3 *db; /* Database handle */ + int rc; /* file_control() return code */ + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SIZE"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) + || Tcl_GetWideIntFromObj(interp, objv[3], &nSize) + ){ + return TCL_ERROR; + } + zDb = Tcl_GetString(objv[2]); + if( zDb[0]=='\0' ) zDb = NULL; + + rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_SIZE_HINT, (void *)&nSize); + if( rc ){ + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + return TCL_ERROR; + } + return TCL_OK; +} + /* ** tclcmd: file_control_lockproxy_test DB PWD ** @@ -5186,6 +5224,73 @@ static int test_wal_checkpoint( return TCL_OK; } +/* +** tclcmd: sqlite3_wal_checkpoint_v2 db MODE ?NAME? +** +** This command calls the wal_checkpoint_v2() function with the specified +** mode argument (passive, full or restart). If present, the database name +** NAME is passed as the second argument to wal_checkpoint_v2(). If it the +** NAME argument is not present, a NULL pointer is passed instead. +** +** If wal_checkpoint_v2() returns any value other than SQLITE_BUSY or +** SQLITE_OK, then this command returns TCL_ERROR. The Tcl result is set +** to the error message obtained from sqlite3_errmsg(). +** +** Otherwise, this command returns a list of three integers. The first integer +** is 1 if SQLITE_BUSY was returned, or 0 otherwise. The following two integers +** are the values returned via the output paramaters by wal_checkpoint_v2() - +** the number of frames in the log and the number of frames in the log +** that have been checkpointed. +*/ +static int test_wal_checkpoint_v2( + ClientData clientData, /* Unused */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + char *zDb = 0; + sqlite3 *db; + int rc; + + int eMode; + int nLog = -555; + int nCkpt = -555; + Tcl_Obj *pRet; + + const char * aMode[] = { "passive", "full", "restart", 0 }; + assert( SQLITE_CHECKPOINT_PASSIVE==0 ); + assert( SQLITE_CHECKPOINT_FULL==1 ); + assert( SQLITE_CHECKPOINT_RESTART==2 ); + + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?"); + return TCL_ERROR; + } + + if( objc==4 ){ + zDb = Tcl_GetString(objv[3]); + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) + || Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode) + ){ + return TCL_ERROR; + } + + rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt); + if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ + Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE); + return TCL_ERROR; + } + + pRet = Tcl_NewObj(); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(rc==SQLITE_BUSY?1:0)); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nLog)); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCkpt)); + Tcl_SetObjResult(interp, pRet); + + return TCL_OK; +} + /* ** tclcmd: test_sqlite3_log ?SCRIPT? */ @@ -5541,6 +5646,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "file_control_lasterrno_test", file_control_lasterrno_test, 0 }, { "file_control_lockproxy_test", file_control_lockproxy_test, 0 }, { "file_control_chunksize_test", file_control_chunksize_test, 0 }, + { "file_control_sizehint_test", file_control_sizehint_test, 0 }, { "sqlite3_vfs_list", vfs_list, 0 }, { "sqlite3_create_function_v2", test_create_function_v2, 0 }, @@ -5572,8 +5678,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_unlock_notify", test_unlock_notify, 0 }, #endif { "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 }, + { "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 }, { "test_sqlite3_log", test_sqlite3_log, 0 }, +#ifndef SQLITE_OMIT_EXPLAIN { "print_explain_query_plan", test_print_eqp, 0 }, +#endif }; static int bitmask_size = sizeof(Bitmask)*8; int i; diff --git a/src/test4.c b/src/test4.c index 49447d3..5c94370 100644 --- a/src/test4.c +++ b/src/test4.c @@ -13,7 +13,7 @@ */ #include "sqliteInt.h" #include "tcl.h" -#if defined(SQLITE_OS_UNIX) && OS_UNIX==1 && SQLITE_THREADSAFE +#if SQLITE_OS_UNIX && SQLITE_THREADSAFE #include #include #include diff --git a/src/test7.c b/src/test7.c index 3383d86..852cd1d 100644 --- a/src/test7.c +++ b/src/test7.c @@ -20,7 +20,7 @@ ** the SQLITE_SERVER option. */ #if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \ - defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE + SQLITE_OS_UNIX && SQLITE_THREADSAFE #include #include diff --git a/src/test_config.c b/src/test_config.c index def12a9..03d2f4e 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -91,6 +91,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "mutex", "1", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_MUTEX_NOOP + Tcl_SetVar2(interp, "sqlite_options", "mutex_noop", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "mutex_noop", "0", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_ALTERTABLE Tcl_SetVar2(interp, "sqlite_options", "altertable", "0", TCL_GLOBAL_ONLY); #else diff --git a/src/test_demovfs.c b/src/test_demovfs.c index 8c51192..e56e806 100644 --- a/src/test_demovfs.c +++ b/src/test_demovfs.c @@ -115,7 +115,7 @@ ** operations. */ -#if !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX) +#if !defined(SQLITE_TEST) || SQLITE_OS_UNIX #include @@ -637,14 +637,14 @@ sqlite3_vfs *sqlite3_demovfs(void){ return &demovfs; } -#endif /* !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX) */ +#endif /* !defined(SQLITE_TEST) || SQLITE_OS_UNIX */ #ifdef SQLITE_TEST #include -#ifdef SQLITE_OS_UNIX +#if SQLITE_OS_UNIX static int register_demovfs( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ diff --git a/src/test_func.c b/src/test_func.c index c3277a0..a123943 100644 --- a/src/test_func.c +++ b/src/test_func.c @@ -149,8 +149,13 @@ static void test_destructor_count( ** arguments. It returns the text value returned by the sqlite3_errmsg16() ** API function. */ +#ifndef SQLITE_OMIT_BUILTIN_TEST void sqlite3BeginBenignMalloc(void); void sqlite3EndBenignMalloc(void); +#else + #define sqlite3BeginBenignMalloc() + #define sqlite3EndBenignMalloc() +#endif static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){ } static void test_agg_errmsg16_final(sqlite3_context *ctx){ diff --git a/src/test_fuzzer.c b/src/test_fuzzer.c new file mode 100644 index 0000000..cf59257 --- /dev/null +++ b/src/test_fuzzer.c @@ -0,0 +1,944 @@ +/* +** 2011 March 24 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** Code for demonstartion virtual table that generates variations +** on an input word at increasing edit distances from the original. +** +** A fuzzer virtual table is created like this: +** +** CREATE VIRTUAL TABLE temp.f USING fuzzer; +** +** The name of the new virtual table in the example above is "f". +** Note that all fuzzer virtual tables must be TEMP tables. The +** "temp." prefix in front of the table name is required when the +** table is being created. The "temp." prefix can be omitted when +** using the table as long as the name is unambiguous. +** +** Before being used, the fuzzer needs to be programmed by giving it +** character transformations and a cost associated with each transformation. +** Examples: +** +** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100); +** +** The above statement says that the cost of inserting a letter 'a' is +** 100. (All costs are integers. We recommend that costs be scaled so +** that the average cost is around 100.) +** +** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87); +** +** The above statement says that the cost of deleting a single letter +** 'b' is 87. +** +** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38); +** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40); +** +** This third example says that the cost of transforming the single +** letter "o" into the two-letter sequence "oe" is 38 and that the +** cost of transforming "oe" back into "o" is 40. +** +** After all the transformation costs have been set, the fuzzer table +** can be queried as follows: +** +** SELECT word, distance FROM f +** WHERE word MATCH 'abcdefg' +** AND distance<200; +** +** This first query outputs the string "abcdefg" and all strings that +** can be derived from that string by appling the specified transformations. +** The strings are output together with their total transformation cost +** (called "distance") and appear in order of increasing cost. No string +** is output more than once. If there are multiple ways to transform the +** target string into the output string then the lowest cost transform is +** the one that is returned. In the example, the search is limited to +** strings with a total distance of less than 200. +** +** It is important to put some kind of a limit on the fuzzer output. This +** can be either in the form of a LIMIT clause at the end of the query, +** or better, a "distance +#include +#include +#include + +#ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** Forward declaration of objects used by this implementation +*/ +typedef struct fuzzer_vtab fuzzer_vtab; +typedef struct fuzzer_cursor fuzzer_cursor; +typedef struct fuzzer_rule fuzzer_rule; +typedef struct fuzzer_seen fuzzer_seen; +typedef struct fuzzer_stem fuzzer_stem; + +/* +** Type of the "cost" of an edit operation. Might be changed to +** "float" or "double" or "sqlite3_int64" in the future. +*/ +typedef int fuzzer_cost; + + +/* +** Each transformation rule is stored as an instance of this object. +** All rules are kept on a linked list sorted by rCost. +*/ +struct fuzzer_rule { + fuzzer_rule *pNext; /* Next rule in order of increasing rCost */ + fuzzer_cost rCost; /* Cost of this transformation */ + int nFrom, nTo; /* Length of the zFrom and zTo strings */ + char *zFrom; /* Transform from */ + char zTo[4]; /* Transform to (extra space appended) */ +}; + +/* +** A stem object is used to generate variants. It is also used to record +** previously generated outputs. +** +** Every stem is added to a hash table as it is output. Generation of +** duplicate stems is suppressed. +** +** Active stems (those that might generate new outputs) are kepts on a linked +** list sorted by increasing cost. The cost is the sum of rBaseCost and +** pRule->rCost. +*/ +struct fuzzer_stem { + char *zBasis; /* Word being fuzzed */ + int nBasis; /* Length of the zBasis string */ + const fuzzer_rule *pRule; /* Current rule to apply */ + int n; /* Apply pRule at this character offset */ + fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */ + fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */ + fuzzer_stem *pNext; /* Next stem in rCost order */ + fuzzer_stem *pHash; /* Next stem with same hash on zBasis */ +}; + +/* +** A fuzzer virtual-table object +*/ +struct fuzzer_vtab { + sqlite3_vtab base; /* Base class - must be first */ + char *zClassName; /* Name of this class. Default: "fuzzer" */ + fuzzer_rule *pRule; /* All active rules in this fuzzer */ + fuzzer_rule *pNewRule; /* New rules to add when last cursor expires */ + int nCursor; /* Number of active cursors */ +}; + +#define FUZZER_HASH 4001 /* Hash table size */ +#define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */ + +/* A fuzzer cursor object */ +struct fuzzer_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlite3_int64 iRowid; /* The rowid of the current word */ + fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */ + fuzzer_cost rLimit; /* Maximum cost of any term */ + fuzzer_stem *pStem; /* Stem with smallest rCostX */ + fuzzer_stem *pDone; /* Stems already processed to completion */ + fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */ + int mxQueue; /* Largest used index in aQueue[] */ + char *zBuf; /* Temporary use buffer */ + int nBuf; /* Bytes allocated for zBuf */ + int nStem; /* Number of stems allocated */ + fuzzer_rule nullRule; /* Null rule used first */ + fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */ +}; + +/* Methods for the fuzzer module */ +static int fuzzerConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + fuzzer_vtab *pNew; + int n; + if( strcmp(argv[1],"temp")!=0 ){ + *pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]); + return SQLITE_ERROR; + } + n = strlen(argv[0]) + 1; + pNew = sqlite3_malloc( sizeof(*pNew) + n ); + if( pNew==0 ) return SQLITE_NOMEM; + pNew->zClassName = (char*)&pNew[1]; + memcpy(pNew->zClassName, argv[0], n); + sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,cFrom,cTo,cost)"); + memset(pNew, 0, sizeof(*pNew)); + *ppVtab = &pNew->base; + return SQLITE_OK; +} +/* Note that for this virtual table, the xCreate and xConnect +** methods are identical. */ + +static int fuzzerDisconnect(sqlite3_vtab *pVtab){ + fuzzer_vtab *p = (fuzzer_vtab*)pVtab; + assert( p->nCursor==0 ); + do{ + while( p->pRule ){ + fuzzer_rule *pRule = p->pRule; + p->pRule = pRule->pNext; + sqlite3_free(pRule); + } + p->pRule = p->pNewRule; + p->pNewRule = 0; + }while( p->pRule ); + sqlite3_free(p); + return SQLITE_OK; +} +/* The xDisconnect and xDestroy methods are also the same */ + +/* +** The two input rule lists are both sorted in order of increasing +** cost. Merge them together into a single list, sorted by cost, and +** return a pointer to the head of that list. +*/ +static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){ + fuzzer_rule head; + fuzzer_rule *pTail; + + pTail = &head; + while( pA && pB ){ + if( pA->rCost<=pB->rCost ){ + pTail->pNext = pA; + pTail = pA; + pA = pA->pNext; + }else{ + pTail->pNext = pB; + pTail = pB; + pB = pB->pNext; + } + } + if( pA==0 ){ + pTail->pNext = pB; + }else{ + pTail->pNext = pA; + } + return head.pNext; +} + + +/* +** Open a new fuzzer cursor. +*/ +static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + fuzzer_vtab *p = (fuzzer_vtab*)pVTab; + fuzzer_cursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + pCur->pVtab = p; + *ppCursor = &pCur->base; + if( p->nCursor==0 && p->pNewRule ){ + unsigned int i; + fuzzer_rule *pX; + fuzzer_rule *a[15]; + for(i=0; ipNewRule)!=0 ){ + p->pNewRule = pX->pNext; + pX->pNext = 0; + for(i=0; a[i] && ipRule = fuzzerMergeRules(p->pRule, pX); + } + p->nCursor++; + return SQLITE_OK; +} + +/* +** Free all stems in a list. +*/ +static void fuzzerClearStemList(fuzzer_stem *pStem){ + while( pStem ){ + fuzzer_stem *pNext = pStem->pNext; + sqlite3_free(pStem); + pStem = pNext; + } +} + +/* +** Free up all the memory allocated by a cursor. Set it rLimit to 0 +** to indicate that it is at EOF. +*/ +static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){ + int i; + fuzzerClearStemList(pCur->pStem); + fuzzerClearStemList(pCur->pDone); + for(i=0; iaQueue[i]); + pCur->rLimit = (fuzzer_cost)0; + if( clearHash && pCur->nStem ){ + pCur->mxQueue = 0; + pCur->pStem = 0; + pCur->pDone = 0; + memset(pCur->aQueue, 0, sizeof(pCur->aQueue)); + memset(pCur->apHash, 0, sizeof(pCur->apHash)); + } + pCur->nStem = 0; +} + +/* +** Close a fuzzer cursor. +*/ +static int fuzzerClose(sqlite3_vtab_cursor *cur){ + fuzzer_cursor *pCur = (fuzzer_cursor *)cur; + fuzzerClearCursor(pCur, 0); + sqlite3_free(pCur->zBuf); + pCur->pVtab->nCursor--; + sqlite3_free(pCur); + return SQLITE_OK; +} + +/* +** Compute the current output term for a fuzzer_stem. +*/ +static int fuzzerRender( + fuzzer_stem *pStem, /* The stem to be rendered */ + char **pzBuf, /* Write results into this buffer. realloc if needed */ + int *pnBuf /* Size of the buffer */ +){ + const fuzzer_rule *pRule = pStem->pRule; + int n; + char *z; + + n = pStem->nBasis + pRule->nTo - pRule->nFrom; + if( (*pnBuf)n; + z = *pzBuf; + if( n<0 ){ + memcpy(z, pStem->zBasis, pStem->nBasis+1); + }else{ + memcpy(z, pStem->zBasis, n); + memcpy(&z[n], pRule->zTo, pRule->nTo); + memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom], + pStem->nBasis-n-pRule->nFrom+1); + } + return SQLITE_OK; +} + +/* +** Compute a hash on zBasis. +*/ +static unsigned int fuzzerHash(const char *z){ + unsigned int h = 0; + while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); } + return h % FUZZER_HASH; +} + +/* +** Current cost of a stem +*/ +static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){ + return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost; +} + +#if 0 +/* +** Print a description of a fuzzer_stem on stderr. +*/ +static void fuzzerStemPrint( + const char *zPrefix, + fuzzer_stem *pStem, + const char *zSuffix +){ + if( pStem->n<0 ){ + fprintf(stderr, "%s[%s](%d)-->self%s", + zPrefix, + pStem->zBasis, pStem->rBaseCost, + zSuffix + ); + }else{ + char *zBuf = 0; + int nBuf = 0; + if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return; + fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s", + zPrefix, + pStem->zBasis, pStem->rBaseCost, zBuf, pStem->, + zSuffix + ); + sqlite3_free(zBuf); + } +} +#endif + +/* +** Return 1 if the string to which the cursor is point has already +** been emitted. Return 0 if not. Return -1 on a memory allocation +** failures. +*/ +static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){ + unsigned int h; + fuzzer_stem *pLookup; + + if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ + return -1; + } + h = fuzzerHash(pCur->zBuf); + pLookup = pCur->apHash[h]; + while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){ + pLookup = pLookup->pHash; + } + return pLookup!=0; +} + +/* +** Advance a fuzzer_stem to its next value. Return 0 if there are +** no more values that can be generated by this fuzzer_stem. Return +** -1 on a memory allocation failure. +*/ +static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){ + const fuzzer_rule *pRule; + while( (pRule = pStem->pRule)!=0 ){ + while( pStem->n < pStem->nBasis - pRule->nFrom ){ + pStem->n++; + if( pRule->nFrom==0 + || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0 + ){ + /* Found a rewrite case. Make sure it is not a duplicate */ + int rc = fuzzerSeen(pCur, pStem); + if( rc<0 ) return -1; + if( rc==0 ){ + fuzzerCost(pStem); + return 1; + } + } + } + pStem->n = -1; + pStem->pRule = pRule->pNext; + if( pStem->pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0; + } + return 0; +} + +/* +** The two input stem lists are both sorted in order of increasing +** rCostX. Merge them together into a single list, sorted by rCostX, and +** return a pointer to the head of that new list. +*/ +static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){ + fuzzer_stem head; + fuzzer_stem *pTail; + + pTail = &head; + while( pA && pB ){ + if( pA->rCostX<=pB->rCostX ){ + pTail->pNext = pA; + pTail = pA; + pA = pA->pNext; + }else{ + pTail->pNext = pB; + pTail = pB; + pB = pB->pNext; + } + } + if( pA==0 ){ + pTail->pNext = pB; + }else{ + pTail->pNext = pA; + } + return head.pNext; +} + +/* +** Load pCur->pStem with the lowest-cost stem. Return a pointer +** to the lowest-cost stem. +*/ +static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){ + fuzzer_stem *pBest, *pX; + int iBest; + int i; + + if( pCur->pStem==0 ){ + iBest = -1; + pBest = 0; + for(i=0; i<=pCur->mxQueue; i++){ + pX = pCur->aQueue[i]; + if( pX==0 ) continue; + if( pBest==0 || pBest->rCostX>pX->rCostX ){ + pBest = pX; + iBest = i; + } + } + if( pBest ){ + pCur->aQueue[iBest] = pBest->pNext; + pBest->pNext = 0; + pCur->pStem = pBest; + } + } + return pCur->pStem; +} + +/* +** Insert pNew into queue of pending stems. Then find the stem +** with the lowest rCostX and move it into pCur->pStem. +** list. The insert is done such the pNew is in the correct order +** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost. +*/ +static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){ + fuzzer_stem *pX; + int i; + + /* If pCur->pStem exists and is greater than pNew, then make pNew + ** the new pCur->pStem and insert the old pCur->pStem instead. + */ + if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){ + pNew->pNext = 0; + pCur->pStem = pNew; + pNew = pX; + } + + /* Insert the new value */ + pNew->pNext = 0; + pX = pNew; + for(i=0; i<=pCur->mxQueue; i++){ + if( pCur->aQueue[i] ){ + pX = fuzzerMergeStems(pX, pCur->aQueue[i]); + pCur->aQueue[i] = 0; + }else{ + pCur->aQueue[i] = pX; + break; + } + } + if( i>pCur->mxQueue ){ + if( imxQueue = i; + pCur->aQueue[i] = pX; + }else{ + assert( pCur->mxQueue==FUZZER_NQUEUE-1 ); + pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]); + pCur->aQueue[FUZZER_NQUEUE-1] = pX; + } + } + + return fuzzerLowestCostStem(pCur); +} + +/* +** Allocate a new fuzzer_stem. Add it to the hash table but do not +** link it into either the pCur->pStem or pCur->pDone lists. +*/ +static fuzzer_stem *fuzzerNewStem( + fuzzer_cursor *pCur, + const char *zWord, + fuzzer_cost rBaseCost +){ + fuzzer_stem *pNew; + unsigned int h; + + pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 ); + if( pNew==0 ) return 0; + memset(pNew, 0, sizeof(*pNew)); + pNew->zBasis = (char*)&pNew[1]; + pNew->nBasis = strlen(zWord); + memcpy(pNew->zBasis, zWord, pNew->nBasis+1); + pNew->pRule = pCur->pVtab->pRule; + pNew->n = -1; + pNew->rBaseCost = pNew->rCostX = rBaseCost; + h = fuzzerHash(pNew->zBasis); + pNew->pHash = pCur->apHash[h]; + pCur->apHash[h] = pNew; + pCur->nStem++; + return pNew; +} + + +/* +** Advance a cursor to its next row of output +*/ +static int fuzzerNext(sqlite3_vtab_cursor *cur){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + int rc; + fuzzer_stem *pStem, *pNew; + + pCur->iRowid++; + + /* Use the element the cursor is currently point to to create + ** a new stem and insert the new stem into the priority queue. + */ + pStem = pCur->pStem; + if( pStem->rCostX>0 ){ + rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf); + if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM; + pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX); + if( pNew ){ + if( fuzzerAdvance(pCur, pNew)==0 ){ + pNew->pNext = pCur->pDone; + pCur->pDone = pNew; + }else{ + if( fuzzerInsert(pCur, pNew)==pNew ){ + return SQLITE_OK; + } + } + }else{ + return SQLITE_NOMEM; + } + } + + /* Adjust the priority queue so that the first element of the + ** stem list is the next lowest cost word. + */ + while( (pStem = pCur->pStem)!=0 ){ + if( fuzzerAdvance(pCur, pStem) ){ + pCur->pStem = 0; + pStem = fuzzerInsert(pCur, pStem); + if( (rc = fuzzerSeen(pCur, pStem))!=0 ){ + if( rc<0 ) return SQLITE_NOMEM; + continue; + } + return SQLITE_OK; /* New word found */ + } + pCur->pStem = 0; + pStem->pNext = pCur->pDone; + pCur->pDone = pStem; + if( fuzzerLowestCostStem(pCur) ){ + rc = fuzzerSeen(pCur, pCur->pStem); + if( rc<0 ) return SQLITE_NOMEM; + if( rc==0 ){ + return SQLITE_OK; + } + } + } + + /* Reach this point only if queue has been exhausted and there is + ** nothing left to be output. */ + pCur->rLimit = (fuzzer_cost)0; + return SQLITE_OK; +} + +/* +** Called to "rewind" a cursor back to the beginning so that +** it starts its output over again. Always called at least once +** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call. +*/ +static int fuzzerFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor; + const char *zWord = 0; + fuzzer_stem *pStem; + + fuzzerClearCursor(pCur, 1); + pCur->rLimit = 2147483647; + if( idxNum==1 ){ + zWord = (const char*)sqlite3_value_text(argv[0]); + }else if( idxNum==2 ){ + pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]); + }else if( idxNum==3 ){ + zWord = (const char*)sqlite3_value_text(argv[0]); + pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]); + } + if( zWord==0 ) zWord = ""; + pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0); + if( pStem==0 ) return SQLITE_NOMEM; + pCur->nullRule.pNext = pCur->pVtab->pRule; + pCur->nullRule.rCost = 0; + pCur->nullRule.nFrom = 0; + pCur->nullRule.nTo = 0; + pCur->nullRule.zFrom = ""; + pStem->pRule = &pCur->nullRule; + pStem->n = pStem->nBasis; + pCur->iRowid = 1; + return SQLITE_OK; +} + +/* +** Only the word and distance columns have values. All other columns +** return NULL +*/ +static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + if( i==0 ){ + /* the "word" column */ + if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ + return SQLITE_NOMEM; + } + sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT); + }else if( i==1 ){ + /* the "distance" column */ + sqlite3_result_int(ctx, pCur->pStem->rCostX); + }else{ + /* All other columns are NULL */ + sqlite3_result_null(ctx); + } + return SQLITE_OK; +} + +/* +** The rowid. +*/ +static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +/* +** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal +** that the cursor has nothing more to output. +*/ +static int fuzzerEof(sqlite3_vtab_cursor *cur){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + return pCur->rLimit<=(fuzzer_cost)0; +} + +/* +** Search for terms of these forms: +** +** word MATCH $str +** distance < $value +** distance <= $value +** +** The distance< and distance<= are both treated as distance<=. +** The query plan number is as follows: +** +** 0: None of the terms above are found +** 1: There is a "word MATCH" term with $str in filter.argv[0]. +** 2: There is a "distance<" term with $value in filter.argv[0]. +** 3: Both "word MATCH" and "distance<" with $str in argv[0] and +** $value in argv[1]. +*/ +static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int iPlan = 0; + int iDistTerm = -1; + int i; + const struct sqlite3_index_constraint *pConstraint; + pConstraint = pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pConstraint++){ + if( pConstraint->usable==0 ) continue; + if( (iPlan & 1)==0 + && pConstraint->iColumn==0 + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH + ){ + iPlan |= 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + } + if( (iPlan & 2)==0 + && pConstraint->iColumn==1 + && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT + || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) + ){ + iPlan |= 2; + iDistTerm = i; + } + } + if( iPlan==2 ){ + pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1; + }else if( iPlan==3 ){ + pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2; + } + pIdxInfo->idxNum = iPlan; + if( pIdxInfo->nOrderBy==1 + && pIdxInfo->aOrderBy[0].iColumn==1 + && pIdxInfo->aOrderBy[0].desc==0 + ){ + pIdxInfo->orderByConsumed = 1; + } + pIdxInfo->estimatedCost = (double)10000; + + return SQLITE_OK; +} + +/* +** Disallow all attempts to DELETE or UPDATE. Only INSERTs are allowed. +** +** On an insert, the cFrom, cTo, and cost columns are used to construct +** a new rule. All other columns are ignored. The rule is ignored +** if cFrom and cTo are identical. A NULL value for cFrom or cTo is +** interpreted as an empty string. The cost must be positive. +*/ +static int fuzzerUpdate( + sqlite3_vtab *pVTab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid +){ + fuzzer_vtab *p = (fuzzer_vtab*)pVTab; + fuzzer_rule *pRule; + const char *zFrom; + int nFrom; + const char *zTo; + int nTo; + fuzzer_cost rCost; + if( argc!=7 ){ + sqlite3_free(pVTab->zErrMsg); + pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table", + p->zClassName); + return SQLITE_CONSTRAINT; + } + if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){ + sqlite3_free(pVTab->zErrMsg); + pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table", + p->zClassName); + return SQLITE_CONSTRAINT; + } + zFrom = (char*)sqlite3_value_text(argv[4]); + if( zFrom==0 ) zFrom = ""; + zTo = (char*)sqlite3_value_text(argv[5]); + if( zTo==0 ) zTo = ""; + if( strcmp(zFrom,zTo)==0 ){ + /* Silently ignore null transformations */ + return SQLITE_OK; + } + rCost = sqlite3_value_int(argv[6]); + if( rCost<=0 ){ + sqlite3_free(pVTab->zErrMsg); + pVTab->zErrMsg = sqlite3_mprintf("cost must be positive"); + return SQLITE_CONSTRAINT; + } + nFrom = strlen(zFrom); + nTo = strlen(zTo); + pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo ); + if( pRule==0 ){ + return SQLITE_NOMEM; + } + pRule->zFrom = &pRule->zTo[nTo+1]; + pRule->nFrom = nFrom; + memcpy(pRule->zFrom, zFrom, nFrom+1); + memcpy(pRule->zTo, zTo, nTo+1); + pRule->nTo = nTo; + pRule->rCost = rCost; + pRule->pNext = p->pNewRule; + p->pNewRule = pRule; + return SQLITE_OK; +} + +/* +** A virtual table module that provides read-only access to a +** Tcl global variable namespace. +*/ +static sqlite3_module fuzzerModule = { + 0, /* iVersion */ + fuzzerConnect, + fuzzerConnect, + fuzzerBestIndex, + fuzzerDisconnect, + fuzzerDisconnect, + fuzzerOpen, /* xOpen - open a cursor */ + fuzzerClose, /* xClose - close a cursor */ + fuzzerFilter, /* xFilter - configure scan constraints */ + fuzzerNext, /* xNext - advance a cursor */ + fuzzerEof, /* xEof - check for end of scan */ + fuzzerColumn, /* xColumn - read data */ + fuzzerRowid, /* xRowid - read data */ + fuzzerUpdate, /* xUpdate - INSERT */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + +/* +** Register the fuzzer virtual table +*/ +int fuzzer_register(sqlite3 *db){ + int rc = SQLITE_OK; +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0); +#endif + return rc; +} + +#ifdef SQLITE_TEST +#include +/* +** Decode a pointer to an sqlite3 object. +*/ +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); + +/* +** Register the echo virtual table module. +*/ +static int register_fuzzer_module( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + fuzzer_register(db); + return TCL_OK; +} + + +/* +** Register commands with the TCL interpreter. +*/ +int Sqlitetestfuzzer_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + void *clientData; + } aObjCmd[] = { + { "register_fuzzer_module", register_fuzzer_module, 0 }, + }; + int i; + for(i=0; i #include -#include "sqliteInt.h" +#include "test_multiplex.h" + +#ifndef SQLITE_CORE + #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ +#endif +#include "sqlite3ext.h" + +/* +** These should be defined to be the same as the values in +** sqliteInt.h. They are defined seperately here so that +** the multiplex VFS shim can be built as a loadable +** module. +*/ +#define UNUSED_PARAMETER(x) (void)(x) +#define MAX_PAGE_SIZE 0x10000 +#define DEFAULT_SECTOR_SIZE 0x1000 + +/* +** For a build without mutexes, no-op the mutex calls. +*/ +#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 +#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) +#define sqlite3_mutex_free(X) +#define sqlite3_mutex_enter(X) +#define sqlite3_mutex_try(X) SQLITE_OK +#define sqlite3_mutex_leave(X) +#define sqlite3_mutex_held(X) ((void)(X),1) +#define sqlite3_mutex_notheld(X) ((void)(X),1) +#endif /* SQLITE_THREADSAFE==0 */ + /************************ Shim Definitions ******************************/ +#define SQLITE_MULTIPLEX_VFS_NAME "multiplex" + /* This is the limit on the chunk size. It may be changed by calling -** the sqlite3_multiplex_set() interface. +** the xFileControl() interface. It will be rounded up to a +** multiple of MAX_PAGE_SIZE. We default it here to 1GB. */ -#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000 +#define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384) + /* Default limit on number of chunks. Care should be taken ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT ** format specifier. It may be changed by calling -** the sqlite3_multiplex_set() interface. +** the xFileControl() interface. */ #define SQLITE_MULTIPLEX_MAX_CHUNKS 32 @@ -64,10 +97,13 @@ typedef struct multiplexConn multiplexConn; */ struct multiplexGroup { sqlite3_file **pReal; /* Handles to each chunk */ - char *bOpen; /* 0 if chunk not opened */ + char *bOpen; /* array of bools - 0 if chunk not opened */ char *zName; /* Base filename of this group */ int nName; /* Length of base filename */ int flags; /* Flags used for original opening */ + int nChunkSize; /* Chunk size used for this group */ + int nMaxChunks; /* Max number of chunks for this group */ + int bEnabled; /* TRUE to use Multiplex VFS for this file */ multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ }; @@ -126,11 +162,6 @@ static struct { */ multiplexGroup *pGroups; - /* Chunk params. - */ - int nChunkSize; - int nMaxChunks; - /* Storage for temp file names. Allocated during ** initialization to the max pathname of the underlying VFS. */ @@ -146,13 +177,28 @@ static struct { static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); } static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); } +/* +** Compute a string length that is limited to what can be stored in +** lower 30 bits of a 32-bit signed integer. +** +** The value returned will never be negative. Nor will it ever be greater +** than the actual length of the string. For very long strings (greater +** than 1GiB) the value returned might be less than the true string length. +*/ +int multiplexStrlen30(const char *z){ + const char *z2 = z; + if( z==0 ) return 0; + while( *z2 ){ z2++; } + return 0x3fffffff & (int)(z2 - z); +} + /* Translate an sqlite3_file* that is really a multiplexGroup* into ** the sqlite3_file* for the underlying original VFS. */ static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){ multiplexGroup *pGroup = pConn->pGroup; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ - if( iChunknMaxChunks ){ sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */ if( !pGroup->bOpen[iChunk] ){ memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); @@ -177,6 +223,62 @@ static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, return NULL; } +/* +** This is the implementation of the multiplex_control() SQL function. +*/ +static void multiplexControlFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int rc = SQLITE_OK; + sqlite3 *db = sqlite3_context_db_handle(context); + int op; + int iVal; + + if( !db || argc!=2 ){ + rc = SQLITE_ERROR; + }else{ + /* extract params */ + op = sqlite3_value_int(argv[0]); + iVal = sqlite3_value_int(argv[1]); + /* map function op to file_control op */ + switch( op ){ + case 1: + op = MULTIPLEX_CTRL_ENABLE; + break; + case 2: + op = MULTIPLEX_CTRL_SET_CHUNK_SIZE; + break; + case 3: + op = MULTIPLEX_CTRL_SET_MAX_CHUNKS; + break; + default: + rc = SQLITE_NOTFOUND; + break; + } + } + if( rc==SQLITE_OK ){ + rc = sqlite3_file_control(db, 0, op, &iVal); + } + sqlite3_result_error_code(context, rc); +} + +/* +** This is the entry point to register the auto-extension for the +** multiplex_control() function. +*/ +static int multiplexFuncInit( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc; + rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY, + 0, multiplexControlFunc, 0, 0); + return rc; +} + /************************* VFS Method Wrappers *****************************/ /* @@ -198,7 +300,7 @@ static int multiplexOpen( multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ sqlite3_file *pSubOpen; /* Real file descriptor */ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ - int nName = sqlite3Strlen30(zName); + int nName = multiplexStrlen30(zName); int i; int sz; @@ -210,11 +312,11 @@ static int multiplexOpen( multiplexEnter(); pMultiplexOpen = (multiplexConn*)pConn; /* allocate space for group */ - sz = sizeof(multiplexGroup) /* multiplexGroup */ - + (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */ - + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) /* *pReal */ - + gMultiplex.nMaxChunks /* bOpen[] */ - + nName + 1; /* zName */ + sz = sizeof(multiplexGroup) /* multiplexGroup */ + + (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */ + + (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */ + + SQLITE_MULTIPLEX_MAX_CHUNKS /* bOpen[] */ + + nName + 1; /* zName */ #ifndef SQLITE_MULTIPLEX_EXT_OVWR sz += SQLITE_MULTIPLEX_EXT_SZ; assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname); @@ -230,14 +332,18 @@ static int multiplexOpen( char *p = (char *)&pGroup[1]; pMultiplexOpen->pGroup = pGroup; memset(pGroup, 0, sz); + pGroup->bEnabled = -1; + pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; + pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; pGroup->pReal = (sqlite3_file **)p; - p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks); - for(i=0; inMaxChunks); + for(i=0; inMaxChunks; i++){ pGroup->pReal[i] = (sqlite3_file *)p; p += pOrigVfs->szOsFile; } + /* bOpen[] vals should all be zero from memset above */ pGroup->bOpen = p; - p += gMultiplex.nMaxChunks; + p += pGroup->nMaxChunks; pGroup->zName = p; /* save off base filename, name length, and original open flags */ memcpy(pGroup->zName, zName, nName+1); @@ -245,6 +351,14 @@ static int multiplexOpen( pGroup->flags = flags; pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags); if( pSubOpen ){ + /* if this file is already larger than chunk size, disable + ** the multiplex feature. + */ + sqlite3_int64 sz; + int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); + if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){ + pGroup->bEnabled = 0; + } if( pSubOpen->pMethods->iVersion==1 ){ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; }else{ @@ -274,24 +388,29 @@ static int multiplexDelete( ){ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int rc = SQLITE_OK; - int nName = sqlite3Strlen30(zName); + int nName = multiplexStrlen30(zName); int i; UNUSED_PARAMETER(pVfs); multiplexEnter(); memcpy(gMultiplex.zName, zName, nName+1); - for(i=0; ixAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); + rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, + SQLITE_ACCESS_EXISTS, &exists); if( rc2==SQLITE_OK && exists){ /* if it exists, delete it */ rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir); @@ -353,7 +472,7 @@ static int multiplexClose(sqlite3_file *pConn){ int i; multiplexEnter(); /* close any open handles */ - for(i=0; inMaxChunks; i++){ if( pGroup->bOpen[i] ){ sqlite3_file *pSubOpen = pGroup->pReal[i]; int rc2 = pSubOpen->pMethods->xClose(pSubOpen); @@ -384,23 +503,29 @@ static int multiplexRead( sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; + multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); - while( iAmt > 0 ){ - int i = (int)(iOfst/gMultiplex.nChunkSize); - sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); - if( pSubOpen ){ - int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; - if( extra<0 ) extra = 0; - iAmt -= extra; - rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); - if( rc!=SQLITE_OK ) break; - pBuf = (char *)pBuf + iAmt; - iOfst += iAmt; - iAmt = extra; - }else{ - rc = SQLITE_IOERR_READ; - break; + if( !pGroup->bEnabled ){ + sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst); + }else{ + while( iAmt > 0 ){ + int i = (int)(iOfst / pGroup->nChunkSize); + sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); + if( pSubOpen ){ + int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; + if( extra<0 ) extra = 0; + iAmt -= extra; + rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize); + if( rc!=SQLITE_OK ) break; + pBuf = (char *)pBuf + iAmt; + iOfst += iAmt; + iAmt = extra; + }else{ + rc = SQLITE_IOERR_READ; + break; + } } } multiplexLeave(); @@ -418,23 +543,29 @@ static int multiplexWrite( sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; + multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); - while( iAmt > 0 ){ - int i = (int)(iOfst/gMultiplex.nChunkSize); - sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); - if( pSubOpen ){ - int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; - if( extra<0 ) extra = 0; - iAmt -= extra; - rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); - if( rc!=SQLITE_OK ) break; - pBuf = (char *)pBuf + iAmt; - iOfst += iAmt; - iAmt = extra; - }else{ - rc = SQLITE_IOERR_WRITE; - break; + if( !pGroup->bEnabled ){ + sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst); + }else{ + while( iAmt > 0 ){ + int i = (int)(iOfst / pGroup->nChunkSize); + sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); + if( pSubOpen ){ + int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; + if( extra<0 ) extra = 0; + iAmt -= extra; + rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize); + if( rc!=SQLITE_OK ) break; + pBuf = (char *)pBuf + iAmt; + iOfst += iAmt; + iAmt = extra; + }else{ + rc = SQLITE_IOERR_WRITE; + break; + } } } multiplexLeave(); @@ -449,35 +580,44 @@ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; - int rc2; - int i; - sqlite3_file *pSubOpen; - sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ multiplexEnter(); - memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); - /* delete the chunks above the truncate limit */ - for(i=(int)(size/gMultiplex.nChunkSize)+1; ibOpen[i] ){ - pSubOpen = pGroup->pReal[i]; - rc2 = pSubOpen->pMethods->xClose(pSubOpen); - if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; - pGroup->bOpen[i] = 0; - } -#ifdef SQLITE_MULTIPLEX_EXT_OVWR - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); -#else - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); -#endif - rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0); - if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; - } - pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL); - if( pSubOpen ){ - rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize); - if( rc2!=SQLITE_OK ) rc = rc2; + if( !pGroup->bEnabled ){ + sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size); }else{ - rc = SQLITE_IOERR_TRUNCATE; + int rc2; + int i; + sqlite3_file *pSubOpen; + sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ + memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); + /* delete the chunks above the truncate limit */ + for(i=(int)(size / pGroup->nChunkSize)+1; inMaxChunks; i++){ + /* close any open chunks before deleting them */ + if( pGroup->bOpen[i] ){ + pSubOpen = pGroup->pReal[i]; + rc2 = pSubOpen->pMethods->xClose(pSubOpen); + if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; + pGroup->bOpen[i] = 0; + } +#ifdef SQLITE_MULTIPLEX_EXT_OVWR + sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, + gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, + SQLITE_MULTIPLEX_EXT_FMT, i); +#else + sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, + gMultiplex.zName+pGroup->nName, + SQLITE_MULTIPLEX_EXT_FMT, i); +#endif + rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0); + if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; + } + pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL); + if( pSubOpen ){ + rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize); + if( rc2!=SQLITE_OK ) rc = rc2; + }else{ + rc = SQLITE_IOERR_TRUNCATE; + } } multiplexLeave(); return rc; @@ -491,7 +631,7 @@ static int multiplexSync(sqlite3_file *pConn, int flags){ int rc = SQLITE_OK; int i; multiplexEnter(); - for(i=0; inMaxChunks; i++){ /* if we don't have it open, we don't need to sync it */ if( pGroup->bOpen[i] ){ sqlite3_file *pSubOpen = pGroup->pReal[i]; @@ -513,46 +653,56 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ int rc2; int i; multiplexEnter(); - *pSize = 0; - for(i=0; ibOpen[i] ){ - pSubOpen = pGroup->pReal[i]; - }else{ - sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ - int exists = 0; - memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); - if( i ){ + if( !pGroup->bEnabled ){ + sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize); + }else{ + *pSize = 0; + for(i=0; inMaxChunks; i++){ + sqlite3_file *pSubOpen = NULL; + /* if not opened already, check to see if the chunk exists */ + if( pGroup->bOpen[i] ){ + pSubOpen = pGroup->pReal[i]; + }else{ + sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ + int exists = 0; + memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); + if( i ){ #ifdef SQLITE_MULTIPLEX_EXT_OVWR - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); + sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, + gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, + SQLITE_MULTIPLEX_EXT_FMT, i); #else - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); + sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, + gMultiplex.zName+pGroup->nName, + SQLITE_MULTIPLEX_EXT_FMT, i); #endif + } + rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, + SQLITE_ACCESS_EXISTS, &exists); + if( rc2==SQLITE_OK && exists){ + /* if it exists, open it */ + pSubOpen = multiplexSubOpen(p, i, &rc, NULL); + }else{ + /* stop at first "gap" */ + break; + } } - rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); - if( rc2==SQLITE_OK && exists){ - /* if it exists, open it */ - pSubOpen = multiplexSubOpen(p, i, &rc, NULL); + if( pSubOpen ){ + sqlite3_int64 sz; + rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); + if( rc2!=SQLITE_OK ){ + rc = rc2; + }else{ + if( sz>pGroup->nChunkSize ){ + rc = SQLITE_IOERR_FSTAT; + } + *pSize += sz; + } }else{ - /* stop at first "gap" */ break; } } - if( pSubOpen ){ - sqlite3_int64 sz; - rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); - if( rc2!=SQLITE_OK ){ - rc = rc2; - }else{ - if( sz>gMultiplex.nChunkSize ){ - rc = SQLITE_IOERR_FSTAT; - } - *pSize += sz; - } - }else{ - break; - } } multiplexLeave(); return rc; @@ -594,18 +744,62 @@ static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){ return SQLITE_IOERR_CHECKRESERVEDLOCK; } -/* Pass xFileControl requests through to the original VFS unchanged. +/* Pass xFileControl requests through to the original VFS unchanged, +** except for any MULTIPLEX_CTRL_* requests here. */ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){ multiplexConn *p = (multiplexConn*)pConn; - int rc; + multiplexGroup *pGroup = p->pGroup; + int rc = SQLITE_ERROR; sqlite3_file *pSubOpen; - if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK; - pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); - if( pSubOpen ){ - return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); + + if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; + switch( op ){ + case MULTIPLEX_CTRL_ENABLE: + if( pArg ) { + int bEnabled = *(int *)pArg; + pGroup->bEnabled = bEnabled; + rc = SQLITE_OK; + } + break; + case MULTIPLEX_CTRL_SET_CHUNK_SIZE: + if( pArg ) { + int nChunkSize = *(int *)pArg; + if( nChunkSize<1 ){ + rc = SQLITE_MISUSE; + }else{ + /* Round up to nearest multiple of MAX_PAGE_SIZE. */ + nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1)); + nChunkSize &= ~(MAX_PAGE_SIZE-1); + pGroup->nChunkSize = nChunkSize; + rc = SQLITE_OK; + } + } + break; + case MULTIPLEX_CTRL_SET_MAX_CHUNKS: + if( pArg ) { + int nMaxChunks = *(int *)pArg; + if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){ + rc = SQLITE_MISUSE; + }else{ + pGroup->nMaxChunks = nMaxChunks; + rc = SQLITE_OK; + } + } + break; + case SQLITE_FCNTL_SIZE_HINT: + case SQLITE_FCNTL_CHUNK_SIZE: + /* no-op these */ + rc = SQLITE_OK; + break; + default: + pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + if( pSubOpen ){ + rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); + } + break; } - return SQLITE_ERROR; + return rc; } /* Pass xSectorSize requests through to the original VFS unchanged. @@ -617,7 +811,7 @@ static int multiplexSectorSize(sqlite3_file *pConn){ if( pSubOpen ){ return pSubOpen->pMethods->xSectorSize(pSubOpen); } - return SQLITE_DEFAULT_SECTOR_SIZE; + return DEFAULT_SECTOR_SIZE; } /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. @@ -692,9 +886,10 @@ static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){ /************************** Public Interfaces *****************************/ /* -** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName -** as the VFS that does the actual work. Use the default if -** zOrigVfsName==NULL. +** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() +** +** Use the VFS named zOrigVfsName as the VFS that does the actual work. +** Use the default if zOrigVfsName==NULL. ** ** The multiplex VFS shim is named "multiplex". It will become the default ** VFS if makeDefault is non-zero. @@ -717,14 +912,12 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ sqlite3_mutex_free(gMultiplex.pMutex); return SQLITE_NOMEM; } - gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; - gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; gMultiplex.pGroups = NULL; gMultiplex.isInitialized = 1; gMultiplex.pOrigVfs = pOrigVfs; gMultiplex.sThisVfs = *pOrigVfs; gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); - gMultiplex.sThisVfs.zName = "multiplex"; + gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME; gMultiplex.sThisVfs.xOpen = multiplexOpen; gMultiplex.sThisVfs.xDelete = multiplexDelete; gMultiplex.sThisVfs.xAccess = multiplexAccess; @@ -759,11 +952,14 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); + + sqlite3_auto_extension((void*)multiplexFuncInit); + return SQLITE_OK; } /* -** Shutdown the multiplex system. +** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() ** ** All SQLite database connections must be closed before calling this ** routine. @@ -782,31 +978,9 @@ int sqlite3_multiplex_shutdown(void){ return SQLITE_OK; } -/* -** Adjust chunking params. VFS should be initialized first. -** No files should be open. Re-intializing will reset these -** to the default. -*/ -int sqlite3_multiplex_set( - int nChunkSize, /* Max chunk size */ - int nMaxChunks /* Max number of chunks */ -){ - if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; - if( gMultiplex.pGroups ) return SQLITE_MISUSE; - if( nChunkSize<32 ) return SQLITE_MISUSE; - if( nMaxChunks<1 ) return SQLITE_MISUSE; - if( nMaxChunks>99 ) return SQLITE_MISUSE; - multiplexEnter(); - gMultiplex.nChunkSize = nChunkSize; - gMultiplex.nMaxChunks = nMaxChunks; - multiplexLeave(); - return SQLITE_OK; -} - /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST #include - extern const char *sqlite3TestErrorName(int); @@ -866,36 +1040,6 @@ static int test_multiplex_shutdown( return TCL_OK; } -/* -** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS -*/ -static int test_multiplex_set( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int nChunkSize; /* Max chunk size */ - int nMaxChunks; /* Max number of chunks */ - int rc; /* Value returned by sqlite3_multiplex_set() */ - - UNUSED_PARAMETER(clientData); - - /* Process arguments */ - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS"); - return TCL_ERROR; - } - if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR; - if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR; - - /* Invoke sqlite3_multiplex_set() */ - rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks); - - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); - return TCL_OK; -} - /* ** tclcmd: sqlite3_multiplex_dump */ @@ -929,16 +1073,16 @@ static int test_multiplex_dump( Tcl_NewIntObj(pGroup->flags)); /* count number of chunks with open handles */ - for(i=0; inMaxChunks; i++){ if( pGroup->bOpen[i] ) nChunks++; } Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(nChunks)); Tcl_ListObjAppendElement(interp, pGroupTerm, - Tcl_NewIntObj(gMultiplex.nChunkSize)); + Tcl_NewIntObj(pGroup->nChunkSize)); Tcl_ListObjAppendElement(interp, pGroupTerm, - Tcl_NewIntObj(gMultiplex.nMaxChunks)); + Tcl_NewIntObj(pGroup->nMaxChunks)); Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); } @@ -947,6 +1091,68 @@ static int test_multiplex_dump( return TCL_OK; } +/* +** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? +*/ +static int test_multiplex_control( + ClientData cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; /* Return code from file_control() */ + int idx; /* Index in aSub[] */ + Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */ + sqlite3 *db; /* Underlying db handle for HANDLE */ + int iValue = 0; + void *pArg = 0; + + struct SubCommand { + const char *zName; + int op; + int argtype; + } aSub[] = { + { "enable", MULTIPLEX_CTRL_ENABLE, 1 }, + { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 }, + { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 }, + { 0, 0, 0 } + }; + + if( objc!=5 ){ + Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE"); + return TCL_ERROR; + } + + if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ + Tcl_AppendResult(interp, "expected database handle, got \"", 0); + Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0); + return TCL_ERROR; + }else{ + db = *(sqlite3 **)cmdInfo.objClientData; + } + + rc = Tcl_GetIndexFromObjStruct( + interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx + ); + if( rc!=TCL_OK ) return rc; + + switch( aSub[idx].argtype ){ + case 1: + if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){ + return TCL_ERROR; + } + pArg = (void *)&iValue; + break; + default: + Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND"); + return TCL_ERROR; + } + + rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg); + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR; +} + /* ** This routine registers the custom TCL commands defined in this ** module. This should be the only procedure visible from outside @@ -959,8 +1165,8 @@ int Sqlitemultiplex_Init(Tcl_Interp *interp){ } aCmd[] = { { "sqlite3_multiplex_initialize", test_multiplex_initialize }, { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, - { "sqlite3_multiplex_set", test_multiplex_set }, { "sqlite3_multiplex_dump", test_multiplex_dump }, + { "sqlite3_multiplex_control", test_multiplex_control }, }; int i; diff --git a/src/test_multiplex.h b/src/test_multiplex.h new file mode 100644 index 0000000..ec1ba9b --- /dev/null +++ b/src/test_multiplex.h @@ -0,0 +1,91 @@ +/* +** 2011 March 18 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains a VFS "shim" - a layer that sits in between the +** pager and the real VFS. +** +** This particular shim enforces a multiplex system on DB files. +** This shim shards/partitions a single DB file into smaller +** "chunks" such that the total DB file size may exceed the maximum +** file size of the underlying file system. +** +*/ + +#ifndef _TEST_MULTIPLEX_H +#define _TEST_MULTIPLEX_H + +/* +** CAPI: File-control Operations Supported by Multiplex VFS +** +** Values interpreted by the xFileControl method of a Multiplex VFS db file-handle. +** +** MULTIPLEX_CTRL_ENABLE: +** This file control is used to enable or disable the multiplex +** shim. +** +** MULTIPLEX_CTRL_SET_CHUNK_SIZE: +** This file control is used to set the maximum allowed chunk +** size for a multiplex file set. The chunk size should be +** a multiple of SQLITE_MAX_PAGE_SIZE, and will be rounded up +** if not. +** +** MULTIPLEX_CTRL_SET_MAX_CHUNKS: +** This file control is used to set the maximum number of chunks +** allowed to be used for a mutliplex file set. +*/ +#define MULTIPLEX_CTRL_ENABLE 214014 +#define MULTIPLEX_CTRL_SET_CHUNK_SIZE 214015 +#define MULTIPLEX_CTRL_SET_MAX_CHUNKS 214016 + +/* +** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() +** +** Use the VFS named zOrigVfsName as the VFS that does the actual work. +** Use the default if zOrigVfsName==NULL. +** +** The multiplex VFS shim is named "multiplex". It will become the default +** VFS if makeDefault is non-zero. +** +** An auto-extension is registered which will make the function +** multiplex_control() available to database connections. This +** function gives access to the xFileControl interface of the +** multiplex VFS shim. +** +** SELECT multiplex_control(,); +** +** =1 MULTIPLEX_CTRL_ENABLE +** =0 disable +** =1 enable +** +** =2 MULTIPLEX_CTRL_SET_CHUNK_SIZE +** int, chunk size +** +** =3 MULTIPLEX_CTRL_SET_MAX_CHUNKS +** int, max chunks +** +** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once +** during start-up. +*/ +extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault); + +/* +** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() +** +** All SQLite database connections must be closed before calling this +** routine. +** +** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while +** shutting down in order to free all remaining multiplex groups. +*/ +extern int sqlite3_multiplex_shutdown(void); + +#endif diff --git a/src/test_osinst.c b/src/test_osinst.c index 25e9bcb..50d6250 100644 --- a/src/test_osinst.c +++ b/src/test_osinst.c @@ -213,14 +213,14 @@ static sqlite3_io_methods vfslog_io_methods = { vfslogShmUnmap /* xShmUnmap */ }; -#if defined(SQLITE_OS_UNIX) && !defined(NO_GETTOD) +#if SQLITE_OS_UNIX && !defined(NO_GETTOD) #include static sqlite3_uint64 vfslog_time(){ struct timeval sTime; gettimeofday(&sTime, 0); return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000; } -#elif defined(SQLITE_OS_WIN) +#elif SQLITE_OS_WIN #include #include static sqlite3_uint64 vfslog_time(){ diff --git a/src/test_server.c b/src/test_server.c index 3eb7544..ed0818e 100644 --- a/src/test_server.c +++ b/src/test_server.c @@ -206,7 +206,7 @@ ** and only if the SQLITE_SERVER macro is defined. */ #if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) -#if defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE +#if SQLITE_OS_UNIX && SQLITE_THREADSAFE /* ** We require only pthreads and the public interface of SQLite. @@ -453,7 +453,6 @@ void *sqlite3_server(void *NotUsed){ pthread_mutex_unlock(&pMsg->clientMutex); pthread_cond_signal(&pMsg->clientWakeup); } - sqlite3_thread_cleanup(); pthread_mutex_unlock(&g.serverMutex); return 0; } @@ -487,5 +486,5 @@ void sqlite3_server_stop(void){ pthread_mutex_unlock(&g.serverMutex); } -#endif /* defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE */ +#endif /* SQLITE_OS_UNIX && SQLITE_THREADSAFE */ #endif /* defined(SQLITE_SERVER) */ diff --git a/src/test_syscall.c b/src/test_syscall.c new file mode 100644 index 0000000..b8b05c5 --- /dev/null +++ b/src/test_syscall.c @@ -0,0 +1,674 @@ +/* +** 2011 March 28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** The code in this file implements a Tcl interface used to test error +** handling in the os_unix.c module. Wrapper functions that support fault +** injection are registered as the low-level OS functions using the +** xSetSystemCall() method of the VFS. The Tcl interface is as follows: +** +** +** test_syscall install LIST +** Install wrapper functions for all system calls in argument LIST. +** LIST must be a list consisting of zero or more of the following +** literal values: +** +** open close access getcwd stat fstat +** ftruncate fcntl read pread pread64 write +** pwrite pwrite64 fchmod fallocate +** +** test_syscall uninstall +** Uninstall all wrapper functions. +** +** test_syscall fault ?COUNT PERSIST? +** If [test_syscall fault] is invoked without the two arguments, fault +** injection is disabled. Otherwise, fault injection is configured to +** cause a failure on the COUNT'th next call to a system call with a +** wrapper function installed. A COUNT value of 1 means fail the next +** system call. +** +** Argument PERSIST is interpreted as a boolean. If true, the all +** system calls following the initial failure also fail. Otherwise, only +** the single transient failure is injected. +** +** test_syscall errno CALL ERRNO +** Set the value that the global "errno" is set to following a fault +** in call CALL. Argument CALL must be one of the system call names +** listed above (under [test_syscall install]). ERRNO is a symbolic +** name (i.e. "EACCES"). Not all errno codes are supported. Add extra +** to the aErrno table in function test_syscall_errno() below as +** required. +** +** test_syscall reset ?SYSTEM-CALL? +** With no argument, this is an alias for the [uninstall] command. However, +** this command uses a VFS call of the form: +** +** xSetSystemCall(pVfs, 0, 0); +** +** To restore the default system calls. The [uninstall] command restores +** each system call individually by calling (i.e.): +** +** xSetSystemCall(pVfs, "open", 0); +** +** With an argument, this command attempts to reset the system call named +** by the parameter using the same method as [uninstall]. +** +** test_syscall exists SYSTEM-CALL +** Return true if the named system call exists. Or false otherwise. +** +** test_syscall list +** Return a list of all system calls. The list is constructed using +** the xNextSystemCall() VFS method. +*/ + +#include "sqlite3.h" +#include "tcl.h" +#include +#include +#include + +#include "sqliteInt.h" +#if SQLITE_OS_UNIX + +/* From test1.c */ +extern const char *sqlite3TestErrorName(int); + +#include +#include + +static struct TestSyscallGlobal { + int bPersist; /* 1 for persistent errors, 0 for transient */ + int nCount; /* Fail after this many more calls */ + int nFail; /* Number of failures that have occurred */ +} gSyscall = { 0, 0 }; + +static int ts_open(const char *, int, int); +static int ts_close(int fd); +static int ts_access(const char *zPath, int mode); +static char *ts_getcwd(char *zPath, size_t nPath); +static int ts_stat(const char *zPath, struct stat *p); +static int ts_fstat(int fd, struct stat *p); +static int ts_ftruncate(int fd, off_t n); +static int ts_fcntl(int fd, int cmd, ... ); +static int ts_read(int fd, void *aBuf, size_t nBuf); +static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off); +static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off); +static int ts_write(int fd, const void *aBuf, size_t nBuf); +static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off); +static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off); +static int ts_fchmod(int fd, mode_t mode); +static int ts_fallocate(int fd, off_t off, off_t len); + + +struct TestSyscallArray { + const char *zName; + sqlite3_syscall_ptr xTest; + sqlite3_syscall_ptr xOrig; + int default_errno; /* Default value for errno following errors */ + int custom_errno; /* Current value for errno if error */ +} aSyscall[] = { + /* 0 */ { "open", (sqlite3_syscall_ptr)ts_open, 0, EACCES, 0 }, + /* 1 */ { "close", (sqlite3_syscall_ptr)ts_close, 0, 0, 0 }, + /* 2 */ { "access", (sqlite3_syscall_ptr)ts_access, 0, 0, 0 }, + /* 3 */ { "getcwd", (sqlite3_syscall_ptr)ts_getcwd, 0, 0, 0 }, + /* 4 */ { "stat", (sqlite3_syscall_ptr)ts_stat, 0, 0, 0 }, + /* 5 */ { "fstat", (sqlite3_syscall_ptr)ts_fstat, 0, 0, 0 }, + /* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 }, + /* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, EACCES, 0 }, + /* 8 */ { "read", (sqlite3_syscall_ptr)ts_read, 0, 0, 0 }, + /* 9 */ { "pread", (sqlite3_syscall_ptr)ts_pread, 0, 0, 0 }, + /* 10 */ { "pread64", (sqlite3_syscall_ptr)ts_pread64, 0, 0, 0 }, + /* 11 */ { "write", (sqlite3_syscall_ptr)ts_write, 0, 0, 0 }, + /* 12 */ { "pwrite", (sqlite3_syscall_ptr)ts_pwrite, 0, 0, 0 }, + /* 13 */ { "pwrite64", (sqlite3_syscall_ptr)ts_pwrite64, 0, 0, 0 }, + /* 14 */ { "fchmod", (sqlite3_syscall_ptr)ts_fchmod, 0, 0, 0 }, + /* 15 */ { "fallocate", (sqlite3_syscall_ptr)ts_fallocate, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } +}; + +#define orig_open ((int(*)(const char *, int, int))aSyscall[0].xOrig) +#define orig_close ((int(*)(int))aSyscall[1].xOrig) +#define orig_access ((int(*)(const char*,int))aSyscall[2].xOrig) +#define orig_getcwd ((char*(*)(char*,size_t))aSyscall[3].xOrig) +#define orig_stat ((int(*)(const char*,struct stat*))aSyscall[4].xOrig) +#define orig_fstat ((int(*)(int,struct stat*))aSyscall[5].xOrig) +#define orig_ftruncate ((int(*)(int,off_t))aSyscall[6].xOrig) +#define orig_fcntl ((int(*)(int,int,...))aSyscall[7].xOrig) +#define orig_read ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig) +#define orig_pread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig) +#define orig_pread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].xOrig) +#define orig_write ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig) +#define orig_pwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[12].xOrig) +#define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[13].xOrig) +#define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig) +#define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig) + +/* +** This function is called exactly once from within each invocation of a +** system call wrapper in this file. It returns 1 if the function should +** fail, or 0 if it should succeed. +*/ +static int tsIsFail(void){ + gSyscall.nCount--; + if( gSyscall.nCount==0 || (gSyscall.nFail && gSyscall.bPersist) ){ + gSyscall.nFail++; + return 1; + } + return 0; +} + +/* +** Return the current error-number value for function zFunc. zFunc must be +** the name of a system call in the aSyscall[] table. +** +** Usually, the current error-number is the value that errno should be set +** to if the named system call fails. The exception is "fallocate". See +** comments above the implementation of ts_fallocate() for details. +*/ +static int tsErrno(const char *zFunc){ + int i; + int nFunc = strlen(zFunc); + for(i=0; aSyscall[i].zName; i++){ + if( strlen(aSyscall[i].zName)!=nFunc ) continue; + if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue; + return aSyscall[i].custom_errno; + } + + assert(0); + return 0; +} + +/* +** A wrapper around tsIsFail(). If tsIsFail() returns non-zero, set the +** value of errno before returning. +*/ +static int tsIsFailErrno(const char *zFunc){ + if( tsIsFail() ){ + errno = tsErrno(zFunc); + return 1; + } + return 0; +} + +/* +** A wrapper around open(). +*/ +static int ts_open(const char *zFile, int flags, int mode){ + if( tsIsFailErrno("open") ){ + return -1; + } + return orig_open(zFile, flags, mode); +} + +/* +** A wrapper around close(). +*/ +static int ts_close(int fd){ + if( tsIsFail() ){ + /* Even if simulating an error, close the original file-descriptor. + ** This is to stop the test process from running out of file-descriptors + ** when running a long test. If a call to close() appears to fail, SQLite + ** never attempts to use the file-descriptor afterwards (or even to close + ** it a second time). */ + orig_close(fd); + return -1; + } + return orig_close(fd); +} + +/* +** A wrapper around access(). +*/ +static int ts_access(const char *zPath, int mode){ + if( tsIsFail() ){ + return -1; + } + return orig_access(zPath, mode); +} + +/* +** A wrapper around getcwd(). +*/ +static char *ts_getcwd(char *zPath, size_t nPath){ + if( tsIsFail() ){ + return NULL; + } + return orig_getcwd(zPath, nPath); +} + +/* +** A wrapper around stat(). +*/ +static int ts_stat(const char *zPath, struct stat *p){ + if( tsIsFail() ){ + return -1; + } + return orig_stat(zPath, p); +} + +/* +** A wrapper around fstat(). +*/ +static int ts_fstat(int fd, struct stat *p){ + if( tsIsFailErrno("fstat") ){ + return -1; + } + return orig_fstat(fd, p); +} + +/* +** A wrapper around ftruncate(). +*/ +static int ts_ftruncate(int fd, off_t n){ + if( tsIsFailErrno("ftruncate") ){ + return -1; + } + return orig_ftruncate(fd, n); +} + +/* +** A wrapper around fcntl(). +*/ +static int ts_fcntl(int fd, int cmd, ... ){ + va_list ap; + void *pArg; + if( tsIsFailErrno("fcntl") ){ + return -1; + } + va_start(ap, cmd); + pArg = va_arg(ap, void *); + return orig_fcntl(fd, cmd, pArg); +} + +/* +** A wrapper around read(). +*/ +static int ts_read(int fd, void *aBuf, size_t nBuf){ + if( tsIsFailErrno("read") ){ + return -1; + } + return orig_read(fd, aBuf, nBuf); +} + +/* +** A wrapper around pread(). +*/ +static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){ + if( tsIsFailErrno("pread") ){ + return -1; + } + return orig_pread(fd, aBuf, nBuf, off); +} + +/* +** A wrapper around pread64(). +*/ +static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){ + if( tsIsFailErrno("pread64") ){ + return -1; + } + return orig_pread64(fd, aBuf, nBuf, off); +} + +/* +** A wrapper around write(). +*/ +static int ts_write(int fd, const void *aBuf, size_t nBuf){ + if( tsIsFailErrno("write") ){ + return -1; + } + return orig_write(fd, aBuf, nBuf); +} + +/* +** A wrapper around pwrite(). +*/ +static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){ + if( tsIsFailErrno("pwrite") ){ + return -1; + } + return orig_pwrite(fd, aBuf, nBuf, off); +} + +/* +** A wrapper around pwrite64(). +*/ +static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){ + if( tsIsFailErrno("pwrite64") ){ + return -1; + } + return orig_pwrite64(fd, aBuf, nBuf, off); +} + +/* +** A wrapper around fchmod(). +*/ +static int ts_fchmod(int fd, mode_t mode){ + if( tsIsFail() ){ + return -1; + } + return orig_fchmod(fd, mode); +} + +/* +** A wrapper around fallocate(). +** +** SQLite assumes that the fallocate() function is compatible with +** posix_fallocate(). According to the Linux man page (2009-09-30): +** +** posix_fallocate() returns zero on success, or an error number on +** failure. Note that errno is not set. +*/ +static int ts_fallocate(int fd, off_t off, off_t len){ + if( tsIsFail() ){ + return tsErrno("fallocate"); + } + return orig_fallocate(fd, off, len); +} + +static int test_syscall_install( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_vfs *pVfs; + int nElem; + int i; + Tcl_Obj **apElem; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL-LIST"); + return TCL_ERROR; + } + if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){ + return TCL_ERROR; + } + pVfs = sqlite3_vfs_find(0); + + for(i=0; ixGetSystemCall(pVfs, aSyscall[iCall].zName); + pVfs->xSetSystemCall(pVfs, aSyscall[iCall].zName, aSyscall[iCall].xTest); + } + aSyscall[iCall].custom_errno = aSyscall[iCall].default_errno; + } + + return TCL_OK; +} + +static int test_syscall_uninstall( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_vfs *pVfs; + int i; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + pVfs = sqlite3_vfs_find(0); + for(i=0; aSyscall[i].zName; i++){ + if( aSyscall[i].xOrig ){ + pVfs->xSetSystemCall(pVfs, aSyscall[i].zName, 0); + aSyscall[i].xOrig = 0; + } + } + return TCL_OK; +} + +static int test_syscall_reset( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_vfs *pVfs; + int i; + int rc; + + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + pVfs = sqlite3_vfs_find(0); + if( objc==2 ){ + rc = pVfs->xSetSystemCall(pVfs, 0, 0); + for(i=0; aSyscall[i].zName; i++) aSyscall[i].xOrig = 0; + }else{ + int nFunc; + char *zFunc = Tcl_GetStringFromObj(objv[2], &nFunc); + rc = pVfs->xSetSystemCall(pVfs, Tcl_GetString(objv[2]), 0); + for(i=0; rc==SQLITE_OK && aSyscall[i].zName; i++){ + if( strlen(aSyscall[i].zName)!=nFunc ) continue; + if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue; + aSyscall[i].xOrig = 0; + } + } + if( rc!=SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1)); + return TCL_ERROR; + } + + Tcl_ResetResult(interp); + return TCL_OK; +} + +static int test_syscall_exists( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_vfs *pVfs; + sqlite3_syscall_ptr x; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + pVfs = sqlite3_vfs_find(0); + x = pVfs->xGetSystemCall(pVfs, Tcl_GetString(objv[2])); + + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(x!=0)); + return TCL_OK; +} + +static int test_syscall_fault( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int nCount = 0; + int bPersist = 0; + + if( objc!=2 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?COUNT PERSIST?"); + return TCL_ERROR; + } + + if( objc==4 ){ + if( Tcl_GetIntFromObj(interp, objv[2], &nCount) + || Tcl_GetBooleanFromObj(interp, objv[3], &bPersist) + ){ + return TCL_ERROR; + } + } + + Tcl_SetObjResult(interp, Tcl_NewIntObj(gSyscall.nFail)); + gSyscall.nCount = nCount; + gSyscall.bPersist = bPersist; + gSyscall.nFail = 0; + return TCL_OK; +} + +static int test_syscall_errno( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int iCall; + int iErrno; + int rc; + + struct Errno { + const char *z; + int i; + } aErrno[] = { + { "EACCES", EACCES }, + { "EINTR", EINTR }, + { "EIO", EIO }, + { "EOVERFLOW", EOVERFLOW }, + { "ENOMEM", ENOMEM }, + { "EAGAIN", EAGAIN }, + { "ETIMEDOUT", ETIMEDOUT }, + { "EBUSY", EBUSY }, + { "EPERM", EPERM }, + { "EDEADLK", EDEADLK }, + { "ENOLCK", ENOLCK }, + { 0, 0 } + }; + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL ERRNO"); + return TCL_ERROR; + } + + rc = Tcl_GetIndexFromObjStruct(interp, + objv[2], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall + ); + if( rc!=TCL_OK ) return rc; + rc = Tcl_GetIndexFromObjStruct(interp, + objv[3], aErrno, sizeof(aErrno[0]), "errno", 0, &iErrno + ); + if( rc!=TCL_OK ) return rc; + + aSyscall[iCall].custom_errno = aErrno[iErrno].i; + return TCL_OK; +} + +static int test_syscall_list( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + const char *zSys; + sqlite3_vfs *pVfs; + Tcl_Obj *pList; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + pVfs = sqlite3_vfs_find(0); + pList = Tcl_NewObj(); + Tcl_IncrRefCount(pList); + for(zSys = pVfs->xNextSystemCall(pVfs, 0); + zSys!=0; + zSys = pVfs->xNextSystemCall(pVfs, zSys) + ){ + Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(zSys, -1)); + } + + Tcl_SetObjResult(interp, pList); + Tcl_DecrRefCount(pList); + return TCL_OK; +} + +static int test_syscall_defaultvfs( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_vfs *pVfs; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + pVfs = sqlite3_vfs_find(0); + Tcl_SetObjResult(interp, Tcl_NewStringObj(pVfs->zName, -1)); + return TCL_OK; +} + +static int test_syscall( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + struct SyscallCmd { + const char *zName; + Tcl_ObjCmdProc *xCmd; + } aCmd[] = { + { "fault", test_syscall_fault }, + { "install", test_syscall_install }, + { "uninstall", test_syscall_uninstall }, + { "reset", test_syscall_reset }, + { "errno", test_syscall_errno }, + { "exists", test_syscall_exists }, + { "list", test_syscall_list }, + { "defaultvfs", test_syscall_defaultvfs }, + { 0, 0 } + }; + int iCmd; + int rc; + + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); + return TCL_ERROR; + } + rc = Tcl_GetIndexFromObjStruct(interp, + objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd + ); + if( rc!=TCL_OK ) return rc; + return aCmd[iCmd].xCmd(clientData, interp, objc, objv); +} + +int SqlitetestSyscall_Init(Tcl_Interp *interp){ + struct SyscallCmd { + const char *zName; + Tcl_ObjCmdProc *xCmd; + } aCmd[] = { + { "test_syscall", test_syscall}, + }; + int i; + + for(i=0; i +#include +#include "sqlite3.h" + +/* +** An instance of this structure is attached to the each trace VFS to +** provide auxiliary information. +*/ +typedef struct vfstrace_info vfstrace_info; +struct vfstrace_info { + sqlite3_vfs *pRootVfs; /* The underlying real VFS */ + int (*xOut)(const char*, void*); /* Send output here */ + void *pOutArg; /* First argument to xOut */ + const char *zVfsName; /* Name of this trace-VFS */ + sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */ +}; + +/* +** The sqlite3_file object for the trace VFS +*/ +typedef struct vfstrace_file vfstrace_file; +struct vfstrace_file { + sqlite3_file base; /* Base class. Must be first */ + vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */ + const char *zFName; /* Base name of the file */ + sqlite3_file *pReal; /* The real underlying file */ +}; + +/* +** Method declarations for vfstrace_file. +*/ +static int vfstraceClose(sqlite3_file*); +static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); +static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size); +static int vfstraceSync(sqlite3_file*, int flags); +static int vfstraceFileSize(sqlite3_file*, sqlite3_int64 *pSize); +static int vfstraceLock(sqlite3_file*, int); +static int vfstraceUnlock(sqlite3_file*, int); +static int vfstraceCheckReservedLock(sqlite3_file*, int *); +static int vfstraceFileControl(sqlite3_file*, int op, void *pArg); +static int vfstraceSectorSize(sqlite3_file*); +static int vfstraceDeviceCharacteristics(sqlite3_file*); +static int vfstraceShmLock(sqlite3_file*,int,int,int); +static int vfstraceShmMap(sqlite3_file*,int,int,int, void volatile **); +static void vfstraceShmBarrier(sqlite3_file*); +static int vfstraceShmUnmap(sqlite3_file*,int); + +/* +** Method declarations for vfstrace_vfs. +*/ +static int vfstraceOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +static int vfstraceDelete(sqlite3_vfs*, const char *zName, int syncDir); +static int vfstraceAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int vfstraceFullPathname(sqlite3_vfs*, const char *zName, int, char *); +static void *vfstraceDlOpen(sqlite3_vfs*, const char *zFilename); +static void vfstraceDlError(sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*vfstraceDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); +static void vfstraceDlClose(sqlite3_vfs*, void*); +static int vfstraceRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int vfstraceSleep(sqlite3_vfs*, int microseconds); +static int vfstraceCurrentTime(sqlite3_vfs*, double*); +static int vfstraceGetLastError(sqlite3_vfs*, int, char*); +static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); +static int vfstraceSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr); +static sqlite3_syscall_ptr vfstraceGetSystemCall(sqlite3_vfs*, const char *); +static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName); + +/* +** Return a pointer to the tail of the pathname. Examples: +** +** /home/drh/xyzzy.txt -> xyzzy.txt +** xyzzy.txt -> xyzzy.txt +*/ +static const char *fileTail(const char *z){ + int i; + if( z==0 ) return 0; + i = strlen(z)-1; + while( i>0 && z[i-1]!='/' ){ i--; } + return &z[i]; +} + +/* +** Send trace output defined by zFormat and subsequent arguments. +*/ +static void vfstrace_printf( + vfstrace_info *pInfo, + const char *zFormat, + ... +){ + va_list ap; + char *zMsg; + va_start(ap, zFormat); + zMsg = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + pInfo->xOut(zMsg, pInfo->pOutArg); + sqlite3_free(zMsg); +} + +/* +** Convert value rc into a string and print it using zFormat. zFormat +** should have exactly one %s +*/ +static void vfstrace_print_errcode( + vfstrace_info *pInfo, + const char *zFormat, + int rc +){ + char zBuf[50]; + char *zVal; + switch( rc ){ + case SQLITE_OK: zVal = "SQLITE_OK"; break; + case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break; + case SQLITE_PERM: zVal = "SQLITE_PERM"; break; + case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break; + case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break; + case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break; + case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break; + case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break; + case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break; + case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break; + case SQLITE_FULL: zVal = "SQLITE_FULL"; break; + case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break; + case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break; + case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break; + case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break; + case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break; + case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break; + case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break; + case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break; + case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break; + case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break; + case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break; + case SQLITE_IOERR_FSYNC: zVal = "SQLITE_IOERR_FSYNC"; break; + case SQLITE_IOERR_DIR_FSYNC: zVal = "SQLITE_IOERR_DIR_FSYNC"; break; + case SQLITE_IOERR_TRUNCATE: zVal = "SQLITE_IOERR_TRUNCATE"; break; + case SQLITE_IOERR_FSTAT: zVal = "SQLITE_IOERR_FSTAT"; break; + case SQLITE_IOERR_UNLOCK: zVal = "SQLITE_IOERR_UNLOCK"; break; + case SQLITE_IOERR_RDLOCK: zVal = "SQLITE_IOERR_RDLOCK"; break; + case SQLITE_IOERR_DELETE: zVal = "SQLITE_IOERR_DELETE"; break; + case SQLITE_IOERR_BLOCKED: zVal = "SQLITE_IOERR_BLOCKED"; break; + case SQLITE_IOERR_NOMEM: zVal = "SQLITE_IOERR_NOMEM"; break; + case SQLITE_IOERR_ACCESS: zVal = "SQLITE_IOERR_ACCESS"; break; + case SQLITE_IOERR_CHECKRESERVEDLOCK: + zVal = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; + case SQLITE_IOERR_LOCK: zVal = "SQLITE_IOERR_LOCK"; break; + case SQLITE_IOERR_CLOSE: zVal = "SQLITE_IOERR_CLOSE"; break; + case SQLITE_IOERR_DIR_CLOSE: zVal = "SQLITE_IOERR_DIR_CLOSE"; break; + case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break; + case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break; + case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break; + case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break; + case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break; + case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break; + default: { + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); + zVal = zBuf; + break; + } + } + vfstrace_printf(pInfo, zFormat, zVal); +} + +/* +** Append to a buffer. +*/ +static void strappend(char *z, int *pI, const char *zAppend){ + int i = *pI; + while( zAppend[0] ){ z[i++] = *(zAppend++); } + z[i] = 0; + *pI = i; +} + +/* +** Close an vfstrace-file. +*/ +static int vfstraceClose(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xClose(p->pReal); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + if( rc==SQLITE_OK ){ + sqlite3_free((void*)p->base.pMethods); + p->base.pMethods = 0; + } + return rc; +} + +/* +** Read data from an vfstrace-file. +*/ +static int vfstraceRead( + sqlite3_file *pFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)", + pInfo->zVfsName, p->zFName, iAmt, iOfst); + rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Write data to an vfstrace-file. +*/ +static int vfstraceWrite( + sqlite3_file *pFile, + const void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)", + pInfo->zVfsName, p->zFName, iAmt, iOfst); + rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Truncate an vfstrace-file. +*/ +static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName, + size); + rc = p->pReal->pMethods->xTruncate(p->pReal, size); + vfstrace_printf(pInfo, " -> %d\n", rc); + return rc; +} + +/* +** Sync an vfstrace-file. +*/ +static int vfstraceSync(sqlite3_file *pFile, int flags){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + int i; + char zBuf[100]; + memcpy(zBuf, "|0", 3); + i = 0; + if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL"); + else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL"); + if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY"); + if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){ + sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags); + } + vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName, + &zBuf[1]); + rc = p->pReal->pMethods->xSync(p->pReal, flags); + vfstrace_printf(pInfo, " -> %d\n", rc); + return rc; +} + +/* +** Return the current file-size of an vfstrace-file. +*/ +static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); + vfstrace_print_errcode(pInfo, " -> %s,", rc); + vfstrace_printf(pInfo, " size=%lld\n", *pSize); + return rc; +} + +/* +** Return the name of a lock. +*/ +static const char *lockName(int eLock){ + const char *azLockNames[] = { + "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE" + }; + if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){ + return "???"; + }else{ + return azLockNames[eLock]; + } +} + +/* +** Lock an vfstrace-file. +*/ +static int vfstraceLock(sqlite3_file *pFile, int eLock){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName, + lockName(eLock)); + rc = p->pReal->pMethods->xLock(p->pReal, eLock); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Unlock an vfstrace-file. +*/ +static int vfstraceUnlock(sqlite3_file *pFile, int eLock){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName, + lockName(eLock)); + rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Check if another file-handle holds a RESERVED lock on an vfstrace-file. +*/ +static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)", + pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); + vfstrace_print_errcode(pInfo, " -> %s", rc); + vfstrace_printf(pInfo, ", out=%d\n", *pResOut); + return rc; +} + +/* +** File control method. For custom operations on an vfstrace-file. +*/ +static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + char zBuf[100]; + char *zOp; + switch( op ){ + case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break; + case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break; + case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break; + case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break; + case SQLITE_FCNTL_SIZE_HINT: { + sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld", + *(sqlite3_int64*)pArg); + zOp = zBuf; + break; + } + case SQLITE_FCNTL_CHUNK_SIZE: { + sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg); + zOp = zBuf; + break; + } + case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break; + case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break; + case 0xca093fa0: zOp = "DB_UNCHANGED"; break; + default: { + sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op); + zOp = zBuf; + break; + } + } + vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)", + pInfo->zVfsName, p->zFName, zOp); + rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Return the sector-size in bytes for an vfstrace-file. +*/ +static int vfstraceSectorSize(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xSectorSize(p->pReal); + vfstrace_printf(pInfo, " -> %d\n", rc); + return rc; +} + +/* +** Return the device characteristic flags supported by an vfstrace-file. +*/ +static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)", + pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); + vfstrace_printf(pInfo, " -> 0x%08x\n", rc); + return rc; +} + +/* +** Shared-memory operations. +*/ +static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + char zLck[100]; + int i = 0; + memcpy(zLck, "|0", 3); + if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK"); + if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK"); + if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED"); + if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE"); + if( flags & ~(0xf) ){ + sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags); + } + vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d,n=%d,%s)", + pInfo->zVfsName, p->zFName, ofst, n, &zLck[1]); + rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} +static int vfstraceShmMap( + sqlite3_file *pFile, + int iRegion, + int szRegion, + int isWrite, + void volatile **pp +){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)", + pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite); + rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} +static void vfstraceShmBarrier(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName); + p->pReal->pMethods->xShmBarrier(p->pReal); +} +static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)", + pInfo->zVfsName, p->zFName, delFlag); + rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + + + +/* +** Open an vfstrace file handle. +*/ +static int vfstraceOpen( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_file *pFile, + int flags, + int *pOutFlags +){ + int rc; + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + p->pInfo = pInfo; + p->zFName = zName ? fileTail(zName) : ""; + p->pReal = (sqlite3_file *)&p[1]; + rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags); + vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)", + pInfo->zVfsName, p->zFName, flags); + if( p->pReal->pMethods ){ + sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) ); + const sqlite3_io_methods *pSub = p->pReal->pMethods; + memset(pNew, 0, sizeof(*pNew)); + pNew->iVersion = pSub->iVersion; + pNew->xClose = vfstraceClose; + pNew->xRead = vfstraceRead; + pNew->xWrite = vfstraceWrite; + pNew->xTruncate = vfstraceTruncate; + pNew->xSync = vfstraceSync; + pNew->xFileSize = vfstraceFileSize; + pNew->xLock = vfstraceLock; + pNew->xUnlock = vfstraceUnlock; + pNew->xCheckReservedLock = vfstraceCheckReservedLock; + pNew->xFileControl = vfstraceFileControl; + pNew->xSectorSize = vfstraceSectorSize; + pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics; + if( pNew->iVersion>=2 ){ + pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0; + pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0; + pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0; + pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0; + } + pFile->pMethods = pNew; + } + vfstrace_print_errcode(pInfo, " -> %s", rc); + if( pOutFlags ){ + vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags); + }else{ + vfstrace_printf(pInfo, "\n"); + } + return rc; +} + +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + int rc; + vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", + pInfo->zVfsName, zPath, dirSync); + rc = pRoot->xDelete(pRoot, zPath, dirSync); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +*/ +static int vfstraceAccess( + sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + int rc; + vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", + pInfo->zVfsName, zPath, flags); + rc = pRoot->xAccess(pRoot, zPath, flags, pResOut); + vfstrace_print_errcode(pInfo, " -> %s", rc); + vfstrace_printf(pInfo, ", out=%d\n", *pResOut); + return rc; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (DEVSYM_MAX_PATHNAME+1) bytes. +*/ +static int vfstraceFullPathname( + sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + int rc; + vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")", + pInfo->zVfsName, zPath); + rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut); + vfstrace_print_errcode(pInfo, " -> %s", rc); + vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut); + return rc; +} + +/* +** Open the dynamic library located at zPath and return a handle. +*/ +static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath); + return pRoot->xDlOpen(pRoot, zPath); +} + +/* +** Populate the buffer zErrMsg (size nByte bytes) with a human readable +** utf-8 string describing the most recent error encountered associated +** with dynamic libraries. +*/ +static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte); + pRoot->xDlError(pRoot, nByte, zErrMsg); + vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg); +} + +/* +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. +*/ +static void (*vfstraceDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlSym(\"%s\")\n", pInfo->zVfsName, zSym); + return pRoot->xDlSym(pRoot, p, zSym); +} + +/* +** Close the dynamic library handle pHandle. +*/ +static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName); + pRoot->xDlClose(pRoot, pHandle); +} + +/* +** Populate the buffer pointed to by zBufOut with nByte bytes of +** random data. +*/ +static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte); + return pRoot->xRandomness(pRoot, nByte, zBufOut); +} + +/* +** Sleep for nMicro microseconds. Return the number of microseconds +** actually slept. +*/ +static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xSleep(pRoot, nMicro); +} + +/* +** Return the current time as a Julian Day number in *pTimeOut. +*/ +static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xCurrentTime(pRoot, pTimeOut); +} +static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xCurrentTimeInt64(pRoot, pTimeOut); +} + +/* +** Return th3 emost recent error code and message +*/ +static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xGetLastError(pRoot, iErr, zErr); +} + +/* +** Override system calls. +*/ +static int vfstraceSetSystemCall( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_syscall_ptr pFunc +){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xSetSystemCall(pRoot, zName, pFunc); +} +static sqlite3_syscall_ptr vfstraceGetSystemCall( + sqlite3_vfs *pVfs, + const char *zName +){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xGetSystemCall(pRoot, zName); +} +static const char *vfstraceNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xNextSystemCall(pRoot, zName); +} + + +/* +** Clients invoke this routine to construct a new trace-vfs shim. +** +** Return SQLITE_OK on success. +** +** SQLITE_NOMEM is returned in the case of a memory allocation error. +** SQLITE_NOTFOUND is returned if zOldVfsName does not exist. +*/ +int vfstrace_register( + const char *zTraceName, /* Name of the newly constructed VFS */ + const char *zOldVfsName, /* Name of the underlying VFS */ + int (*xOut)(const char*,void*), /* Output routine. ex: fputs */ + void *pOutArg, /* 2nd argument to xOut. ex: stderr */ + int makeDefault /* True to make the new VFS the default */ +){ + sqlite3_vfs *pNew; + sqlite3_vfs *pRoot; + vfstrace_info *pInfo; + int nName; + int nByte; + + pRoot = sqlite3_vfs_find(zOldVfsName); + if( pRoot==0 ) return SQLITE_NOTFOUND; + nName = strlen(zTraceName); + nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1; + pNew = sqlite3_malloc( nByte ); + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, nByte); + pInfo = (vfstrace_info*)&pNew[1]; + pNew->iVersion = pRoot->iVersion; + pNew->szOsFile = pRoot->szOsFile + sizeof(vfstrace_file); + pNew->mxPathname = pRoot->mxPathname; + pNew->zName = (char*)&pInfo[1]; + memcpy((char*)&pInfo[1], zTraceName, nName+1); + pNew->pAppData = pInfo; + pNew->xOpen = vfstraceOpen; + pNew->xDelete = vfstraceDelete; + pNew->xAccess = vfstraceAccess; + pNew->xFullPathname = vfstraceFullPathname; + pNew->xDlOpen = pRoot->xDlOpen==0 ? 0 : vfstraceDlOpen; + pNew->xDlError = pRoot->xDlError==0 ? 0 : vfstraceDlError; + pNew->xDlSym = pRoot->xDlSym==0 ? 0 : vfstraceDlSym; + pNew->xDlClose = pRoot->xDlClose==0 ? 0 : vfstraceDlClose; + pNew->xRandomness = vfstraceRandomness; + pNew->xSleep = vfstraceSleep; + pNew->xCurrentTime = vfstraceCurrentTime; + pNew->xGetLastError = pRoot->xGetLastError==0 ? 0 : vfstraceGetLastError; + if( pNew->iVersion>=2 ){ + pNew->xCurrentTimeInt64 = pRoot->xCurrentTimeInt64==0 ? 0 : + vfstraceCurrentTimeInt64; + if( pNew->iVersion>=3 ){ + pNew->xSetSystemCall = pRoot->xSetSystemCall==0 ? 0 : + vfstraceSetSystemCall; + pNew->xGetSystemCall = pRoot->xGetSystemCall==0 ? 0 : + vfstraceGetSystemCall; + pNew->xNextSystemCall = pRoot->xNextSystemCall==0 ? 0 : + vfstraceNextSystemCall; + } + } + pInfo->pRootVfs = pRoot; + pInfo->xOut = xOut; + pInfo->pOutArg = pOutArg; + pInfo->zVfsName = pNew->zName; + pInfo->pTraceVfs = pNew; + vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n", + pInfo->zVfsName, pRoot->zName); + return sqlite3_vfs_register(pNew, makeDefault); +} diff --git a/src/test_wholenumber.c b/src/test_wholenumber.c new file mode 100644 index 0000000..150dc95 --- /dev/null +++ b/src/test_wholenumber.c @@ -0,0 +1,311 @@ +/* +** 2011 April 02 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements a virtual table that returns the whole numbers +** between 1 and 4294967295, inclusive. +** +** Example: +** +** CREATE VIRTUAL TABLE nums USING wholenumber; +** SELECT value FROM nums WHERE value<10; +** +** Results in: +** +** 1 2 3 4 5 6 7 8 9 +*/ +#include "sqlite3.h" +#include +#include + +#ifndef SQLITE_OMIT_VIRTUALTABLE + + +/* A wholenumber cursor object */ +typedef struct wholenumber_cursor wholenumber_cursor; +struct wholenumber_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + unsigned iValue; /* Current value */ + unsigned mxValue; /* Maximum value */ +}; + +/* Methods for the wholenumber module */ +static int wholenumberConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + sqlite3_vtab *pNew; + pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); + if( pNew==0 ) return SQLITE_NOMEM; + sqlite3_declare_vtab(db, "CREATE TABLE x(value)"); + memset(pNew, 0, sizeof(*pNew)); + return SQLITE_OK; +} +/* Note that for this virtual table, the xCreate and xConnect +** methods are identical. */ + +static int wholenumberDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} +/* The xDisconnect and xDestroy methods are also the same */ + + +/* +** Open a new wholenumber cursor. +*/ +static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + wholenumber_cursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Close a wholenumber cursor. +*/ +static int wholenumberClose(sqlite3_vtab_cursor *cur){ + sqlite3_free(cur); + return SQLITE_OK; +} + + +/* +** Advance a cursor to its next row of output +*/ +static int wholenumberNext(sqlite3_vtab_cursor *cur){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + pCur->iValue++; + return SQLITE_OK; +} + +/* +** Return the value associated with a wholenumber. +*/ +static int wholenumberColumn( + sqlite3_vtab_cursor *cur, + sqlite3_context *ctx, + int i +){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + sqlite3_result_int64(ctx, pCur->iValue); + return SQLITE_OK; +} + +/* +** The rowid. +*/ +static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + *pRowid = pCur->iValue; + return SQLITE_OK; +} + +/* +** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal +** that the cursor has nothing more to output. +*/ +static int wholenumberEof(sqlite3_vtab_cursor *cur){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + return pCur->iValue>pCur->mxValue || pCur->iValue==0; +} + +/* +** Called to "rewind" a cursor back to the beginning so that +** it starts its output over again. Always called at least once +** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call. +** +** idxNum Constraints +** ------ --------------------- +** 0 (none) +** 1 value > $argv0 +** 2 value >= $argv0 +** 4 value < $argv0 +** 8 value <= $argv0 +** +** 5 value > $argv0 AND value < $argv1 +** 6 value >= $argv0 AND value < $argv1 +** 9 value > $argv0 AND value <= $argv1 +** 10 value >= $argv0 AND value <= $argv1 +*/ +static int wholenumberFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor; + sqlite3_int64 v; + int i = 0; + pCur->iValue = 1; + pCur->mxValue = 0xffffffff; /* 4294967295 */ + if( idxNum & 3 ){ + v = sqlite3_value_int64(argv[0]) + (idxNum&1); + if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v; + i++; + } + if( idxNum & 12 ){ + v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1); + if( v>=pCur->iValue && vmxValue ) pCur->mxValue = v; + } + return SQLITE_OK; +} + +/* +** Search for terms of these forms: +** +** (1) value > $value +** (2) value >= $value +** (4) value < $value +** (8) value <= $value +** +** idxNum is an ORed combination of 1 or 2 with 4 or 8. +*/ +static int wholenumberBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + int i; + int idxNum = 0; + int argvIdx = 1; + int ltIdx = -1; + int gtIdx = -1; + const struct sqlite3_index_constraint *pConstraint; + pConstraint = pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pConstraint++){ + if( pConstraint->usable==0 ) continue; + if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){ + idxNum |= 1; + ltIdx = i; + } + if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){ + idxNum |= 2; + ltIdx = i; + } + if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ + idxNum |= 4; + gtIdx = i; + } + if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){ + idxNum |= 8; + gtIdx = i; + } + } + pIdxInfo->idxNum = idxNum; + if( ltIdx>=0 ){ + pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++; + pIdxInfo->aConstraintUsage[ltIdx].omit = 1; + } + if( gtIdx>=0 ){ + pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx; + pIdxInfo->aConstraintUsage[gtIdx].omit = 1; + } + if( pIdxInfo->nOrderBy==1 + && pIdxInfo->aOrderBy[0].desc==0 + ){ + pIdxInfo->orderByConsumed = 1; + } + pIdxInfo->estimatedCost = (double)1; + return SQLITE_OK; +} + +/* +** A virtual table module that provides read-only access to a +** Tcl global variable namespace. +*/ +static sqlite3_module wholenumberModule = { + 0, /* iVersion */ + wholenumberConnect, + wholenumberConnect, + wholenumberBestIndex, + wholenumberDisconnect, + wholenumberDisconnect, + wholenumberOpen, /* xOpen - open a cursor */ + wholenumberClose, /* xClose - close a cursor */ + wholenumberFilter, /* xFilter - configure scan constraints */ + wholenumberNext, /* xNext - advance a cursor */ + wholenumberEof, /* xEof - check for end of scan */ + wholenumberColumn, /* xColumn - read data */ + wholenumberRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + +/* +** Register the wholenumber virtual table +*/ +int wholenumber_register(sqlite3 *db){ + int rc = SQLITE_OK; +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0); +#endif + return rc; +} + +#ifdef SQLITE_TEST +#include +/* +** Decode a pointer to an sqlite3 object. +*/ +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); + +/* +** Register the echo virtual table module. +*/ +static int register_wholenumber_module( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + wholenumber_register(db); + return TCL_OK; +} + + +/* +** Register commands with the TCL interpreter. +*/ +int Sqlitetestwholenumber_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + void *clientData; + } aObjCmd[] = { + { "register_wholenumber_module", register_wholenumber_module, 0 }, + }; + int i; + for(i=0; ipSchema ){ HashElem *p; + assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ Trigger *pTrig = (Trigger *)sqliteHashData(p); if( pTrig->pTabSchema==pTab->pSchema @@ -165,10 +166,14 @@ void sqlite3BeginTrigger( if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto trigger_cleanup; } + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash), zName, sqlite3Strlen30(zName)) ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto trigger_cleanup; } @@ -262,7 +267,6 @@ void sqlite3FinishTrigger( int iDb; /* Database containing the trigger */ Token nameToken; /* Trigger name for error reporting */ - pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; zName = pTrig->zName; @@ -305,6 +309,7 @@ void sqlite3FinishTrigger( if( db->init.busy ){ Trigger *pLink = pTrig; Hash *pHash = &db->aDb[iDb].pSchema->trigHash; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig); if( pTrig ){ db->mallocFailed = 1; @@ -486,15 +491,19 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){ zDb = pName->a[0].zDatabase; zName = pName->a[0].zName; nName = sqlite3Strlen30(zName); + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName); if( pTrigger ) break; } if( !pTrigger ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, zDb); } pParse->checkSchema = 1; goto drop_trigger_cleanup; @@ -562,7 +571,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3OpenMasterTable(pParse, iDb); base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); - sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, 0); + sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT); sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Close, 0, 0); @@ -577,8 +586,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ ** Remove a trigger from the hash tables of the sqlite* pointer. */ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ - Hash *pHash = &(db->aDb[iDb].pSchema->trigHash); Trigger *pTrigger; + Hash *pHash; + + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pHash = &(db->aDb[iDb].pSchema->trigHash); pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0); if( ALWAYS(pTrigger) ){ if( pTrigger->pSchema==pTrigger->pTabSchema ){ @@ -624,8 +636,12 @@ Trigger *sqlite3TriggersExist( int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ){ int mask = 0; - Trigger *pList = sqlite3TriggerList(pParse, pTab); + Trigger *pList = 0; Trigger *p; + + if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){ + pList = sqlite3TriggerList(pParse, pTab); + } assert( pList==0 || IsVirtual(pTab)==0 ); for(p=pList; p; p=p->pNext){ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ diff --git a/src/update.c b/src/update.c index 8bf58d7..315034d 100644 --- a/src/update.c +++ b/src/update.c @@ -128,7 +128,6 @@ void sqlite3Update( int regNew; int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ - int regRec; /* Register used for new table record to insert */ memset(&sContext, 0, sizeof(sContext)); db = pParse->db; @@ -286,7 +285,6 @@ void sqlite3Update( } regNew = pParse->nMem + 1; pParse->nMem += pTab->nCol; - regRec = ++pParse->nMem; /* Start the view context. */ if( isView ){ @@ -396,7 +394,7 @@ void sqlite3Update( pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; inCol; i++){ - if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<=zEnd ) goto do_atoi_calc; - if( *zNum=='-' ){ - neg = 1; - zNum+=incr; - }else if( *zNum=='+' ){ - zNum+=incr; + if( zNum='0' && c<='9'; i+=incr){ - v = v*10 + c - '0'; + u = u*10 + c - '0'; + } + if( u>LARGEST_INT64 ){ + *pNum = SMALLEST_INT64; + }else if( neg ){ + *pNum = -(i64)u; + }else{ + *pNum = (i64)u; } - *pNum = neg ? -v : v; testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); @@ -487,14 +496,25 @@ do_atoi_calc: return 1; }else if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ + assert( u<=LARGEST_INT64 ); return 0; }else{ - /* 19-digit numbers must be no larger than 9223372036854775807 if positive - ** or 9223372036854775808 if negative. Note that 9223372036854665808 - ** is 2^63. Return 1 if to large */ - c=compare2pow63(zNum, incr); - if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */ - return c0 ){ + /* zNum is greater than 9223372036854775808 so it overflows */ + return 1; + }else{ + /* zNum is exactly 9223372036854775808. Fits if negative. The + ** special case 2 overflow if positive */ + assert( u-1==LARGEST_INT64 ); + assert( (*pNum)==SMALLEST_INT64 ); + return neg ? 0 : 2; + } } } @@ -1060,3 +1080,71 @@ int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ return 1; } } + +/* +** Attempt to add, substract, or multiply the 64-bit signed value iB against +** the other 64-bit signed integer at *pA and store the result in *pA. +** Return 0 on success. Or if the operation would have resulted in an +** overflow, leave *pA unchanged and return 1. +*/ +int sqlite3AddInt64(i64 *pA, i64 iB){ + i64 iA = *pA; + testcase( iA==0 ); testcase( iA==1 ); + testcase( iB==-1 ); testcase( iB==0 ); + if( iB>=0 ){ + testcase( iA>0 && LARGEST_INT64 - iA == iB ); + testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); + if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; + *pA += iB; + }else{ + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); + if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; + *pA += iB; + } + return 0; +} +int sqlite3SubInt64(i64 *pA, i64 iB){ + testcase( iB==SMALLEST_INT64+1 ); + if( iB==SMALLEST_INT64 ){ + testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); + if( (*pA)>=0 ) return 1; + *pA -= iB; + return 0; + }else{ + return sqlite3AddInt64(pA, -iB); + } +} +#define TWOPOWER32 (((i64)1)<<32) +#define TWOPOWER31 (((i64)1)<<31) +int sqlite3MulInt64(i64 *pA, i64 iB){ + i64 iA = *pA; + i64 iA1, iA0, iB1, iB0, r; + + iA1 = iA/TWOPOWER32; + iA0 = iA % TWOPOWER32; + iB1 = iB/TWOPOWER32; + iB0 = iB % TWOPOWER32; + if( iA1*iB1 != 0 ) return 1; + assert( iA1*iB0==0 || iA0*iB1==0 ); + r = iA1*iB0 + iA0*iB1; + testcase( r==(-TWOPOWER31)-1 ); + testcase( r==(-TWOPOWER31) ); + testcase( r==TWOPOWER31 ); + testcase( r==TWOPOWER31-1 ); + if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; + r *= TWOPOWER32; + if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; + *pA = r; + return 0; +} + +/* +** Compute the absolute value of a 32-bit signed integer, of possible. Or +** if the integer has a value of -2147483648, return +2147483647 +*/ +int sqlite3AbsInt32(int x){ + if( x>=0 ) return x; + if( x==(int)0x80000000 ) return 0x7fffffff; + return -x; +} diff --git a/src/vacuum.c b/src/vacuum.c index 4babf5b..5a4ed32 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -335,8 +335,11 @@ end_of_vacuum: pDb->pSchema = 0; } - sqlite3ResetInternalSchema(db, 0); + /* This both clears the schemas and reduces the size of the db->aDb[] + ** array. */ + sqlite3ResetInternalSchema(db, -1); return rc; } + #endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */ diff --git a/src/vdbe.c b/src/vdbe.c index eddd1e5..5376b08 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -551,7 +551,7 @@ int sqlite3VdbeExec( Op *pOp; /* Current operation */ int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ - u8 resetSchemaOnFault = 0; /* Reset schema after an error if true */ + u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK int checkProgress; /* True if progress callbacks are enabled */ @@ -571,7 +571,7 @@ int sqlite3VdbeExec( /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ - sqlite3VdbeMutexArrayEnter(p); + sqlite3VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ @@ -1246,19 +1246,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ iA = pIn1->u.i; iB = pIn2->u.i; switch( pOp->opcode ){ - case OP_Add: iB += iA; break; - case OP_Subtract: iB -= iA; break; - case OP_Multiply: iB *= iA; break; + case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break; + case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break; + case OP_Multiply: if( sqlite3MulInt64(&iB,iA) ) goto fp_math; break; case OP_Divide: { if( iA==0 ) goto arithmetic_result_is_null; - /* Dividing the largest possible negative 64-bit integer (1<<63) by - ** -1 returns an integer too large to store in a 64-bit data-type. On - ** some architectures, the value overflows to (1<<63). On others, - ** a SIGFPE is issued. The following statement normalizes this - ** behavior so that all architectures behave as if integer - ** overflow occurred. - */ - if( iA==-1 && iB==SMALLEST_INT64 ) iA = 1; + if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math; iB /= iA; break; } @@ -1272,6 +1265,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); }else{ +fp_math: rA = sqlite3VdbeRealValue(pIn1); rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ @@ -1431,6 +1425,15 @@ case OP_Function: { if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } + +#if 0 + /* The app-defined function has done something that as caused this + ** statement to expire. (Perhaps the function called sqlite3_exec() + ** with a CREATE TABLE statement.) + */ + if( p->expired ) rc = SQLITE_ABORT; +#endif + REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; @@ -1466,8 +1469,10 @@ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ - i64 a; - i64 b; + i64 iA; + u64 uA; + i64 iB; + u8 op; pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; @@ -1476,16 +1481,38 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ sqlite3VdbeMemSetNull(pOut); break; } - a = sqlite3VdbeIntValue(pIn2); - b = sqlite3VdbeIntValue(pIn1); - switch( pOp->opcode ){ - case OP_BitAnd: a &= b; break; - case OP_BitOr: a |= b; break; - case OP_ShiftLeft: a <<= b; break; - default: assert( pOp->opcode==OP_ShiftRight ); - a >>= b; break; + iA = sqlite3VdbeIntValue(pIn2); + iB = sqlite3VdbeIntValue(pIn1); + op = pOp->opcode; + if( op==OP_BitAnd ){ + iA &= iB; + }else if( op==OP_BitOr ){ + iA |= iB; + }else if( iB!=0 ){ + assert( op==OP_ShiftRight || op==OP_ShiftLeft ); + + /* If shifting by a negative amount, shift in the other direction */ + if( iB<0 ){ + assert( OP_ShiftRight==OP_ShiftLeft+1 ); + op = 2*OP_ShiftLeft + 1 - op; + iB = iB>(-64) ? -iB : 64; + } + + if( iB>=64 ){ + iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1; + }else{ + memcpy(&uA, &iA, sizeof(uA)); + if( op==OP_ShiftLeft ){ + uA <<= iB; + }else{ + uA >>= iB; + /* Sign-extend on a right shift of a negative number */ + if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB); + } + memcpy(&iA, &uA, sizeof(iA)); + } } - pOut->u.i = a; + pOut->u.i = iA; MemSetTypeFlag(pOut, MEM_Int); break; } @@ -2411,7 +2438,6 @@ case OP_MakeRecord: { */ nData = 0; /* Number of bytes of data space */ nHdr = 0; /* Number of bytes of header space */ - nByte = 0; /* Data space required for this record */ nZero = 0; /* Number of zero bytes at the end of the record */ nField = pOp->p1; zAffinity = pOp->p4.z; @@ -2632,7 +2658,7 @@ case OP_Savepoint: { } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } } @@ -2772,7 +2798,7 @@ case OP_Transaction: { Btree *pBt; assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (1<p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ @@ -2828,7 +2854,7 @@ case OP_ReadCookie: { /* out2-prerelease */ assert( pOp->p3=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); - assert( (p->btreeMask & (1<btreeMask & (((yDbMask)1)<aDb[iDb].pBt, iCookie, (u32 *)&iMeta); pOut->u.i = iMeta; @@ -2849,9 +2875,10 @@ case OP_SetCookie: { /* in3 */ Db *pDb; assert( pOp->p2p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (1<p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); + assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); /* See note about index shifting on OP_ReadCookie */ @@ -2873,10 +2900,12 @@ case OP_SetCookie: { /* in3 */ break; } -/* Opcode: VerifyCookie P1 P2 * +/* Opcode: VerifyCookie P1 P2 P3 * * ** ** Check the value of global database parameter number 0 (the -** schema version) and make sure it is equal to P2. +** schema version) and make sure it is equal to P2 and that the +** generation counter on the local schema parse equals P3. +** ** P1 is the database number which is 0 for the main database file ** and 1 for the file holding temporary tables and some higher number ** for auxiliary databases. @@ -2891,16 +2920,20 @@ case OP_SetCookie: { /* in3 */ */ case OP_VerifyCookie: { int iMeta; + int iGen; Btree *pBt; + assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (1<p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); + assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); + iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ - iMeta = 0; + iGen = iMeta = 0; } - if( iMeta!=pOp->p2 ){ + if( iMeta!=pOp->p2 || iGen!=pOp->p3 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie @@ -2920,7 +2953,7 @@ case OP_VerifyCookie: { sqlite3ResetInternalSchema(db, pOp->p1); } - sqlite3ExpirePreparedStatements(db); + p->expired = 1; rc = SQLITE_SCHEMA; } break; @@ -2996,12 +3029,13 @@ case OP_OpenWrite: { p2 = pOp->p2; iDb = pOp->p3; assert( iDb>=0 && iDbnDb ); - assert( (p->btreeMask & (1<btreeMask & (((yDbMask)1)<aDb[iDb]; pX = pDb->pBt; assert( pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ wrFlag = 1; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->file_format < p->minWriteFileFormat ){ p->minWriteFileFormat = pDb->pSchema->file_format; } @@ -3685,7 +3719,6 @@ case OP_NewRowid: { /* out2-prerelease */ ** and try again, up to 100 times. */ assert( pC->isTable ); - cnt = 0; #ifdef SQLITE_32BIT_ROWID # define MAX_ROWID 0x7fffffff @@ -4492,14 +4525,16 @@ case OP_Destroy: { /* out2-prerelease */ }else{ iDb = pOp->p3; assert( iCnt==1 ); - assert( (p->btreeMask & (1<btreeMask & (((yDbMask)1)<aDb[iDb].pBt, pOp->p1, &iMoved); pOut->flags = MEM_Int; pOut->u.i = iMoved; #ifndef SQLITE_OMIT_AUTOVACUUM if( rc==SQLITE_OK && iMoved!=0 ){ - sqlite3RootPageMoved(&db->aDb[iDb], iMoved, pOp->p1); - resetSchemaOnFault = 1; + sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1); + /* All OP_Destroy operations occur on the same btree */ + assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 ); + resetSchemaOnFault = iDb+1; } #endif } @@ -4528,7 +4563,7 @@ case OP_Clear: { int nChange; nChange = 0; - assert( (p->btreeMask & (1<p2))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) ); @@ -4573,7 +4608,7 @@ case OP_CreateTable: { /* out2-prerelease */ pgno = 0; assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (1<p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ @@ -4587,14 +4622,10 @@ case OP_CreateTable: { /* out2-prerelease */ break; } -/* Opcode: ParseSchema P1 P2 * P4 * +/* Opcode: ParseSchema P1 * * P4 * ** ** Read and parse all entries from the SQLITE_MASTER table of database P1 -** that match the WHERE clause P4. P2 is the "force" flag. Always do -** the parsing if P2 is true. If P2 is false, then this routine is a -** no-op if the schema is not currently loaded. In other words, if P2 -** is false, the SQLITE_MASTER table is only parsed if the rest of the -** schema is already loaded into the symbol table. +** that match the WHERE clause P4. ** ** This opcode invokes the parser to create a new virtual machine, ** then runs the new virtual machine. It is thus a re-entrant opcode. @@ -4605,33 +4636,20 @@ case OP_ParseSchema: { char *zSql; InitData initData; + /* Any prepared statement that invokes this opcode will hold mutexes + ** on every btree. This is a prerequisite for invoking + ** sqlite3InitCallback(). + */ +#ifdef SQLITE_DEBUG + for(iDb=0; iDbnDb; iDb++){ + assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); + } +#endif + iDb = pOp->p1; assert( iDb>=0 && iDbnDb ); - - /* If pOp->p2 is 0, then this opcode is being executed to read a - ** single row, for example the row corresponding to a new index - ** created by this VDBE, from the sqlite_master table. It only - ** does this if the corresponding in-memory schema is currently - ** loaded. Otherwise, the new index definition can be loaded along - ** with the rest of the schema when it is required. - ** - ** Although the mutex on the BtShared object that corresponds to - ** database iDb (the database containing the sqlite_master table - ** read by this instruction) is currently held, it is necessary to - ** obtain the mutexes on all attached databases before checking if - ** the schema of iDb is loaded. This is because, at the start of - ** the sqlite3_exec() call below, SQLite will invoke - ** sqlite3BtreeEnterAll(). If all mutexes are not already held, the - ** iDb mutex may be temporarily released to avoid deadlock. If - ** this happens, then some other thread may delete the in-memory - ** schema of database iDb before the SQL statement runs. The schema - ** will not be reloaded becuase the db->init.busy flag is set. This - ** can result in a "no such table: sqlite_master" or "malformed - ** database schema" error being returned to the user. - */ - assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); - sqlite3BtreeEnterAll(db); - if( pOp->p2 || DbHasProperty(db, iDb, DB_SchemaLoaded) ){ + assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); + /* Used to be a conditional */ { zMaster = SCHEMA_TABLE(iDb); initData.db = db; initData.iDb = pOp->p1; @@ -4652,7 +4670,6 @@ case OP_ParseSchema: { db->init.busy = 0; } } - sqlite3BtreeLeaveAll(db); if( rc==SQLITE_NOMEM ){ goto no_mem; } @@ -4753,7 +4770,7 @@ case OP_IntegrityCk: { } aRoot[j] = 0; assert( pOp->p5nDb ); - assert( (p->btreeMask & (1<p5))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<p5))!=0 ); z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot, (int)pnErr->u.i, &nErr); sqlite3DbFree(db, aRoot); @@ -5181,7 +5198,9 @@ case OP_AggStep: { sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); rc = ctx.isError; } + sqlite3VdbeMemRelease(&ctx.s); + break; } @@ -5215,13 +5234,36 @@ case OP_AggFinal: { } #ifndef SQLITE_OMIT_WAL -/* Opcode: Checkpoint P1 * * * * +/* Opcode: Checkpoint P1 P2 P3 * * ** ** Checkpoint database P1. This is a no-op if P1 is not currently in -** WAL mode. +** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL +** or RESTART. Write 1 or 0 into mem[P3] if the checkpoint returns +** SQLITE_BUSY or not, respectively. Write the number of pages in the +** WAL after the checkpoint into mem[P3+1] and the number of pages +** in the WAL that have been checkpointed after the checkpoint +** completes into mem[P3+2]. However on an error, mem[P3+1] and +** mem[P3+2] are initialized to -1. */ case OP_Checkpoint: { - rc = sqlite3Checkpoint(db, pOp->p1); + int i; /* Loop counter */ + int aRes[3]; /* Results */ + Mem *pMem; /* Write results here */ + + aRes[0] = 0; + aRes[1] = aRes[2] = -1; + assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE + || pOp->p2==SQLITE_CHECKPOINT_FULL + || pOp->p2==SQLITE_CHECKPOINT_RESTART + ); + rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]); + if( rc==SQLITE_BUSY ){ + rc = SQLITE_OK; + aRes[0] = 1; + } + for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){ + sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]); + } break; }; #endif @@ -5256,25 +5298,6 @@ case OP_JournalMode: { /* out2-prerelease */ ); assert( pOp->p1>=0 && pOp->p1nDb ); - /* This opcode is used in two places: PRAGMA journal_mode and ATTACH. - ** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called - ** when the statment is prepared and so p->aMutex.nMutex>0. All mutexes - ** are already acquired. But when used in ATTACH, sqlite3VdbeUsesBtree() - ** is not called when the statement is prepared because it requires the - ** iDb index of the database as a parameter, and the database has not - ** yet been attached so that index is unavailable. We have to wait - ** until runtime (now) to get the mutex on the newly attached database. - ** No other mutexes are required by the ATTACH command so this is safe - ** to do. - */ - assert( (p->btreeMask & (1<p1))!=0 || p->aMutex.nMutex==0 ); - if( p->aMutex.nMutex==0 ){ - /* This occurs right after ATTACH. Get a mutex on the newly ATTACHed - ** database. */ - sqlite3VdbeUsesBtree(p, pOp->p1); - sqlite3VdbeMutexArrayEnter(p); - } - pBt = db->aDb[pOp->p1].pBt; pPager = sqlite3BtreePager(pBt); eOld = sqlite3PagerGetJournalMode(pPager); @@ -5372,7 +5395,7 @@ case OP_IncrVacuum: { /* jump */ Btree *pBt; assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (1<p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3BtreeIncrVacuum(pBt); if( rc==SQLITE_DONE ){ @@ -5421,7 +5444,7 @@ case OP_TableLock: { if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ int p1 = pOp->p1; assert( p1>=0 && p1nDb ); - assert( (p->btreeMask & (1<btreeMask & (((yDbMask)1)<aDb[p1].pBt, pOp->p2, isWriteLock); if( (rc&0xFF)==SQLITE_LOCKED ){ @@ -5910,13 +5933,15 @@ vdbe_error_halt: sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; - if( resetSchemaOnFault ) sqlite3ResetInternalSchema(db, 0); + if( resetSchemaOnFault>0 ){ + sqlite3ResetInternalSchema(db, resetSchemaOnFault-1); + } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: - sqlite3BtreeMutexArrayLeave(&p->aMutex); + sqlite3VdbeLeave(p); return rc; /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH diff --git a/src/vdbe.h b/src/vdbe.h index 71c871d..4304453 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -108,7 +108,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ #define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ #define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ -#define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */ +#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ #define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ diff --git a/src/vdbeInt.h b/src/vdbeInt.h index b42729d..5e96c6f 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -302,10 +302,10 @@ struct Vdbe { u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ - int btreeMask; /* Bitmask of db->aDb[] entries referenced */ + yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ + yDbMask lockMask; /* Subset of btreeMask that requires a lock */ int iStatement; /* Statement number (or 0 if has not opened stmt) */ int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ - BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif @@ -388,6 +388,14 @@ void sqlite3VdbeFrameDelete(VdbeFrame*); int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 + void sqlite3VdbeEnter(Vdbe*); + void sqlite3VdbeLeave(Vdbe*); +#else +# define sqlite3VdbeEnter(X) +# define sqlite3VdbeLeave(X) +#endif + #ifdef SQLITE_DEBUG void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); #endif @@ -398,12 +406,6 @@ int sqlite3VdbeCheckFk(Vdbe *, int); # define sqlite3VdbeCheckFk(p,i) 0 #endif -#ifndef SQLITE_OMIT_SHARED_CACHE -void sqlite3VdbeMutexArrayEnter(Vdbe *p); -#else -# define sqlite3VdbeMutexArrayEnter(p) -#endif - int sqlite3VdbeMemTranslate(Mem*, u8); #ifdef SQLITE_DEBUG void sqlite3VdbePrintSql(Vdbe*); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 5578b86..90baacc 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -682,13 +682,11 @@ int sqlite3_data_count(sqlite3_stmt *pStmt){ */ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ Vdbe *pVm; - int vals; Mem *pOut; pVm = (Vdbe *)pStmt; if( pVm && pVm->pResultSet!=0 && inResColumn && i>=0 ){ sqlite3_mutex_enter(pVm->db->mutex); - vals = sqlite3_data_count(pStmt); pOut = &pVm->pResultSet[i]; }else{ /* If the value passed as the second argument is out of range, return @@ -706,7 +704,11 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ #if defined(SQLITE_DEBUG) && defined(__GNUC__) __attribute__((aligned(8))) #endif - = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0, 0, 0 }; + = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0, +#ifdef SQLITE_DEBUG + 0, 0, /* pScopyFrom, pFiller */ +#endif + 0, 0 }; if( pVm && ALWAYS(pVm->db) ){ sqlite3_mutex_enter(pVm->db->mutex); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index de69e54..4d4bb22 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -157,6 +157,12 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; p->expired = 0; + if( op==OP_ParseSchema ){ + /* Any program that uses the OP_ParseSchema opcode needs to lock + ** all btrees. */ + int j; + for(j=0; jdb->nDb; j++) sqlite3VdbeUsesBtree(p, j); + } #ifdef SQLITE_DEBUG pOp->zComment = 0; if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); @@ -457,7 +463,7 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ assert( aOp && !p->db->mallocFailed ); /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ - assert( p->aMutex.nMutex==0 ); + assert( p->btreeMask==0 ); resolveP2Values(p, pnMaxArg); *pnOp = p->nOp; @@ -559,6 +565,7 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){ ** the address of the next instruction to be coded. */ void sqlite3VdbeJumpHere(Vdbe *p, int addr){ + assert( addr>=0 ); sqlite3VdbeChangeP2(p, addr, p->nOp); } @@ -944,22 +951,80 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** -** The prepared statement has to know in advance which Btree objects -** will be used so that it can acquire mutexes on them all in sorted -** order (via sqlite3VdbeMutexArrayEnter(). Mutexes are acquired -** in order (and released in reverse order) to avoid deadlocks. +** The prepared statements need to know in advance the complete set of +** attached databases that they will be using. A mask of these databases +** is maintained in p->btreeMask and is used for locking and other purposes. */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ - int mask; - assert( i>=0 && idb->nDb && i=0 && idb->nDb && i<(int)sizeof(yDbMask)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); - mask = ((u32)1)<btreeMask & mask)==0 ){ - p->btreeMask |= mask; - sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt); + p->btreeMask |= ((yDbMask)1)<db->aDb[i].pBt) ){ + p->lockMask |= ((yDbMask)1)<0 +/* +** If SQLite is compiled to support shared-cache mode and to be threadsafe, +** this routine obtains the mutex associated with each BtShared structure +** that may be accessed by the VM passed as an argument. In doing so it also +** sets the BtShared.db member of each of the BtShared structures, ensuring +** that the correct busy-handler callback is invoked if required. +** +** If SQLite is not threadsafe but does support shared-cache mode, then +** sqlite3BtreeEnter() is invoked to set the BtShared.db variables +** of all of BtShared structures accessible via the database handle +** associated with the VM. +** +** If SQLite is not threadsafe and does not support shared-cache mode, this +** function is a no-op. +** +** The p->btreeMask field is a bitmask of all btrees that the prepared +** statement p will ever use. Let N be the number of bits in p->btreeMask +** corresponding to btrees that use shared cache. Then the runtime of +** this routine is N*N. But as N is rarely more than 1, this should not +** be a problem. +*/ +void sqlite3VdbeEnter(Vdbe *p){ + int i; + yDbMask mask; + sqlite3 *db; + Db *aDb; + int nDb; + if( p->lockMask==0 ) return; /* The common case */ + db = p->db; + aDb = db->aDb; + nDb = db->nDb; + for(i=0, mask=1; ilockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + sqlite3BtreeEnter(aDb[i].pBt); + } + } +} +#endif + +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 +/* +** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter(). +*/ +void sqlite3VdbeLeave(Vdbe *p){ + int i; + yDbMask mask; + sqlite3 *db; + Db *aDb; + int nDb; + if( p->lockMask==0 ) return; /* The common case */ + db = p->db; + aDb = db->aDb; + nDb = db->nDb; + for(i=0, mask=1; ilockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + sqlite3BtreeLeave(aDb[i].pBt); + } + } +} +#endif #if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) /* @@ -1517,7 +1582,7 @@ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ */ static void closeAllCursors(Vdbe *p){ if( p->pFrame ){ - VdbeFrame *pFrame = p->pFrame; + VdbeFrame *pFrame; for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); sqlite3VdbeFrameRestore(pFrame); } @@ -1703,7 +1768,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - rc = sqlite3BtreeCommitPhaseTwo(pBt); + rc = sqlite3BtreeCommitPhaseTwo(pBt, 0); } } if( rc==SQLITE_OK ){ @@ -1835,7 +1900,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ for(i=0; inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - sqlite3BtreeCommitPhaseTwo(pBt); + sqlite3BtreeCommitPhaseTwo(pBt, 1); } } sqlite3EndBenignMalloc(); @@ -1958,33 +2023,6 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ return rc; } -/* -** If SQLite is compiled to support shared-cache mode and to be threadsafe, -** this routine obtains the mutex associated with each BtShared structure -** that may be accessed by the VM passed as an argument. In doing so it -** sets the BtShared.db member of each of the BtShared structures, ensuring -** that the correct busy-handler callback is invoked if required. -** -** If SQLite is not threadsafe but does support shared-cache mode, then -** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables -** of all of BtShared structures accessible via the database handle -** associated with the VM. Of course only a subset of these structures -** will be accessed by the VM, and we could use Vdbe.btreeMask to figure -** that subset out, but there is no advantage to doing so. -** -** If SQLite is not threadsafe and does not support shared-cache mode, this -** function is a no-op. -*/ -#ifndef SQLITE_OMIT_SHARED_CACHE -void sqlite3VdbeMutexArrayEnter(Vdbe *p){ -#if SQLITE_THREADSAFE - sqlite3BtreeMutexArrayEnter(&p->aMutex); -#else - sqlite3BtreeEnterAll(p->db); -#endif -} -#endif - /* ** This function is called when a transaction opened by the database ** handle associated with the VM passed as an argument is about to be @@ -2057,7 +2095,7 @@ int sqlite3VdbeHalt(Vdbe *p){ int isSpecialError; /* Set to true if a 'special' error */ /* Lock all btrees used by the statement */ - sqlite3VdbeMutexArrayEnter(p); + sqlite3VdbeEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; @@ -2108,17 +2146,22 @@ int sqlite3VdbeHalt(Vdbe *p){ && db->writeVdbeCnt==(p->readOnly==0) ){ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - if( sqlite3VdbeCheckFk(p, 1) ){ - sqlite3BtreeMutexArrayLeave(&p->aMutex); - return SQLITE_ERROR; + rc = sqlite3VdbeCheckFk(p, 1); + if( rc!=SQLITE_OK ){ + if( NEVER(p->readOnly) ){ + sqlite3VdbeLeave(p); + return SQLITE_ERROR; + } + rc = SQLITE_CONSTRAINT; + }else{ + /* The auto-commit flag is true, the vdbe program was successful + ** or hit an 'OR FAIL' constraint and there are no deferred foreign + ** key constraints to hold up the transaction. This means a commit + ** is required. */ + rc = vdbeCommit(db, p); } - /* The auto-commit flag is true, the vdbe program was successful - ** or hit an 'OR FAIL' constraint and there are no deferred foreign - ** key constraints to hold up the transaction. This means a commit - ** is required. */ - rc = vdbeCommit(db, p); - if( rc==SQLITE_BUSY ){ - sqlite3BtreeMutexArrayLeave(&p->aMutex); + if( rc==SQLITE_BUSY && p->readOnly ){ + sqlite3VdbeLeave(p); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; @@ -2185,12 +2228,12 @@ int sqlite3VdbeHalt(Vdbe *p){ /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } /* Release the locks */ - sqlite3BtreeMutexArrayLeave(&p->aMutex); + sqlite3VdbeLeave(p); } /* We have successfully halted and closed the VM. Record this fact. */ @@ -2216,7 +2259,7 @@ int sqlite3VdbeHalt(Vdbe *p){ } assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 ); - return SQLITE_OK; + return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); } @@ -2492,7 +2535,13 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){ if( file_format>=4 && (i&1)==i ){ return 8+(u32)i; } - u = i<0 ? -i : i; + if( i<0 ){ + if( i<(-MAX_6BYTE) ) return 6; + /* Previous test prevents: u = -(-9223372036854775808) */ + u = -i; + }else{ + u = i; + } if( u<=127 ) return 1; if( u<=32767 ) return 2; if( u<=8388607 ) return 3; diff --git a/src/vdbeblob.c b/src/vdbeblob.c index f26cc87..18fdd46 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -266,6 +266,7 @@ int sqlite3_blob_open( /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); + sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); diff --git a/src/vdbemem.c b/src/vdbemem.c index 4831d80..882c686 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -367,7 +367,7 @@ i64 sqlite3VdbeIntValue(Mem *pMem){ }else if( flags & MEM_Real ){ return doubleToInt64(pMem->r); }else if( flags & (MEM_Str|MEM_Blob) ){ - i64 value; + i64 value = 0; assert( pMem->z || pMem->n==0 ); testcase( pMem->z==0 ); sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); @@ -1077,11 +1077,19 @@ int sqlite3ValueFromExpr( /* This branch happens for multiple negative signs. Ex: -(-5) */ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ sqlite3VdbeMemNumerify(pVal); - pVal->u.i = -1 * pVal->u.i; - /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ - pVal->r = (double)-1 * pVal->r; + if( pVal->u.i==SMALLEST_INT64 ){ + pVal->flags &= MEM_Int; + pVal->flags |= MEM_Real; + pVal->r = (double)LARGEST_INT64; + }else{ + pVal->u.i = -pVal->u.i; + } + pVal->r = -pVal->r; sqlite3ValueApplyAffinity(pVal, affinity, enc); } + }else if( op==TK_NULL ){ + pVal = sqlite3ValueNew(db); + if( pVal==0 ) goto no_mem; } #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ diff --git a/src/vdbetrace.c b/src/vdbetrace.c index b422fa9..de123b5 100644 --- a/src/vdbetrace.c +++ b/src/vdbetrace.c @@ -85,7 +85,7 @@ char *sqlite3VdbeExpandSql( const char *zStart = zRawSql; while( *(zRawSql++)!='\n' && *zRawSql ); sqlite3StrAccumAppend(&out, "-- ", 3); - sqlite3StrAccumAppend(&out, zStart, zRawSql-zStart); + sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); } }else{ while( zRawSql[0] ){ diff --git a/src/vtab.c b/src/vtab.c index e460ee5..b052de2 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -48,7 +48,7 @@ static int createModule( if( pDel==pMod ){ db->mallocFailed = 1; } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); }else if( xDestroy ){ xDestroy(pAux); } @@ -145,10 +145,9 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ ** that contains table p is held by the caller. See header comments ** above function sqlite3VtabUnlockList() for an explanation of why ** this makes it safe to access the sqlite3.pDisconnect list of any - ** database connection that may have an entry in the p->pVTable list. */ - assert( db==0 || - sqlite3BtreeHoldsMutex(db->aDb[sqlite3SchemaToIndex(db, p->pSchema)].pBt) - ); + ** database connection that may have an entry in the p->pVTable list. + */ + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); while( pVTable ){ sqlite3 *db2 = pVTable->db; @@ -372,7 +371,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); - sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC); + sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, pTab->zName, sqlite3Strlen30(pTab->zName) + 1); } @@ -387,6 +386,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ Schema *pSchema = pTab->pSchema; const char *zName = pTab->zName; int nName = sqlite3Strlen30(zName); + assert( sqlite3SchemaMutexHeld(db, 0, pSchema) ); pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab); if( pOld ){ db->mallocFailed = 1; diff --git a/src/wal.c b/src/wal.c index ff327bf..51ea18f 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1558,6 +1558,34 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ return rc; } +/* +** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and +** n. If the attempt fails and parameter xBusy is not NULL, then it is a +** busy-handler function. Invoke it and retry the lock until either the +** lock is successfully obtained or the busy-handler returns 0. +*/ +static int walBusyLock( + Wal *pWal, /* WAL connection */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ + int lockIdx, /* Offset of first byte to lock */ + int n /* Number of bytes to lock */ +){ + int rc; + do { + rc = walLockExclusive(pWal, lockIdx, n); + }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); + return rc; +} + +/* +** The cache of the wal-index header must be valid to call this function. +** Return the page-size in bytes used by the database. +*/ +static int walPagesize(Wal *pWal){ + return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); +} + /* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. @@ -1591,8 +1619,10 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ */ static int walCheckpoint( Wal *pWal, /* Wal connection */ + int eMode, /* One of PASSIVE, FULL or RESTART */ + int (*xBusyCall)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags for OsSync() (or 0) */ - int nBuf, /* Size of zBuf in bytes */ u8 *zBuf /* Temporary buffer to use */ ){ int rc; /* Return code */ @@ -1604,8 +1634,9 @@ static int walCheckpoint( u32 mxPage; /* Max database page to write */ int i; /* Loop counter */ volatile WalCkptInfo *pInfo; /* The checkpoint status information */ + int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */ - szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); + szPage = walPagesize(pWal); testcase( szPage<=32768 ); testcase( szPage>=65536 ); pInfo = walCkptInfo(pWal); @@ -1618,11 +1649,7 @@ static int walCheckpoint( } assert( pIter ); - /*** TODO: Move this test out to the caller. Make it an assert() here ***/ - if( szPage!=nBuf ){ - rc = SQLITE_CORRUPT_BKPT; - goto walcheckpoint_out; - } + if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall; /* Compute in mxSafeFrame the index of the last frame of the WAL that is ** safe to write into the database. Frames beyond mxSafeFrame might @@ -1633,14 +1660,15 @@ static int walCheckpoint( mxPage = pWal->hdr.nPage; for(i=1; iaReadMark[i]; - if( mxSafeFrame>=y ){ + if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); - rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ pInfo->aReadMark[i] = READMARK_NOT_USED; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc==SQLITE_BUSY ){ mxSafeFrame = y; + xBusy = 0; }else{ goto walcheckpoint_out; } @@ -1648,7 +1676,7 @@ static int walCheckpoint( } if( pInfo->nBackfillnBackfill; @@ -1701,13 +1729,32 @@ static int walCheckpoint( /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); - }else if( rc==SQLITE_BUSY ){ + } + + if( rc==SQLITE_BUSY ){ /* Reset the return code so as not to report a checkpoint failure - ** just because active readers prevent any backfill. - */ + ** just because there are active readers. */ rc = SQLITE_OK; } + /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal + ** file has been copied into the database file, then block until all + ** readers have finished using the wal file. This ensures that the next + ** process to write to the database restarts the wal file. + */ + if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + assert( pWal->writeLock ); + if( pInfo->nBackfillhdr.mxFrame ){ + rc = SQLITE_BUSY; + }else if( eMode==SQLITE_CHECKPOINT_RESTART ){ + assert( mxSafeFrame==pWal->hdr.mxFrame ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); + if( rc==SQLITE_OK ){ + walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + } + } + } + walcheckpoint_out: walIteratorFree(pIter); return rc; @@ -1739,7 +1786,9 @@ int sqlite3WalClose( if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } - rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf); + rc = sqlite3WalCheckpoint( + pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 + ); if( rc==SQLITE_OK ){ isDelete = 1; } @@ -1951,10 +2000,31 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ assert( pWal->readLock<0 ); /* Not currently locked */ - /* Take steps to avoid spinning forever if there is a protocol error. */ + /* Take steps to avoid spinning forever if there is a protocol error. + ** + ** Circumstances that cause a RETRY should only last for the briefest + ** instances of time. No I/O or other system calls are done while the + ** locks are held, so the locks should not be held for very long. But + ** if we are unlucky, another process that is holding a lock might get + ** paged out or take a page-fault that is time-consuming to resolve, + ** during the few nanoseconds that it is holding the lock. In that case, + ** it might take longer than normal for the lock to free. + ** + ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few + ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this + ** is more of a scheduler yield than an actual delay. But on the 10th + ** an subsequent retries, the delays start becoming longer and longer, + ** so that on the 100th (and last) RETRY we delay for 21 milliseconds. + ** The total delay time before giving up is less than 1 second. + */ if( cnt>5 ){ - if( cnt>100 ) return SQLITE_PROTOCOL; - sqlite3OsSleep(pWal->pVfs, 1); + int nDelay = 1; /* Pause time in microseconds */ + if( cnt>100 ){ + VVA_ONLY( pWal->lockError = 1; ) + return SQLITE_PROTOCOL; + } + if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */ + sqlite3OsSleep(pWal->pVfs, nDelay); } if( !useWal ){ @@ -2036,22 +2106,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ mxI = i; } } - if( mxI==0 ){ - /* If we get here, it means that all of the aReadMark[] entries between - ** 1 and WAL_NREADER-1 are zero. Try to initialize aReadMark[1] to - ** be mxFrame, then retry. - */ - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), 1); - if( rc==SQLITE_OK ){ - pInfo->aReadMark[1] = pWal->hdr.mxFrame; - walUnlockExclusive(pWal, WAL_READ_LOCK(1), 1); - rc = WAL_RETRY; - }else if( rc==SQLITE_BUSY ){ - rc = WAL_RETRY; - } - return rc; - }else{ - if( mxReadMark < pWal->hdr.mxFrame ){ + /* There was once an "if" here. The extra "{" is to preserve indentation. */ + { + if( mxReadMark < pWal->hdr.mxFrame || mxI==0 ){ for(i=1; inBackfill==pWal->hdr.mxFrame ); if( pInfo->nBackfill>0 ){ + u32 salt1; + sqlite3_randomness(4, &salt1); rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no @@ -2458,7 +2525,7 @@ static int walRestartLog(Wal *pWal){ pWal->nCkpt++; pWal->hdr.mxFrame = 0; sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); - sqlite3_randomness(4, &aSalt[1]); + aSalt[1] = salt1; walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; iaReadMark[i] = READMARK_NOT_USED; @@ -2475,6 +2542,10 @@ static int walRestartLog(Wal *pWal){ int notUsed; rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); }while( rc==WAL_RETRY ); + assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ + testcase( (rc&0xff)==SQLITE_IOERR ); + testcase( rc==SQLITE_PROTOCOL ); + testcase( rc==SQLITE_OK ); } return rc; } @@ -2654,17 +2725,27 @@ int sqlite3WalFrames( ** ** Obtain a CHECKPOINT lock and then backfill as much information as ** we can from WAL into the database. +** +** If parameter xBusy is not NULL, it is a pointer to a busy-handler +** callback. In this case this function runs a blocking checkpoint. */ int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ + int eMode, /* PASSIVE, FULL or RESTART */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ - u8 *zBuf /* Temporary buffer to use */ + u8 *zBuf, /* Temporary buffer to use */ + int *pnLog, /* OUT: Number of frames in WAL */ + int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ){ int rc; /* Return code */ int isChanged = 0; /* True if a new wal-index header is loaded */ + int eMode2 = eMode; /* Mode to pass to walCheckpoint() */ assert( pWal->ckptLock==0 ); + assert( pWal->writeLock==0 ); WALTRACE(("WAL%p: checkpoint begins\n", pWal)); rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); @@ -2676,11 +2757,45 @@ int sqlite3WalCheckpoint( } pWal->ckptLock = 1; - /* Copy data from the log to the database file. */ - rc = walIndexReadHdr(pWal, &isChanged); - if( rc==SQLITE_OK ){ - rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf); + /* If this is a blocking-checkpoint, then obtain the write-lock as well + ** to prevent any writers from running while the checkpoint is underway. + ** This has to be done before the call to walIndexReadHdr() below. + ** + ** If the writer lock cannot be obtained, then a passive checkpoint is + ** run instead. Since the checkpointer is not holding the writer lock, + ** there is no point in blocking waiting for any readers. Assuming no + ** other error occurs, this function will return SQLITE_BUSY to the caller. + */ + if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ){ + pWal->writeLock = 1; + }else if( rc==SQLITE_BUSY ){ + eMode2 = SQLITE_CHECKPOINT_PASSIVE; + rc = SQLITE_OK; + } } + + /* Read the wal-index header. */ + if( rc==SQLITE_OK ){ + rc = walIndexReadHdr(pWal, &isChanged); + } + + /* Copy data from the log to the database file. */ + if( rc==SQLITE_OK ){ + if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf); + } + + /* If no error occurred, set the output variables. */ + if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ + if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; + if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); + } + } + if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was ** performed, then the pager-cache associated with pWal is now @@ -2692,10 +2807,11 @@ int sqlite3WalCheckpoint( } /* Release the locks. */ + sqlite3WalEndWriteTransaction(pWal); walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); pWal->ckptLock = 0; WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); - return rc; + return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); } /* Return the value to pass to a sqlite3_wal_hook callback, the diff --git a/src/wal.h b/src/wal.h index 35f695c..2039c70 100644 --- a/src/wal.h +++ b/src/wal.h @@ -20,22 +20,22 @@ #include "sqliteInt.h" #ifdef SQLITE_OMIT_WAL -# define sqlite3WalOpen(x,y,z) 0 -# define sqlite3WalClose(w,x,y,z) 0 -# define sqlite3WalBeginReadTransaction(y,z) 0 +# define sqlite3WalOpen(x,y,z) 0 +# define sqlite3WalClose(w,x,y,z) 0 +# define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) -# define sqlite3WalRead(v,w,x,y,z) 0 -# define sqlite3WalDbsize(y) 0 -# define sqlite3WalBeginWriteTransaction(y) 0 -# define sqlite3WalEndWriteTransaction(x) 0 -# define sqlite3WalUndo(x,y,z) 0 +# define sqlite3WalRead(v,w,x,y,z) 0 +# define sqlite3WalDbsize(y) 0 +# define sqlite3WalBeginWriteTransaction(y) 0 +# define sqlite3WalEndWriteTransaction(x) 0 +# define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) -# define sqlite3WalSavepointUndo(y,z) 0 -# define sqlite3WalFrames(u,v,w,x,y,z) 0 -# define sqlite3WalCheckpoint(u,v,w,x) 0 -# define sqlite3WalCallback(z) 0 -# define sqlite3WalExclusiveMode(y,z) 0 -# define sqlite3WalHeapMemory(z) 0 +# define sqlite3WalSavepointUndo(y,z) 0 +# define sqlite3WalFrames(u,v,w,x,y,z) 0 +# define sqlite3WalCheckpoint(r,s,t,u,v,w,x,y,z) 0 +# define sqlite3WalCallback(z) 0 +# define sqlite3WalExclusiveMode(y,z) 0 +# define sqlite3WalHeapMemory(z) 0 #else #define WAL_SAVEPOINT_NDATA 4 @@ -86,9 +86,14 @@ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ + int eMode, /* One of PASSIVE, FULL and RESTART */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ - u8 *zBuf /* Temporary buffer to use */ + u8 *zBuf, /* Temporary buffer to use */ + int *pnLog, /* OUT: Number of frames in WAL */ + int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ); /* Return the value to pass to a sqlite3_wal_hook callback, the diff --git a/src/where.c b/src/where.c index 4a5026f..cf30d94 100644 --- a/src/where.c +++ b/src/where.c @@ -18,6 +18,7 @@ */ #include "sqliteInt.h" + /* ** Trace output macros */ @@ -117,6 +118,11 @@ struct WhereTerm { #define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ #define TERM_OR_OK 0x40 /* Used during OR-clause processing */ +#ifdef SQLITE_ENABLE_STAT2 +# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */ +#else +# define TERM_VNULL 0x00 /* Disabled if not using stat2 */ +#endif /* ** An instance of the following structure holds all information about a @@ -210,6 +216,7 @@ struct WhereCost { #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ +#define WO_NOOP 0x800 /* This term does not restrict search space */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ @@ -392,7 +399,7 @@ static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){ */ static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){ int i; - assert( pMaskSet->n<=sizeof(Bitmask)*8 ); + assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); for(i=0; in; i++){ if( pMaskSet->ix[i]==iCursor ){ return ((Bitmask)1)<eOperator = 0; /* case 1 trumps case 2 */ + pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ } } } @@ -1324,6 +1331,47 @@ static void exprAnalyze( } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#ifdef SQLITE_ENABLE_STAT2 + /* When sqlite_stat2 histogram data is available an operator of the + ** form "x IS NOT NULL" can sometimes be evaluated more efficiently + ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a + ** virtual term of that form. + ** + ** Note that the virtual term must be tagged with TERM_VNULL. This + ** TERM_VNULL tag will suppress the not-null check at the beginning + ** of the loop. Without the TERM_VNULL flag, the not-null check at + ** the start of the loop will prevent any results from being returned. + */ + if( pExpr->op==TK_NOTNULL + && pExpr->pLeft->op==TK_COLUMN + && pExpr->pLeft->iColumn>=0 + ){ + Expr *pNewExpr; + Expr *pLeft = pExpr->pLeft; + int idxNew; + WhereTerm *pNewTerm; + + pNewExpr = sqlite3PExpr(pParse, TK_GT, + sqlite3ExprDup(db, pLeft, 0), + sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0); + + idxNew = whereClauseInsert(pWC, pNewExpr, + TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); + if( idxNew ){ + pNewTerm = &pWC->a[idxNew]; + pNewTerm->prereqRight = 0; + pNewTerm->leftCursor = pLeft->iTable; + pNewTerm->u.leftColumn = pLeft->iColumn; + pNewTerm->eOperator = WO_GT; + pNewTerm->iParent = idxTerm; + pTerm = &pWC->a[idxTerm]; + pTerm->nChild = 1; + pTerm->wtFlags |= TERM_COPIED; + pNewTerm->prereqAll = pTerm->prereqAll; + } + } +#endif /* SQLITE_ENABLE_STAT2 */ + /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. */ @@ -1376,6 +1424,7 @@ static int isSortingIndex( int base, /* Cursor number for the table to be sorted */ ExprList *pOrderBy, /* The ORDER BY clause */ int nEqCol, /* Number of index columns with == constraints */ + int wsFlags, /* Index usages flags */ int *pbRev /* Set to 1 if ORDER BY is DESC */ ){ int i, j; /* Loop counters */ @@ -1481,11 +1530,14 @@ static int isSortingIndex( return 1; } if( pIdx->onError!=OE_None && i==pIdx->nColumn + && (wsFlags & WHERE_COLUMN_NULL)==0 && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){ /* All terms of this index match some prefix of the ORDER BY clause ** and the index is UNIQUE and no terms on the tail of the ORDER BY ** clause reference other tables in a join. If this is all true then - ** the order by clause is superfluous. */ + ** the order by clause is superfluous. Not that if the matching + ** condition is IS NULL then the result is not necessarily unique + ** even on a UNIQUE index, so disallow those cases. */ return 1; } return 0; @@ -1722,7 +1774,7 @@ static void bestAutomaticIndex( pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTermrCost, costTempIdx)); pCost->rCost = costTempIdx; pCost->plan.nRow = logN + 1; @@ -1843,7 +1895,7 @@ static void constructAutomaticIndex( idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - pIdx->azColl[n] = pColl->zName; + pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY"; n++; } } @@ -2201,11 +2253,18 @@ static void bestVirtualIndex( /* ** Argument pIdx is a pointer to an index structure that has an array of ** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column -** stored in Index.aSample. The domain of values stored in said column -** may be thought of as divided into (SQLITE_INDEX_SAMPLES+1) regions. -** Region 0 contains all values smaller than the first sample value. Region -** 1 contains values larger than or equal to the value of the first sample, -** but smaller than the value of the second. And so on. +** stored in Index.aSample. These samples divide the domain of values stored +** the index into (SQLITE_INDEX_SAMPLES+1) regions. +** Region 0 contains all values less than the first sample value. Region +** 1 contains values between the first and second samples. Region 2 contains +** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES +** contains values larger than the last sample. +** +** If the index contains many duplicates of a single value, then it is +** possible that two or more adjacent samples can hold the same value. +** When that is the case, the smallest possible region code is returned +** when roundUp is false and the largest possible region code is returned +** when roundUp is true. ** ** If successful, this function determines which of the regions value ** pVal lies in, sets *piRegion to the region index (a value between 0 @@ -2218,8 +2277,10 @@ static int whereRangeRegion( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ sqlite3_value *pVal, /* Value to consider */ + int roundUp, /* Return largest valid region if true */ int *piRegion /* OUT: Region of domain in which value lies */ ){ + assert( roundUp==0 || roundUp==1 ); if( ALWAYS(pVal) ){ IndexSample *aSample = pIdx->aSample; int i = 0; @@ -2229,7 +2290,17 @@ static int whereRangeRegion( double r = sqlite3_value_double(pVal); for(i=0; i=SQLITE_TEXT || aSample[i].u.r>r ) break; + if( aSample[i].eType>=SQLITE_TEXT ) break; + if( roundUp ){ + if( aSample[i].u.r>r ) break; + }else{ + if( aSample[i].u.r>=r ) break; + } + } + }else if( eType==SQLITE_NULL ){ + i = 0; + if( roundUp ){ + while( idb; @@ -2260,7 +2331,7 @@ static int whereRangeRegion( n = sqlite3ValueBytes(pVal, pColl->enc); for(i=0; imallocFailed ); return SQLITE_NOMEM; } - r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); + c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); sqlite3DbFree(db, zSample); }else #endif { - r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); + c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); } - if( r>0 ) break; + if( c-roundUp>=0 ) break; } } @@ -2364,9 +2435,9 @@ static int valueFromExpr( ** constraints. ** ** In the absence of sqlite_stat2 ANALYZE data, each range inequality -** reduces the search space by 2/3rds. Hence a single constraint (x>?) -** results in a return of 33 and a range constraint (x>? AND x?) +** results in a return of 25 and a range constraint (x>? AND xpTable->aCol[p->aiColumn[0]].affinity; if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); + assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); + roundUpLower = (pLower->eOperator==WO_GT) ?1:0; } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); + assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); + roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0; } if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ @@ -2402,28 +2479,29 @@ static int whereRangeScanEst( sqlite3ValueFree(pUpperVal); goto range_est_fallback; }else if( pLowerVal==0 ){ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( pLower ) iLower = iUpper/2; }else if( pUpperVal==0 ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; }else{ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( rc==SQLITE_OK ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); } } + WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper)); iEst = iUpper - iLower; testcase( iEst==SQLITE_INDEX_SAMPLES ); assert( iEst<=SQLITE_INDEX_SAMPLES ); if( iEst<1 ){ - iEst = 1; + *piEst = 50/SQLITE_INDEX_SAMPLES; + }else{ + *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES; } - sqlite3ValueFree(pLowerVal); sqlite3ValueFree(pUpperVal); - *piEst = (iEst * 100)/SQLITE_INDEX_SAMPLES; return rc; } range_est_fallback: @@ -2433,22 +2511,156 @@ range_est_fallback: UNUSED_PARAMETER(nEq); #endif assert( pLower || pUpper ); - if( pLower && pUpper ){ - *piEst = 11; - }else{ - *piEst = 33; - } + *piEst = 100; + if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4; + if( pUpper ) *piEst /= 4; return rc; } +#ifdef SQLITE_ENABLE_STAT2 +/* +** Estimate the number of rows that will be returned based on +** an equality constraint x=VALUE and where that VALUE occurs in +** the histogram data. This only works when x is the left-most +** column of an index and sqlite_stat2 histogram data is available +** for that index. When pExpr==NULL that means the constraint is +** "x IS NULL" instead of "x=VALUE". +** +** Write the estimated row count into *pnRow and return SQLITE_OK. +** If unable to make an estimate, leave *pnRow unchanged and return +** non-zero. +** +** This routine can fail if it is unable to load a collating sequence +** required for string comparison, or if unable to allocate memory +** for a UTF conversion required for comparison. The error is stored +** in the pParse structure. +*/ +static int whereEqualScanEst( + Parse *pParse, /* Parsing & code generating context */ + Index *p, /* The index whose left-most column is pTerm */ + Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ + double *pnRow /* Write the revised row estimate here */ +){ + sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ + int iLower, iUpper; /* Range of histogram regions containing pRhs */ + u8 aff; /* Column affinity */ + int rc; /* Subfunction return code */ + double nRowEst; /* New estimate of the number of rows */ + + assert( p->aSample!=0 ); + aff = p->pTable->aCol[p->aiColumn[0]].affinity; + if( pExpr ){ + rc = valueFromExpr(pParse, pExpr, aff, &pRhs); + if( rc ) goto whereEqualScanEst_cancel; + }else{ + pRhs = sqlite3ValueNew(pParse->db); + } + if( pRhs==0 ) return SQLITE_NOTFOUND; + rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower); + if( rc ) goto whereEqualScanEst_cancel; + rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper); + if( rc ) goto whereEqualScanEst_cancel; + WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper)); + if( iLower>=iUpper ){ + nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2); + if( nRowEst<*pnRow ) *pnRow = nRowEst; + }else{ + nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES; + *pnRow = nRowEst; + } + +whereEqualScanEst_cancel: + sqlite3ValueFree(pRhs); + return rc; +} +#endif /* defined(SQLITE_ENABLE_STAT2) */ + +#ifdef SQLITE_ENABLE_STAT2 +/* +** Estimate the number of rows that will be returned based on +** an IN constraint where the right-hand side of the IN operator +** is a list of values. Example: +** +** WHERE x IN (1,2,3,4) +** +** Write the estimated row count into *pnRow and return SQLITE_OK. +** If unable to make an estimate, leave *pnRow unchanged and return +** non-zero. +** +** This routine can fail if it is unable to load a collating sequence +** required for string comparison, or if unable to allocate memory +** for a UTF conversion required for comparison. The error is stored +** in the pParse structure. +*/ +static int whereInScanEst( + Parse *pParse, /* Parsing & code generating context */ + Index *p, /* The index whose left-most column is pTerm */ + ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ + double *pnRow /* Write the revised row estimate here */ +){ + sqlite3_value *pVal = 0; /* One value from list */ + int iLower, iUpper; /* Range of histogram regions containing pRhs */ + u8 aff; /* Column affinity */ + int rc = SQLITE_OK; /* Subfunction return code */ + double nRowEst; /* New estimate of the number of rows */ + int nSpan = 0; /* Number of histogram regions spanned */ + int nSingle = 0; /* Histogram regions hit by a single value */ + int nNotFound = 0; /* Count of values that are not constants */ + int i; /* Loop counter */ + u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */ + u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */ + + assert( p->aSample!=0 ); + aff = p->pTable->aCol[p->aiColumn[0]].affinity; + memset(aSpan, 0, sizeof(aSpan)); + memset(aSingle, 0, sizeof(aSingle)); + for(i=0; inExpr; i++){ + sqlite3ValueFree(pVal); + rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal); + if( rc ) break; + if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ + nNotFound++; + continue; + } + rc = whereRangeRegion(pParse, p, pVal, 0, &iLower); + if( rc ) break; + rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper); + if( rc ) break; + if( iLower>=iUpper ){ + aSingle[iLower] = 1; + }else{ + assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES ); + while( iLoweraiRowEst[0]/(2*SQLITE_INDEX_SAMPLES) + + nNotFound*p->aiRowEst[1]; + if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; + *pnRow = nRowEst; + WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n", + nSpan, nSingle, nNotFound, nRowEst)); + } + sqlite3ValueFree(pVal); + return rc; +} +#endif /* defined(SQLITE_ENABLE_STAT2) */ + /* -** Find the query plan for accessing a particular table. Write the +** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. ** ** The lowest cost plan wins. The cost is an estimate of the amount of -** CPU and disk I/O need to process the request using the selected plan. +** CPU and disk I/O needed to process the requested result. ** Factors that influence cost include: ** ** * The estimated number of rows that will be retrieved. (The @@ -2467,7 +2679,7 @@ range_est_fallback: ** ** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table ** in the SELECT statement, then no indexes are considered. However, the -** selected plan may still take advantage of the tables built-in rowid +** selected plan may still take advantage of the built-in rowid primary key ** index. */ static void bestBtreeIndex( @@ -2510,9 +2722,11 @@ static void bestBtreeIndex( wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); eqTermMask = idxEqTermMask; }else{ - /* There is no INDEXED BY clause. Create a fake Index object to - ** represent the primary key */ - Index *pFirst; /* Any other index on the table */ + /* There is no INDEXED BY clause. Create a fake Index object in local + ** variable sPk to represent the rowid primary key index. Make this + ** fake index the first in a chain of Index objects with all of the real + ** indices to follow */ + Index *pFirst; /* First of real indices on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; @@ -2523,6 +2737,8 @@ static void bestBtreeIndex( aiRowEstPk[1] = 1; pFirst = pSrc->pTab->pIndex; if( pSrc->notIndexed==0 ){ + /* The real indices of the table are only considered if the + ** NOT INDEXED qualifier is omitted from the FROM clause */ sPk.pNext = pFirst; } pProbe = &sPk; @@ -2539,16 +2755,19 @@ static void bestBtreeIndex( const unsigned int * const aiRowEst = pProbe->aiRowEst; double cost; /* Cost of using pProbe */ double nRow; /* Estimated number of rows in result set */ + double log10N; /* base-10 logarithm of nRow (inexact) */ int rev; /* True to scan in reverse order */ int wsFlags = 0; Bitmask used = 0; /* The following variables are populated based on the properties of - ** scan being evaluated. They are then used to determine the expected + ** index being evaluated. They are then used to determine the expected ** cost and number of rows returned. ** ** nEq: ** Number of equality terms that can be implemented using the index. + ** In other words, the number of initial fields in the index that + ** are used in == or IN or NOT NULL constraints of the WHERE clause. ** ** nInMul: ** The "in-multiplier". This is an estimate of how many seek operations @@ -2572,7 +2791,9 @@ static void bestBtreeIndex( ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used - ** in determining the value of nInMul. + ** in determining the value of nInMul. Note that the RHS of the + ** IN operator must be a SELECT, not a value list, for this variable + ** to be true. ** ** estBound: ** An estimate on the amount of the table that must be searched. A @@ -2580,8 +2801,8 @@ static void bestBtreeIndex( ** might reduce this to a value less than 100 to indicate that only ** a fraction of the table needs searching. In the absence of ** sqlite_stat2 ANALYZE data, a single inequality reduces the search - ** space to 1/3rd its original size. So an x>? constraint reduces - ** estBound to 33. Two constraints (x>? AND x? constraint reduces + ** estBound to 25. Two constraints (x>? AND xnColumn; nEq++){ @@ -2619,19 +2846,24 @@ static void bestBtreeIndex( Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ nInMul *= 25; bInEst = 1; - }else if( ALWAYS(pExpr->x.pList) ){ - nInMul *= pExpr->x.pList->nExpr + 1; + }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ + /* "x IN (value, value, ...)" */ + nInMul *= pExpr->x.pList->nExpr; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } +#ifdef SQLITE_ENABLE_STAT2 + if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; +#endif used |= pTerm->prereqRight; } /* Determine the value of estBound. */ - if( nEqnColumn ){ + if( nEqnColumn && pProbe->bUnordered==0 ){ int j = pProbe->aiColumn[nEq]; if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); @@ -2662,8 +2894,10 @@ static void bestBtreeIndex( ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index ** will scan rows in a different order, set the bSort variable. */ if( pOrderBy ){ - if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 - && isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) + if( (wsFlags & WHERE_COLUMN_IN)==0 + && pProbe->bUnordered==0 + && isSortingIndex(pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, + nEq, wsFlags, &rev) ){ wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; wsFlags |= (rev ? WHERE_REVERSE : 0); @@ -2694,8 +2928,8 @@ static void bestBtreeIndex( } /* - ** Estimate the number of rows of output. For an IN operator, - ** do not let the estimate exceed half the rows in the table. + ** Estimate the number of rows of output. For an "x IN (SELECT...)" + ** constraint, do not let the estimate exceed half the rows in the table. */ nRow = (double)(aiRowEst[nEq] * nInMul); if( bInEst && nRow*2>aiRowEst[0] ){ @@ -2703,31 +2937,90 @@ static void bestBtreeIndex( nInMul = (int)(nRow / aiRowEst[nEq]); } - /* Assume constant cost to access a row and logarithmic cost to - ** do a binary search. Hence, the initial cost is the number of output - ** rows plus log2(table-size) times the number of binary searches. +#ifdef SQLITE_ENABLE_STAT2 + /* If the constraint is of the form x=VALUE and histogram + ** data is available for column x, then it might be possible + ** to get a better estimate on the number of rows based on + ** VALUE and how common that value is according to the histogram. */ - cost = nRow + nInMul*estLog(aiRowEst[0]); + if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 ){ + if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ + testcase( pFirstTerm->eOperator==WO_EQ ); + testcase( pFirstTerm->eOperator==WO_ISNULL ); + whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); + }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){ + whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); + } + } +#endif /* SQLITE_ENABLE_STAT2 */ - /* Adjust the number of rows and the cost downward to reflect rows + /* Adjust the number of output rows and downward to reflect rows ** that are excluded by range constraints. */ nRow = (nRow * (double)estBound) / (double)100; - cost = (cost * (double)estBound) / (double)100; + if( nRow<1 ) nRow = 1; - /* Add in the estimated cost of sorting the result + /* Experiments run on real SQLite databases show that the time needed + ** to do a binary search to locate a row in a table or index is roughly + ** log10(N) times the time to move from one row to the next row within + ** a table or index. The actual times can vary, with the size of + ** records being an important factor. Both moves and searches are + ** slower with larger records, presumably because fewer records fit + ** on one page and hence more pages have to be fetched. + ** + ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do + ** not give us data on the relative sizes of table and index records. + ** So this computation assumes table records are about twice as big + ** as index records + */ + if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ + /* The cost of a full table scan is a number of move operations equal + ** to the number of rows in the table. + ** + ** We add an additional 4x penalty to full table scans. This causes + ** the cost function to err on the side of choosing an index over + ** choosing a full scan. This 4x full-scan penalty is an arguable + ** decision and one which we expect to revisit in the future. But + ** it seems to be working well enough at the moment. + */ + cost = aiRowEst[0]*4; + }else{ + log10N = estLog(aiRowEst[0]); + cost = nRow; + if( pIdx ){ + if( bLookup ){ + /* For an index lookup followed by a table lookup: + ** nInMul index searches to find the start of each index range + ** + nRow steps through the index + ** + nRow table searches to lookup the table entry using the rowid + */ + cost += (nInMul + nRow)*log10N; + }else{ + /* For a covering index: + ** nInMul index searches to find the initial entry + ** + nRow steps through the index + */ + cost += nInMul*log10N; + } + }else{ + /* For a rowid primary key lookup: + ** nInMult table searches to find the initial entry for each range + ** + nRow steps through the table + */ + cost += nInMul*log10N; + } + } + + /* Add in the estimated cost of sorting the result. Actual experimental + ** measurements of sorting performance in SQLite show that sorting time + ** adds C*N*log10(N) to the cost, where N is the number of rows to be + ** sorted and C is a factor between 1.95 and 4.3. We will split the + ** difference and select C of 3.0. */ if( bSort ){ - cost += cost*estLog(cost); + cost += nRow*estLog(nRow)*3; } - /* If all information can be taken directly from the index, we avoid - ** doing table lookups. This reduces the cost by half. (Not really - - ** this needs to be fixed.) - */ - if( pIdx && bLookup==0 ){ - cost /= (double)2; - } /**** Cost of using this index has now been computed ****/ /* If there are additional constraints on this table that cannot @@ -2768,15 +3061,19 @@ static void bestBtreeIndex( } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ - /* Ignore the first nBound range constraints since the index + /* Ignore the first nSkipRange range constraints since the index ** has already accounted for these */ nSkipRange--; }else{ /* Assume each additional range constraint reduces the result - ** set size by a factor of 3 */ + ** set size by a factor of 3. Indexed range constraints reduce + ** the search space by a larger factor: 4. We make indexed range + ** more selective intentionally because of the subjective + ** observation that indexed range constraints really are more + ** selective in practice, on average. */ nRow /= 3; } - }else{ + }else if( pTerm->eOperator!=WO_NOOP ){ /* Any other expression lowers the output row count by half */ nRow /= 2; } @@ -2787,10 +3084,10 @@ static void bestBtreeIndex( WHERETRACE(( "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n" - " notReady=0x%llx nRow=%.2f cost=%.2f used=0x%llx\n", + " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), nEq, nInMul, estBound, bSort, bLookup, wsFlags, - notReady, nRow, cost, used + notReady, log10N, nRow, cost, used )); /* If this index is the best we have seen so far, then record this @@ -3614,7 +3911,9 @@ static Bitmask codeOneLoopStart( if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){ + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + } if( zStartAff ){ if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions @@ -3653,7 +3952,9 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + } if( zEndAff ){ if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions @@ -3711,7 +4012,13 @@ static Bitmask codeOneLoopStart( /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ - pLevel->op = bRev ? OP_Prev : OP_Next; + if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ + pLevel->op = OP_Noop; + }else if( bRev ){ + pLevel->op = OP_Prev; + }else{ + pLevel->op = OP_Next; + } pLevel->p1 = iIdxCur; }else @@ -3757,7 +4064,6 @@ static Bitmask codeOneLoopStart( ** */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ - WhereTerm *pFinal; /* Final subterm within the OR-clause. */ SrcList *pOrTab; /* Shortened table list or OR-clause generation */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ @@ -3773,7 +4079,6 @@ static Bitmask codeOneLoopStart( assert( pTerm->eOperator==WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; - pFinal = &pOrWc->a[pOrWc->nTerm-1]; pLevel->op = OP_Return; pLevel->p1 = regReturn; @@ -3882,7 +4187,6 @@ static Bitmask codeOneLoopStart( ** the use of indices become tests that are evaluated against each row of ** the relevant input tables. */ - k = 0; for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ @@ -3900,7 +4204,6 @@ static Bitmask codeOneLoopStart( continue; } sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); - k = 1; pTerm->wtFlags |= TERM_CODED; } @@ -4208,8 +4511,6 @@ WhereInfo *sqlite3WhereBegin( ** clause. */ notReady = ~(Bitmask)0; - pTabItem = pTabList->a; - pLevel = pWInfo->a; andFlags = ~0; WHERETRACE(("*** Optimizer Start ***\n")); for(i=iFrom=0, pLevel=pWInfo->a; ipIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) diff --git a/test/alter.test b/test/alter.test index bf7cf00..d4b72a6 100644 --- a/test/alter.test +++ b/test/alter.test @@ -840,4 +840,23 @@ do_test alter-14.2 { } {1 {Cannot add a PRIMARY KEY column}} +#------------------------------------------------------------------------- +# Test that it is not possible to use ALTER TABLE on any system table. +# +set system_table_list {1 sqlite_master} +catchsql ANALYZE +ifcapable analyze { lappend system_table_list 2 sqlite_stat1 } +ifcapable stat2 { lappend system_table_list 3 sqlite_stat2 } + +foreach {tn tbl} $system_table_list { + do_test alter-15.$tn.1 { + catchsql "ALTER TABLE $tbl RENAME TO xyz" + } [list 1 "table $tbl may not be altered"] + + do_test alter-15.$tn.2 { + catchsql "ALTER TABLE $tbl ADD COLUMN xyz" + } [list 1 "table $tbl may not be altered"] +} + + finish_test diff --git a/test/analyze.test b/test/analyze.test index 177936c..766cd50 100644 --- a/test/analyze.test +++ b/test/analyze.test @@ -96,7 +96,7 @@ do_test analyze-1.11 { execsql { SELECT * FROM sqlite_stat1 } -} {t1 {} 0} +} {} do_test analyze-1.12 { catchsql { ANALYZE t1; @@ -106,7 +106,7 @@ do_test analyze-1.13 { execsql { SELECT * FROM sqlite_stat1 } -} {t1 {} 0} +} {} # Create some indices that can be analyzed. But do not yet add # data. Without data in the tables, no analysis is done. @@ -117,21 +117,21 @@ do_test analyze-2.1 { ANALYZE main.t1; SELECT * FROM sqlite_stat1 ORDER BY idx; } -} {t1 {} 0} +} {} do_test analyze-2.2 { execsql { CREATE INDEX t1i2 ON t1(b); ANALYZE t1; SELECT * FROM sqlite_stat1 ORDER BY idx; } -} {t1 {} 0} +} {} do_test analyze-2.3 { execsql { CREATE INDEX t1i3 ON t1(a,b); ANALYZE main; SELECT * FROM sqlite_stat1 ORDER BY idx; } -} {t1 {} 0} +} {} # Start adding data to the table. Verify that the analysis # is done correctly. diff --git a/test/analyze2.test b/test/analyze2.test index 039fb37..de2567b 100644 --- a/test/analyze2.test +++ b/test/analyze2.test @@ -154,22 +154,22 @@ do_eqp_test 2.6 { do_eqp_test 2.7 { SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300 } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x? AND x? AND y? AND y? AND x? AND x? AND y? AND y? AND x? AND x'h' @@ -242,10 +242,12 @@ do_test analyze2-4.1 { } {} do_test analyze2-4.2 { execsql { + PRAGMA automatic_index=OFF; SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't3a' - GROUP BY tbl,idx + GROUP BY tbl,idx; + PRAGMA automatic_index=ON; } } {t3 t3a {AfA bEj CEj dEj EEj fEj GEj hEj IEj jEj}} do_test analyze2-4.3 { @@ -408,7 +410,7 @@ do_test analyze2-6.2.1 { t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a? AND a1 AND t5.a<15 AND t6.a>1 } -} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.3 { sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.4 { execsql { PRAGMA writable_schema = 1; @@ -434,7 +436,7 @@ do_test analyze2-6.2.4 { t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a? AND a1 AND t5.a<15 AND t6.a>1 } -} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a? AND a1 AND t5.a<15 AND t6.a>1 } -} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} #-------------------------------------------------------------------- # These tests, analyze2-7.*, test that the sqlite_stat2 functionality @@ -501,7 +503,7 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.6 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db2 @@ -509,7 +511,7 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db2 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.7 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db1 @@ -517,7 +519,7 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.8 { execsql { DELETE FROM sqlite_stat2 } db2 @@ -526,14 +528,14 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.9 { execsql { SELECT * FROM sqlite_master } db2 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db2 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.10 { incr_schema_cookie test.db @@ -542,7 +544,7 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a? AND a? AND b? AND b=25 && $i<=50}] + set z [expr {($i>=400) + ($i>=700) + ($i>=875)}] + set x $z + set w $z + set t [expr {$z+0.5}] + switch $z { + 0 {set u "alpha"; unset x} + 1 {set u "bravo"} + 2 {set u "charlie"} + 3 {set u "delta"; unset w} + } + if {$i%2} {set v $u} {set v [string toupper $u]} + db eval {INSERT INTO t1 VALUES($t,$u,$v,$w,$x,$y,$z)} + } + db eval { + CREATE INDEX t1t ON t1(t); -- 0.5, 1.5, 2.5, and 3.5 + CREATE INDEX t1u ON t1(u); -- text + CREATE INDEX t1v ON t1(v); -- mixed case text + CREATE INDEX t1w ON t1(w); -- integers 0, 1, 2 and a few NULLs + CREATE INDEX t1x ON t1(x); -- integers 1, 2, 3 and many NULLs + CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s + CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3 + ANALYZE; + SELECT sample FROM sqlite_stat2 WHERE idx='t1u' ORDER BY sampleno; + } +} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta} +do_test analyze5-1.1 { + string tolower \ + [db eval {SELECT sample from sqlite_stat2 WHERE idx='t1v' ORDER BY sampleno}] +} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta} +do_test analyze5-1.2 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1w' ORDER BY sampleno} +} {{} 0 0 0 0 1 1 1 2 2} +do_test analyze5-1.3 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno} +} {{} {} {} {} 1 1 1 2 2 3} +do_test analyze5-1.4 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1y' ORDER BY sampleno} +} {0 0 0 0 0 0 0 0 0 0} +do_test analyze5-1.5 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1z' ORDER BY sampleno} +} {0 0 0 0 1 1 1 2 2 3} +do_test analyze5-1.6 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1t' ORDER BY sampleno} +} {0.5 0.5 0.5 0.5 1.5 1.5 1.5 2.5 2.5 3.5} + + +# Verify that range queries generate the correct row count estimates +# +foreach {testid where index rows} { + 1 {z>=0 AND z<=0} t1z 400 + 2 {z>=1 AND z<=1} t1z 300 + 3 {z>=2 AND z<=2} t1z 200 + 4 {z>=3 AND z<=3} t1z 100 + 5 {z>=4 AND z<=4} t1z 50 + 6 {z>=-1 AND z<=-1} t1z 50 + 7 {z>1 AND z<3} t1z 200 + 8 {z>0 AND z<100} t1z 600 + 9 {z>=1 AND z<100} t1z 600 + 10 {z>1 AND z<100} t1z 300 + 11 {z>=2 AND z<100} t1z 300 + 12 {z>2 AND z<100} t1z 100 + 13 {z>=3 AND z<100} t1z 100 + 14 {z>3 AND z<100} t1z 50 + 15 {z>=4 AND z<100} t1z 50 + 16 {z>=-100 AND z<=-1} t1z 50 + 17 {z>=-100 AND z<=0} t1z 400 + 18 {z>=-100 AND z<0} t1z 50 + 19 {z>=-100 AND z<=1} t1z 700 + 20 {z>=-100 AND z<2} t1z 700 + 21 {z>=-100 AND z<=2} t1z 900 + 22 {z>=-100 AND z<3} t1z 900 + + 31 {z>=0.0 AND z<=0.0} t1z 400 + 32 {z>=1.0 AND z<=1.0} t1z 300 + 33 {z>=2.0 AND z<=2.0} t1z 200 + 34 {z>=3.0 AND z<=3.0} t1z 100 + 35 {z>=4.0 AND z<=4.0} t1z 50 + 36 {z>=-1.0 AND z<=-1.0} t1z 50 + 37 {z>1.5 AND z<3.0} t1z 200 + 38 {z>0.5 AND z<100} t1z 600 + 39 {z>=1.0 AND z<100} t1z 600 + 40 {z>1.5 AND z<100} t1z 300 + 41 {z>=2.0 AND z<100} t1z 300 + 42 {z>2.1 AND z<100} t1z 100 + 43 {z>=3.0 AND z<100} t1z 100 + 44 {z>3.2 AND z<100} t1z 50 + 45 {z>=4.0 AND z<100} t1z 50 + 46 {z>=-100 AND z<=-1.0} t1z 50 + 47 {z>=-100 AND z<=0.0} t1z 400 + 48 {z>=-100 AND z<0.0} t1z 50 + 49 {z>=-100 AND z<=1.0} t1z 700 + 50 {z>=-100 AND z<2.0} t1z 700 + 51 {z>=-100 AND z<=2.0} t1z 900 + 52 {z>=-100 AND z<3.0} t1z 900 + + 101 {z=-1} t1z 50 + 102 {z=0} t1z 400 + 103 {z=1} t1z 300 + 104 {z=2} t1z 200 + 105 {z=3} t1z 100 + 106 {z=4} t1z 50 + 107 {z=-10.0} t1z 50 + 108 {z=0.0} t1z 400 + 109 {z=1.0} t1z 300 + 110 {z=2.0} t1z 200 + 111 {z=3.0} t1z 100 + 112 {z=4.0} t1z 50 + 113 {z=1.5} t1z 50 + 114 {z=2.5} t1z 50 + + 201 {z IN (-1)} t1z 50 + 202 {z IN (0)} t1z 400 + 203 {z IN (1)} t1z 300 + 204 {z IN (2)} t1z 200 + 205 {z IN (3)} t1z 100 + 206 {z IN (4)} t1z 50 + 207 {z IN (0.5)} t1z 50 + 208 {z IN (0,1)} t1z 700 + 209 {z IN (0,1,2)} t1z 900 + 210 {z IN (0,1,2,3)} {} 100 + 211 {z IN (0,1,2,3,4,5)} {} 100 + 212 {z IN (1,2)} t1z 500 + 213 {z IN (2,3)} t1z 300 + 214 {z=3 OR z=2} t1z 300 + 215 {z IN (-1,3)} t1z 150 + 216 {z=-1 OR z=3} t1z 150 + + 300 {y=0} {} 100 + 301 {y=1} t1y 50 + 302 {y=0.1} t1y 50 + + 400 {x IS NULL} t1x 400 + +} { + # Verify that the expected index is used with the expected row count + do_test analyze5-1.${testid}a { + set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] + set idx {} + regexp {INDEX (t1.) } $x all idx + regexp {~([0-9]+) rows} $x all nrow + list $idx $nrow + } [list $index $rows] + + # Verify that the same result is achieved regardless of whether or not + # the index is used + do_test analyze5-1.${testid}b { + set w2 [string map {y +y z +z} $where] + set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\ + ORDER BY +rowid"] + set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"] + if {$a1==$a2} { + set res ok + } else { + set res "a1=\[$a1\] a2=\[$a2\]" + } + set res + } {ok} +} + +# Increase the number of NULLs in column x +# +db eval { + UPDATE t1 SET x=NULL; + UPDATE t1 SET x=rowid + WHERE rowid IN (SELECT rowid FROM t1 ORDER BY random() LIMIT 5); + ANALYZE; +} + +# Verify that range queries generate the correct row count estimates +# +foreach {testid where index rows} { + 500 {x IS NULL AND u='charlie'} t1u 20 + 501 {x=1 AND u='charlie'} t1x 5 + 502 {x IS NULL} {} 100 + 503 {x=1} t1x 50 + 504 {x IS NOT NULL} t1x 25 + 505 {+x IS NOT NULL} {} 500 + 506 {upper(x) IS NOT NULL} {} 500 + +} { + # Verify that the expected index is used with the expected row count + do_test analyze5-1.${testid}a { + set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] + set idx {} + regexp {INDEX (t1.) } $x all idx + regexp {~([0-9]+) rows} $x all nrow + list $idx $nrow + } [list $index $rows] + + # Verify that the same result is achieved regardless of whether or not + # the index is used + do_test analyze5-1.${testid}b { + set w2 [string map {y +y z +z} $where] + set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\ + ORDER BY +rowid"] + set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"] + if {$a1==$a2} { + set res ok + } else { + set res "a1=\[$a1\] a2=\[$a2\]" + } + set res + } {ok} +} + +finish_test diff --git a/test/analyze6.test b/test/analyze6.test new file mode 100644 index 0000000..b090b5b --- /dev/null +++ b/test/analyze6.test @@ -0,0 +1,122 @@ +# 2011 March 3 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements tests for SQLite library. The focus of the tests +# in this file a corner-case query planner optimization involving the +# join order of two tables of different sizes. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat2 { + finish_test + return +} + +set testprefix analyze6 + +proc eqp {sql {db db}} { + uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db +} + +do_test analyze6-1.0 { + db eval { + CREATE TABLE cat(x INT); + CREATE UNIQUE INDEX catx ON cat(x); + /* Give cat 16 unique integers */ + INSERT INTO cat VALUES(1); + INSERT INTO cat VALUES(2); + INSERT INTO cat SELECT x+2 FROM cat; + INSERT INTO cat SELECT x+4 FROM cat; + INSERT INTO cat SELECT x+8 FROM cat; + + CREATE TABLE ev(y INT); + CREATE INDEX evy ON ev(y); + /* ev will hold 32 copies of 16 integers found in cat */ + INSERT INTO ev SELECT x FROM cat; + INSERT INTO ev SELECT x FROM cat; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + ANALYZE; + SELECT count(*) FROM cat; + SELECT count(*) FROM ev; + } +} {16 512} + +# The lowest cost plan is to scan CAT and for each integer there, do a single +# lookup of the first corresponding entry in EV then read off the equal values +# in EV. (Prior to the 2011-03-04 enhancement to where.c, this query would +# have used EV for the outer loop instead of CAT - which was about 3x slower.) +# +do_test analyze6-1.1 { + eqp {SELECT count(*) FROM ev, cat WHERE x=y} +} {0 0 1 {SCAN TABLE cat (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} + +# The same plan is chosen regardless of the order of the tables in the +# FROM clause. +# +do_test analyze6-1.2 { + eqp {SELECT count(*) FROM cat, ev WHERE x=y} +} {0 0 0 {SCAN TABLE cat (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} + + +# Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30 +# If ANALYZE is run on an empty table, make sure indices are used +# on the table. +# +do_test analyze6-2.1 { + execsql { + CREATE TABLE t201(x INTEGER PRIMARY KEY, y UNIQUE, z); + CREATE INDEX t201z ON t201(z); + ANALYZE; + } + eqp {SELECT * FROM t201 WHERE z=5} +} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} +do_test analyze6-2.2 { + eqp {SELECT * FROM t201 WHERE y=5} +} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} +do_test analyze6-2.3 { + eqp {SELECT * FROM t201 WHERE x=5} +} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +do_test analyze6-2.4 { + execsql { + INSERT INTO t201 VALUES(1,2,3); + ANALYZE t201; + } + eqp {SELECT * FROM t201 WHERE z=5} +} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} +do_test analyze6-2.5 { + eqp {SELECT * FROM t201 WHERE y=5} +} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} +do_test analyze6-2.6 { + eqp {SELECT * FROM t201 WHERE x=5} +} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +do_test analyze6-2.7 { + execsql { + INSERT INTO t201 VALUES(4,5,7); + INSERT INTO t201 SELECT x+100, y+100, z+100 FROM t201; + INSERT INTO t201 SELECT x+200, y+200, z+200 FROM t201; + INSERT INTO t201 SELECT x+400, y+400, z+400 FROM t201; + ANALYZE t201; + } + eqp {SELECT * FROM t201 WHERE z=5} +} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} +do_test analyze6-2.8 { + eqp {SELECT * FROM t201 WHERE y=5} +} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} +do_test analyze6-2.9 { + eqp {SELECT * FROM t201 WHERE x=5} +} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + +finish_test diff --git a/test/analyze7.test b/test/analyze7.test new file mode 100644 index 0000000..4892a22 --- /dev/null +++ b/test/analyze7.test @@ -0,0 +1,111 @@ +# 2011 April 1 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# This file implements tests for the ANALYZE command when an idnex +# name is given as the argument. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# There is nothing to test if ANALYZE is disable for this build. +# +ifcapable {!analyze||!vtab} { + finish_test + return +} + +# Generate some test data +# +do_test analyze7-1.0 { + register_wholenumber_module db + execsql { + CREATE TABLE t1(a,b,c,d); + CREATE INDEX t1a ON t1(a); + CREATE INDEX t1b ON t1(b); + CREATE INDEX t1cd ON t1(c,d); + CREATE VIRTUAL TABLE nums USING wholenumber; + INSERT INTO t1 SELECT value, value, value/100, value FROM nums + WHERE value BETWEEN 1 AND 256; + EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123; + } +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~10 rows)}} +do_test analyze7-1.1 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} +do_test analyze7-1.2 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}} + +# Run an analyze on one of the three indices. Verify that this +# effects the row-count estimate on the one query that uses that +# one index. +# +do_test analyze7-2.0 { + execsql {ANALYZE t1a;} + db cache flush + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +do_test analyze7-2.1 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} +do_test analyze7-2.2 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}} + +# Verify that since the query planner now things that t1a is more +# selective than t1b, it prefers to use t1a. +# +do_test analyze7-2.3 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} + +# Run an analysis on another of the three indices. Verify that this +# new analysis works and does not disrupt the previous analysis. +# +do_test analyze7-3.0 { + execsql {ANALYZE t1cd;} + db cache flush; + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +do_test analyze7-3.1 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} +do_test analyze7-3.2.1 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}} +ifcapable stat2 { + # If ENABLE_STAT2 is defined, SQLite comes up with a different estimated + # row count for (c=2) than it does for (c=?). + do_test analyze7-3.2.2 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} + } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~51 rows)}} +} else { + # If ENABLE_STAT2 is not defined, the expected row count for (c=2) is the + # same as that for (c=?). + do_test analyze7-3.2.3 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} + } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}} +} +do_test analyze7-3.3 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +do_test analyze7-3.4 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}} +do_test analyze7-3.5 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +do_test analyze7-3.6 { + execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?) (~1 rows)}} + +finish_test diff --git a/test/attach.test b/test/attach.test index 61e1272..1bb596c 100644 --- a/test/attach.test +++ b/test/attach.test @@ -152,14 +152,16 @@ do_test attach-1.18 { ATTACH 'test.db' as db11; } } {0 {}} -do_test attach-1.19 { - catchsql { - ATTACH 'test.db' as db12; - } -} {1 {too many attached databases - max 10}} -do_test attach-1.19.1 { - db errorcode -} {1} +if {$SQLITE_MAX_ATTACHED==10} { + do_test attach-1.19 { + catchsql { + ATTACH 'test.db' as db12; + } + } {1 {too many attached databases - max 10}} + do_test attach-1.19.1 { + db errorcode + } {1} +} do_test attach-1.20.1 { execsql { DETACH db5; @@ -179,14 +181,16 @@ do_test attach-1.21 { ATTACH 'test.db' as db12; } } {0 {}} -do_test attach-1.22 { - catchsql { - ATTACH 'test.db' as db13; - } -} {1 {too many attached databases - max 10}} -do_test attach-1.22.1 { - db errorcode -} {1} +if {$SQLITE_MAX_ATTACHED==10} { + do_test attach-1.22 { + catchsql { + ATTACH 'test.db' as db13; + } + } {1 {too many attached databases - max 10}} + do_test attach-1.22.1 { + db errorcode + } {1} +} do_test attach-1.23 { catchsql { DETACH "db14"; @@ -835,4 +839,5 @@ do_test attach-10.2 { PRAGMA database_list; }] 9 end } {4 noname {} 5 inmem {}} + finish_test diff --git a/test/attach4.test b/test/attach4.test new file mode 100644 index 0000000..f597f5d --- /dev/null +++ b/test/attach4.test @@ -0,0 +1,116 @@ +# 200 July 1 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script is attaching many database files to a single +# connection. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix attach4 + +ifcapable !attach { + finish_test + return +} + +puts "Testing with SQLITE_MAX_ATTACHED=$SQLITE_MAX_ATTACHED" + +set files {main test.db} +for {set ii 0} {$ii < $SQLITE_MAX_ATTACHED} {incr ii} { + lappend files aux$ii "test.db$ii" +} + +do_test 1.1 { + sqlite3_limit db SQLITE_LIMIT_ATTACHED -1 +} $SQLITE_MAX_ATTACHED + +do_test 1.2.1 { + db close + foreach {name f} $files { forcedelete $f } + sqlite3 db test.db + + foreach {name f} $files { + if {$name == "main"} continue + execsql "ATTACH '$f' AS $name" + } + + db eval {PRAGMA database_list} { + lappend L $name [file tail $file] + } + set L +} $files + +do_catchsql_test 1.2.2 { + ATTACH 'x.db' AS next; +} [list 1 "too many attached databases - max $SQLITE_MAX_ATTACHED"] + +do_test 1.3 { + execsql BEGIN; + foreach {name f} $files { + execsql "CREATE TABLE $name.tbl(x)" + execsql "INSERT INTO $name.tbl VALUES('$f')" + } + execsql COMMIT; +} {} + +do_test 1.4 { + set L [list] + foreach {name f} $files { + lappend L $name [execsql "SELECT x FROM $name.tbl"] + } + set L +} $files + +set L [list] +set S "" +foreach {name f} $files { + if {[permutation] == "journaltest"} { + lappend L delete + } else { + lappend L wal + } + append S " + PRAGMA $name.journal_mode = WAL; + UPDATE $name.tbl SET x = '$name'; + " +} +do_execsql_test 1.5 $S $L + +do_test 1.6 { + set L [list] + foreach {name f} $files { + lappend L [execsql "SELECT x FROM $name.tbl"] $f + } + set L +} $files + +do_test 1.7 { + execsql BEGIN; + foreach {name f} $files { + execsql "UPDATE $name.tbl SET x = '$f'" + } + execsql COMMIT; +} {} + +do_test 1.8 { + set L [list] + foreach {name f} $files { + lappend L $name [execsql "SELECT x FROM $name.tbl"] + } + set L +} $files + +db close +foreach {name f} $files { forcedelete $f } + +finish_test diff --git a/test/auth.test b/test/auth.test index bb974ea..8d2159e 100644 --- a/test/auth.test +++ b/test/auth.test @@ -1628,9 +1628,20 @@ ifcapable attach { ATTACH DATABASE ':memory:' AS test1 } } {0 {}} - do_test auth-1.252 { + do_test auth-1.252a { set ::authargs } {:memory: {} {} {}} + do_test auth-1.252b { + db eval {DETACH test1} + set ::attachfilename :memory: + db eval {ATTACH $::attachfilename AS test1} + set ::authargs + } {{} {} {} {}} + do_test auth-1.252c { + db eval {DETACH test1} + db eval {ATTACH ':mem' || 'ory:' AS test1} + set ::authargs + } {{} {} {} {}} do_test auth-1.253 { catchsql {DETACH DATABASE test1} proc auth {code arg1 arg2 arg3 arg4} { diff --git a/test/backcompat.test b/test/backcompat.test index 3b11607..fa643f9 100644 --- a/test/backcompat.test +++ b/test/backcompat.test @@ -369,4 +369,77 @@ do_allbackcompat_test { } } +#------------------------------------------------------------------------- +# Test that Rtree tables may be read/written by different versions of +# SQLite. +# +set contents { + CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2, y1, y2); +} +foreach {id x1 x2 y1 y2} { + 1 -47.64 43.87 33.86 34.42 2 -21.51 17.32 2.05 31.04 + 3 -43.67 -38.33 -19.79 3.43 4 32.41 35.16 9.12 19.82 + 5 33.28 34.87 14.78 28.26 6 49.31 116.59 -9.87 75.09 + 7 -14.93 34.51 -17.64 64.09 8 -43.05 23.43 -1.19 69.44 + 9 44.79 133.56 28.09 80.30 10 -2.66 81.47 -41.38 -10.46 + 11 -42.89 -3.54 15.76 71.63 12 -3.50 84.96 -11.64 64.95 + 13 -45.69 26.25 11.14 55.06 14 -44.09 11.23 17.52 44.45 + 15 36.23 133.49 -19.38 53.67 16 -17.89 81.54 14.64 50.61 + 17 -41.97 -24.04 -39.43 28.95 18 -5.85 7.76 -6.38 47.02 + 19 18.82 27.10 42.82 100.09 20 39.17 113.45 26.14 73.47 + 21 22.31 103.17 49.92 106.05 22 -43.06 40.38 -1.75 76.08 + 23 2.43 57.27 -14.19 -3.83 24 -47.57 -4.35 8.93 100.06 + 25 -37.47 49.14 -29.11 8.81 26 -7.86 75.72 49.34 107.42 + 27 1.53 45.49 20.36 49.74 28 -48.48 32.54 28.81 54.45 + 29 2.67 39.77 -4.05 13.67 30 4.11 62.88 -47.44 -5.72 + 31 -21.47 51.75 37.25 116.09 32 45.59 111.37 -6.43 43.64 + 33 35.23 48.29 23.54 113.33 34 16.61 68.35 -14.69 65.97 + 35 13.98 16.60 48.66 102.87 36 19.74 23.84 31.15 77.27 + 37 -27.61 24.43 7.96 94.91 38 -34.77 12.05 -22.60 -6.29 + 39 -25.83 8.71 -13.48 -12.53 40 -17.11 -1.01 18.06 67.89 + 41 14.13 71.72 -3.78 39.25 42 23.75 76.00 -16.30 8.23 + 43 -39.15 28.63 38.12 125.88 44 48.62 86.09 36.49 102.95 + 45 -31.39 -21.98 2.52 89.78 46 5.65 56.04 15.94 89.10 + 47 18.28 95.81 46.46 143.08 48 30.93 102.82 -20.08 37.36 + 49 -20.78 -3.48 -5.58 35.46 50 49.85 90.58 -24.48 46.29 +} { +if {$x1 >= $x2 || $y1 >= $y2} { error "$x1 $x2 $y1 $y2" } + append contents "INSERT INTO t1 VALUES($id, $x1, $x2, $y1, $y2);" +} +set queries { + 1 "SELECT id FROM t1 WHERE x1>10 AND x2<44" + 2 "SELECT id FROM t1 WHERE y1<100" + 3 "SELECT id FROM t1 WHERE y1<100 AND x1>0" + 4 "SELECT id FROM t1 WHERE y1>10 AND x1>0 AND x2<50 AND y2<550" +} +do_allbackcompat_test { + if {[code1 {set ::sqlite_options(fts3)}] + && [code2 {set ::sqlite_options(fts3)}] + } { + + do_test backcompat-4.1 { sql1 $contents } {} + + foreach {n q} $::queries { + do_test backcompat-4.2.$n [list sql1 $q] [sql2 $q] + } + + do_test backcompat-4.3 { sql1 { + INSERT INTO t1 SELECT id+100, x1+10.0, x2+10.0, y1-10.0, y2-10.0 FROM t1; + } } {} + + foreach {n q} $::queries { + do_test backcompat-4.4.$n [list sql1 $q] [sql2 $q] + } + + do_test backcompat-4.5 { sql2 { + INSERT INTO t1 SELECT id+200, x1+20.0, x2+20.0, y1-20.0, y2-20.0 FROM t1; + } } {} + + foreach {n q} $::queries { + do_test backcompat-4.6.$n [list sql1 $q] [sql2 $q] + } + + } +} + finish_test diff --git a/test/badutf2.test b/test/badutf2.test new file mode 100644 index 0000000..36b40fb --- /dev/null +++ b/test/badutf2.test @@ -0,0 +1,121 @@ +# 2011 March 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file checks to make sure SQLite is able to gracEFully +# handle malformed UTF-8. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +proc utf8_to_ustr2 {s} { + set r "" + foreach i [split $s ""] { + scan $i %c c + append r [format \\u%04.4X $c] + } + set r +} + +proc utf8_to_hstr {in} { + regsub -all -- {(..)} $in {%[format "%s" \1]} out + subst $out +} + +proc utf8_to_xstr {in} { + regsub -all -- {(..)} $in {\\\\x[format "%s" \1]} out + subst $out +} + +proc utf8_to_ustr {in} { + regsub -all -- {(..)} $in {\\\\u[format "%04.4X" 0x\1]} out + subst $out +} + +do_test badutf2-1.0 { + db close + forcedelete test.db + sqlite3 db test.db + db eval "PRAGMA encoding = 'UTF-8'" +} {} + +do_test badutf2-4.0 { + set S [sqlite3_prepare_v2 db "SELECT ?" -1 dummy] + sqlite3_expired $S +} {0} + +foreach { i len uval xstr ustr u2u } { +1 1 00 \x00 {} {} +2 1 01 \x01 "\\u0001" 01 +3 1 3F \x3F "\\u003F" 3F +4 1 7F \x7F "\\u007F" 7F +5 1 80 \x80 "\\u0080" C280 +6 1 C3BF \xFF "\\u00FF" C3BF +7 3 EFBFBD \xEF\xBF\xBD "\\uFFFD" {} +} { + + set hstr [ utf8_to_hstr $uval ] + + ifcapable bloblit { + if {$hstr != "%00"} { + do_test badutf2-2.1.$i { + set sql "SELECT '$hstr'=CAST(x'$uval' AS text) AS x;" + set res [ sqlite3_exec db $sql ] + lindex [ lindex $res 1] 1 + } {1} + do_test badutf2-2.2.$i { + set sql "SELECT CAST('$hstr' AS blob)=x'$uval' AS x;" + set res [ sqlite3_exec db $sql ] + lindex [ lindex $res 1] 1 + } {1} + } + do_test badutf2-2.3.$i { + set sql "SELECT hex(CAST(x'$uval' AS text)) AS x;" + set res [ sqlite3_exec db $sql ] + lindex [ lindex $res 1] 1 + } $uval + do_test badutf2-2.4.$i { + set sql "SELECT hex(CAST(x'$uval' AS text)) AS x;" + set res [ sqlite3_exec db $sql ] + lindex [ lindex $res 1] 1 + } $uval + } + + if {$hstr != "%00"} { + do_test badutf2-3.1.$i { + set sql "SELECT hex('$hstr') AS x;" + set res [ sqlite3_exec db $sql ] + lindex [ lindex $res 1] 1 + } $uval + } + + do_test badutf2-4.1.$i { + sqlite3_reset $S + sqlite3_bind_text $S 1 $xstr $len + sqlite3_step $S + utf8_to_ustr2 [ sqlite3_column_text $S 0 ] + } $ustr + + ifcapable debug { + do_test badutf2-5.1.$i { + utf8_to_utf8 $uval + } $u2u + } + +} + +do_test badutf2-4.2 { + sqlite3_finalize $S +} {SQLITE_OK} + + +finish_test diff --git a/test/capi3.test b/test/capi3.test index 4f5e02c..cc1461a 100644 --- a/test/capi3.test +++ b/test/capi3.test @@ -651,11 +651,11 @@ do_test capi3-6.1 { } {SQLITE_BUSY} do_test capi3-6.2 { sqlite3_step $STMT -} {SQLITE_ROW} -check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1} +} {SQLITE_ERROR} +#check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1} do_test capi3-6.3 { sqlite3_finalize $STMT -} {SQLITE_OK} +} {SQLITE_SCHEMA} do_test capi3-6.4-misuse { db cache flush sqlite3_close $DB diff --git a/test/capi3e.test b/test/capi3e.test index 388c17b..21304cb 100644 --- a/test/capi3e.test +++ b/test/capi3e.test @@ -77,22 +77,24 @@ foreach name $names { } {1} } -set i 0 -foreach name $names { - incr i - do_test capi3e-2.1.$i { - set db2 [sqlite3_open16 [utf16 $name] {}] - sqlite3_errcode $db2 - } {SQLITE_OK} - do_test capi3e-2.2.$i { - sqlite3_close $db2 - } {SQLITE_OK} - do_test capi3e-2.3.$i { - file isfile $name - } {1} +ifcapable {utf16} { + set i 0 + foreach name $names { + incr i + do_test capi3e-2.1.$i { + set db2 [sqlite3_open16 [utf16 $name] {}] + sqlite3_errcode $db2 + } {SQLITE_OK} + do_test capi3e-2.2.$i { + sqlite3_close $db2 + } {SQLITE_OK} + do_test capi3e-2.3.$i { + file isfile $name + } {1} + } } -ifcapable attach { +ifcapable attach { do_test capi3e-3.1 { sqlite3 db2 base.db } {} diff --git a/test/corrupt3.test b/test/corrupt3.test index 790f521..fba3ba7 100644 --- a/test/corrupt3.test +++ b/test/corrupt3.test @@ -18,7 +18,10 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -if {[sqlite3 -has-codec]} { finish_test ; return } +# Do not use a codec for tests in this file, as the database file is +# manipulated directly using tcl scripts (using the [hexio_write] command). +# +do_not_use_codec # We must have the page_size pragma for these tests to work. # diff --git a/test/corruptA.test b/test/corruptA.test index 58e14da..9c7d228 100644 --- a/test/corruptA.test +++ b/test/corruptA.test @@ -19,6 +19,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +# Do not use a codec for tests in this file, as the database file is +# manipulated directly using tcl scripts (using the [hexio_write] command). +# +do_not_use_codec + # Create a database to work with. # diff --git a/test/corruptD.test b/test/corruptD.test index 8090ecc..61dada6 100644 --- a/test/corruptD.test +++ b/test/corruptD.test @@ -14,6 +14,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +# Do not use a codec for tests in this file, as the database file is +# manipulated directly using tcl scripts (using the [hexio_write] command). +# +do_not_use_codec + #-------------------------------------------------------------------------- # OVERVIEW # diff --git a/test/e_createtable.test b/test/e_createtable.test index 6696484..c67195e 100644 --- a/test/e_createtable.test +++ b/test/e_createtable.test @@ -1379,7 +1379,7 @@ do_createtable_tests 4.10 { {0 0 0 {SCAN TABLE t2 USING INDEX sqlite_autoindex_t2_1 (~1000000 rows)}} 3 "EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b=10 AND c>10" - {0 0 0 {SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (b=? AND c>?) (~3 rows)}} + {0 0 0 {SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (b=? AND c>?) (~2 rows)}} } # EVIDENCE-OF: R-45493-35653 A CHECK constraint may be attached to a diff --git a/test/e_expr.test b/test/e_expr.test index 437e88b..1e76853 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -1444,16 +1444,20 @@ do_qexpr_test e_expr-27.4.2 { CAST(456 AS blob) } X'343536' do_qexpr_test e_expr-27.4.3 { CAST(1.78 AS blob) } X'312E3738' rename db db2 sqlite3 db :memory: +ifcapable {utf16} { db eval { PRAGMA encoding = 'utf-16le' } do_qexpr_test e_expr-27.4.4 { CAST('ghi' AS blob) } X'670068006900' do_qexpr_test e_expr-27.4.5 { CAST(456 AS blob) } X'340035003600' do_qexpr_test e_expr-27.4.6 { CAST(1.78 AS blob) } X'31002E0037003800' +} db close sqlite3 db :memory: db eval { PRAGMA encoding = 'utf-16be' } +ifcapable {utf16} { do_qexpr_test e_expr-27.4.7 { CAST('ghi' AS blob) } X'006700680069' do_qexpr_test e_expr-27.4.8 { CAST(456 AS blob) } X'003400350036' do_qexpr_test e_expr-27.4.9 { CAST(1.78 AS blob) } X'0031002E00370038' +} db close rename db2 db @@ -1466,8 +1470,10 @@ do_expr_test e_expr-28.1.2 { CAST (X'670068006900' AS text) } text g rename db db2 sqlite3 db :memory: db eval { PRAGMA encoding = 'utf-16le' } +ifcapable {utf16} { do_expr_test e_expr-28.1.3 { CAST (X'676869' AS text) == 'ghi' } integer 0 do_expr_test e_expr-28.1.4 { CAST (X'670068006900' AS text) } text ghi +} db close rename db2 db @@ -1493,6 +1499,7 @@ do_expr_test e_expr-29.1.3 { CAST (X'2D392E3837' AS REAL) } real -9.87 do_expr_test e_expr-29.1.4 { CAST (X'302E30303031' AS REAL) } real 0.0001 rename db db2 sqlite3 db :memory: +ifcapable {utf16} { db eval { PRAGMA encoding = 'utf-16le' } do_expr_test e_expr-29.1.5 { CAST (X'31002E0032003300' AS REAL) } real 1.23 @@ -1502,6 +1509,7 @@ do_expr_test e_expr-29.1.7 { CAST (X'2D0039002E0038003700' AS REAL) } real -9.87 do_expr_test e_expr-29.1.8 { CAST (X'30002E003000300030003100' AS REAL) } real 0.0001 +} db close rename db2 db @@ -1543,6 +1551,7 @@ do_expr_test e_expr-30.1.4 { rename db db2 sqlite3 db :memory: +ifcapable {utf16} { execsql { PRAGMA encoding = 'utf-16be' } do_expr_test e_expr-30.1.5 { CAST(X'003100320033' AS INTEGER) } integer 123 do_expr_test e_expr-30.1.6 { CAST(X'002D003600370038' AS INTEGER) } integer -678 @@ -1552,6 +1561,7 @@ do_expr_test e_expr-30.1.7 { do_expr_test e_expr-30.1.8 { CAST(X'002D0031003100320035003800390039003900300036003800340032003600320034' AS INTEGER) } integer -1125899906842624 +} db close rename db2 db @@ -1632,6 +1642,7 @@ do_expr_test e_expr-32.2.4 { # into a non-BLOB value may be different depending on whether the # database encoding is UTF-8, UTF-16be, or UTF-16le. # +ifcapable {utf16} { sqlite3 db1 :memory: ; db1 eval { PRAGMA encoding = 'utf-8' } sqlite3 db2 :memory: ; db2 eval { PRAGMA encoding = 'utf-16le' } sqlite3 db3 :memory: ; db3 eval { PRAGMA encoding = 'utf-16be' } @@ -1658,6 +1669,7 @@ foreach {tn castexpr differs} { db1 close db2 close db3 close +} #------------------------------------------------------------------------- # Test statements related to the EXISTS and NOT EXISTS operators. diff --git a/test/e_vacuum.test b/test/e_vacuum.test index a7863c4..414c854 100644 --- a/test/e_vacuum.test +++ b/test/e_vacuum.test @@ -175,6 +175,7 @@ do_test e_vacuum-1.3.2.1 { # EVIDENCE-OF: R-48521-51450 When in write-ahead log mode, only the # auto_vacuum support property can be changed using VACUUM. # +ifcapable wal { do_test e_vacuum-1.3.3.1 { execsql { PRAGMA journal_mode = wal } execsql { PRAGMA page_size ; PRAGMA auto_vacuum } @@ -185,6 +186,7 @@ do_test e_vacuum-1.3.3.2 { execsql VACUUM execsql { PRAGMA page_size ; PRAGMA auto_vacuum } } {2048 1} +} # EVIDENCE-OF: R-38001-03952 VACUUM only works on the main database. It # is not possible to VACUUM an attached database file. diff --git a/test/eqp.test b/test/eqp.test index cd08b8f..91a18d0 100644 --- a/test/eqp.test +++ b/test/eqp.test @@ -21,6 +21,8 @@ set testprefix eqp # eqp-2.*: Tests for single select statements. # eqp-3.*: Select statements that execute sub-selects. # eqp-4.*: Compound select statements. +# ... +# eqp-7.*: "SELECT count(*) FROM tbl" statements (VDBE code OP_Count). # proc det {args} { uplevel do_eqp_test $args } @@ -392,7 +394,7 @@ det 5.3.1 "SELECT a, b FROM t1 WHERE a=1" { # (~1000000 rows) do_execsql_test 5.4.0 {CREATE TABLE t2(c, d)} det 5.4.1 "SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2" { - 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~3 rows)} + 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~2 rows)} 0 1 1 {SCAN TABLE t2 (~1000000 rows)} } @@ -401,7 +403,7 @@ det 5.4.1 "SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2" { # USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|0|SCAN TABLE t2 # (~1000000 rows) det 5.5 "SELECT t1.*, t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2" { - 0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~3 rows)} + 0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~2 rows)} 0 1 0 {SCAN TABLE t2 (~1000000 rows)} } @@ -507,6 +509,7 @@ set boilerplate { exit } +# Do a "Print Explain Query Plan" test. proc do_peqp_test {tn sql res} { set fd [open script.tcl w] puts $fd [string map [list %SQL% $sql] $::boilerplate] @@ -529,7 +532,47 @@ do_peqp_test 6.1 { 0 0 0 COMPOUND SUBQUERIES 1 AND 2 (EXCEPT) }] +#------------------------------------------------------------------------- +# The following tests - eqp-7.* - test that queries that use the OP_Count +# optimization return something sensible with EQP. +# +drop_all_tables +do_execsql_test 7.0 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + CREATE INDEX i1 ON t2(a); +} + +det 7.1 "SELECT count(*) FROM t1" { + 0 0 0 {SCAN TABLE t1 (~1000000 rows)} +} + +det 7.2 "SELECT count(*) FROM t2" { + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~1000000 rows)} +} + +do_execsql_test 7.3 { + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + + INSERT INTO t2 VALUES(1, 2); + INSERT INTO t2 VALUES(3, 4); + INSERT INTO t2 VALUES(5, 6); + + ANALYZE; +} + +db close +sqlite3 db test.db + +det 7.4 "SELECT count(*) FROM t1" { + 0 0 0 {SCAN TABLE t1 (~2 rows)} +} + +det 7.5 "SELECT count(*) FROM t2" { + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~3 rows)} +} finish_test diff --git a/test/exclusive2.test b/test/exclusive2.test index b2b98ff..18f304d 100644 --- a/test/exclusive2.test +++ b/test/exclusive2.test @@ -15,6 +15,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +# Do not use a codec for tests in this file, as the database file is +# manipulated directly using tcl scripts (using the [hexio_write] command). +# +do_not_use_codec + ifcapable {!pager_pragmas} { finish_test return diff --git a/test/exists.test b/test/exists.test new file mode 100644 index 0000000..c7c14a2 --- /dev/null +++ b/test/exists.test @@ -0,0 +1,198 @@ +# 2011 April 9 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the various schema modification statements +# that feature "IF EXISTS" or "IF NOT EXISTS" clauses. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl + + +foreach jm {rollback wal} { + + set testprefix exists-$jm + + # This block of tests is targeted at CREATE XXX IF NOT EXISTS statements. + # + do_multiclient_test tn { + + # TABLE objects. + # + do_test 1.$tn.1.1 { + if {$jm == "wal"} { sql2 { PRAGMA journal_mode = WAL } } + sql2 { CREATE TABLE t1(x) } + sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } + sql2 { DROP TABLE t1 } + sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {t1} + + do_test 1.$tn.1.2 { + sql2 { CREATE TABLE t2(x) } + sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } + sql2 { DROP TABLE t2 } + sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {t1 t2} + + + # INDEX objects. + # + do_test 1.$tn.2 { + sql2 { CREATE INDEX i1 ON t1(a) } + sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } + sql2 { DROP INDEX i1 } + sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } + sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } + } {i1} + + # VIEW objects. + # + do_test 1.$tn.3 { + sql2 { CREATE VIEW v1 AS SELECT * FROM t1 } + sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } + sql2 { DROP VIEW v1 } + sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } + } {v1} + + # TRIGGER objects. + # + do_test $tn.4 { + sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql2 { DROP TRIGGER tr1 } + sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } + } {tr1} + } + + # This block of tests is targeted at DROP XXX IF EXISTS statements. + # + do_multiclient_test tn { + + # TABLE objects. + # + do_test 2.$tn.1 { + if {$jm == "wal"} { sql1 { PRAGMA journal_mode = WAL } } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { CREATE TABLE t1(x) } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {} + + # INDEX objects. + # + do_test 2.$tn.2 { + sql1 { CREATE TABLE t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { CREATE INDEX i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } + } {} + + # VIEW objects. + # + do_test 2.$tn.3 { + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { CREATE VIEW v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } + } {} + + # TRIGGER objects. + # + do_test 2.$tn.4 { + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } + } {} + } + + # This block of tests is targeted at DROP XXX IF EXISTS statements with + # attached databases. + # + do_multiclient_test tn { + + forcedelete test.db2 + do_test 3.$tn.0 { + sql1 { ATTACH 'test.db2' AS aux } + sql2 { ATTACH 'test.db2' AS aux } + } {} + + # TABLE objects. + # + do_test 3.$tn.1.1 { + sql1 { DROP TABLE IF EXISTS aux.t1 } + sql2 { CREATE TABLE aux.t1(x) } + sql1 { DROP TABLE IF EXISTS aux.t1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } + } {} + do_test 3.$tn.1.2 { + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { CREATE TABLE aux.t1(x) } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } + } {} + + # INDEX objects. + # + do_test 3.$tn.2.1 { + sql1 { CREATE TABLE aux.t2(x) } + sql1 { DROP INDEX IF EXISTS aux.i2 } + sql2 { CREATE INDEX aux.i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS aux.i2 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } + } {} + do_test 3.$tn.2.2 { + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { CREATE INDEX aux.i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } + } {} + + # VIEW objects. + # + do_test 3.$tn.3.1 { + sql1 { DROP VIEW IF EXISTS aux.v1 } + sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS aux.v1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } + } {} + do_test 3.$tn.3.2 { + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } + } {} + + # TRIGGER objects. + # + do_test 3.$tn.4.1 { + sql1 { DROP TRIGGER IF EXISTS aux.tr1 } + sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS aux.tr1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } + } {} + do_test 3.$tn.4.2 { + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } + } {} + } +} + + +finish_test diff --git a/test/expr.test b/test/expr.test index 140ab8d..5a3d167 100644 --- a/test/expr.test +++ b/test/expr.test @@ -82,8 +82,18 @@ test_expr expr-1.43 {i1=1, i2=2} {i1&i2} {0} test_expr expr-1.43b {i1=1, i2=2} {4&5} {4} test_expr expr-1.44 {i1=1} {~i1} {-2} test_expr expr-1.44b {i1=NULL} {~i1} {{}} -test_expr expr-1.45 {i1=1, i2=3} {i1<>i2} {4} +test_expr expr-1.45a {i1=1, i2=3} {i1<>i2} {8} +test_expr expr-1.45c {i1=1, i2=0} {i1<>i2} {0} +test_expr expr-1.46a {i1=32, i2=3} {i1>>i2} {4} +test_expr expr-1.46b {i1=32, i2=6} {i1>>i2} {0} +test_expr expr-1.46c {i1=-32, i2=3} {i1>>i2} {-4} +test_expr expr-1.46d {i1=-32, i2=100} {i1>>i2} {-1} +test_expr expr-1.46e {i1=32, i2=-3} {i1>>i2} {256} test_expr expr-1.47 {i1=9999999999, i2=8888888888} {i1i2} 1 @@ -154,10 +164,10 @@ ifcapable floatingpoint { } if {[working_64bit_int]} { - test_expr expr-1.106 {i1=0} {(1<<63)/-1} -9223372036854775808 + test_expr expr-1.106 {i1=0} {-9223372036854775808/-1} 9.22337203685478e+18 } -test_expr expr-1.107 {i1=0} {(1<<63)%-1} 0 +test_expr expr-1.107 {i1=0} {-9223372036854775808%-1} 0 test_expr expr-1.108 {i1=0} {1%0} {{}} test_expr expr-1.109 {i1=0} {1/0} {{}} @@ -190,6 +200,107 @@ test_expr expr-1.125 {i1=6, i2=NULL} \ test_expr expr-1.126 {i1=8, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no +ifcapable floatingpoint {if {[working_64bit_int]} { + test_expr expr-1.200\ + {i1=9223372036854775806, i2=1} {i1+i2} 9223372036854775807 + test_expr expr-1.201\ + {i1=9223372036854775806, i2=2} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.202\ + {i1=9223372036854775806, i2=100000} {i1+i2} 9.22337203685488e+18 + test_expr expr-1.203\ + {i1=9223372036854775807, i2=0} {i1+i2} 9223372036854775807 + test_expr expr-1.204\ + {i1=9223372036854775807, i2=1} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.205\ + {i2=9223372036854775806, i1=1} {i1+i2} 9223372036854775807 + test_expr expr-1.206\ + {i2=9223372036854775806, i1=2} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.207\ + {i2=9223372036854775806, i1=100000} {i1+i2} 9.22337203685488e+18 + test_expr expr-1.208\ + {i2=9223372036854775807, i1=0} {i1+i2} 9223372036854775807 + test_expr expr-1.209\ + {i2=9223372036854775807, i1=1} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.210\ + {i1=-9223372036854775807, i2=-1} {i1+i2} -9223372036854775808 + test_expr expr-1.211\ + {i1=-9223372036854775807, i2=-2} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.212\ + {i1=-9223372036854775807, i2=-100000} {i1+i2} -9.22337203685488e+18 + test_expr expr-1.213\ + {i1=-9223372036854775808, i2=0} {i1+i2} -9223372036854775808 + test_expr expr-1.214\ + {i1=-9223372036854775808, i2=-1} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.215\ + {i2=-9223372036854775807, i1=-1} {i1+i2} -9223372036854775808 + test_expr expr-1.216\ + {i2=-9223372036854775807, i1=-2} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.217\ + {i2=-9223372036854775807, i1=-100000} {i1+i2} -9.22337203685488e+18 + test_expr expr-1.218\ + {i2=-9223372036854775808, i1=0} {i1+i2} -9223372036854775808 + test_expr expr-1.219\ + {i2=-9223372036854775808, i1=-1} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.220\ + {i1=9223372036854775806, i2=-1} {i1-i2} 9223372036854775807 + test_expr expr-1.221\ + {i1=9223372036854775806, i2=-2} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.222\ + {i1=9223372036854775806, i2=-100000} {i1-i2} 9.22337203685488e+18 + test_expr expr-1.223\ + {i1=9223372036854775807, i2=0} {i1-i2} 9223372036854775807 + test_expr expr-1.224\ + {i1=9223372036854775807, i2=-1} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.225\ + {i2=-9223372036854775806, i1=1} {i1-i2} 9223372036854775807 + test_expr expr-1.226\ + {i2=-9223372036854775806, i1=2} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.227\ + {i2=-9223372036854775806, i1=100000} {i1-i2} 9.22337203685488e+18 + test_expr expr-1.228\ + {i2=-9223372036854775807, i1=0} {i1-i2} 9223372036854775807 + test_expr expr-1.229\ + {i2=-9223372036854775807, i1=1} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.230\ + {i1=-9223372036854775807, i2=1} {i1-i2} -9223372036854775808 + test_expr expr-1.231\ + {i1=-9223372036854775807, i2=2} {i1-i2} -9.22337203685478e+18 + test_expr expr-1.232\ + {i1=-9223372036854775807, i2=100000} {i1-i2} -9.22337203685488e+18 + test_expr expr-1.233\ + {i1=-9223372036854775808, i2=0} {i1-i2} -9223372036854775808 + test_expr expr-1.234\ + {i1=-9223372036854775808, i2=1} {i1-i2} -9.22337203685478e+18 + test_expr expr-1.235\ + {i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808 + test_expr expr-1.236\ + {i2=9223372036854775807, i1=-2} {i1-i2} -9.22337203685478e+18 + test_expr expr-1.237\ + {i2=9223372036854775807, i1=-100000} {i1-i2} -9.22337203685488e+18 + test_expr expr-1.238\ + {i2=9223372036854775807, i1=0} {i1-i2} -9223372036854775807 + test_expr expr-1.239\ + {i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808 + + test_expr expr-1.250\ + {i1=4294967296, i2=2147483648} {i1*i2} 9.22337203685478e+18 + test_expr expr-1.251\ + {i1=4294967296, i2=2147483647} {i1*i2} 9223372032559808512 + test_expr expr-1.252\ + {i1=-4294967296, i2=2147483648} {i1*i2} -9223372036854775808 + test_expr expr-1.253\ + {i1=-4294967296, i2=2147483647} {i1*i2} -9223372032559808512 + test_expr expr-1.254\ + {i1=4294967296, i2=-2147483648} {i1*i2} -9223372036854775808 + test_expr expr-1.255\ + {i1=4294967296, i2=-2147483647} {i1*i2} -9223372032559808512 + test_expr expr-1.256\ + {i1=-4294967296, i2=-2147483648} {i1*i2} 9.22337203685478e+18 + test_expr expr-1.257\ + {i1=-4294967296, i2=-2147483647} {i1*i2} 9223372032559808512 + +}} + ifcapable floatingpoint { test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57 test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11 diff --git a/test/filefmt.test b/test/filefmt.test index 16b4596..72edbee 100644 --- a/test/filefmt.test +++ b/test/filefmt.test @@ -193,4 +193,24 @@ db close sqlite3 db test.db integrity_check filefmt-2.2.7 +#-------------------------------------------------------------------------- +# Check that ticket 89b8c9ac54 is fixed. Before the fix, the SELECT +# statement would return SQLITE_CORRUPT. The database file was not actually +# corrupted, but SQLite was reporting that it was. +# +db close +forcedelete test.db +sqlite3 db test.db +do_execsql_test filefmt-3.1 { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(a, b); +} {} +do_test filefmt-3.2 { + sql36231 { DROP TABLE t1 } +} {} +do_execsql_test filefmt-3.3 { + SELECT * FROM sqlite_master; + PRAGMA integrity_check; +} {ok} + finish_test diff --git a/test/fkey4.test b/test/fkey4.test new file mode 100644 index 0000000..d6dd2fc --- /dev/null +++ b/test/fkey4.test @@ -0,0 +1,55 @@ +# 2011 Feb 04 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file test deferred foreign key constraint processing to make +# sure that when a statement not within BEGIN...END fails a constraint, +# that statement doesn't hold the transaction open thus allowing +# a subsequent statement to fail a deferred constraint with impunity. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable {!foreignkey||!trigger} { + finish_test + return +} + +# Create a table and some data to work with. +# +do_test fkey4-1.1 { + execsql { + PRAGMA foreign_keys = ON; + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(c REFERENCES t1 DEFERRABLE INITIALLY DEFERRED, d); + INSERT INTO t1 VALUES(1,2); + INSERT INTO t2 VALUES(1,3); + } +} {} + +do_test fkey4-1.2 { + set ::DB [sqlite3_connection_pointer db] + set ::SQL {INSERT INTO t2 VALUES(2,4)} + set ::STMT1 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] + sqlite3_step $::STMT1 +} {SQLITE_CONSTRAINT} +do_test fkey4-1.3 { + set ::STMT2 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] + sqlite3_step $::STMT2 +} {SQLITE_CONSTRAINT} +do_test fkey4-1.4 { + db eval {SELECT * FROM t2} +} {1 3} +sqlite3_finalize $::STMT1 +sqlite3_finalize $::STMT2 + +finish_test diff --git a/test/fts3aux1.test b/test/fts3aux1.test new file mode 100644 index 0000000..5359521 --- /dev/null +++ b/test/fts3aux1.test @@ -0,0 +1,462 @@ +# 2011 January 27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# This file implements regression tests for SQLite library. The +# focus of this script is testing the FTS3 module. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable !fts3 { finish_test ; return } +set ::testprefix fts3aux1 + +do_execsql_test 1.1 { + CREATE VIRTUAL TABLE t1 USING fts4; + INSERT INTO t1 VALUES('one two three four'); + INSERT INTO t1 VALUES('three four five six'); + INSERT INTO t1 VALUES('one three five seven'); + + CREATE VIRTUAL TABLE terms USING fts4aux(t1); + SELECT term, documents, occurrences FROM terms WHERE col = '*'; +} { + five 2 2 four 2 2 one 2 2 seven 1 1 + six 1 1 three 3 3 two 1 1 +} + +do_execsql_test 1.2 { + INSERT INTO t1 VALUES('one one one three three three'); + SELECT term, documents, occurrences FROM terms WHERE col = '*'; +} { + five 2 2 four 2 2 one 3 5 seven 1 1 + six 1 1 three 4 6 two 1 1 +} + +do_execsql_test 1.3 { + DELETE FROM t1; + SELECT term, documents, occurrences FROM terms WHERE col = '*'; +} {} + +do_execsql_test 1.4 { + INSERT INTO t1 VALUES('a b a b a b a'); + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + SELECT term, documents, occurrences FROM terms WHERE col = '*'; +} {a 256 1024 b 256 768} + +#------------------------------------------------------------------------- +# The following tests verify that the fts4aux module uses the full-text +# index to reduce the number of rows scanned in the following circumstances: +# +# * when there is equality comparison against the term column using the +# BINARY collating sequence. +# +# * when there is a range constraint on the term column using the BINARY +# collating sequence. +# +# And also uses the full-text index to optimize ORDER BY clauses of the +# form "ORDER BY term ASC" or equivalent. +# +# Test organization is: +# +# fts3aux1-2.1.*: equality constraints. +# fts3aux1-2.2.*: range constraints. +# fts3aux1-2.3.*: ORDER BY optimization. +# + +do_execsql_test 2.0 { + DROP TABLE t1; + DROP TABLE terms; + + CREATE VIRTUAL TABLE x1 USING fts4(x); + INSERT INTO x1(x1) VALUES('nodesize=24'); + CREATE VIRTUAL TABLE terms USING fts4aux(x1); + + CREATE VIEW terms_v AS + SELECT term, documents, occurrences FROM terms WHERE col = '*'; + + INSERT INTO x1 VALUES('braes brag bragged bragger bragging'); + INSERT INTO x1 VALUES('brags braid braided braiding braids'); + INSERT INTO x1 VALUES('brain brainchild brained braining brains'); + INSERT INTO x1 VALUES('brainstem brainstems brainstorm brainstorms'); +} + +proc rec {varname x} { + global $varname + incr $varname + return 1 +} +db func rec rec + +# Use EQP to show that the WHERE expression "term='braid'" uses a different +# index number (1) than "+term='braid'" (0). +# +do_execsql_test 2.1.1.1 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term='braid' +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} } +do_execsql_test 2.1.1.2 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term='braid' +} {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}} + +# Now show that using "term='braid'" means the virtual table returns +# only 1 row to SQLite, but "+term='braid'" means all 19 are returned. +# +do_test 2.1.2.1 { + set cnt 0 + execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND term='braid' } + set cnt +} {2} +do_test 2.1.2.2 { + set cnt 0 + execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND +term='braid' } + set cnt +} {38} + +# Similar to the test immediately above, but using a term ("breakfast") that +# is not featured in the dataset. +# +do_test 2.1.3.1 { + set cnt 0 + execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND term='breakfast' } + set cnt +} {0} +do_test 2.1.3.2 { + set cnt 0 + execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND +term='breakfast' } + set cnt +} {38} + +do_execsql_test 2.1.4.1 { SELECT * FROM terms_v WHERE term='braid' } {braid 1 1} +do_execsql_test 2.1.4.2 { SELECT * FROM terms_v WHERE +term='braid'} {braid 1 1} +do_execsql_test 2.1.4.3 { SELECT * FROM terms_v WHERE term='breakfast' } {} +do_execsql_test 2.1.4.4 { SELECT * FROM terms_v WHERE +term='breakfast' } {} + +do_execsql_test 2.1.4.5 { SELECT * FROM terms_v WHERE term='cba' } {} +do_execsql_test 2.1.4.6 { SELECT * FROM terms_v WHERE +term='cba' } {} +do_execsql_test 2.1.4.7 { SELECT * FROM terms_v WHERE term='abc' } {} +do_execsql_test 2.1.4.8 { SELECT * FROM terms_v WHERE +term='abc' } {} + +# Special case: term=NULL +# +do_execsql_test 2.1.5 { SELECT * FROM terms WHERE term=NULL } {} + +do_execsql_test 2.2.1.1 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term>'brain' +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2: (~0 rows)} } +do_execsql_test 2.2.1.2 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term>'brain' +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } + +do_execsql_test 2.2.1.3 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term<'brain' +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4: (~0 rows)} } +do_execsql_test 2.2.1.4 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term<'brain' +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } + +do_execsql_test 2.2.1.5 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term BETWEEN 'brags' AND 'brain' +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6: (~0 rows)} } +do_execsql_test 2.2.1.6 { + EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term BETWEEN 'brags' AND 'brain' +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } + +do_test 2.2.2.1 { + set cnt 0 + execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term>'brain' } + set cnt +} {18} +do_test 2.2.2.2 { + set cnt 0 + execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term>'brain' } + set cnt +} {38} +do_execsql_test 2.2.2.3 { + SELECT term, documents, occurrences FROM terms_v WHERE term>'brain' +} { + brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 + brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 +} +do_execsql_test 2.2.2.4 { + SELECT term, documents, occurrences FROM terms_v WHERE +term>'brain' +} { + brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 + brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 +} +do_execsql_test 2.2.2.5 { + SELECT term, documents, occurrences FROM terms_v WHERE term>='brain' +} { + brain 1 1 + brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 + brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 +} +do_execsql_test 2.2.2.6 { + SELECT term, documents, occurrences FROM terms_v WHERE +term>='brain' +} { + brain 1 1 + brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 + brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 +} + +do_execsql_test 2.2.2.7 { + SELECT term, documents, occurrences FROM terms_v WHERE term>='abc' +} { + braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 + bragging 1 1 brags 1 1 braid 1 1 braided 1 1 + braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1 + brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 + brainstems 1 1 brainstorm 1 1 brainstorms 1 1 +} +do_execsql_test 2.2.2.8 { + SELECT term, documents, occurrences FROM terms_v WHERE +term>='abc' +} { + braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 + bragging 1 1 brags 1 1 braid 1 1 braided 1 1 + braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1 + brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 + brainstems 1 1 brainstorm 1 1 brainstorms 1 1 +} + +do_execsql_test 2.2.2.9 { + SELECT term, documents, occurrences FROM terms_v WHERE term>='brainstorms' +} {brainstorms 1 1} +do_execsql_test 2.2.2.10 { + SELECT term, documents, occurrences FROM terms_v WHERE term>='brainstorms' +} {brainstorms 1 1} +do_execsql_test 2.2.2.11 { SELECT * FROM terms_v WHERE term>'brainstorms' } {} +do_execsql_test 2.2.2.12 { SELECT * FROM terms_v WHERE term>'brainstorms' } {} + +do_execsql_test 2.2.2.13 { SELECT * FROM terms_v WHERE term>'cba' } {} +do_execsql_test 2.2.2.14 { SELECT * FROM terms_v WHERE term>'cba' } {} + +do_test 2.2.3.1 { + set cnt 0 + execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term<'brain' } + set cnt +} {22} +do_test 2.2.3.2 { + set cnt 0 + execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term<'brain' } + set cnt +} {38} +do_execsql_test 2.2.3.3 { + SELECT term, documents, occurrences FROM terms_v WHERE term<'brain' +} { + braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 + brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 +} +do_execsql_test 2.2.3.4 { + SELECT term, documents, occurrences FROM terms_v WHERE +term<'brain' +} { + braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 + brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 +} +do_execsql_test 2.2.3.5 { + SELECT term, documents, occurrences FROM terms_v WHERE term<='brain' +} { + braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 + brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 + brain 1 1 +} +do_execsql_test 2.2.3.6 { + SELECT term, documents, occurrences FROM terms_v WHERE +term<='brain' +} { + braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 + brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 + brain 1 1 +} + +do_test 2.2.4.1 { + set cnt 0 + execsql { + SELECT term, documents, occurrences FROM terms + WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain' + } + set cnt +} {12} +do_test 2.2.4.2 { + set cnt 0 + execsql { + SELECT term, documents, occurrences FROM terms + WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain' + } + set cnt +} {38} +do_execsql_test 2.2.4.3 { + SELECT term, documents, occurrences FROM terms_v + WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain' +} { + brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 +} +do_execsql_test 2.2.4.4 { + SELECT term, documents, occurrences FROM terms_v + WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain' +} { + brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 +} +do_execsql_test 2.2.4.5 { + SELECT term, documents, occurrences FROM terms_v + WHERE rec('cnt', term) AND term > 'brags' AND term < 'brain' +} { + braid 1 1 braided 1 1 braiding 1 1 braids 1 1 +} +do_execsql_test 2.2.4.6 { + SELECT term, documents, occurrences FROM terms_v + WHERE rec('cnt', term) AND +term > 'brags' AND +term < 'brain' +} { + braid 1 1 braided 1 1 braiding 1 1 braids 1 1 +} + +# Check that "ORDER BY term ASC" and equivalents are sorted by the +# virtual table implementation. Any other ORDER BY clause requires +# SQLite to sort results using a temporary b-tree. +# +foreach {tn sort orderby} { + 1 0 "ORDER BY term ASC" + 2 0 "ORDER BY term" + 3 1 "ORDER BY term DESC" + 4 1 "ORDER BY documents ASC" + 5 1 "ORDER BY documents" + 6 1 "ORDER BY documents DESC" + 7 1 "ORDER BY occurrences ASC" + 8 1 "ORDER BY occurrences" + 9 1 "ORDER BY occurrences DESC" +} { + + set res [list 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}] + if {$sort} { lappend res 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } + + set sql "SELECT * FROM terms $orderby" + do_execsql_test 2.3.1.$tn "EXPLAIN QUERY PLAN $sql" $res +} + +#------------------------------------------------------------------------- +# The next set of tests, fts3aux1-3.*, test error conditions in the +# fts4aux module. Except, fault injection testing (OOM, IO error etc.) is +# done in fts3fault2.test +# + +do_execsql_test 3.1.1 { + CREATE VIRTUAL TABLE t2 USING fts4; +} + +do_catchsql_test 3.1.2 { + CREATE VIRTUAL TABLE terms2 USING fts4aux; +} {1 {wrong number of arguments to fts4aux constructor}} +do_catchsql_test 3.1.3 { + CREATE VIRTUAL TABLE terms2 USING fts4aux(t2, t2); +} {1 {wrong number of arguments to fts4aux constructor}} + +do_execsql_test 3.2.1 { + CREATE VIRTUAL TABLE terms3 USING fts4aux(does_not_exist) +} +do_catchsql_test 3.2.2 { + SELECT * FROM terms3 +} {1 {SQL logic error or missing database}} +do_catchsql_test 3.2.3 { + SELECT * FROM terms3 WHERE term = 'abc' +} {1 {SQL logic error or missing database}} + +do_catchsql_test 3.3.1 { + INSERT INTO terms VALUES(1,2,3); +} {1 {table terms may not be modified}} +do_catchsql_test 3.3.2 { + DELETE FROM terms +} {1 {table terms may not be modified}} +do_catchsql_test 3.3.3 { + UPDATE terms set documents = documents+1; +} {1 {table terms may not be modified}} + + +#------------------------------------------------------------------------- +# The following tests - fts4aux-4.* - test that joins work with fts4aux +# tables. And that fts4aux provides reasonably sane cost information via +# xBestIndex to the query planner. +# +db close +forcedelete test.db +sqlite3 db test.db +do_execsql_test 4.1 { + CREATE VIRTUAL TABLE x1 USING fts4(x); + CREATE VIRTUAL TABLE terms USING fts4aux(x1); + CREATE TABLE x2(y); + CREATE TABLE x3(y); + CREATE INDEX i1 ON x3(y); + + INSERT INTO x1 VALUES('a b c d e'); + INSERT INTO x1 VALUES('f g h i j'); + INSERT INTO x1 VALUES('k k l l a'); + + INSERT INTO x2 SELECT term FROM terms WHERE col = '*'; + INSERT INTO x3 SELECT term FROM terms WHERE col = '*'; +} + +proc do_plansql_test {tn sql r} { + uplevel do_execsql_test $tn [list "EXPLAIN QUERY PLAN $sql ; $sql"] [list $r] +} + +do_plansql_test 4.2 { + SELECT y FROM x2, terms WHERE y = term AND col = '*' +} { + 0 0 0 {SCAN TABLE x2 (~1000000 rows)} + 0 1 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} + a b c d e f g h i j k l +} + +do_plansql_test 4.3 { + SELECT y FROM terms, x2 WHERE y = term AND col = '*' +} { + 0 0 1 {SCAN TABLE x2 (~1000000 rows)} + 0 1 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} + a b c d e f g h i j k l +} + +do_plansql_test 4.4 { + SELECT y FROM x3, terms WHERE y = term AND col = '*' +} { + 0 0 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} + 0 1 0 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} + a b c d e f g h i j k l +} + +do_plansql_test 4.5 { + SELECT y FROM terms, x3 WHERE y = term AND occurrences>1 AND col = '*' +} { + 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} + 0 1 1 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} + a k l +} + +#------------------------------------------------------------------------- +# The following tests check that fts4aux can handle an fts table with an +# odd name (one that requires quoting for use in SQL statements). And that +# the argument to the fts4aux constructor is properly dequoted before use. +# +# +do_execsql_test 5.1 { + CREATE VIRTUAL TABLE "abc '!' def" USING fts4(x, y); + INSERT INTO "abc '!' def" VALUES('XX', 'YY'); + + CREATE VIRTUAL TABLE terms3 USING fts4aux("abc '!' def"); + SELECT * FROM terms3; +} {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} + +do_execsql_test 5.2 { + CREATE VIRTUAL TABLE "%%^^%%" USING fts4aux('abc ''!'' def'); + SELECT * FROM "%%^^%%"; +} {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} + + +finish_test diff --git a/test/fts3comp1.test b/test/fts3comp1.test new file mode 100644 index 0000000..9f13aaa --- /dev/null +++ b/test/fts3comp1.test @@ -0,0 +1,115 @@ +# 2011 January 27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# This file implements regression tests for SQLite library. The +# focus of this script is testing the FTS3 module. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable !fts3 { finish_test ; return } +set ::testprefix fts3comp1 + +# Create a pretend compression system. +# +# Each time the [zip] function is called, an entry is added to the ::strings +# array mapping from an integer key to the string argument to zip. The key +# is returned. Later on, when the key is passed to [unzip], the original +# string is retrieved from the ::strings array and returned. +# +set next_x 0 +proc zip {x} { + incr ::next_x + set ::strings($::next_x) $x + return $::next_x +} +proc unzip {x} { + return $::strings($x) +} + +foreach {tn zip unzip} { + 1 zip unzip + 2 {z.i.p!!} {un "zip"} +} { + + set next_x 0 + catch {db close} + forcedelete test.db + sqlite3 db test.db + db func $zip zip + db func $unzip unzip + + # Create a table that uses zip/unzip. Check that content inserted into + # the table can be read back (using a full-scan query). Check that the + # underlying %_content table contains the compressed (integer) values. + # + do_execsql_test 1.$tn.0 " + CREATE VIRTUAL TABLE t1 USING fts4( + a, b, + compress='$zip', uncompress='$unzip' + ); + " + do_execsql_test 1.$tn.1 { + INSERT INTO t1 VALUES('one two three', 'two four six'); + SELECT a, b FROM t1; + } {{one two three} {two four six}} + do_execsql_test 1.$tn.2 { + SELECT c0a, c1b FROM t1_content; + } {1 2} + + # Insert another row and check that it can be read back. Also that the + # %_content table still contains all compressed content. This time, try + # full-text index and by-docid queries too. + # + do_execsql_test 1.$tn.3 { + INSERT INTO t1 VALUES('three six nine', 'four eight twelve'); + SELECT a, b FROM t1; + } {{one two three} {two four six} {three six nine} {four eight twelve}} + do_execsql_test 1.$tn.4 { + SELECT c0a, c1b FROM t1_content; + } {1 2 3 4} + + do_execsql_test 1.$tn.5 { + SELECT a, b FROM t1 WHERE docid = 2 + } {{three six nine} {four eight twelve}} + do_execsql_test 1.$tn.6 { + SELECT a, b FROM t1 WHERE t1 MATCH 'two' + } {{one two three} {two four six}} + + # Delete a row and check that the full-text index is correctly updated. + # Inspect the full-text index using an fts4aux table. + # + do_execsql_test 1.$tn.7 { + CREATE VIRTUAL TABLE terms USING fts4aux(t1); + SELECT term, documents, occurrences FROM terms WHERE col = '*'; + } { + eight 1 1 four 2 2 nine 1 1 one 1 1 + six 2 2 three 2 2 twelve 1 1 two 1 2 + } + do_execsql_test 1.$tn.8 { + DELETE FROM t1 WHERE docid = 1; + SELECT term, documents, occurrences FROM terms WHERE col = '*'; + } { + eight 1 1 four 1 1 nine 1 1 + six 1 1 three 1 1 twelve 1 1 + } + do_execsql_test 1.$tn.9 { SELECT c0a, c1b FROM t1_content } {3 4} +} + +# Test that is an error to specify just one of compress and uncompress. +# +do_catchsql_test 2.1 { + CREATE VIRTUAL TABLE t2 USING fts4(x, compress=zip) +} {1 {missing uncompress parameter in fts4 constructor}} +do_catchsql_test 2.2 { + CREATE VIRTUAL TABLE t2 USING fts4(x, uncompress=unzip) +} {1 {missing compress parameter in fts4 constructor}} + +finish_test diff --git a/test/fts3corrupt.test b/test/fts3corrupt.test index c2889b8..b8b45c3 100644 --- a/test/fts3corrupt.test +++ b/test/fts3corrupt.test @@ -131,5 +131,32 @@ do_catchsql_test 4.3 { SELECT rowid FROM t1 WHERE t1 MATCH 'world'; } {1 {database disk image is malformed}} +# Test a special kind of corruption, where the %_stat table contains +# an invalid entry. At one point this could lead to a division-by-zero +# error in fts4. +# +do_execsql_test 5.0 { + DROP TABLE t1; + CREATE VIRTUAL TABLE t1 USING fts4; +} +do_test 5.1 { + db func nn nn + execsql BEGIN + execsql { INSERT INTO t1 VALUES('one') } + execsql { INSERT INTO t1 VALUES('two') } + execsql { INSERT INTO t1 VALUES('three') } + execsql { INSERT INTO t1 VALUES('four') } + execsql COMMIT +} {} +do_catchsql_test 5.2 { + UPDATE t1_stat SET value = X'0000'; + SELECT matchinfo(t1, 'nxa') FROM t1 WHERE t1 MATCH 't*'; +} {1 {database disk image is malformed}} +do_catchsql_test 5.3 { + UPDATE t1_stat SET value = NULL; + SELECT matchinfo(t1, 'nxa') FROM t1 WHERE t1 MATCH 't*'; +} {1 {database disk image is malformed}} + + finish_test diff --git a/test/fts3defer2.test b/test/fts3defer2.test index 142c92a..8441803 100644 --- a/test/fts3defer2.test +++ b/test/fts3defer2.test @@ -80,7 +80,7 @@ do_execsql_test 2.1.2 "INSERT INTO t2 VALUES('[string repeat {a } 10000]')" do_execsql_test 2.1.3 "INSERT INTO t2 VALUES('b [string repeat {z } 10000]')" do_execsql_test 2.1.4 [string repeat "INSERT INTO t2 VALUES('x');" 50] do_execsql_test 2.1.5 { - INSERT INTO t2 VALUES('a b c d e f g'); + INSERT INTO t2 VALUES('a b c d e f g z'); INSERT INTO t2 VALUES('a b c d e f g'); } foreach {tn sql} { @@ -92,12 +92,21 @@ foreach {tn sql} { } { execsql $sql - do_execsql_test 2.2.$tn { + do_execsql_test 2.2.$tn.1 { SELECT mit(matchinfo(t2, 'pcxnal')) FROM t2 WHERE t2 MATCH 'a b'; } [list \ - [list 2 1 1 54 54 1 3 3 54 372 7] \ + [list 2 1 1 54 54 1 3 3 54 372 8] \ [list 2 1 1 54 54 1 3 3 54 372 7] \ ] + + set sqlite_fts3_enable_parentheses 1 + do_execsql_test 2.2.$tn.2 { + SELECT mit(matchinfo(t2, 'x')) FROM t2 WHERE t2 MATCH 'g OR (g z)'; + } [list \ + [list 1 2 2 1 2 2 1 54 54] \ + [list 1 2 2 1 2 2 0 54 54] \ + ] + set sqlite_fts3_enable_parentheses 0 } do_execsql_test 2.3.1 { diff --git a/test/fts3fault2.test b/test/fts3fault2.test new file mode 100644 index 0000000..fb87773 --- /dev/null +++ b/test/fts3fault2.test @@ -0,0 +1,85 @@ +# 2011 February 3 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix fts3fault2 + +# If SQLITE_ENABLE_FTS3 is not defined, omit this file. +ifcapable !fts3 { finish_test ; return } + +do_test 1.0 { + execsql { + CREATE VIRTUAL TABLE t1 USING fts4(x); + INSERT INTO t1 VALUES('a b c'); + INSERT INTO t1 VALUES('c d e'); + CREATE VIRTUAL TABLE terms USING fts4aux(t1); + } + faultsim_save_and_close +} {} + +do_faultsim_test 1.1 -prep { + faultsim_restore_and_reopen + db eval {SELECT * FROM sqlite_master} +} -body { + execsql "CREATE VIRTUAL TABLE terms2 USING fts4aux(t1)" +} -test { + faultsim_test_result {0 {}} +} + +do_faultsim_test 1.2 -prep { + faultsim_restore_and_reopen + db eval {SELECT * FROM sqlite_master} +} -body { + execsql "SELECT * FROM terms" +} -test { + faultsim_test_result {0 {a * 1 1 a 0 1 1 b * 1 1 b 0 1 1 c * 2 2 c 0 2 2 d * 1 1 d 0 1 1 e * 1 1 e 0 1 1}} +} + +do_faultsim_test 1.3 -prep { + faultsim_restore_and_reopen + db eval {SELECT * FROM sqlite_master} +} -body { + execsql "SELECT * FROM terms WHERE term>'a' AND TERM < 'd'" +} -test { + faultsim_test_result {0 {b * 1 1 b 0 1 1 c * 2 2 c 0 2 2}} +} + +do_faultsim_test 1.4 -prep { + faultsim_restore_and_reopen + db eval {SELECT * FROM sqlite_master} +} -body { + execsql "SELECT * FROM terms WHERE term='c'" +} -test { + faultsim_test_result {0 {c * 2 2 c 0 2 2}} +} + +do_test 2.0 { + faultsim_delete_and_reopen + execsql { + CREATE VIRTUAL TABLE tx USING fts4(a, b); + INSERT INTO tx VALUES('a b c', 'x y z'); + CREATE VIRTUAL TABLE terms2 USING fts4aux(tx); + } + faultsim_save_and_close +} {} + +do_faultsim_test 2.1 -prep { + faultsim_restore_and_reopen + db eval {SELECT * FROM sqlite_master} +} -body { + execsql "SELECT * FROM terms2" +} -test { + faultsim_test_result {0 {a * 1 1 a 0 1 1 b * 1 1 b 0 1 1 c * 1 1 c 0 1 1 x * 1 1 x 1 1 1 y * 1 1 y 1 1 1 z * 1 1 z 1 1 1}} +} + +finish_test diff --git a/test/fuzzer1.test b/test/fuzzer1.test new file mode 100644 index 0000000..d0575d2 --- /dev/null +++ b/test/fuzzer1.test @@ -0,0 +1,1382 @@ +# 2011 March 25 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for TCL interface to the +# SQLite library. +# +# The focus of the tests is the word-fuzzer virtual table. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !vtab { + finish_test + return +} + +register_fuzzer_module db +do_test fuzzer1-1.0 { + catchsql {CREATE VIRTUAL TABLE fault1 USING fuzzer;} +} {1 {fuzzer virtual tables must be TEMP}} +do_test fuzzer1-1.1 { + db eval {CREATE VIRTUAL TABLE temp.f1 USING fuzzer;} +} {} +do_test fuzzer1-1.2 { + db eval { + INSERT INTO f1(cfrom, cto, cost) VALUES('e','a',1); + INSERT INTO f1(cfrom, cto, cost) VALUES('a','e',10); + INSERT INTO f1(cfrom, cto, cost) VALUES('e','o',100); + } +} {} + +do_test fuzzer1-1.3 { + db eval { + SELECT word, distance FROM f1 WHERE word MATCH 'abcde' + } +} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210} + +do_test fuzzer1-2.0 { + execsql { + CREATE VIRTUAL TABLE temp.f2 USING fuzzer; + -- costs based on English letter frequencies + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','e',24); + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','o',47); + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','u',50); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','a',23); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','i',33); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','o',37); + INSERT INTO f2(cFrom,cTo,cost) VALUES('i','e',33); + INSERT INTO f2(cFrom,cTo,cost) VALUES('i','y',33); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','a',41); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','e',46); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','u',57); + INSERT INTO f2(cFrom,cTo,cost) VALUES('u','o',58); + INSERT INTO f2(cFrom,cTo,cost) VALUES('y','i',33); + + INSERT INTO f2(cFrom,cTo,cost) VALUES('t','th',70); + INSERT INTO f2(cFrom,cTo,cost) VALUES('th','t',66); + + + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','',84); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','b',106); + INSERT INTO f2(cFrom,cTo,cost) VALUES('b','',106); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','c',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('c','',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','d',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('d','',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','e',83); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','',83); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','f',97); + INSERT INTO f2(cFrom,cTo,cost) VALUES('f','',97); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','g',99); + INSERT INTO f2(cFrom,cTo,cost) VALUES('g','',99); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','h',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('h','',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','i',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('i','',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','j',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('j','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','k',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('k','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','l',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('l','',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','m',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('m','',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','n',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('n','',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','o',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','p',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('p','',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','q',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('q','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','r',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('r','',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','s',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('s','',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','t',84); + INSERT INTO f2(cFrom,cTo,cost) VALUES('t','',84); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','u',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('u','',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','v',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('v','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','w',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('w','',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','x',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('x','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','y',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('y','',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','z',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('z','',120); + + -- Street names for the 28269 ZIPCODE. + -- + CREATE TEMP TABLE streetname(n TEXT UNIQUE); + INSERT INTO streetname VALUES('abbotsinch'); + INSERT INTO streetname VALUES('abbottsgate'); + INSERT INTO streetname VALUES('abbywood'); + INSERT INTO streetname VALUES('abner'); + INSERT INTO streetname VALUES('acacia ridge'); + INSERT INTO streetname VALUES('acorn creek'); + INSERT INTO streetname VALUES('acorn forest'); + INSERT INTO streetname VALUES('adel'); + INSERT INTO streetname VALUES('ainslie'); + INSERT INTO streetname VALUES('airways'); + INSERT INTO streetname VALUES('alabaster'); + INSERT INTO streetname VALUES('alba'); + INSERT INTO streetname VALUES('albertine'); + INSERT INTO streetname VALUES('alden glen'); + INSERT INTO streetname VALUES('alderson'); + INSERT INTO streetname VALUES('allen'); + INSERT INTO streetname VALUES('allen a brown'); + INSERT INTO streetname VALUES('allness glen'); + INSERT INTO streetname VALUES('aloysia'); + INSERT INTO streetname VALUES('alpine'); + INSERT INTO streetname VALUES('alwyn'); + INSERT INTO streetname VALUES('amaranthus'); + INSERT INTO streetname VALUES('amber glen'); + INSERT INTO streetname VALUES('amber leigh way'); + INSERT INTO streetname VALUES('amber meadows'); + INSERT INTO streetname VALUES('amberway'); + INSERT INTO streetname VALUES('ame'); + INSERT INTO streetname VALUES('amesbury hill'); + INSERT INTO streetname VALUES('anderson'); + INSERT INTO streetname VALUES('andrew thomas'); + INSERT INTO streetname VALUES('anduin falls'); + INSERT INTO streetname VALUES('ankeny'); + INSERT INTO streetname VALUES('annandale'); + INSERT INTO streetname VALUES('annbick'); + INSERT INTO streetname VALUES('antelope'); + INSERT INTO streetname VALUES('anzack'); + INSERT INTO streetname VALUES('apple glen'); + INSERT INTO streetname VALUES('applevalley'); + INSERT INTO streetname VALUES('appley mead'); + INSERT INTO streetname VALUES('aragorn'); + INSERT INTO streetname VALUES('arbor creek'); + INSERT INTO streetname VALUES('arbor day'); + INSERT INTO streetname VALUES('arbor meadows'); + INSERT INTO streetname VALUES('arbor spring'); + INSERT INTO streetname VALUES('arborview'); + INSERT INTO streetname VALUES('arklow'); + INSERT INTO streetname VALUES('armitage'); + INSERT INTO streetname VALUES('arvin'); + INSERT INTO streetname VALUES('ash cove'); + INSERT INTO streetname VALUES('ashford leigh'); + INSERT INTO streetname VALUES('ashmont'); + INSERT INTO streetname VALUES('atlas'); + INSERT INTO streetname VALUES('atwater'); + INSERT INTO streetname VALUES('auburn hill'); + INSERT INTO streetname VALUES('aulton link'); + INSERT INTO streetname VALUES('austin dekota'); + INSERT INTO streetname VALUES('austin knoll'); + INSERT INTO streetname VALUES('auten'); + INSERT INTO streetname VALUES('autumn harvest'); + INSERT INTO streetname VALUES('autumn oak'); + INSERT INTO streetname VALUES('autumn ridge'); + INSERT INTO streetname VALUES('avalon forest'); + INSERT INTO streetname VALUES('avalon loop'); + INSERT INTO streetname VALUES('avon farm'); + INSERT INTO streetname VALUES('avonhurst'); + INSERT INTO streetname VALUES('avonlea'); + INSERT INTO streetname VALUES('aynrand'); + INSERT INTO streetname VALUES('azure valley'); + INSERT INTO streetname VALUES('baberton'); + INSERT INTO streetname VALUES('baffin'); + INSERT INTO streetname VALUES('baggins'); + INSERT INTO streetname VALUES('balata'); + INSERT INTO streetname VALUES('ballantray'); + INSERT INTO streetname VALUES('ballston'); + INSERT INTO streetname VALUES('balsam tree'); + INSERT INTO streetname VALUES('bambi'); + INSERT INTO streetname VALUES('banwell'); + INSERT INTO streetname VALUES('barbee'); + INSERT INTO streetname VALUES('barefoot forest'); + INSERT INTO streetname VALUES('barnview'); + INSERT INTO streetname VALUES('baroda'); + INSERT INTO streetname VALUES('barson'); + INSERT INTO streetname VALUES('baskerville'); + INSERT INTO streetname VALUES('battle creek'); + INSERT INTO streetname VALUES('baucom'); + INSERT INTO streetname VALUES('bay pines'); + INSERT INTO streetname VALUES('beaker'); + INSERT INTO streetname VALUES('beard'); + INSERT INTO streetname VALUES('beardsley'); + INSERT INTO streetname VALUES('bearoak'); + INSERT INTO streetname VALUES('beauvista'); + INSERT INTO streetname VALUES('beaver creek'); + INSERT INTO streetname VALUES('beaver hollow'); + INSERT INTO streetname VALUES('bedlington'); + INSERT INTO streetname VALUES('beech cove'); + INSERT INTO streetname VALUES('beech crest'); + INSERT INTO streetname VALUES('beith'); + INSERT INTO streetname VALUES('bell glen'); + INSERT INTO streetname VALUES('bellmore'); + INSERT INTO streetname VALUES('bells mill'); + INSERT INTO streetname VALUES('bellville'); + INSERT INTO streetname VALUES('belmar place'); + INSERT INTO streetname VALUES('bembridge'); + INSERT INTO streetname VALUES('bennett neely'); + INSERT INTO streetname VALUES('bentgrass run'); + INSERT INTO streetname VALUES('benthaven'); + INSERT INTO streetname VALUES('bernardy'); + INSERT INTO streetname VALUES('bernbrook shadow'); + INSERT INTO streetname VALUES('berrybrook'); + INSERT INTO streetname VALUES('berrybush'); + INSERT INTO streetname VALUES('berwick'); + INSERT INTO streetname VALUES('betterton'); + INSERT INTO streetname VALUES('bickham'); + INSERT INTO streetname VALUES('billingham'); + INSERT INTO streetname VALUES('birchcroft'); + INSERT INTO streetname VALUES('birchstone'); + INSERT INTO streetname VALUES('birdwell'); + INSERT INTO streetname VALUES('bisaner'); + INSERT INTO streetname VALUES('bitterbush'); + INSERT INTO streetname VALUES('bitterroot'); + INSERT INTO streetname VALUES('black fox'); + INSERT INTO streetname VALUES('black maple'); + INSERT INTO streetname VALUES('black trail'); + INSERT INTO streetname VALUES('blackbird'); + INSERT INTO streetname VALUES('blake a dare'); + INSERT INTO streetname VALUES('blasdell'); + INSERT INTO streetname VALUES('blue aster'); + INSERT INTO streetname VALUES('blue finch'); + INSERT INTO streetname VALUES('blue lilac'); + INSERT INTO streetname VALUES('blue sky'); + INSERT INTO streetname VALUES('blue tick'); + INSERT INTO streetname VALUES('bob beatty'); + INSERT INTO streetname VALUES('bobcat'); + INSERT INTO streetname VALUES('bolton'); + INSERT INTO streetname VALUES('boomerang'); + INSERT INTO streetname VALUES('boulder'); + INSERT INTO streetname VALUES('boxer'); + INSERT INTO streetname VALUES('boxmeer'); + INSERT INTO streetname VALUES('brachnell view'); + INSERT INTO streetname VALUES('bradford lake'); + INSERT INTO streetname VALUES('bradwell'); + INSERT INTO streetname VALUES('brady'); + INSERT INTO streetname VALUES('braids bend'); + INSERT INTO streetname VALUES('bralers'); + INSERT INTO streetname VALUES('brandie glen'); + INSERT INTO streetname VALUES('brandy ridge'); + INSERT INTO streetname VALUES('brandybuck'); + INSERT INTO streetname VALUES('branthurst'); + INSERT INTO streetname VALUES('brassy creek'); + INSERT INTO streetname VALUES('brathay'); + INSERT INTO streetname VALUES('brawer farm'); + INSERT INTO streetname VALUES('breezy morn'); + INSERT INTO streetname VALUES('brenda'); + INSERT INTO streetname VALUES('brenly'); + INSERT INTO streetname VALUES('brenock'); + INSERT INTO streetname VALUES('brianwood'); + INSERT INTO streetname VALUES('briar rose'); + INSERT INTO streetname VALUES('briarcrest'); + INSERT INTO streetname VALUES('briarthorne'); + INSERT INTO streetname VALUES('brick dust'); + INSERT INTO streetname VALUES('bridgepath'); + INSERT INTO streetname VALUES('bridle ridge'); + INSERT INTO streetname VALUES('briggs'); + INSERT INTO streetname VALUES('brightleaf'); + INSERT INTO streetname VALUES('brigstock'); + INSERT INTO streetname VALUES('broad ridge'); + INSERT INTO streetname VALUES('brock'); + INSERT INTO streetname VALUES('brockhampton'); + INSERT INTO streetname VALUES('broken pine'); + INSERT INTO streetname VALUES('brompton'); + INSERT INTO streetname VALUES('brook falls'); + INSERT INTO streetname VALUES('brookings'); + INSERT INTO streetname VALUES('browne'); + INSERT INTO streetname VALUES('brownes creek'); + INSERT INTO streetname VALUES('brownes ferry'); + INSERT INTO streetname VALUES('brownestone view'); + INSERT INTO streetname VALUES('brumit'); + INSERT INTO streetname VALUES('bryn athyn'); + INSERT INTO streetname VALUES('buck'); + INSERT INTO streetname VALUES('bucklebury'); + INSERT INTO streetname VALUES('buckminister'); + INSERT INTO streetname VALUES('buckspring'); + INSERT INTO streetname VALUES('burch'); + INSERT INTO streetname VALUES('burch shire'); + INSERT INTO streetname VALUES('burkston'); + INSERT INTO streetname VALUES('burmith'); + INSERT INTO streetname VALUES('burnaby'); + INSERT INTO streetname VALUES('butterfly'); + INSERT INTO streetname VALUES('cabin creek'); + INSERT INTO streetname VALUES('cairns mill'); + INSERT INTO streetname VALUES('callender'); + INSERT INTO streetname VALUES('cambellton'); + INSERT INTO streetname VALUES('cambridge bay'); + INSERT INTO streetname VALUES('canary'); + INSERT INTO streetname VALUES('canbury'); + INSERT INTO streetname VALUES('candle leaf'); + INSERT INTO streetname VALUES('canipe'); + INSERT INTO streetname VALUES('canipe farm'); + INSERT INTO streetname VALUES('cannon'); + INSERT INTO streetname VALUES('canopy'); + INSERT INTO streetname VALUES('canso'); + INSERT INTO streetname VALUES('canterbrook'); + INSERT INTO streetname VALUES('cardinal glen'); + INSERT INTO streetname VALUES('cardinal point'); + INSERT INTO streetname VALUES('cardinals nest'); + INSERT INTO streetname VALUES('carlota'); + INSERT INTO streetname VALUES('carmathen'); + INSERT INTO streetname VALUES('carver'); + INSERT INTO streetname VALUES('carver pond'); + INSERT INTO streetname VALUES('casa loma'); + INSERT INTO streetname VALUES('caselton'); + INSERT INTO streetname VALUES('castello'); + INSERT INTO streetname VALUES('castle ridge'); + INSERT INTO streetname VALUES('castleglen'); + INSERT INTO streetname VALUES('castlemaine'); + INSERT INTO streetname VALUES('cavett'); + INSERT INTO streetname VALUES('caymus'); + INSERT INTO streetname VALUES('cedardale ridge'); + INSERT INTO streetname VALUES('cedarhurst'); + INSERT INTO streetname VALUES('cemkey way'); + INSERT INTO streetname VALUES('cerise'); + INSERT INTO streetname VALUES('chaceview'); + INSERT INTO streetname VALUES('chadsworth'); + INSERT INTO streetname VALUES('chadwell'); + INSERT INTO streetname VALUES('champions crest'); + INSERT INTO streetname VALUES('chandler haven'); + INSERT INTO streetname VALUES('chapel crossing'); + INSERT INTO streetname VALUES('chapel ridge'); + INSERT INTO streetname VALUES('charles crawford'); + INSERT INTO streetname VALUES('charminster'); + INSERT INTO streetname VALUES('chasewind'); + INSERT INTO streetname VALUES('chavel'); + INSERT INTO streetname VALUES('chelsea jade'); + INSERT INTO streetname VALUES('chestnut knoll'); + INSERT INTO streetname VALUES('cheviot'); + INSERT INTO streetname VALUES('chickadee'); + INSERT INTO streetname VALUES('chidley'); + INSERT INTO streetname VALUES('chimney ridge'); + INSERT INTO streetname VALUES('chimney springs'); + INSERT INTO streetname VALUES('chinaberry'); + INSERT INTO streetname VALUES('chinemist'); + INSERT INTO streetname VALUES('chinquapin'); + INSERT INTO streetname VALUES('chiswell'); + INSERT INTO streetname VALUES('christenbury'); + INSERT INTO streetname VALUES('christenbury hills'); + INSERT INTO streetname VALUES('churchill'); + INSERT INTO streetname VALUES('cindy'); + INSERT INTO streetname VALUES('cinnamon teal'); + INSERT INTO streetname VALUES('citadel'); + INSERT INTO streetname VALUES('clare olivia'); + INSERT INTO streetname VALUES('clarke creek'); + INSERT INTO streetname VALUES('clarke ridge'); + INSERT INTO streetname VALUES('clear day'); + INSERT INTO streetname VALUES('clear stream'); + INSERT INTO streetname VALUES('cleve brown'); + INSERT INTO streetname VALUES('cliff cameron'); + INSERT INTO streetname VALUES('cliffvale'); + INSERT INTO streetname VALUES('cloverside'); + INSERT INTO streetname VALUES('clymer'); + INSERT INTO streetname VALUES('coatbridge'); + INSERT INTO streetname VALUES('cobble glen'); + INSERT INTO streetname VALUES('cochran farm'); + INSERT INTO streetname VALUES('cochrane'); + INSERT INTO streetname VALUES('coleridge'); + INSERT INTO streetname VALUES('coleshire'); + INSERT INTO streetname VALUES('collins'); + INSERT INTO streetname VALUES('colvard'); + INSERT INTO streetname VALUES('colvard park'); + INSERT INTO streetname VALUES('condor'); + INSERT INTO streetname VALUES('conner ridge'); + INSERT INTO streetname VALUES('connery'); + INSERT INTO streetname VALUES('cooper run'); + INSERT INTO streetname VALUES('coopers ridge'); + INSERT INTO streetname VALUES('copper hill'); + INSERT INTO streetname VALUES('coppermine'); + INSERT INTO streetname VALUES('cornelia'); + INSERT INTO streetname VALUES('corner'); + INSERT INTO streetname VALUES('cornerstone'); + INSERT INTO streetname VALUES('cottage oaks'); + INSERT INTO streetname VALUES('cougar'); + INSERT INTO streetname VALUES('coves end'); + INSERT INTO streetname VALUES('cragland'); + INSERT INTO streetname VALUES('crail'); + INSERT INTO streetname VALUES('cranberry nook'); + INSERT INTO streetname VALUES('crawford brook'); + INSERT INTO streetname VALUES('crayton'); + INSERT INTO streetname VALUES('creek breeze'); + INSERT INTO streetname VALUES('crescent ridge'); + INSERT INTO streetname VALUES('crescent view'); + INSERT INTO streetname VALUES('cresta'); + INSERT INTO streetname VALUES('crestfield'); + INSERT INTO streetname VALUES('crestland'); + INSERT INTO streetname VALUES('crestwick'); + INSERT INTO streetname VALUES('crisfield'); + INSERT INTO streetname VALUES('crisp wood'); + INSERT INTO streetname VALUES('croft haven'); + INSERT INTO streetname VALUES('crofton springs'); + INSERT INTO streetname VALUES('cross'); + INSERT INTO streetname VALUES('crosspoint center'); + INSERT INTO streetname VALUES('crownvista'); + INSERT INTO streetname VALUES('crystal arms'); + INSERT INTO streetname VALUES('crystal crest'); + INSERT INTO streetname VALUES('crystal leaf'); + INSERT INTO streetname VALUES('cunningham park'); + INSERT INTO streetname VALUES('cypress pond'); + INSERT INTO streetname VALUES('daffodil'); + INSERT INTO streetname VALUES('daisyfield'); + INSERT INTO streetname VALUES('dalecrest'); + INSERT INTO streetname VALUES('dannelly park'); + INSERT INTO streetname VALUES('daphne'); + INSERT INTO streetname VALUES('daria'); + INSERT INTO streetname VALUES('dartmouth'); + INSERT INTO streetname VALUES('datha'); + INSERT INTO streetname VALUES('david cox'); + INSERT INTO streetname VALUES('davis'); + INSERT INTO streetname VALUES('davis crossing'); + INSERT INTO streetname VALUES('davis lake'); + INSERT INTO streetname VALUES('davis ridge'); + INSERT INTO streetname VALUES('dawnmist'); + INSERT INTO streetname VALUES('daybreak'); + INSERT INTO streetname VALUES('dearmon'); + INSERT INTO streetname VALUES('dearview'); + INSERT INTO streetname VALUES('deaton hill'); + INSERT INTO streetname VALUES('deer cross'); + INSERT INTO streetname VALUES('deerton'); + INSERT INTO streetname VALUES('degrasse'); + INSERT INTO streetname VALUES('delamere'); + INSERT INTO streetname VALUES('dellfield'); + INSERT INTO streetname VALUES('dellinger'); + INSERT INTO streetname VALUES('demington'); + INSERT INTO streetname VALUES('denmeade'); + INSERT INTO streetname VALUES('derita'); + INSERT INTO streetname VALUES('derita woods'); + INSERT INTO streetname VALUES('deruyter'); + INSERT INTO streetname VALUES('dervish'); + INSERT INTO streetname VALUES('devas'); + INSERT INTO streetname VALUES('devon croft'); + INSERT INTO streetname VALUES('devonbridge'); + INSERT INTO streetname VALUES('devongate'); + INSERT INTO streetname VALUES('devonhill'); + INSERT INTO streetname VALUES('dewmorn'); + INSERT INTO streetname VALUES('distribution center'); + INSERT INTO streetname VALUES('dominion crest'); + INSERT INTO streetname VALUES('dominion green'); + INSERT INTO streetname VALUES('dominion village'); + INSERT INTO streetname VALUES('dorshire'); + INSERT INTO streetname VALUES('double creek crossing'); + INSERT INTO streetname VALUES('dow'); + INSERT INTO streetname VALUES('downfield wood'); + INSERT INTO streetname VALUES('downing creek'); + INSERT INTO streetname VALUES('driscol'); + INSERT INTO streetname VALUES('driwood'); + INSERT INTO streetname VALUES('dry brook'); + INSERT INTO streetname VALUES('dumont'); + INSERT INTO streetname VALUES('dunblane'); + INSERT INTO streetname VALUES('dunfield'); + INSERT INTO streetname VALUES('dunoon'); + INSERT INTO streetname VALUES('dunslow'); + INSERT INTO streetname VALUES('dunstaff'); + INSERT INTO streetname VALUES('durham'); + INSERT INTO streetname VALUES('durston'); + INSERT INTO streetname VALUES('dusty cedar'); + INSERT INTO streetname VALUES('dusty trail'); + INSERT INTO streetname VALUES('dutchess'); + INSERT INTO streetname VALUES('duxford'); + INSERT INTO streetname VALUES('eagle creek'); + INSERT INTO streetname VALUES('eagles field'); + INSERT INTO streetname VALUES('eargle'); + INSERT INTO streetname VALUES('earlswood'); + INSERT INTO streetname VALUES('early mist'); + INSERT INTO streetname VALUES('earthenware'); + INSERT INTO streetname VALUES('eastfield park'); + INSERT INTO streetname VALUES('eastfield village'); + INSERT INTO streetname VALUES('easy'); + INSERT INTO streetname VALUES('eben'); + INSERT INTO streetname VALUES('edgepine'); + INSERT INTO streetname VALUES('edgewier'); + INSERT INTO streetname VALUES('edinburgh'); + INSERT INTO streetname VALUES('edinmeadow'); + INSERT INTO streetname VALUES('edmonton'); + INSERT INTO streetname VALUES('edwin jones'); + INSERT INTO streetname VALUES('elberon'); + INSERT INTO streetname VALUES('elderslie'); + INSERT INTO streetname VALUES('elementary view'); + INSERT INTO streetname VALUES('elendil'); + INSERT INTO streetname VALUES('elizabeth'); + INSERT INTO streetname VALUES('elm cove'); + INSERT INTO streetname VALUES('elrond'); + INSERT INTO streetname VALUES('elsenham'); + INSERT INTO streetname VALUES('elven'); + INSERT INTO streetname VALUES('emma lynn'); + INSERT INTO streetname VALUES('english setter'); + INSERT INTO streetname VALUES('enoch'); + INSERT INTO streetname VALUES('equipment'); + INSERT INTO streetname VALUES('ernest russell'); + INSERT INTO streetname VALUES('ernie'); + INSERT INTO streetname VALUES('esmeralda'); + INSERT INTO streetname VALUES('evergreen hollow'); + INSERT INTO streetname VALUES('eversfield'); + INSERT INTO streetname VALUES('ewen'); + INSERT INTO streetname VALUES('ewert cut'); + INSERT INTO streetname VALUES('exbury'); + INSERT INTO streetname VALUES('fair grounds park'); + INSERT INTO streetname VALUES('fairbourne'); + INSERT INTO streetname VALUES('fairchase'); + INSERT INTO streetname VALUES('faircreek'); + INSERT INTO streetname VALUES('fairglen'); + INSERT INTO streetname VALUES('fairlea'); + INSERT INTO streetname VALUES('fairmead'); + INSERT INTO streetname VALUES('fairmeadows'); + INSERT INTO streetname VALUES('fairstone'); + INSERT INTO streetname VALUES('fairvista'); + INSERT INTO streetname VALUES('fairway point'); + INSERT INTO streetname VALUES('falconcrest'); + INSERT INTO streetname VALUES('falls ridge'); + INSERT INTO streetname VALUES('falmouth'); + INSERT INTO streetname VALUES('far west'); + INSERT INTO streetname VALUES('farlow'); + INSERT INTO streetname VALUES('farris wheel'); + INSERT INTO streetname VALUES('fawndale'); + INSERT INTO streetname VALUES('feather bend'); + INSERT INTO streetname VALUES('fernledge'); + INSERT INTO streetname VALUES('fernmoss'); + INSERT INTO streetname VALUES('ferrell commons'); + INSERT INTO streetname VALUES('fieldstone'); + INSERT INTO streetname VALUES('fillian'); + INSERT INTO streetname VALUES('fincher'); + INSERT INTO streetname VALUES('foggy meadow'); + INSERT INTO streetname VALUES('fordyce'); + INSERT INTO streetname VALUES('forest grove'); + INSERT INTO streetname VALUES('forest path'); + INSERT INTO streetname VALUES('forestridge commons'); + INSERT INTO streetname VALUES('forestrock'); + INSERT INTO streetname VALUES('fortunes ridge'); + INSERT INTO streetname VALUES('founders club'); + INSERT INTO streetname VALUES('fountaingrass'); + INSERT INTO streetname VALUES('fox chase'); + INSERT INTO streetname VALUES('fox glen'); + INSERT INTO streetname VALUES('fox hill'); + INSERT INTO streetname VALUES('fox point'); + INSERT INTO streetname VALUES('fox trot'); + INSERT INTO streetname VALUES('foxbriar'); + INSERT INTO streetname VALUES('frank little'); + INSERT INTO streetname VALUES('franzia'); + INSERT INTO streetname VALUES('french woods'); + INSERT INTO streetname VALUES('frostmoor'); + INSERT INTO streetname VALUES('frye'); + INSERT INTO streetname VALUES('furlong'); + INSERT INTO streetname VALUES('galena view'); + INSERT INTO streetname VALUES('gallery pointe'); + INSERT INTO streetname VALUES('gammon'); + INSERT INTO streetname VALUES('garden grove'); + INSERT INTO streetname VALUES('gardendale'); + INSERT INTO streetname VALUES('garganey'); + INSERT INTO streetname VALUES('garnet field'); + INSERT INTO streetname VALUES('garrison'); + INSERT INTO streetname VALUES('garvin'); + INSERT INTO streetname VALUES('garvis'); + INSERT INTO streetname VALUES('gaskill'); + INSERT INTO streetname VALUES('gemstone'); + INSERT INTO streetname VALUES('gibbon'); + INSERT INTO streetname VALUES('gibbon terrace'); + INSERT INTO streetname VALUES('gibbons link'); + INSERT INTO streetname VALUES('gillman'); + INSERT INTO streetname VALUES('gladwood'); + INSERT INTO streetname VALUES('gladwyne'); + INSERT INTO streetname VALUES('glamorgan'); + INSERT INTO streetname VALUES('glaze'); + INSERT INTO streetname VALUES('glen brook'); + INSERT INTO streetname VALUES('glen cove'); + INSERT INTO streetname VALUES('glen hope'); + INSERT INTO streetname VALUES('glen manor'); + INSERT INTO streetname VALUES('glen olden'); + INSERT INTO streetname VALUES('glencairn'); + INSERT INTO streetname VALUES('glendock'); + INSERT INTO streetname VALUES('glenolden'); + INSERT INTO streetname VALUES('glenover'); + INSERT INTO streetname VALUES('glenshire'); + INSERT INTO streetname VALUES('glenstone'); + INSERT INTO streetname VALUES('gold dust'); + INSERT INTO streetname VALUES('golden pond'); + INSERT INTO streetname VALUES('goldenblush'); + INSERT INTO streetname VALUES('goldenfield'); + INSERT INTO streetname VALUES('goose landing'); + INSERT INTO streetname VALUES('gorham gate'); + INSERT INTO streetname VALUES('grabill'); + INSERT INTO streetname VALUES('graburns ford'); + INSERT INTO streetname VALUES('graham'); + INSERT INTO streetname VALUES('grahamson'); + INSERT INTO streetname VALUES('granard'); + INSERT INTO streetname VALUES('grand teton'); + INSERT INTO streetname VALUES('grande heights'); + INSERT INTO streetname VALUES('grandeur'); + INSERT INTO streetname VALUES('granite creek'); + INSERT INTO streetname VALUES('grasset'); + INSERT INTO streetname VALUES('graypark'); + INSERT INTO streetname VALUES('grays ridge'); + INSERT INTO streetname VALUES('great bear'); + INSERT INTO streetname VALUES('green clover'); + INSERT INTO streetname VALUES('green hedge'); + INSERT INTO streetname VALUES('green meadow'); + INSERT INTO streetname VALUES('green pasture'); + INSERT INTO streetname VALUES('greene'); + INSERT INTO streetname VALUES('greenloch'); + INSERT INTO streetname VALUES('greenock ridge'); + INSERT INTO streetname VALUES('greenware'); + INSERT INTO streetname VALUES('greenway village'); + INSERT INTO streetname VALUES('grenelefe village'); + INSERT INTO streetname VALUES('grey dogwood'); + INSERT INTO streetname VALUES('greyhound'); + INSERT INTO streetname VALUES('greylock ridge'); + INSERT INTO streetname VALUES('grosbeak'); + INSERT INTO streetname VALUES('grove'); + INSERT INTO streetname VALUES('groveton'); + INSERT INTO streetname VALUES('groveview'); + INSERT INTO streetname VALUES('hackberry creek'); + INSERT INTO streetname VALUES('hackberry grove'); + INSERT INTO streetname VALUES('hackett'); + INSERT INTO streetname VALUES('haddington'); + INSERT INTO streetname VALUES('hagler'); + INSERT INTO streetname VALUES('halcott'); + INSERT INTO streetname VALUES('half dome'); + INSERT INTO streetname VALUES('hallam'); + INSERT INTO streetname VALUES('hamilton russell'); + INSERT INTO streetname VALUES('hampton place'); + INSERT INTO streetname VALUES('hankins'); + INSERT INTO streetname VALUES('harburn forest'); + INSERT INTO streetname VALUES('harringham'); + INSERT INTO streetname VALUES('harrington woods'); + INSERT INTO streetname VALUES('harris corners'); + INSERT INTO streetname VALUES('harris cove'); + INSERT INTO streetname VALUES('harris glen'); + INSERT INTO streetname VALUES('harris hill'); + INSERT INTO streetname VALUES('harris oak'); + INSERT INTO streetname VALUES('harris pointe'); + INSERT INTO streetname VALUES('harris pond'); + INSERT INTO streetname VALUES('harris ridge'); + INSERT INTO streetname VALUES('harris technology'); + INSERT INTO streetname VALUES('harris woods'); + INSERT INTO streetname VALUES('hartfield downs'); + INSERT INTO streetname VALUES('hattie little'); + INSERT INTO streetname VALUES('hatwynn'); + INSERT INTO streetname VALUES('hawkins'); + INSERT INTO streetname VALUES('hawksnest'); + INSERT INTO streetname VALUES('haybridge'); + INSERT INTO streetname VALUES('hayden'); + INSERT INTO streetname VALUES('hazelcroft'); + INSERT INTO streetname VALUES('hazlitt'); + INSERT INTO streetname VALUES('hazy valley'); + INSERT INTO streetname VALUES('hearst'); + INSERT INTO streetname VALUES('heathcrest'); + INSERT INTO streetname VALUES('heathcroft'); + INSERT INTO streetname VALUES('hedge maple'); + INSERT INTO streetname VALUES('hedgecrest'); + INSERT INTO streetname VALUES('hedingham'); + INSERT INTO streetname VALUES('heman'); + INSERT INTO streetname VALUES('henderson'); + INSERT INTO streetname VALUES('henderson oaks'); + INSERT INTO streetname VALUES('henderson valley'); + INSERT INTO streetname VALUES('hendry'); + INSERT INTO streetname VALUES('heritage hills'); + INSERT INTO streetname VALUES('heritage woods'); + INSERT INTO streetname VALUES('heron cove'); + INSERT INTO streetname VALUES('heron glen'); + INSERT INTO streetname VALUES('hewitt'); + INSERT INTO streetname VALUES('hey rock'); + INSERT INTO streetname VALUES('heysham'); + INSERT INTO streetname VALUES('hickory cove'); + INSERT INTO streetname VALUES('hidden meadow'); + INSERT INTO streetname VALUES('high glen'); + INSERT INTO streetname VALUES('high laurel'); + INSERT INTO streetname VALUES('high valley'); + INSERT INTO streetname VALUES('highcroft'); + INSERT INTO streetname VALUES('highland'); + INSERT INTO streetname VALUES('highland commons'); + INSERT INTO streetname VALUES('highland creek'); + INSERT INTO streetname VALUES('highland glen'); + INSERT INTO streetname VALUES('highland park'); + INSERT INTO streetname VALUES('highlander'); + INSERT INTO streetname VALUES('highstream'); + INSERT INTO streetname VALUES('hilltop'); + INSERT INTO streetname VALUES('hobbitshire'); + INSERT INTO streetname VALUES('hoffman'); + INSERT INTO streetname VALUES('hogans way'); + INSERT INTO streetname VALUES('holbert'); + INSERT INTO streetname VALUES('hollow ridge'); + INSERT INTO streetname VALUES('holly vista'); + INSERT INTO streetname VALUES('hollywood'); + INSERT INTO streetname VALUES('hoover'); + INSERT INTO streetname VALUES('hopkins'); + INSERT INTO streetname VALUES('horace mann'); + INSERT INTO streetname VALUES('hornbeam'); + INSERT INTO streetname VALUES('horse pasture'); + INSERT INTO streetname VALUES('hosta'); + INSERT INTO streetname VALUES('howard'); + INSERT INTO streetname VALUES('hubbard'); + INSERT INTO streetname VALUES('hubbard falls'); + INSERT INTO streetname VALUES('hubbard woods'); + INSERT INTO streetname VALUES('hucks'); + INSERT INTO streetname VALUES('hunters creek'); + INSERT INTO streetname VALUES('hunters pointe'); + INSERT INTO streetname VALUES('hunters spring'); + INSERT INTO streetname VALUES('hunters whip'); + INSERT INTO streetname VALUES('huntmeadow'); + INSERT INTO streetname VALUES('hutchison mcdonald'); + INSERT INTO streetname VALUES('ingleton'); + INSERT INTO streetname VALUES('insdale'); + INSERT INTO streetname VALUES('interstate 85 service'); + INSERT INTO streetname VALUES('iola'); + INSERT INTO streetname VALUES('iredell'); + INSERT INTO streetname VALUES('iron brigade'); + INSERT INTO streetname VALUES('irwin valley'); + INSERT INTO streetname VALUES('irwin wood'); + INSERT INTO streetname VALUES('ivy brook'); + INSERT INTO streetname VALUES('ivy ridge'); + INSERT INTO streetname VALUES('jack russell'); + INSERT INTO streetname VALUES('jackson'); + INSERT INTO streetname VALUES('jacob martin'); + INSERT INTO streetname VALUES('jamison'); + INSERT INTO streetname VALUES('jane'); + INSERT INTO streetname VALUES('jaspar crest'); + INSERT INTO streetname VALUES('jessica'); + INSERT INTO streetname VALUES('jimmy oehler'); + INSERT INTO streetname VALUES('jocelyn'); + INSERT INTO streetname VALUES('johnston mill'); + INSERT INTO streetname VALUES('johnston oehler'); + INSERT INTO streetname VALUES('judal'); + INSERT INTO streetname VALUES('junipeous'); + INSERT INTO streetname VALUES('juniper'); + INSERT INTO streetname VALUES('juniperus'); + INSERT INTO streetname VALUES('kalispell'); + INSERT INTO streetname VALUES('karylsturn'); + INSERT INTO streetname VALUES('katelyn'); + INSERT INTO streetname VALUES('kayron'); + INSERT INTO streetname VALUES('keaton'); + INSERT INTO streetname VALUES('keble'); + INSERT INTO streetname VALUES('keels'); + INSERT INTO streetname VALUES('keith'); + INSERT INTO streetname VALUES('keithwood'); + INSERT INTO streetname VALUES('kelden walker'); + INSERT INTO streetname VALUES('kelsey emma'); + INSERT INTO streetname VALUES('kendrick'); + INSERT INTO streetname VALUES('kenmont'); + INSERT INTO streetname VALUES('kennerly cove'); + INSERT INTO streetname VALUES('kenninghall'); + INSERT INTO streetname VALUES('kent village'); + INSERT INTO streetname VALUES('kestral ridge'); + INSERT INTO streetname VALUES('kestrel'); + INSERT INTO streetname VALUES('kilmartin'); + INSERT INTO streetname VALUES('kilty'); + INSERT INTO streetname VALUES('kinglet'); + INSERT INTO streetname VALUES('kingsland'); + INSERT INTO streetname VALUES('kingsnorth'); + INSERT INTO streetname VALUES('kinsmore'); + INSERT INTO streetname VALUES('kirkgard'); + INSERT INTO streetname VALUES('kirkmont'); + INSERT INTO streetname VALUES('knightsgate'); + INSERT INTO streetname VALUES('kobuk'); + INSERT INTO streetname VALUES('kotlik'); + INSERT INTO streetname VALUES('kotz'); + INSERT INTO streetname VALUES('kyndall walk'); + INSERT INTO streetname VALUES('laborde'); + INSERT INTO streetname VALUES('lady bank'); + INSERT INTO streetname VALUES('lagrande'); + INSERT INTO streetname VALUES('lake'); + INSERT INTO streetname VALUES('lakeridge commons'); + INSERT INTO streetname VALUES('lakeview'); + INSERT INTO streetname VALUES('lakewood edge'); + INSERT INTO streetname VALUES('lakota'); + INSERT INTO streetname VALUES('lambrook'); + INSERT INTO streetname VALUES('lampkin'); + INSERT INTO streetname VALUES('lampkin park'); + INSERT INTO streetname VALUES('langham'); + INSERT INTO streetname VALUES('lanzerac manor'); + INSERT INTO streetname VALUES('larkmead forest'); + INSERT INTO streetname VALUES('lattice'); + INSERT INTO streetname VALUES('laurel crest'); + INSERT INTO streetname VALUES('laurel ridge'); + INSERT INTO streetname VALUES('laurel run'); + INSERT INTO streetname VALUES('laurenfield'); + INSERT INTO streetname VALUES('laveta'); + INSERT INTO streetname VALUES('lazy day'); + INSERT INTO streetname VALUES('leawood run'); + INSERT INTO streetname VALUES('lee marie'); + INSERT INTO streetname VALUES('legacy lake'); + INSERT INTO streetname VALUES('legacy park'); + INSERT INTO streetname VALUES('legato'); + INSERT INTO streetname VALUES('legolas'); + INSERT INTO streetname VALUES('leigh glen'); + INSERT INTO streetname VALUES('lence'); + INSERT INTO streetname VALUES('lenox hill'); + INSERT INTO streetname VALUES('leonine'); + INSERT INTO streetname VALUES('leslie'); + INSERT INTO streetname VALUES('lester hill'); + INSERT INTO streetname VALUES('levisey'); + INSERT INTO streetname VALUES('liberty bell'); + INSERT INTO streetname VALUES('linden berry'); + INSERT INTO streetname VALUES('lisbon'); + INSERT INTO streetname VALUES('little stoney'); + INSERT INTO streetname VALUES('livengood'); + INSERT INTO streetname VALUES('lochway'); + INSERT INTO streetname VALUES('lockman'); + INSERT INTO streetname VALUES('loganville'); + INSERT INTO streetname VALUES('lone tree'); + INSERT INTO streetname VALUES('long creek park'); + INSERT INTO streetname VALUES('long forest'); + INSERT INTO streetname VALUES('looking glass'); + INSERT INTO streetname VALUES('lookout point'); + INSERT INTO streetname VALUES('lowen'); + INSERT INTO streetname VALUES('lusby'); + INSERT INTO streetname VALUES('lyleton'); + INSERT INTO streetname VALUES('lynn lee'); + INSERT INTO streetname VALUES('lynnewood glen'); + INSERT INTO streetname VALUES('machrie'); + INSERT INTO streetname VALUES('mackinac'); + INSERT INTO streetname VALUES('maddox'); + INSERT INTO streetname VALUES('madison park'); + INSERT INTO streetname VALUES('mallard'); + INSERT INTO streetname VALUES('mallard cove'); + INSERT INTO streetname VALUES('mallard forest'); + INSERT INTO streetname VALUES('mallard grove'); + INSERT INTO streetname VALUES('mallard hill'); + INSERT INTO streetname VALUES('mallard park'); + INSERT INTO streetname VALUES('mallard ridge'); + INSERT INTO streetname VALUES('mallard view'); + INSERT INTO streetname VALUES('manbey'); + INSERT INTO streetname VALUES('manning'); + INSERT INTO streetname VALUES('mantario'); + INSERT INTO streetname VALUES('maple'); + INSERT INTO streetname VALUES('maple cove'); + INSERT INTO streetname VALUES('maple park'); + INSERT INTO streetname VALUES('marathon hill'); + INSERT INTO streetname VALUES('marbury'); + INSERT INTO streetname VALUES('marett'); + INSERT INTO streetname VALUES('marigold'); + INSERT INTO streetname VALUES('marionwood'); + INSERT INTO streetname VALUES('marshbank'); + INSERT INTO streetname VALUES('mason'); + INSERT INTO streetname VALUES('mayapple'); + INSERT INTO streetname VALUES('maylandia'); + INSERT INTO streetname VALUES('mayspring'); + INSERT INTO streetname VALUES('mcadam'); + INSERT INTO streetname VALUES('mcchesney'); + INSERT INTO streetname VALUES('mccurdy'); + INSERT INTO streetname VALUES('mcgrath'); + INSERT INTO streetname VALUES('mckendree'); + INSERT INTO streetname VALUES('mclaughlin'); + INSERT INTO streetname VALUES('mctaggart'); + INSERT INTO streetname VALUES('meadow green'); + INSERT INTO streetname VALUES('meadow knoll'); + INSERT INTO streetname VALUES('meadow post'); + INSERT INTO streetname VALUES('meadowmont'); + INSERT INTO streetname VALUES('meadowmont view'); + INSERT INTO streetname VALUES('meadowview hills'); + INSERT INTO streetname VALUES('melshire'); + INSERT INTO streetname VALUES('melstrand'); + INSERT INTO streetname VALUES('mentone'); + INSERT INTO streetname VALUES('meridale crossing'); + INSERT INTO streetname VALUES('merion hills'); + INSERT INTO streetname VALUES('merlot'); + INSERT INTO streetname VALUES('mersham'); + INSERT INTO streetname VALUES('metromont'); + INSERT INTO streetname VALUES('metromont industrial'); + INSERT INTO streetname VALUES('michaw'); + INSERT INTO streetname VALUES('milhaven'); + INSERT INTO streetname VALUES('milhof'); + INSERT INTO streetname VALUES('millstream ridge'); + INSERT INTO streetname VALUES('mineral ridge'); + INSERT INTO streetname VALUES('mint thistle'); + INSERT INTO streetname VALUES('mintleaf'); + INSERT INTO streetname VALUES('mintvale'); + INSERT INTO streetname VALUES('misty'); + INSERT INTO streetname VALUES('misty arbor'); + INSERT INTO streetname VALUES('misty creek'); + INSERT INTO streetname VALUES('misty oaks'); + INSERT INTO streetname VALUES('misty wood'); + INSERT INTO streetname VALUES('mitzi deborah'); + INSERT INTO streetname VALUES('mobile'); + INSERT INTO streetname VALUES('molly elizabeth'); + INSERT INTO streetname VALUES('monmouth'); + INSERT INTO streetname VALUES('montrose'); + INSERT INTO streetname VALUES('moonlight'); + INSERT INTO streetname VALUES('moose'); + INSERT INTO streetname VALUES('morning dew'); + INSERT INTO streetname VALUES('morningsong'); + INSERT INTO streetname VALUES('morningview'); + INSERT INTO streetname VALUES('morsey'); + INSERT INTO streetname VALUES('moss glen'); + INSERT INTO streetname VALUES('mossy bank'); + INSERT INTO streetname VALUES('motor sport'); + INSERT INTO streetname VALUES('mountain laurel'); + INSERT INTO streetname VALUES('mourning dove'); + INSERT INTO streetname VALUES('mozart'); + INSERT INTO streetname VALUES('munsing'); + INSERT INTO streetname VALUES('murray'); + INSERT INTO streetname VALUES('nathan'); + INSERT INTO streetname VALUES('netherhall'); + INSERT INTO streetname VALUES('netherton'); + INSERT INTO streetname VALUES('neuhoff'); + INSERT INTO streetname VALUES('nevin'); + INSERT INTO streetname VALUES('nevin brook'); + INSERT INTO streetname VALUES('nevin glen'); + INSERT INTO streetname VALUES('nevin place'); + INSERT INTO streetname VALUES('new england'); + INSERT INTO streetname VALUES('new house'); + INSERT INTO streetname VALUES('newbary'); + INSERT INTO streetname VALUES('newchurch'); + INSERT INTO streetname VALUES('newfane'); + INSERT INTO streetname VALUES('newgard'); + INSERT INTO streetname VALUES('nicholas'); + INSERT INTO streetname VALUES('nicole'); + INSERT INTO streetname VALUES('nobility'); + INSERT INTO streetname VALUES('norcroft'); + INSERT INTO streetname VALUES('northridge'); + INSERT INTO streetname VALUES('northside'); + INSERT INTO streetname VALUES('northwoods business'); + INSERT INTO streetname VALUES('norway'); + INSERT INTO streetname VALUES('nottinghill'); + INSERT INTO streetname VALUES('numenore'); + INSERT INTO streetname VALUES('nyewood'); + INSERT INTO streetname VALUES('oak'); + INSERT INTO streetname VALUES('oak cove'); + INSERT INTO streetname VALUES('oak pasture'); + INSERT INTO streetname VALUES('oakburn'); + INSERT INTO streetname VALUES('oakwinds'); + INSERT INTO streetname VALUES('oakwood'); + INSERT INTO streetname VALUES('obrien'); + INSERT INTO streetname VALUES('ocala'); + INSERT INTO streetname VALUES('old bridge'); + INSERT INTO streetname VALUES('old fox'); + INSERT INTO streetname VALUES('old potters'); + INSERT INTO streetname VALUES('old statesville'); + INSERT INTO streetname VALUES('old steine'); + INSERT INTO streetname VALUES('old stoney creek'); + INSERT INTO streetname VALUES('old sugar creek'); + INSERT INTO streetname VALUES('old timber'); + INSERT INTO streetname VALUES('old wagon'); + INSERT INTO streetname VALUES('old willow'); + INSERT INTO streetname VALUES('oldenway'); + INSERT INTO streetname VALUES('oneida'); + INSERT INTO streetname VALUES('ontario'); + INSERT INTO streetname VALUES('oriole'); + INSERT INTO streetname VALUES('orofino'); + INSERT INTO streetname VALUES('orr'); + INSERT INTO streetname VALUES('osage'); + INSERT INTO streetname VALUES('osceola'); + INSERT INTO streetname VALUES('osprey knoll'); + INSERT INTO streetname VALUES('oxford hill'); + INSERT INTO streetname VALUES('painted fern'); + INSERT INTO streetname VALUES('painted pony'); + INSERT INTO streetname VALUES('paisley'); + INSERT INTO streetname VALUES('pale moss'); + INSERT INTO streetname VALUES('palladium'); + INSERT INTO streetname VALUES('palmutum'); + INSERT INTO streetname VALUES('palustris'); + INSERT INTO streetname VALUES('panglemont'); + INSERT INTO streetname VALUES('panther'); + INSERT INTO streetname VALUES('panthersville'); + INSERT INTO streetname VALUES('paper whites'); + INSERT INTO streetname VALUES('park'); + INSERT INTO streetname VALUES('parker green'); + INSERT INTO streetname VALUES('parkhouse'); + INSERT INTO streetname VALUES('passour ridge'); + INSERT INTO streetname VALUES('pasture view'); + INSERT INTO streetname VALUES('patricia ann'); + INSERT INTO streetname VALUES('patton'); + INSERT INTO streetname VALUES('patton ridge'); + INSERT INTO streetname VALUES('pawpaw'); + INSERT INTO streetname VALUES('peach'); + INSERT INTO streetname VALUES('peakwood'); + INSERT INTO streetname VALUES('pebble creek'); + INSERT INTO streetname VALUES('pecan cove'); + INSERT INTO streetname VALUES('pedigree'); + INSERT INTO streetname VALUES('pelorus'); + INSERT INTO streetname VALUES('penmore'); + INSERT INTO streetname VALUES('pensfold'); + INSERT INTO streetname VALUES('pepperstone'); + INSERT INTO streetname VALUES('peregrine'); + INSERT INTO streetname VALUES('periwinkle'); + INSERT INTO streetname VALUES('perkins'); + INSERT INTO streetname VALUES('pete brown'); + INSERT INTO streetname VALUES('phillips'); + INSERT INTO streetname VALUES('pickway'); + INSERT INTO streetname VALUES('piercy woods'); + INSERT INTO streetname VALUES('pierpoint'); + INSERT INTO streetname VALUES('pine'); + INSERT INTO streetname VALUES('pine branch'); + INSERT INTO streetname VALUES('pine meadow'); + INSERT INTO streetname VALUES('pineleaf'); + INSERT INTO streetname VALUES('pinewood'); + INSERT INTO streetname VALUES('pintail'); + INSERT INTO streetname VALUES('pipestone'); + INSERT INTO streetname VALUES('placer maple'); + INSERT INTO streetname VALUES('plover'); + INSERT INTO streetname VALUES('plum'); + INSERT INTO streetname VALUES('po box'); + INSERT INTO streetname VALUES('pochard'); + INSERT INTO streetname VALUES('pointview'); + INSERT INTO streetname VALUES('polk and white'); + INSERT INTO streetname VALUES('pond valley'); + INSERT INTO streetname VALUES('pondridge'); + INSERT INTO streetname VALUES('pope farm'); + INSERT INTO streetname VALUES('poplar grove'); + INSERT INTO streetname VALUES('poplar springs'); + INSERT INTO streetname VALUES('portola'); + INSERT INTO streetname VALUES('potters glen'); + INSERT INTO streetname VALUES('powatan'); + INSERT INTO streetname VALUES('prairie valley'); + INSERT INTO streetname VALUES('prescott'); + INSERT INTO streetname VALUES('presmann'); + INSERT INTO streetname VALUES('prestigious'); + INSERT INTO streetname VALUES('princess'); + INSERT INTO streetname VALUES('prosperity'); + INSERT INTO streetname VALUES('prosperity church'); + INSERT INTO streetname VALUES('prosperity commons'); + INSERT INTO streetname VALUES('prosperity park'); + INSERT INTO streetname VALUES('prosperity point'); + INSERT INTO streetname VALUES('prosperity ridge'); + INSERT INTO streetname VALUES('prosperity view'); + INSERT INTO streetname VALUES('purple finch'); + INSERT INTO streetname VALUES('quail'); + INSERT INTO streetname VALUES('queensbury'); + INSERT INTO streetname VALUES('quinn'); + INSERT INTO streetname VALUES('racine'); + INSERT INTO streetname VALUES('radbourne'); + INSERT INTO streetname VALUES('raddington'); + INSERT INTO streetname VALUES('raku'); + INSERT INTO streetname VALUES('rancliffe'); + INSERT INTO streetname VALUES('ravencrest'); + INSERT INTO streetname VALUES('reames'); + INSERT INTO streetname VALUES('rebecca run'); + INSERT INTO streetname VALUES('red bluff'); + INSERT INTO streetname VALUES('red clay'); + INSERT INTO streetname VALUES('red clover'); + INSERT INTO streetname VALUES('red rose'); + INSERT INTO streetname VALUES('red shed'); + INSERT INTO streetname VALUES('red tail'); + INSERT INTO streetname VALUES('redbridge'); + INSERT INTO streetname VALUES('redstart'); + INSERT INTO streetname VALUES('redstone view'); + INSERT INTO streetname VALUES('reedmont'); + INSERT INTO streetname VALUES('reeves'); + INSERT INTO streetname VALUES('regal'); + INSERT INTO streetname VALUES('reinbeck'); + INSERT INTO streetname VALUES('retriever'); + INSERT INTO streetname VALUES('ribbonwalk'); + INSERT INTO streetname VALUES('richardson park'); + INSERT INTO streetname VALUES('richfield'); + INSERT INTO streetname VALUES('riddings'); + INSERT INTO streetname VALUES('ridge'); + INSERT INTO streetname VALUES('ridge cliff'); + INSERT INTO streetname VALUES('ridge path'); + INSERT INTO streetname VALUES('ridge peak'); + INSERT INTO streetname VALUES('ridgefield'); + INSERT INTO streetname VALUES('ridgeline'); + INSERT INTO streetname VALUES('ridgeview commons'); + INSERT INTO streetname VALUES('riley'); + INSERT INTO streetname VALUES('riley woods'); + INSERT INTO streetname VALUES('rillet'); + INSERT INTO streetname VALUES('rindle'); + INSERT INTO streetname VALUES('rivendell'); + INSERT INTO streetname VALUES('robin'); + INSERT INTO streetname VALUES('robins nest'); + INSERT INTO streetname VALUES('robur'); + INSERT INTO streetname VALUES('robyns glen'); + INSERT INTO streetname VALUES('rock stream'); + INSERT INTO streetname VALUES('rockwell'); + INSERT INTO streetname VALUES('rockwell church'); + INSERT INTO streetname VALUES('rocky brook'); + INSERT INTO streetname VALUES('rocky ford club'); + INSERT INTO streetname VALUES('rotary'); + INSERT INTO streetname VALUES('rouda'); + INSERT INTO streetname VALUES('royal bluff'); + INSERT INTO streetname VALUES('royal celadon'); + INSERT INTO streetname VALUES('rubin lura'); + INSERT INTO streetname VALUES('runswyck'); + INSERT INTO streetname VALUES('ruth ferrell'); + INSERT INTO streetname VALUES('ruth polk'); + INSERT INTO streetname VALUES('ryan jay'); + INSERT INTO streetname VALUES('sackett'); + INSERT INTO streetname VALUES('saddle pace'); + INSERT INTO streetname VALUES('saddle run'); + INSERT INTO streetname VALUES('saddle trail'); + INSERT INTO streetname VALUES('saguaro'); + INSERT INTO streetname VALUES('saint audrey'); + INSERT INTO streetname VALUES('saint bernard'); + INSERT INTO streetname VALUES('saint frances'); + INSERT INTO streetname VALUES('sam roper'); + INSERT INTO streetname VALUES('samara'); + INSERT INTO streetname VALUES('sanders creek'); + INSERT INTO streetname VALUES('saquache'); + INSERT INTO streetname VALUES('sarnia'); + INSERT INTO streetname VALUES('savannah springs'); + INSERT INTO streetname VALUES('sawgrass ridge'); + INSERT INTO streetname VALUES('saxonbury'); + INSERT INTO streetname VALUES('scotch moss'); + INSERT INTO streetname VALUES('seasons'); + INSERT INTO streetname VALUES('serenity'); + INSERT INTO streetname VALUES('seths'); + INSERT INTO streetname VALUES('shadow lawn'); + INSERT INTO streetname VALUES('shadow oaks'); + INSERT INTO streetname VALUES('shadow pine'); + INSERT INTO streetname VALUES('shadyside'); + INSERT INTO streetname VALUES('shallow oak'); + INSERT INTO streetname VALUES('shelley'); + INSERT INTO streetname VALUES('shining oak'); + INSERT INTO streetname VALUES('ship'); + INSERT INTO streetname VALUES('shore haven'); + INSERT INTO streetname VALUES('shuman'); + INSERT INTO streetname VALUES('sidney'); + INSERT INTO streetname VALUES('silver birch'); + INSERT INTO streetname VALUES('silvermere'); + INSERT INTO streetname VALUES('simonton'); + INSERT INTO streetname VALUES('singing hills'); + INSERT INTO streetname VALUES('singing oak'); + INSERT INTO streetname VALUES('sipes'); + INSERT INTO streetname VALUES('six point'); + INSERT INTO streetname VALUES('skycrest'); + INSERT INTO streetname VALUES('skyline'); + INSERT INTO streetname VALUES('small'); + INSERT INTO streetname VALUES('smith corners'); + INSERT INTO streetname VALUES('smithwood'); + INSERT INTO streetname VALUES('snow hill'); + INSERT INTO streetname VALUES('soapstone'); + INSERT INTO streetname VALUES('sobeck'); + INSERT INTO streetname VALUES('socata'); + INSERT INTO streetname VALUES('solace'); + INSERT INTO streetname VALUES('solway'); + INSERT INTO streetname VALUES('song sparrow'); + INSERT INTO streetname VALUES('sorrento'); + INSERT INTO streetname VALUES('spector'); + INSERT INTO streetname VALUES('spin drift'); + INSERT INTO streetname VALUES('spring crest'); + INSERT INTO streetname VALUES('spring lee'); + INSERT INTO streetname VALUES('spring park'); + INSERT INTO streetname VALUES('spring terrace'); + INSERT INTO streetname VALUES('spring trace'); + INSERT INTO streetname VALUES('springhaven'); + INSERT INTO streetname VALUES('squirrel trail'); + INSERT INTO streetname VALUES('stardust'); + INSERT INTO streetname VALUES('stargaze'); + INSERT INTO streetname VALUES('starita'); + INSERT INTO streetname VALUES('starmount'); + INSERT INTO streetname VALUES('statesville'); + INSERT INTO streetname VALUES('steed'); + INSERT INTO streetname VALUES('steelewood'); + INSERT INTO streetname VALUES('steepleglen'); + INSERT INTO streetname VALUES('stephens farm'); + INSERT INTO streetname VALUES('stewarton'); + INSERT INTO streetname VALUES('stone park'); + INSERT INTO streetname VALUES('stonebrook'); + INSERT INTO streetname VALUES('stonefield'); + INSERT INTO streetname VALUES('stoneglen'); + INSERT INTO streetname VALUES('stonemarsh'); + INSERT INTO streetname VALUES('stoney garden'); + INSERT INTO streetname VALUES('stoney run'); + INSERT INTO streetname VALUES('stoney valley'); + INSERT INTO streetname VALUES('stoneykirk'); + INSERT INTO streetname VALUES('stream bank'); + INSERT INTO streetname VALUES('stream ridge'); + INSERT INTO streetname VALUES('suburban'); + INSERT INTO streetname VALUES('suffield'); + INSERT INTO streetname VALUES('sugar creek'); + INSERT INTO streetname VALUES('sugarberry'); + INSERT INTO streetname VALUES('sugarstone'); + INSERT INTO streetname VALUES('summer creek'); + INSERT INTO streetname VALUES('summer valley'); + INSERT INTO streetname VALUES('summercrest'); + INSERT INTO streetname VALUES('summercroft'); + INSERT INTO streetname VALUES('summerford'); + INSERT INTO streetname VALUES('summergold'); + INSERT INTO streetname VALUES('sunbeam'); + INSERT INTO streetname VALUES('sunbridge'); + INSERT INTO streetname VALUES('sunpath'); + INSERT INTO streetname VALUES('sunset'); + INSERT INTO streetname VALUES('sunset ridge'); + INSERT INTO streetname VALUES('sunstone'); + INSERT INTO streetname VALUES('suntrace'); + INSERT INTO streetname VALUES('sunwalk'); + INSERT INTO streetname VALUES('sutters hill'); + INSERT INTO streetname VALUES('suttonview'); + INSERT INTO streetname VALUES('swallow tail'); + INSERT INTO streetname VALUES('swanston'); + INSERT INTO streetname VALUES('sweet grove'); + INSERT INTO streetname VALUES('sweet rose'); + INSERT INTO streetname VALUES('sweetbriar ridge'); + INSERT INTO streetname VALUES('sweetfield'); + INSERT INTO streetname VALUES('sydney overlook'); + INSERT INTO streetname VALUES('sylvan'); + INSERT INTO streetname VALUES('symphony woods'); + INSERT INTO streetname VALUES('tallia'); + INSERT INTO streetname VALUES('tallu'); + INSERT INTO streetname VALUES('talwyn'); + INSERT INTO streetname VALUES('tanager'); + INSERT INTO streetname VALUES('tanager park'); + INSERT INTO streetname VALUES('tangley'); + INSERT INTO streetname VALUES('taranasay'); + INSERT INTO streetname VALUES('tarby'); + INSERT INTO streetname VALUES('tarland'); + INSERT INTO streetname VALUES('tarpway'); + INSERT INTO streetname VALUES('tauten'); + INSERT INTO streetname VALUES('taymouth'); + INSERT INTO streetname VALUES('ten trees'); + INSERT INTO streetname VALUES('terrace view'); + INSERT INTO streetname VALUES('terrier'); + INSERT INTO streetname VALUES('tesh'); + INSERT INTO streetname VALUES('teton'); + INSERT INTO streetname VALUES('tewkesbury'); + INSERT INTO streetname VALUES('thelema'); + INSERT INTO streetname VALUES('thistle bloom'); + INSERT INTO streetname VALUES('thistledown'); + INSERT INTO streetname VALUES('thomas ridge'); + INSERT INTO streetname VALUES('thornbrook'); + INSERT INTO streetname VALUES('tifton grass'); + INSERT INTO streetname VALUES('tigerton'); + INSERT INTO streetname VALUES('tomsie efird'); + INSERT INTO streetname VALUES('tor'); + INSERT INTO streetname VALUES('torphin'); + INSERT INTO streetname VALUES('torrence'); + INSERT INTO streetname VALUES('towering pine'); + INSERT INTO streetname VALUES('towhee'); + INSERT INTO streetname VALUES('toxaway'); + INSERT INTO streetname VALUES('tracy glenn'); + INSERT INTO streetname VALUES('tradition view'); + INSERT INTO streetname VALUES('trailer'); + INSERT INTO streetname VALUES('transport'); + INSERT INTO streetname VALUES('trehurst'); + INSERT INTO streetname VALUES('trexler'); + INSERT INTO streetname VALUES('trillium fields'); + INSERT INTO streetname VALUES('trimbach'); + INSERT INTO streetname VALUES('tucker'); + INSERT INTO streetname VALUES('tullamore'); + INSERT INTO streetname VALUES('tullock creek'); + INSERT INTO streetname VALUES('tunston'); + INSERT INTO streetname VALUES('tupelo'); + INSERT INTO streetname VALUES('turnabout'); + INSERT INTO streetname VALUES('turney'); + INSERT INTO streetname VALUES('turtle cross'); + INSERT INTO streetname VALUES('turtleback'); + INSERT INTO streetname VALUES('twelvestone'); + INSERT INTO streetname VALUES('twin'); + INSERT INTO streetname VALUES('twin brook'); + INSERT INTO streetname VALUES('twin lakes'); + INSERT INTO streetname VALUES('twisted pine'); + INSERT INTO streetname VALUES('tyler finley'); + INSERT INTO streetname VALUES('university station'); + INSERT INTO streetname VALUES('uphill'); + INSERT INTO streetname VALUES('valeview'); + INSERT INTO streetname VALUES('valhalla'); + INSERT INTO streetname VALUES('van'); + INSERT INTO streetname VALUES('vance davis'); + INSERT INTO streetname VALUES('vanhoy'); + INSERT INTO streetname VALUES('veckman'); + INSERT INTO streetname VALUES('victoria'); + INSERT INTO streetname VALUES('victory'); + INSERT INTO streetname VALUES('village glen'); + INSERT INTO streetname VALUES('vireo'); + INSERT INTO streetname VALUES('viscount'); + INSERT INTO streetname VALUES('voeltz'); + INSERT INTO streetname VALUES('wade e morgan'); + INSERT INTO streetname VALUES('wake'); + INSERT INTO streetname VALUES('wales'); + INSERT INTO streetname VALUES('wallace ridge'); + INSERT INTO streetname VALUES('waltham'); + INSERT INTO streetname VALUES('wanamassa'); + INSERT INTO streetname VALUES('warbler wood'); + INSERT INTO streetname VALUES('washington'); + INSERT INTO streetname VALUES('water'); + INSERT INTO streetname VALUES('waterelm'); + INSERT INTO streetname VALUES('waterford hills'); + INSERT INTO streetname VALUES('waterford valley'); + INSERT INTO streetname VALUES('waterloo'); + INSERT INTO streetname VALUES('waterton leas'); + INSERT INTO streetname VALUES('waverly lynn'); + INSERT INTO streetname VALUES('waverlyglen'); + INSERT INTO streetname VALUES('wayside'); + INSERT INTO streetname VALUES('westbury lake'); + INSERT INTO streetname VALUES('westray'); + INSERT INTO streetname VALUES('whistlers chase'); + INSERT INTO streetname VALUES('whistley green'); + INSERT INTO streetname VALUES('whistling oak'); + INSERT INTO streetname VALUES('whitcomb'); + INSERT INTO streetname VALUES('white aspen'); + INSERT INTO streetname VALUES('white cascade'); + INSERT INTO streetname VALUES('white mist'); + INSERT INTO streetname VALUES('white rock'); + INSERT INTO streetname VALUES('white stag'); + INSERT INTO streetname VALUES('whitegate'); + INSERT INTO streetname VALUES('whitehill'); + INSERT INTO streetname VALUES('whitetail'); + INSERT INTO streetname VALUES('whitewood'); + INSERT INTO streetname VALUES('wilburn park'); + INSERT INTO streetname VALUES('wild garden'); + INSERT INTO streetname VALUES('wild rose'); + INSERT INTO streetname VALUES('wilkins terrace'); + INSERT INTO streetname VALUES('william ficklen'); + INSERT INTO streetname VALUES('wiltshire ridge'); + INSERT INTO streetname VALUES('windchase'); + INSERT INTO streetname VALUES('winding jordan'); + INSERT INTO streetname VALUES('windy meadow'); + INSERT INTO streetname VALUES('winghaven'); + INSERT INTO streetname VALUES('wingmont'); + INSERT INTO streetname VALUES('winslow'); + INSERT INTO streetname VALUES('winter pine'); + INSERT INTO streetname VALUES('winter view'); + INSERT INTO streetname VALUES('wolf creek'); + INSERT INTO streetname VALUES('wondering oak'); + INSERT INTO streetname VALUES('woodard'); + INSERT INTO streetname VALUES('woodfire'); + INSERT INTO streetname VALUES('woodland commons'); + INSERT INTO streetname VALUES('woodland hills'); + INSERT INTO streetname VALUES('woodnotch'); + INSERT INTO streetname VALUES('woodstone'); + INSERT INTO streetname VALUES('worsley'); + INSERT INTO streetname VALUES('wren creek'); + INSERT INTO streetname VALUES('wrens nest'); + INSERT INTO streetname VALUES('wrexham'); + INSERT INTO streetname VALUES('wt harris'); + INSERT INTO streetname VALUES('wylie meadow'); + INSERT INTO streetname VALUES('wynborough'); + INSERT INTO streetname VALUES('wynbrook'); + INSERT INTO streetname VALUES('wyndham hill'); + INSERT INTO streetname VALUES('yandem'); + INSERT INTO streetname VALUES('yellow rose'); + INSERT INTO streetname VALUES('yellow spaniel'); + INSERT INTO streetname VALUES('yorkford'); + INSERT INTO streetname VALUES('ziegler'); + INSERT INTO streetname VALUES('zion renaissance'); + + SELECT count(*) FROM streetname; + } +} {1228} + +do_test fuzzer1-2.1 { + execsql { + SELECT n, distance FROM f2, streetname + WHERE f2.word MATCH 'wersley' + AND f2.distance<=150 + AND f2.word=streetname.n + } +} {worsley 37} +do_test fuzzer1-2.2 { + execsql { + SELECT n, distance FROM f2, streetname + WHERE f2.word MATCH 'testledown' + AND f2.distance<=150 + AND f2.word=streetname.n + } +} {thistledown 103} +do_test fuzzer1-2.3 { + execsql { + SELECT DISTINCT streetname.n FROM f2, streetname + WHERE f2.word MATCH 'tayle' + AND f2.distance<=200 + AND streetname.n>=f2.word AND streetname.n<=(f2.word || x'F7BFBFBF') + } +} {steelewood tallia tallu talwyn taymouth thelema trailer {tyler finley}} + + +finish_test diff --git a/test/incrvacuum2.test b/test/incrvacuum2.test index d8a391b..902517c 100644 --- a/test/incrvacuum2.test +++ b/test/incrvacuum2.test @@ -23,6 +23,7 @@ ifcapable {!autovacuum || !pragma} { return } +set testprefix incrvacuum2 # Create a database in incremental vacuum mode that has many # pages on the freelist. @@ -131,6 +132,80 @@ do_test incrvacuum2-3.2 { } } {} -integrity_check incremental2-3.3 +integrity_check incrvacuum2-3.3 + +ifcapable wal { + # At one point, when a specific page was being extracted from the b-tree + # free-list (e.g. during an incremental-vacuum), all trunk pages that + # occurred before the specific page in the free-list trunk were being + # written to the journal or wal file. This is not necessary. Only the + # extracted page and the page that contains the pointer to it need to + # be journalled. + # + # This problem was fixed by [d03d63d77e] (just before 3.7.6 release). + # + # This test case builds a database containing many free pages. Then runs + # "PRAGMA incremental_vacuum(1)" until the db contains zero free pages. + # Each "PRAGMA incremental_vacuum(1)" should modify at most 4 pages. The + # worst case is when a trunk page is removed from the end of the db file. + # In this case pages written are: + # + # 1. The previous trunk page (that contains a pointer to the recycled + # trunk page), and + # 2. The leaf page transformed into a trunk page to replace the recycled + # page, and + # 3. The trunk page that contained a pointer to the leaf page used + # in (2), and + # 4. Page 1. Page 1 is always updated, even in WAL mode, since it contains + # the "number of free-list pages" field. + # + db close + forcedelete test.db + sqlite3 db test.db + + do_execsql_test 4.1 { + PRAGMA page_size = 512; + PRAGMA auto_vacuum = 2; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 SELECT * FROM t1; -- 2 + INSERT INTO t1 SELECT * FROM t1; -- 4 + INSERT INTO t1 SELECT * FROM t1; -- 8 + INSERT INTO t1 SELECT * FROM t1; -- 16 + INSERT INTO t1 SELECT * FROM t1; -- 32 + INSERT INTO t1 SELECT * FROM t1; -- 128 + INSERT INTO t1 SELECT * FROM t1; -- 256 + INSERT INTO t1 SELECT * FROM t1; -- 512 + INSERT INTO t1 SELECT * FROM t1; -- 1024 + INSERT INTO t1 SELECT * FROM t1; -- 2048 + INSERT INTO t1 SELECT * FROM t1; -- 4096 + INSERT INTO t1 SELECT * FROM t1; -- 8192 + DELETE FROM t1 WHERE oid>512; + DELETE FROM t1; + } + + do_test 4.2 { + execsql { + PRAGMA journal_mode = WAL; + PRAGMA incremental_vacuum(1); + PRAGMA wal_checkpoint; + } + file size test.db-wal + } {1640} + + do_test 4.3 { + db close + sqlite3 db test.db + set maxsz 0 + while {[file size test.db] > [expr 512*3]} { + execsql { PRAGMA journal_mode = WAL } + execsql { PRAGMA wal_checkpoint } + execsql { PRAGMA incremental_vacuum(1) } + set newsz [file size test.db-wal] + if {$newsz>$maxsz} {set maxsz $newsz} + } + set maxsz + } {2176} +} finish_test diff --git a/test/index.test b/test/index.test index 0a41a6d..790bed9 100644 --- a/test/index.test +++ b/test/index.test @@ -354,7 +354,7 @@ do_test index-11.1 { } set sqlite_search_count 0 concat [execsql {SELECT c FROM t3 WHERE b==10}] $sqlite_search_count -} {0.1 3} +} {0.1 2} integrity_check index-11.2 diff --git a/test/indexedby.test b/test/indexedby.test index b8b5be6..7ccc4de 100644 --- a/test/indexedby.test +++ b/test/indexedby.test @@ -154,10 +154,10 @@ do_test indexedby-4.4 { do_execsql_test indexedby-5.1 { CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5; EXPLAIN QUERY PLAN SELECT * FROM v2 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~330000 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~250000 rows)}} do_execsql_test indexedby-5.2 { EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~33000 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~25000 rows)}} do_test indexedby-5.3 { execsql { DROP INDEX i1 } catchsql { SELECT * FROM v2 } diff --git a/test/like.test b/test/like.test index e60d89b..bd9a6c3 100644 --- a/test/like.test +++ b/test/like.test @@ -707,32 +707,32 @@ ifcapable like_opt&&!icu { INSERT INTO t10 VALUES(45,45,45,45,45,45); } count { - SELECT a FROM t10 WHERE b LIKE '12%' ORDER BY a; + SELECT a FROM t10 WHERE b LIKE '12%' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.2 { count { - SELECT a FROM t10 WHERE c LIKE '12%' ORDER BY a; + SELECT a FROM t10 WHERE c LIKE '12%' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.3 { count { - SELECT a FROM t10 WHERE d LIKE '12%' ORDER BY a; + SELECT a FROM t10 WHERE d LIKE '12%' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.4 { count { - SELECT a FROM t10 WHERE e LIKE '12%' ORDER BY a; + SELECT a FROM t10 WHERE e LIKE '12%' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.5 { count { - SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY a; + SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY +a; } } {12 123 scan 3 like 0} do_test like-10.6 { count { - SELECT a FROM t10 WHERE a LIKE '12%' ORDER BY a; + SELECT a FROM t10 WHERE a LIKE '12%' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.10 { @@ -748,32 +748,32 @@ ifcapable like_opt&&!icu { INSERT INTO t10b SELECT * FROM t10; } count { - SELECT a FROM t10b WHERE b GLOB '12*' ORDER BY a; + SELECT a FROM t10b WHERE b GLOB '12*' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.11 { count { - SELECT a FROM t10b WHERE c GLOB '12*' ORDER BY a; + SELECT a FROM t10b WHERE c GLOB '12*' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.12 { count { - SELECT a FROM t10b WHERE d GLOB '12*' ORDER BY a; + SELECT a FROM t10b WHERE d GLOB '12*' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.13 { count { - SELECT a FROM t10b WHERE e GLOB '12*' ORDER BY a; + SELECT a FROM t10b WHERE e GLOB '12*' ORDER BY +a; } } {12 123 scan 5 like 6} do_test like-10.14 { count { - SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY a; + SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY +a; } } {12 123 scan 3 like 0} do_test like-10.15 { count { - SELECT a FROM t10b WHERE a GLOB '12*' ORDER BY a; + SELECT a FROM t10b WHERE a GLOB '12*' ORDER BY +a; } } {12 123 scan 5 like 6} } @@ -819,7 +819,7 @@ do_test like-11.3 { queryplan { PRAGMA case_sensitive_like=OFF; CREATE INDEX t11b ON t11(b); - SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; + SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11b} do_test like-11.4 { @@ -833,37 +833,37 @@ do_test like-11.5 { PRAGMA case_sensitive_like=OFF; DROP INDEX t11b; CREATE INDEX t11bnc ON t11(b COLLATE nocase); - SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; + SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11bnc} do_test like-11.6 { queryplan { CREATE INDEX t11bb ON t11(b COLLATE binary); - SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; + SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11bnc} do_test like-11.7 { queryplan { PRAGMA case_sensitive_like=ON; - SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; + SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd sort {} t11bb} do_test like-11.8 { queryplan { PRAGMA case_sensitive_like=OFF; - SELECT b FROM t11 WHERE b GLOB 'abc*' ORDER BY a; + SELECT b FROM t11 WHERE b GLOB 'abc*' ORDER BY +a; } } {abc abcd sort {} t11bb} do_test like-11.9 { queryplan { CREATE INDEX t11cnc ON t11(c COLLATE nocase); CREATE INDEX t11cb ON t11(c COLLATE binary); - SELECT c FROM t11 WHERE c LIKE 'abc%' ORDER BY a; + SELECT c FROM t11 WHERE c LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11cnc} do_test like-11.10 { queryplan { - SELECT c FROM t11 WHERE c GLOB 'abc*' ORDER BY a; + SELECT c FROM t11 WHERE c GLOB 'abc*' ORDER BY +a; } } {abc abcd sort {} t11cb} diff --git a/test/malloc.test b/test/malloc.test index f1eee17..678b2be 100644 --- a/test/malloc.test +++ b/test/malloc.test @@ -867,7 +867,7 @@ if {[db eval {PRAGMA locking_mode}]!="exclusive"} { catch { db2 close } } -ifcapable stat2 { +ifcapable stat2&&utf16 { do_malloc_test 38 -tclprep { add_test_collate db 0 0 1 execsql { diff --git a/test/malloc_common.tcl b/test/malloc_common.tcl index 379b970..e7f6156 100644 --- a/test/malloc_common.tcl +++ b/test/malloc_common.tcl @@ -117,8 +117,8 @@ proc do_faultsim_test {name args} { set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" - set DEFAULT(-install) "" - set DEFAULT(-uninstall) "" + set DEFAULT(-install) "" + set DEFAULT(-uninstall) "" fix_testname name diff --git a/test/mem5.test b/test/mem5.test new file mode 100644 index 0000000..6365f1c --- /dev/null +++ b/test/mem5.test @@ -0,0 +1,66 @@ +# 2011 March 9 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file contains tests of the mem5 allocation subsystem. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !mem5 { + finish_test + return +} + +# The tests in this file configure the lookaside allocator after a +# connection is opened. This will not work if there is any "presql" +# configured (SQL run within the [sqlite3] wrapper in tester.tcl). +if {[info exists ::G(perm:presql)]} { + finish_test + return +} + +do_test mem5-1.1 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 25000000 0 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_OK} + +# try with min request size = 2^30 +do_test mem5-1.2 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 1 1073741824 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_NOMEM} + +# try with min request size = 2^30+1 +# previously this was causing the memsys5Log() func to infinitely loop. +do_test mem5-1.3 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 1 1073741825 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_NOMEM} + +do_test mem5-1.4 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 0 0 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_OK} + +finish_test diff --git a/test/memdb.test b/test/memdb.test index d987d21..1da3d7c 100644 --- a/test/memdb.test +++ b/test/memdb.test @@ -363,19 +363,13 @@ do_test memdb-6.15 { } } {} -ifcapable subquery { +ifcapable subquery&&vtab { do_test memdb-7.1 { + register_wholenumber_module db execsql { CREATE TABLE t6(x); - INSERT INTO t6 VALUES(1); - INSERT INTO t6 SELECT x+1 FROM t6; - INSERT INTO t6 SELECT x+2 FROM t6; - INSERT INTO t6 SELECT x+4 FROM t6; - INSERT INTO t6 SELECT x+8 FROM t6; - INSERT INTO t6 SELECT x+16 FROM t6; - INSERT INTO t6 SELECT x+32 FROM t6; - INSERT INTO t6 SELECT x+64 FROM t6; - INSERT INTO t6 SELECT x+128 FROM t6; + CREATE VIRTUAL TABLE nums USING wholenumber; + INSERT INTO t6 SELECT value FROM nums WHERE value BETWEEN 1 AND 256; SELECT count(*) FROM (SELECT DISTINCT x FROM t6); } } {256} diff --git a/test/minmax3.test b/test/minmax3.test index 8e8a0a5..483fa61 100644 --- a/test/minmax3.test +++ b/test/minmax3.test @@ -52,6 +52,7 @@ do_test minmax3-1.0 { INSERT INTO t1 VALUES('2', 'V', 'five'); INSERT INTO t1 VALUES('3', 'VI', 'six'); COMMIT; + PRAGMA automatic_index=OFF; } } {} do_test minmax3-1.1.1 { diff --git a/test/multiplex.test b/test/multiplex.test index 742ca50..518cbe3 100644 --- a/test/multiplex.test +++ b/test/multiplex.test @@ -14,7 +14,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl -set g_chunk_size 2147483648 +set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ] set g_max_chunks 32 # This handles appending the chunk number @@ -32,13 +32,17 @@ proc multiplex_name {name chunk} { } # This saves off the parameters and calls the -# underlying sqlite3_multiplex_set() API. -proc multiplex_set {chunk_size max_chunks} { +# underlying sqlite3_multiplex_control() API. +proc multiplex_set {db name chunk_size max_chunks} { global g_chunk_size global g_max_chunks - set g_chunk_size $chunk_size + set g_chunk_size [ expr (($chunk_size+($::SQLITE_MAX_PAGE_SIZE-1)) & ~($::SQLITE_MAX_PAGE_SIZE-1)) ] set g_max_chunks $max_chunks - sqlite3_multiplex_set $chunk_size $max_chunks + set rc [catch {sqlite3_multiplex_control $db $name chunk_size $chunk_size} msg] + if { $rc==0 } { + set rc [catch {sqlite3_multiplex_control $db $name max_chunks $max_chunks} msg] + } + list $msg } # This attempts to delete the base file and @@ -70,14 +74,51 @@ do_test multiplex-1.6 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.8 { sqlite3_multiplex_shutdown } {SQLITE_OK} -do_test multiplex-1.9 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} -do_test multiplex-1.10.1 { multiplex_set 32768 16 } {SQLITE_OK} -do_test multiplex-1.10.2 { multiplex_set 32768 -1 } {SQLITE_MISUSE} -do_test multiplex-1.10.3 { multiplex_set -1 16 } {SQLITE_MISUSE} -do_test multiplex-1.10.4 { multiplex_set 31 16 } {SQLITE_MISUSE} -do_test multiplex-1.10.5 { multiplex_set 32768 100 } {SQLITE_MISUSE} -do_test multiplex-1.11 { sqlite3_multiplex_shutdown } {SQLITE_OK} +do_test multiplex-1.9.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} +do_test multiplex-1.9.2 { sqlite3 db test.db } {} +do_test multiplex-1.9.3 { multiplex_set db main 32768 16 } {SQLITE_OK} +do_test multiplex-1.9.4 { multiplex_set db main 32768 -1 } {SQLITE_MISUSE} +do_test multiplex-1.9.5 { multiplex_set db main -1 16 } {SQLITE_MISUSE} +do_test multiplex-1.9.6 { multiplex_set db main 31 16 } {SQLITE_OK} +do_test multiplex-1.9.7 { multiplex_set db main 32768 100 } {SQLITE_MISUSE} +do_test multiplex-1.9.8 { multiplex_set db main 1073741824 1 } {SQLITE_OK} +do_test multiplex-1.9.9 { db close } {} +do_test multiplex-1.9.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} + +do_test multiplex-1.10.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} +do_test multiplex-1.10.2 { sqlite3 db test.db } {} +do_test multiplex-1.10.3 { lindex [ catchsql { SELECT multiplex_control(2, 32768); } ] 0 } {0} +do_test multiplex-1.10.4 { lindex [ catchsql { SELECT multiplex_control(3, -1); } ] 0 } {1} +do_test multiplex-1.10.5 { lindex [ catchsql { SELECT multiplex_control(2, -1); } ] 0 } {1} +do_test multiplex-1.10.6 { lindex [ catchsql { SELECT multiplex_control(2, 31); } ] 0 } {0} +do_test multiplex-1.10.7 { lindex [ catchsql { SELECT multiplex_control(3, 100); } ] 0 } {1} +do_test multiplex-1.10.8 { lindex [ catchsql { SELECT multiplex_control(2, 1073741824); } ] 0 } {0} +do_test multiplex-1.10.9 { db close } {} +do_test multiplex-1.10.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} + +do_test multiplex-1.11.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} +do_test multiplex-1.11.2 { sqlite3 db test.db } {} +do_test multiplex-1.11.3 { sqlite3_multiplex_control db main enable 0 } {SQLITE_OK} +do_test multiplex-1.11.4 { sqlite3_multiplex_control db main enable 1 } {SQLITE_OK} +do_test multiplex-1.11.5 { sqlite3_multiplex_control db main enable -1 } {SQLITE_OK} +do_test multiplex-1.11.6 { db close } {} +do_test multiplex-1.11.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} + +do_test multiplex-1.12.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} +do_test multiplex-1.12.2 { sqlite3 db test.db } {} +do_test multiplex-1.12.3 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0} +do_test multiplex-1.12.4 { lindex [ catchsql { SELECT multiplex_control(1, 1); } ] 0 } {0} +do_test multiplex-1.12.5 { lindex [ catchsql { SELECT multiplex_control(1, -1); } ] 0 } {0} +do_test multiplex-1.12.6 { db close } {} +do_test multiplex-1.12.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} + +do_test multiplex-1.13.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} +do_test multiplex-1.13.2 { sqlite3 db test.db } {} +do_test multiplex-1.13.3 { lindex [ catchsql { SELECT multiplex_control(-1, 0); } ] 0 } {1} +do_test multiplex-1.13.4 { lindex [ catchsql { SELECT multiplex_control(4, 1); } ] 0 } {1} +do_test multiplex-1.13.6 { db close } {} +do_test multiplex-1.13.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} #------------------------------------------------------------------------- # Some simple warm-body tests with a single database file in rollback @@ -98,9 +139,12 @@ do_test multiplex-1.11 { sqlite3_multiplex_shutdown } {SQLITE_OK} # # multiplex-2.6.*: More reading/writing with varying small chunk sizes, as # well as varying journal mode. +# +# multiplex-2.7.*: Disable/enable tests. +# sqlite3_multiplex_initialize "" 1 -multiplex_set 32768 16 +multiplex_set db main 32768 16 do_test multiplex-2.1.2 { sqlite3 db test.db @@ -130,6 +174,7 @@ do_test multiplex-2.3.1 { db2 close } {} + do_test multiplex-2.4.1 { sqlite3_multiplex_shutdown } {SQLITE_MISUSE} @@ -146,11 +191,11 @@ do_test multiplex-2.4.99 { do_test multiplex-2.5.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 - multiplex_set 4096 16 + sqlite3 db test.db + multiplex_set db main 4096 16 } {SQLITE_OK} do_test multiplex-2.5.2 { - sqlite3 db test.db execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; @@ -165,7 +210,9 @@ do_test multiplex-2.5.3 { INSERT INTO t1 VALUES(2, randomblob(4000)); INSERT INTO t1 VALUES(3, 'three'); INSERT INTO t1 VALUES(4, randomblob(4000)); - INSERT INTO t1 VALUES(5, 'five') + INSERT INTO t1 VALUES(5, 'five'); + INSERT INTO t1 VALUES(6, randomblob($g_chunk_size)); + INSERT INTO t1 VALUES(7, randomblob($g_chunk_size)); } } {} @@ -205,11 +252,11 @@ foreach jmode $all_journal_modes { do_test multiplex-2.6.1.$sz.$jmode { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 - multiplex_set $sz 32 + sqlite3 db test.db + multiplex_set db main $sz 32 } {SQLITE_OK} do_test multiplex-2.6.2.$sz.$jmode { - sqlite3 db test.db db eval { PRAGMA page_size = 1024; PRAGMA auto_vacuum = off; @@ -243,6 +290,31 @@ foreach jmode $all_journal_modes { } } +do_test multiplex-2.7.1 { multiplex_delete test.db } {} +do_test multiplex-2.7.2 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} +do_test multiplex-2.7.3 { sqlite3 db test.db } {} +do_test multiplex-2.7.4 { lindex [ catchsql { SELECT multiplex_control(2, 65536); } ] 0 } {0} +do_test multiplex-2.7.5 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0} +do_test multiplex-2.7.6 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, randomblob(1000)); + } +} {} +# verify only one file, and file size is less than chunks size +do_test multiplex-2.7.7 { expr ([file size [multiplex_name test.db 0]] < 65536) } {1} +do_test multiplex-2.7.8 { file exists [multiplex_name test.db 1] } {0} +do_test multiplex-2.7.9 { + execsql { + INSERT INTO t1 VALUES(2, randomblob(65536)); + } +} {} +# verify only one file, and file size exceeds chunks size +do_test multiplex-2.7.10 { expr ([file size [multiplex_name test.db 0]] > 65536) } {1} +do_test multiplex-2.7.11 { file exists [multiplex_name test.db 1] } {0} +do_test multiplex-2.7.12 { db close } {} +do_test multiplex-2.7.13 { sqlite3_multiplex_shutdown } {SQLITE_OK} + #------------------------------------------------------------------------- # Try some tests with more than one connection to a database file. Still # in rollback mode. @@ -255,10 +327,10 @@ foreach jmode $all_journal_modes { do_test multiplex-3.1.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 - multiplex_set 32768 16 + sqlite3 db test.db + multiplex_set db main 32768 16 } {SQLITE_OK} do_test multiplex-3.1.2 { - sqlite3 db test.db execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; @@ -341,7 +413,7 @@ do_test multiplex-3.2.X { # sqlite3_multiplex_initialize "" 1 -multiplex_set 32768 16 +multiplex_set db main 32768 16 # Return a list of all currently defined multiplexs. proc multiplex_list {} { @@ -403,7 +475,7 @@ do_test multiplex-4.1.12 { # sqlite3_multiplex_initialize "" 1 -multiplex_set 32768 16 +multiplex_set db main 32768 16 do_faultsim_test multiplex-5.1 -prep { catch {db close} @@ -448,7 +520,7 @@ do_faultsim_test multiplex-5.5 -prep { catch { sqlite3_multiplex_shutdown } } -body { sqlite3_multiplex_initialize "" 1 - multiplex_set 32768 16 + multiplex_set db main 32768 16 } # test that mismatch filesize is detected @@ -473,20 +545,20 @@ if {0==[info exists ::G(perm:presql)] || $::G(perm:presql) == ""} { do_test multiplex-5.6.2.$jmode { execsql { CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, randomblob(1100)); - INSERT INTO t1 VALUES(2, randomblob(1100)); - INSERT INTO t1 VALUES(3, randomblob(1100)); - INSERT INTO t1 VALUES(4, randomblob(1100)); - INSERT INTO t1 VALUES(5, randomblob(1100)); + INSERT INTO t1 VALUES(1, randomblob(15000)); + INSERT INTO t1 VALUES(2, randomblob(15000)); + INSERT INTO t1 VALUES(3, randomblob(15000)); + INSERT INTO t1 VALUES(4, randomblob(15000)); + INSERT INTO t1 VALUES(5, randomblob(15000)); } db close sqlite3_multiplex_initialize "" 1 - multiplex_set 4096 16 sqlite3 db test.db - } {} + multiplex_set db main 4096 16 + } {SQLITE_OK} do_test multiplex-5.6.3.$jmode { catchsql { - INSERT INTO t1 VALUES(6, randomblob(1100)); + INSERT INTO t1 VALUES(6, randomblob(15000)); } } {1 {disk I/O error}} do_test multiplex-5.6.4.$jmode { diff --git a/test/oserror.test b/test/oserror.test new file mode 100644 index 0000000..2d3a24a --- /dev/null +++ b/test/oserror.test @@ -0,0 +1,122 @@ +# 2011 February 19 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing that error messages are logged via the +# sqlite3_log() mechanism when certain errors are encountered in the +# default unix or windows VFS modules. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +if {$::tcl_platform(platform)!="unix"} { finish_test ; return } +set ::testprefix oserror + +db close +sqlite3_shutdown +test_sqlite3_log xLog +proc xLog {error_code msg} { + if {[string match os_* $msg]} { + lappend ::log $msg + } +} + +proc do_re_test {tn script expression} { + uplevel do_test $tn [list [subst -nocommands { + set res [eval { $script }] + if {[regexp {$expression} [set res]]} { + set {} {$expression} + } else { + set res + } + }]] [list $expression] + +} + +#-------------------------------------------------------------------------- +# Tests oserror-1.* test failures in the open() system call. +# + +# Test a failure in open() due to too many files. +# +# The xOpen() method of the unix VFS calls getcwd() as well as open(). +# Although this does not appear to be documented in the man page, on OSX +# a call to getcwd() may fail if there are no free file descriptors. So +# an error may be reported for either open() or getcwd() here. +# +do_test 1.1.1 { + set ::log [list] + list [catch { + for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } + } msg] $msg +} {1 {unable to open database file}} +do_test 1.1.2 { + catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } +} {1} +do_re_test 1.1.3 { + lindex $::log 0 +} {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } + + +# Test a failure in open() due to the path being a directory. +# +do_test 1.2.1 { + file mkdir dir.db + set ::log [list] + list [catch { sqlite3 dbh dir.db } msg] $msg +} {1 {unable to open database file}} + +do_re_test 1.2.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*dir.db\) - } + +# Test a failure in open() due to the path not existing. +# +do_test 1.3.1 { + set ::log [list] + list [catch { sqlite3 dbh /x/y/z/test.db } msg] $msg +} {1 {unable to open database file}} + +do_re_test 1.3.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*test.db\) - } + +# Test a failure in open() due to the path not existing. +# +do_test 1.4.1 { + set ::log [list] + list [catch { sqlite3 dbh /root/test.db } msg] $msg +} {1 {unable to open database file}} + +do_re_test 1.4.2 { lindex $::log 0 } {^os_unix.c:\d*: \(\d+\) open\(.*test.db\) - } + +#-------------------------------------------------------------------------- +# Tests oserror-1.* test failures in the unlink() system call. +# +do_test 2.1.1 { + set ::log [list] + file mkdir test.db-wal + forcedelete test.db + list [catch { + sqlite3 dbh test.db + execsql { SELECT * FROM sqlite_master } dbh + } msg] $msg +} {1 {disk I/O error}} + +do_re_test 2.1.2 { + lindex $::log 0 +} {^os_unix.c:\d+: \(\d+\) unlink\(.*test.db-wal\) - } +do_test 2.1.3 { + catch { dbh close } + forcedelete test.db-wal +} {} + + +test_syscall reset +sqlite3_shutdown +test_sqlite3_log +sqlite3_initialize +finish_test diff --git a/test/pager1.test b/test/pager1.test index 481ef4a..8c31e15 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -1992,7 +1992,7 @@ do_test pager1-22.1.1 { INSERT INTO ko DEFAULT VALUES; } execsql { PRAGMA wal_checkpoint } -} {} +} {0 -1 -1} do_test pager1-22.2.1 { testvfs tv -default 1 tv filter xSync diff --git a/test/pagerfault3.test b/test/pagerfault3.test index 6f63b83..3d192e6 100644 --- a/test/pagerfault3.test +++ b/test/pagerfault3.test @@ -26,6 +26,7 @@ if {[permutation] == "inmemory_journal"} { # do_test pagerfault3-pre1 { execsql { + PRAGMA auto_vacuum = 0; PRAGMA page_size = 2048; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(1200)); diff --git a/test/permutations.test b/test/permutations.test index d336a3a..9c48d9a 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -181,6 +181,8 @@ test_suite "fts3" -prefix "" -description { fts3near.test fts3query.test fts3shared.test fts3snippet.test fts3fault.test fts3malloc.test fts3matchinfo.test + + fts3aux1.test fts3comp1.test } @@ -196,6 +198,7 @@ test_suite "coverage-wal" -description { wal.test wal2.test wal3.test walmode.test walbak.test walhook.test walcrash2.test walcksum.test walfault.test walbig.test walnoshm.test + wal5.test } test_suite "coverage-pager" -description { diff --git a/test/releasetest.tcl b/test/releasetest.tcl index 71ad431..84542bb 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -60,11 +60,15 @@ array set ::Configs { "Default" { -O2 } + "Ftrapv" { + -O2 -ftrapv + -DSQLITE_MAX_ATTACHED=55 + -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + } "Unlock-Notify" { -O2 -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_THREADSAFE - -DOS_UNIX -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 } "Secure-Delete" { @@ -145,6 +149,7 @@ array set ::Configs { } "Extra-Robustness" { -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + -DSQLITE_MAX_ATTACHED=62 } } @@ -156,6 +161,7 @@ array set ::Platforms { "Debug-One" test "Extra-Robustness" test "Device-Two" test + "Ftrapv" test "Default" "threadtest test" "Device-One" fulltest } diff --git a/test/superlock.test b/test/superlock.test index 41a5514..b12863f 100644 --- a/test/superlock.test +++ b/test/superlock.test @@ -60,7 +60,7 @@ do_execsql_test 2.1 { do_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 2.3 { SELECT * FROM t1 } {1 {database is locked}} do_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} -do_catchsql_test 2.5 { PRAGMA wal_checkpoint } {1 {database is locked}} +do_catchsql_test 2.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}} do_test 2.6 { unlock } {} do_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) } @@ -68,15 +68,15 @@ do_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) } do_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 3.3 { SELECT * FROM t1 } {1 {database is locked}} do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} -do_catchsql_test 3.5 { PRAGMA wal_checkpoint } {1 {database is locked}} +do_catchsql_test 3.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}} do_test 3.6 { unlock } {} -do_execsql_test 4.1 { PRAGMA wal_checkpoint } {} +do_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 2 2} do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 4.3 { SELECT * FROM t1 } {1 {database is locked}} do_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} -do_catchsql_test 4.5 { PRAGMA wal_checkpoint } {1 {database is locked}} +do_catchsql_test 4.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}} do_test 4.6 { unlock } {} do_multiclient_test tn { @@ -116,7 +116,7 @@ do_multiclient_test tn { do_test 5.$tn.5 { csql3 { INSERT INTO t1 VALUES(5, 6) } } {1 {database is locked}} - do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {1 {database is locked}} + do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {0 {1 -1 -1}} do_test 5.$tn.7 { unlock } {} @@ -220,7 +220,7 @@ db_swap test.db2 test.db do_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}} do_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}} -do_execsql_test 6.6 { PRAGMA wal_checkpoint } +do_execsql_test 6.6 { PRAGMA wal_checkpoint } {0 0 0} db_swap test.db2 test.db do_catchsql_test 6.7 { SELECT * FROM t1 } {1 {no such table: t1}} diff --git a/test/syscall.test b/test/syscall.test new file mode 100644 index 0000000..4442612 --- /dev/null +++ b/test/syscall.test @@ -0,0 +1,276 @@ +# 2011 March 29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl + +if {[llength [info commands test_syscall]]==0} { + finish_test + return +} + +if {[test_syscall defaultvfs] != "unix"} { + finish_test + return +} +set testprefix syscall + +#------------------------------------------------------------------------- +# Tests for the xSetSystemCall method. +# +do_test 1.1.1 { + list [catch { test_syscall reset open } msg] $msg +} {0 {}} +do_test 1.1.2 { + list [catch { test_syscall reset nosuchcall } msg] $msg +} {1 SQLITE_NOTFOUND} +do_test 1.1.3 { + list [catch { test_syscall reset open } msg] $msg +} {0 {}} +do_test 1.1.4 { + list [catch { test_syscall reset ""} msg] $msg +} {1 SQLITE_NOTFOUND} + +do_test 1.2 { test_syscall reset } {} + +do_test 1.3.1 { test_syscall install {open getcwd access} } {} +do_test 1.3.2 { test_syscall reset } {} + +#------------------------------------------------------------------------- +# Tests for the xGetSystemCall method. +# +do_test 2.1.1 { test_syscall exists open } 1 +do_test 2.1.2 { test_syscall exists nosuchcall } 0 + +#------------------------------------------------------------------------- +# Tests for the xNextSystemCall method. +# +foreach s { + open close access getcwd stat fstat ftruncate + fcntl read pread write pwrite fchmod fallocate + pread64 pwrite64 +} { + if {[test_syscall exists $s]} {lappend syscall_list $s} +} +do_test 3.1 { lsort [test_syscall list] } [lsort $syscall_list] + +#------------------------------------------------------------------------- +# This test verifies that if a call to open() fails and errno is set to +# EINTR, the call is retried. If it succeeds, execution continues as if +# nothing happened. +# +test_syscall reset +forcedelete test.db2 +do_execsql_test 4.1 { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 2); + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t2(x, y); + INSERT INTO t2 VALUES(3, 4); +} + +db_save_and_close +test_syscall install open +foreach jrnl [list wal delete] { + for {set i 1} {$i < 20} {incr i} { + db_restore_and_reopen + test_syscall fault $i 0 + test_syscall errno open EINTR + + do_test 4.2.$jrnl.$i { + sqlite3 db test.db + execsql { ATTACH 'test.db2' AS aux } + execsql "PRAGMA main.journal_mode = $jrnl" + execsql "PRAGMA aux.journal_mode = $jrnl" + execsql { + BEGIN; + INSERT INTO t1 VALUES(5, 6); + INSERT INTO t2 VALUES(7, 8); + COMMIT; + } + + db close + sqlite3 db test.db + execsql { ATTACH 'test.db2' AS aux } + execsql { + SELECT * FROM t1; + SELECT * FROM t2; + } + } {1 2 5 6 3 4 7 8} + } +} + +#------------------------------------------------------------------------- +# This test verifies that closing database handles does not drop locks +# held by other database handles in the same process on the same file. +# +# The os_unix.c module has to take precautions to prevent this as the +# close() system call drops locks held by other file-descriptors on the +# same file. From the Linux man page: +# +# close() closes a file descriptor, so that it no longer refers to any file +# and may be reused. Any record locks (see fcntl(2)) held on the file it +# was associated with, and owned by the process, are removed (regardless +# of the file descriptor that was used to obtain the lock). +# +catch { db close } +forcedelete test.db test.db2 + +do_multiclient_test tn { + code1 { + sqlite3 dbX1 test.db + sqlite3 dbX2 test.db + } + + do_test syscall-5.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + BEGIN; + INSERT INTO t1 VALUES(3, 4); + } + } {} + + do_test syscall-5.$tn.2 { sql2 { SELECT * FROM t1 } } {1 2} + do_test syscall-5.$tn.3 { + csql2 { INSERT INTO t1 VALUES(5, 6) } + } {1 {database is locked}} + + do_test syscall-5.$tn.4 { + code1 { + dbX1 close + dbX2 close + } + } {} + + do_test syscall-5.$tn.5 { + csql2 { INSERT INTO t1 VALUES(5, 6) } + } {1 {database is locked}} + + do_test syscall-5.$tn.6 { sql1 { COMMIT } } {} + + do_test syscall-5.$tn.7 { + csql2 { INSERT INTO t1 VALUES(5, 6) } + } {0 {}} +} + +catch {db close} +do_test 6.1 { + sqlite3 db1 test.db1 + sqlite3 db2 test.db2 + sqlite3 db3 test.db3 + sqlite3 dbM "" + + db2 close + db3 close + dbM close + db1 close +} {} + +do_test 6.2 { + sqlite3 db test.db + execsql { + PRAGMA temp_store = file; + + PRAGMA main.cache_size = 10; + PRAGMA temp.cache_size = 10; + CREATE TABLE temp.tt(a, b); + INSERT INTO tt VALUES(randomblob(500), randomblob(600)); + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; + } + + db close +} {} + +#------------------------------------------------------------------------- +# Test that a database file a single byte in size is treated as an empty +# file. Whereas a file 2 bytes or larger might be considered corrupt. +# +catch { db close } +forcedelete test.db test.db2 + +proc create_db_file {nByte} { + set fd [open test.db w] + fconfigure $fd -translation binary -encoding binary + puts -nonewline $fd [string range "xSQLite" 1 $nByte] + close $fd +} + +foreach {nByte res} { + 1 {0 {}} + 2 {1 {file is encrypted or is not a database}} + 3 {1 {file is encrypted or is not a database}} +} { + do_test 7.$nByte { + create_db_file $nByte + list [catch { + sqlite3 db test.db + execsql { CREATE TABLE t1(a, b) } + } msg] $msg + } $res + catch { db close } +} + +#------------------------------------------------------------------------- +# +catch { db close } +forcedelete test.db test.db2 + +do_test 8.1 { + sqlite3 db test.db + file_control_chunksize_test db main 4096 + file size test.db +} {0} +foreach {tn hint size} { + 1 1000 4096 + 2 1000 4096 + 3 3000 4096 + 4 4096 4096 + 5 4197 8192 +} { + do_test 8.2.$tn { + file_control_sizehint_test db main $hint + file size test.db + } $size +} + +do_test 8.3 { + db close + forcedelete test.db test.db2 + sqlite3 db test.db + file_control_chunksize_test db main 16 + file size test.db +} {0} +foreach {tn hint size} { + 1 5 16 + 2 13 16 + 3 45 48 + 4 48 48 + 5 49 64 +} { + do_test 8.4.$tn { + file_control_sizehint_test db main $hint + file size test.db + } $size +} + +test_syscall reset +finish_test diff --git a/test/sysfault.test b/test/sysfault.test new file mode 100644 index 0000000..07d525c --- /dev/null +++ b/test/sysfault.test @@ -0,0 +1,247 @@ +# 2011 March 28 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl + +if {[llength [info commands test_syscall]]==0} { + finish_test + return +} + +set testprefix sysfault + +set FAULTSIM(vfsfault-transient) [list \ + -injectinstall vfsfault_install \ + -injectstart vfsfault_injectstart_t \ + -injectstop vfsfault_injectstop \ + -injecterrlist {} \ + -injectuninstall {test_syscall uninstall} \ +] +set FAULTSIM(vfsfault-persistent) [list \ + -injectinstall vfsfault_install \ + -injectstart vfsfault_injectstart_p \ + -injectstop vfsfault_injectstop \ + -injecterrlist {} \ + -injectuninstall {test_syscall uninstall} \ +] + +proc vfsfault_injectstart_t {iFail} { test_syscall fault $iFail 0 } +proc vfsfault_injectstart_p {iFail} { test_syscall fault $iFail 1 } +proc vfsfault_injectstop {} { test_syscall fault } + +faultsim_save_and_close + + +set open_and_write_body { + sqlite3 db test.db + db eval { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + PRAGMA journal_mode = WAL; + INSERT INTO t1 VALUES(3, 4); + SELECT * FROM t1; + CREATE TEMP TABLE t2(x); + INSERT INTO t2 VALUES('y'); + } +} + +proc vfsfault_install {} { test_syscall install {open getcwd} } +do_faultsim_test 1 -faults vfsfault-* -prep { + faultsim_restore +} -body $open_and_write_body -test { + faultsim_test_result {0 {wal 1 2 3 4}} \ + {1 {unable to open database file}} \ + {1 {attempt to write a readonly database}} +} + +#------------------------------------------------------------------------- +# Errors in the fstat() function when opening and writing a file. Cases +# where fstat() fails and sets errno to ENOMEM and EOVERFLOW are both +# tested. EOVERFLOW is interpreted as meaning that a file on disk is +# too large to be opened by the OS. +# +foreach {tn errno errlist} { + 1 ENOMEM {{disk I/O error}} + 2 EOVERFLOW {{disk I/O error} {large file support is disabled}} +} { + proc vfsfault_install {} { test_syscall install fstat } + set errs [list] + foreach e $errlist { lappend errs [list 1 $e] } + do_faultsim_test 1.2.$tn -faults vfsfault-* -prep { + faultsim_restore + } -body " + test_syscall errno fstat $errno + $open_and_write_body + " -test " + faultsim_test_result {0 {wal 1 2 3 4}} $errs + " +} + +#------------------------------------------------------------------------- +# Various errors in locking functions. +# +foreach vfs {unix unix-excl} { + foreach {tn errno errlist} { + 1 EAGAIN {{database is locked} {disk I/O error}} + 2 ETIMEDOUT {{database is locked} {disk I/O error}} + 3 EBUSY {{database is locked} {disk I/O error}} + 4 EINTR {{database is locked} {disk I/O error}} + 5 ENOLCK {{database is locked} {disk I/O error}} + 6 EACCES {{database is locked} {disk I/O error}} + 7 EPERM {{access permission denied} {disk I/O error}} + 8 EDEADLK {{disk I/O error}} + 9 ENOMEM {{disk I/O error}} + } { + proc vfsfault_install {} { test_syscall install fcntl } + set errs [list] + foreach e $errlist { lappend errs [list 1 $e] } + + set body [string map [list %VFS% $vfs] { + sqlite3 db test.db + db eval { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + set fd [open test.db-journal w] + puts $fd "hello world" + close $fd + sqlite3 db test.db -vfs %VFS% + db eval { + SELECT * FROM t1; + } + }] + + do_faultsim_test 1.3.$vfs.$tn -faults vfsfault-* -prep { + faultsim_restore + } -body " + test_syscall errno fcntl $errno + $body + " -test " + faultsim_test_result {0 {1 2}} $errs + " + } +} + +#------------------------------------------------------------------------- +# Check that a single EINTR error does not affect processing. +# +proc vfsfault_install {} { + test_syscall reset + test_syscall install {open ftruncate close read pread pread64 write fallocate} +} + +forcedelete test.db test.db2 +sqlite3 db test.db +do_test 2.setup { + execsql { + CREATE TABLE t1(a, b, c, PRIMARY KEY(a)); + INSERT INTO t1 VALUES('abc', 'def', 'ghi'); + ATTACH 'test.db2' AS 'aux'; + CREATE TABLE aux.t2(x); + INSERT INTO t2 VALUES(1); + } + faultsim_save_and_close +} {} + +do_faultsim_test 2.1 -faults vfsfault-transient -prep { + catch { db close } + faultsim_restore +} -body { + test_syscall errno open EINTR + test_syscall errno ftruncate EINTR + test_syscall errno close EINTR + test_syscall errno read EINTR + test_syscall errno pread EINTR + test_syscall errno pread64 EINTR + test_syscall errno write EINTR + test_syscall errno fallocate EINTR + + sqlite3 db test.db + file_control_chunksize_test db main 8192 + + set res [db eval { + ATTACH 'test.db2' AS 'aux'; + SELECT * FROM t1; + PRAGMA journal_mode = truncate; + BEGIN; + INSERT INTO t1 VALUES('jkl', 'mno', 'pqr'); + INSERT INTO t1 VALUES(randomblob(10000), 0, 0); + UPDATE t2 SET x = 2; + COMMIT; + DELETE FROM t1 WHERE length(a)>3; + SELECT * FROM t1; + SELECT * FROM t2; + }] + db close + set res +} -test { + faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}} +} + +do_faultsim_test 2.2 -faults vfsfault-* -prep { + catch { db close } + faultsim_restore +} -body { + sqlite3 db test.db + set res [db eval { + ATTACH 'test.db2' AS 'aux'; + SELECT * FROM t1; + PRAGMA journal_mode = truncate; + BEGIN; + INSERT INTO t1 VALUES('jkl', 'mno', 'pqr'); + UPDATE t2 SET x = 2; + COMMIT; + SELECT * FROM t1; + SELECT * FROM t2; + }] + db close + set res +} -test { + faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}} \ + {1 {unable to open database file}} \ + {1 {unable to open database: test.db2}} \ + {1 {attempt to write a readonly database}} \ + {1 {disk I/O error}} +} + +#------------------------------------------------------------------------- + +proc vfsfault_install {} { + test_syscall reset + test_syscall install {fstat fallocate} +} +do_faultsim_test 3 -faults vfsfault-* -prep { + faultsim_delete_and_reopen + file_control_chunksize_test db main 8192 + execsql { + CREATE TABLE t1(a, b); + BEGIN; + SELECT * FROM t1; + } +} -body { + test_syscall errno fstat EIO + test_syscall errno fallocate EIO + + execsql { + INSERT INTO t1 VALUES(randomblob(10000), randomblob(10000)); + SELECT length(a) + length(b) FROM t1; + COMMIT; + } +} -test { + faultsim_test_result {0 20000} +} + +finish_test + diff --git a/test/tableapi.test b/test/tableapi.test index e5d0ac0..122267f 100644 --- a/test/tableapi.test +++ b/test/tableapi.test @@ -231,6 +231,14 @@ ifcapable schema_pragmas { } {0 1 1 user_version 0} } +# do_malloc_test closes and deletes the usual db connections and files on +# each iteration. $::dbx is a seperate connection, and on Windows, will +# cause the file deletion of test.db to fail, so we move the close of $::dbx +# up to here before the do_malloc_test. +do_test tableapi-99.0 { + sqlite3_close $::dbx +} {SQLITE_OK} + ifcapable memdebug { do_malloc_test tableapi-7 -sqlprep { DROP TABLE IF EXISTS t1; @@ -245,8 +253,4 @@ ifcapable memdebug { } } -do_test tableapi-99.0 { - sqlite3_close $::dbx -} {SQLITE_OK} - finish_test diff --git a/test/tester.tcl b/test/tester.tcl index 4c7c10e..bae1053 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -154,6 +154,7 @@ if {[info exists cmdlinearg]==0} { # --backtrace=N # --binarylog=N # --soak=N + # --start=[$permutation:]$testfile # set cmdlinearg(soft-heap-limit) 0 set cmdlinearg(maxerror) 1000 @@ -161,6 +162,7 @@ if {[info exists cmdlinearg]==0} { set cmdlinearg(backtrace) 10 set cmdlinearg(binarylog) 0 set cmdlinearg(soak) 0 + set cmdlinearg(start) "" set leftover [list] foreach a $argv { @@ -195,6 +197,16 @@ if {[info exists cmdlinearg]==0} { foreach {dummy cmdlinearg(soak)} [split $a =] break set ::G(issoak) $cmdlinearg(soak) } + {^-+start=.+$} { + foreach {dummy cmdlinearg(start)} [split $a =] break + + set ::G(start:file) $cmdlinearg(start) + if {[regexp {(.*):(.*)} $cmdlinearg(start) -> s.perm s.file]} { + set ::G(start:permutation) ${s.perm} + set ::G(start:file) ${s.file} + } + if {$::G(start:file) == ""} {unset ::G(start:file)} + } default { lappend leftover $a } @@ -1325,6 +1337,15 @@ proc slave_test_script {script} { proc slave_test_file {zFile} { set tail [file tail $zFile] + if {[info exists ::G(start:permutation)]} { + if {[permutation] != $::G(start:permutation)} return + unset ::G(start:permutation) + } + if {[info exists ::G(start:file)]} { + if {$tail != $::G(start:file) && $tail!="$::G(start:file).test"} return + unset ::G(start:file) + } + # Remember the value of the shared-cache setting. So that it is possible # to check afterwards that it was not modified by the test script. # diff --git a/test/thread1.test b/test/thread1.test index 70bcc22..0618f2c 100644 --- a/test/thread1.test +++ b/test/thread1.test @@ -19,10 +19,7 @@ source $testdir/tester.tcl # Skip this whole file if the thread testing code is not enabled # -ifcapable !mutex { - finish_test - return -} +if {[run_thread_tests]==0} { finish_test ; return } if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} { finish_test return diff --git a/test/thread2.test b/test/thread2.test index 253b3d6..dad2bf2 100644 --- a/test/thread2.test +++ b/test/thread2.test @@ -17,11 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !mutex { - finish_test - return -} - +if {[run_thread_tests]==0} { finish_test ; return } # Skip this whole file if the thread testing code is not enabled # diff --git a/test/thread_common.tcl b/test/thread_common.tcl index 9b7a95d..6b17082 100644 --- a/test/thread_common.tcl +++ b/test/thread_common.tcl @@ -89,6 +89,9 @@ proc run_thread_tests {{print_warning 0}} { ifcapable !mutex { set zProblem "SQLite build is not threadsafe" } + ifcapable mutex_noop { + set zProblem "SQLite build uses SQLITE_MUTEX_NOOP" + } if {[info commands sqlthread] eq ""} { set zProblem "SQLite build is not threadsafe" } diff --git a/test/threadtest3.c b/test/threadtest3.c index 82b4708..cb7e2fa 100644 --- a/test/threadtest3.c +++ b/test/threadtest3.c @@ -1394,6 +1394,7 @@ static void dynamic_triggers(int nMs){ print_and_free_err(&err); } +#include "tt3_checkpoint.c" int main(int argc, char **argv){ struct ThreadTest { @@ -1408,8 +1409,11 @@ int main(int argc, char **argv){ { walthread5, "walthread5", 1000 }, { walthread5, "walthread5", 1000 }, - { cgt_pager_1, "cgt_pager_1", 0 }, + { cgt_pager_1, "cgt_pager_1", 0 }, { dynamic_triggers, "dynamic_triggers", 20000 }, + + { checkpoint_starvation_1, "checkpoint_starvation_1", 10000 }, + { checkpoint_starvation_2, "checkpoint_starvation_2", 10000 }, }; int i; diff --git a/test/tkt-752e1646fc.test b/test/tkt-752e1646fc.test new file mode 100644 index 0000000..3837df8 --- /dev/null +++ b/test/tkt-752e1646fc.test @@ -0,0 +1,30 @@ +# 2010 April 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [752e1646fc] has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-752e1646fc-1.1 { + execsql { + CREATE TABLE "test" ("letter" VARCHAR(1) PRIMARY KEY, "number" INTEGER NOT NULL); + INSERT INTO "test" ("letter", "number") VALUES('b', 1); + INSERT INTO "test" ("letter", "number") VALUES('a', 2); + INSERT INTO "test" ("letter", "number") VALUES('c', 2); + SELECT DISTINCT "number" FROM (SELECT "letter", "number" FROM "test" ORDER BY "letter", "number" LIMIT 1) AS "test"; + } +} {2} + +finish_test diff --git a/test/tkt-b72787b1.test b/test/tkt-b72787b1.test new file mode 100644 index 0000000..11ea41e --- /dev/null +++ b/test/tkt-b72787b1.test @@ -0,0 +1,77 @@ +# 2011 February 21 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [b72787b1a7] has been +# fixed. From the ticket: +# +# The sqlite3ExpirePreparedStatements routine marks all statements +# as expired. This includes statements that are not expired. +# +# Steps to reproduce: +# +# * Prepare a statement (A) +# * Alter the schema to invalidate cookie in A +# * Prepare a statement (B) +# * Run B and have A run as part of B +# * A will find a bad cookie and cause *all* statements +# to be expired including the currently running B by calling +# sqlite3ExpirePreparedStatements +# * When control returns to B it will then abort +# +# The bug is that sqlite3ExpirePreparedStatements expires all statements. +# Note that B was prepared after the schema change and hence is perfectly +# valid and then is marked as expired while running. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +unset -nocomplain ::STMT +proc runsql {} { + db eval {CREATE TABLE IF NOT EXISTS t4(q)} + sqlite3_step $::STMT + set rc [sqlite3_column_int $::STMT 0] + sqlite3_reset $::STMT + return $rc +} + +do_test tkt-b72787b1.1 { + db eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + CREATE TABLE t2(y); + INSERT INTO t2 SELECT x+2 FROM t1; + INSERT INTO t2 SELECT x+4 FROM t1; + } + db func runsql ::runsql + set DB [sqlite3_connection_pointer db] + set sql {SELECT max(x) FROM t1} + set ::STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL] + + # The runsql() call on the second row of the first query will + # cause all $::STMT to hit an expired cookie. Prior to the fix + # for [b72787b1a7, the bad cookie would expire all statements, including + # the following compound SELECT, which would cause a fault when the + # second SELECT was reached. After the fix, the current statement + # continues to completion. + db eval { + SELECT CASE WHEN y=3 THEN y+100 WHEN y==4 THEN runsql()+200 + ELSE 300+y END FROM t2 + UNION ALL + SELECT * FROM t1; + } +} {103 202 305 306 1 2} + +sqlite3_finalize $::STMT + +finish_test diff --git a/test/tkt-f7b4edec.test b/test/tkt-f7b4edec.test new file mode 100644 index 0000000..f6d3d5b --- /dev/null +++ b/test/tkt-f7b4edec.test @@ -0,0 +1,74 @@ +# 2011 March 18 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket +# [f7b4edece25c994857dc139207f55a53c8319fae] has been fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Open two database connections to the same database file in +# shared cache mode. Create update hooks that will fire on +# each connection. +# +db close +set ::enable_shared_cache [sqlite3_enable_shared_cache 1] +sqlite3 db1 test.db +sqlite3 db2 test.db +unset -nocomplain HOOKS +set HOOKS {} +proc update_hook {args} { lappend ::HOOKS $args } +db1 update_hook update_hook +db2 update_hook update_hook + +# Create a prepared statement +# +do_test tkt-f7b4edec-1 { + execsql { CREATE TABLE t1(x, y); } db1 + execsql { INSERT INTO t1 VALUES(1, 2) } db1 + set ::HOOKS +} {{INSERT main t1 1}} + +# In the second database connection cause the schema to be reparsed +# without changing the schema cookie. +# +set HOOKS {} +do_test tkt-f7b4edec-2 { + execsql { + BEGIN; + DROP TABLE t1; + CREATE TABLE t1(x, y); + ROLLBACK; + } db2 + set ::HOOKS +} {} + +# Rerun the prepared statement that was created prior to the +# schema reparse. Verify that the update-hook gives the correct +# output. +# +set HOOKS {} +do_test tkt-f7b4edec-3 { + execsql { INSERT INTO t1 VALUES(1, 2) } db1 + set ::HOOKS +} {{INSERT main t1 2}} + +# Be sure to restore the original shared-cache mode setting before +# returning. +# +db1 close +db2 close +sqlite3_enable_shared_cache $::enable_shared_cache + + +finish_test diff --git a/test/tkt3824.test b/test/tkt3824.test index 5b447bf..3198abb 100644 --- a/test/tkt3824.test +++ b/test/tkt3824.test @@ -72,7 +72,7 @@ do_test tkt3824-2.3 { lsort [execsql_status { SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b; }] -} {5 9 sort} +} {5 9 nosort} do_test tkt3824-3.1 { db eval { diff --git a/test/trace2.test b/test/trace2.test index 2cf50d0..a70dd89 100644 --- a/test/trace2.test +++ b/test/trace2.test @@ -128,7 +128,7 @@ ifcapable fts3 { INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph'); } { "INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph');" - "-- INSERT INTO 'main'.'x1_content' VALUES(?,?)" + "-- INSERT INTO 'main'.'x1_content' VALUES(?,(?))" "-- REPLACE INTO 'main'.'x1_docsize' VALUES(?,?)" "-- SELECT value FROM 'main'.'x1_stat' WHERE id=0" "-- REPLACE INTO 'main'.'x1_stat' VALUES(0,?)" @@ -141,8 +141,8 @@ ifcapable fts3 { INSERT INTO x1(x1) VALUES('optimize'); } { "INSERT INTO x1(x1) VALUES('optimize');" - "-- SELECT count(*), max(level) FROM 'main'.'x1_segdir'" "-- SELECT idx, start_block, leaves_end_block, end_block, root FROM 'main'.'x1_segdir' ORDER BY level DESC, idx ASC" + "-- SELECT count(*), max(level) FROM 'main'.'x1_segdir'" "-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)" "-- DELETE FROM 'main'.'x1_segdir'" "-- INSERT INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)" diff --git a/test/tt3_checkpoint.c b/test/tt3_checkpoint.c new file mode 100644 index 0000000..3c28f0d --- /dev/null +++ b/test/tt3_checkpoint.c @@ -0,0 +1,150 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file is part of the test program "threadtest3". Despite being a C +** file it is not compiled separately, but included by threadtest3.c using +** the #include directive normally used with header files. +** +** This file contains the implementation of test cases: +** +** checkpoint_starvation_1 +** checkpoint_starvation_2 +*/ + +/* +** Both test cases involve 1 writer/checkpointer thread and N reader threads. +** +** Each reader thread performs a series of read transactions, one after +** another. Each read transaction lasts for 100 ms. +** +** The writer writes transactions as fast as possible. It uses a callback +** registered with sqlite3_wal_hook() to try to keep the WAL-size limited to +** around 50 pages. +** +** In test case checkpoint_starvation_1, the auto-checkpoint uses +** SQLITE_CHECKPOINT_PASSIVE. In checkpoint_starvation_2, it uses RESTART. +** The expectation is that in the first case the WAL file will grow very +** large, and in the second will be limited to the 50 pages or thereabouts. +** However, the overall transaction throughput will be lower for +** checkpoint_starvation_2, as every checkpoint will block for up to 200 ms +** waiting for readers to clear. +*/ + +/* Frame limit used by the WAL hook for these tests. */ +#define CHECKPOINT_STARVATION_FRAMELIMIT 50 + +/* Duration in ms of each read transaction */ +#define CHECKPOINT_STARVATION_READMS 100 + +struct CheckpointStarvationCtx { + int eMode; + int nMaxFrame; +}; +typedef struct CheckpointStarvationCtx CheckpointStarvationCtx; + +static int checkpoint_starvation_walhook( + void *pCtx, + sqlite3 *db, + const char *zDb, + int nFrame +){ + CheckpointStarvationCtx *p = (CheckpointStarvationCtx *)pCtx; + if( nFrame>p->nMaxFrame ){ + p->nMaxFrame = nFrame; + } + if( nFrame>=CHECKPOINT_STARVATION_FRAMELIMIT ){ + sqlite3_wal_checkpoint_v2(db, zDb, p->eMode, 0, 0); + } + return SQLITE_OK; +} + +static char *checkpoint_starvation_reader(int iTid, int iArg){ + Error err = {0}; + Sqlite db = {0}; + + opendb(&err, &db, "test.db", 0); + while( !timetostop(&err) ){ + i64 iCount1, iCount2; + sql_script(&err, &db, "BEGIN"); + iCount1 = execsql_i64(&err, &db, "SELECT count(x) FROM t1"); + usleep(CHECKPOINT_STARVATION_READMS*1000); + iCount2 = execsql_i64(&err, &db, "SELECT count(x) FROM t1"); + sql_script(&err, &db, "COMMIT"); + + if( iCount1!=iCount2 ){ + test_error(&err, "Isolation failure - %lld %lld", iCount1, iCount2); + } + } + closedb(&err, &db); + + print_and_free_err(&err); + return 0; +} + +static void checkpoint_starvation_main(int nMs, CheckpointStarvationCtx *p){ + Error err = {0}; + Sqlite db = {0}; + Threadset threads = {0}; + int nInsert = 0; + int i; + + opendb(&err, &db, "test.db", 1); + sql_script(&err, &db, + "PRAGMA page_size = 1024;" + "PRAGMA journal_mode = WAL;" + "CREATE TABLE t1(x);" + ); + + setstoptime(&err, nMs); + + for(i=0; i<4; i++){ + launch_thread(&err, &threads, checkpoint_starvation_reader, 0); + usleep(CHECKPOINT_STARVATION_READMS*1000/4); + } + + sqlite3_wal_hook(db.db, checkpoint_starvation_walhook, (void *)p); + while( !timetostop(&err) ){ + sql_script(&err, &db, "INSERT INTO t1 VALUES(randomblob(1200))"); + nInsert++; + } + + printf(" Checkpoint mode : %s\n", + p->eMode==SQLITE_CHECKPOINT_PASSIVE ? "PASSIVE" : "RESTART" + ); + printf(" Peak WAL : %d frames\n", p->nMaxFrame); + printf(" Transaction count: %d transactions\n", nInsert); + + join_all_threads(&err, &threads); + closedb(&err, &db); + print_and_free_err(&err); +} + +static void checkpoint_starvation_1(int nMs){ + Error err = {0}; + CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_PASSIVE, 0 }; + checkpoint_starvation_main(nMs, &ctx); + if( ctx.nMaxFrame<(CHECKPOINT_STARVATION_FRAMELIMIT*10) ){ + test_error(&err, "WAL failed to grow - %d frames", ctx.nMaxFrame); + } + print_and_free_err(&err); +} + +static void checkpoint_starvation_2(int nMs){ + Error err = {0}; + CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_RESTART, 0 }; + checkpoint_starvation_main(nMs, &ctx); + if( ctx.nMaxFrame>CHECKPOINT_STARVATION_FRAMELIMIT+10 ){ + test_error(&err, "WAL grew too large - %d frames", ctx.nMaxFrame); + } + print_and_free_err(&err); +} + + diff --git a/test/unixexcl.test b/test/unixexcl.test new file mode 100644 index 0000000..057ae0a --- /dev/null +++ b/test/unixexcl.test @@ -0,0 +1,83 @@ +# 2011 March 30 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file contains tests for the "unix-excl" VFS module (part of +# os_unix.c). +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl + +if {$::tcl_platform(platform)!="unix" || [info commands test_syscall]==""} { + finish_test + return +} +set testprefix unixexcl + + + +# Test that when using VFS "unix-excl", the first time the database is read +# a process-wide exclusive lock is taken on it. This means other connections +# within the process may still access the db normally, but connections from +# outside the process cannot. +# +do_multiclient_test tn { + do_test unixexcl-1.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES('hello', 'world'); + } + } {} + do_test unixexcl-1.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world} + do_test unixexcl-1.$tn.3 { + code1 { + db close + sqlite3 db test.db -vfs unix-excl + db eval { SELECT * FROM t1 } + } + } {hello world} + if {$tn==1} { + do_test unixexcl-1.$tn.4.multiproc { + csql2 { SELECT * FROM t1 } + } {1 {database is locked}} + } else { + do_test unixexcl-1.$tn.4.singleproc { + csql2 { SELECT * FROM t1 } + } {0 {hello world}} + } +} + +# Test that when using VFS "unix-excl", if a file is opened in read-only mode +# the behaviour is the same as if VFS "unix" were used. +# +do_multiclient_test tn { + do_test unixexcl-2.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES('hello', 'world'); + } + } {} + do_test unixexcl-2.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world} + do_test unixexcl-2.$tn.3 { + code1 { + db close + sqlite3 db test.db -readonly yes -vfs unix-excl + db eval { SELECT * FROM t1 } + } + } {hello world} + do_test unixexcl-2.$tn.4 { + csql2 { SELECT * FROM t1 } + } {0 {hello world}} +} + +finish_test diff --git a/test/unordered.test b/test/unordered.test new file mode 100644 index 0000000..ca4beac --- /dev/null +++ b/test/unordered.test @@ -0,0 +1,65 @@ +# 2011 April 9 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix unordered + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a); + INSERT INTO t1 VALUES(1, 'xxx'); + INSERT INTO t1 SELECT a+1, b FROM t1; + INSERT INTO t1 SELECT a+2, b FROM t1; + INSERT INTO t1 SELECT a+4, b FROM t1; + INSERT INTO t1 SELECT a+8, b FROM t1; + INSERT INTO t1 SELECT a+16, b FROM t1; + INSERT INTO t1 SELECT a+32, b FROM t1; + INSERT INTO t1 SELECT a+64, b FROM t1; + ANALYZE; +} {} + +foreach idxmode {ordered unordered} { + if {$idxmode == "unordered"} { + execsql { UPDATE sqlite_stat1 SET stat = stat || ' unordered' } + db close + sqlite3 db test.db + } + foreach {tn sql r(ordered) r(unordered)} { + 1 "SELECT * FROM t1 ORDER BY a" + {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} + 2 "SELECT * FROM t1 WHERE a >?" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~32 rows)}} + {0 0 0 {SCAN TABLE t1 (~42 rows)}} + 3 "SELECT * FROM t1 WHERE a = ? ORDER BY rowid" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)} + 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} + 4 "SELECT max(a) FROM t1" + {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 (~1 rows)}} + 5 "SELECT group_concat(b) FROM t1 GROUP BY a" + {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}} + + 6 "SELECT * FROM t1 WHERE a = ?" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + } { + do_eqp_test 1.$idxmode.$tn $sql $r($idxmode) + } +} + +finish_test diff --git a/test/vacuum2.test b/test/vacuum2.test index 1a054cc..35524bb 100644 --- a/test/vacuum2.test +++ b/test/vacuum2.test @@ -16,6 +16,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +# Do not use a codec for tests in this file, as the database file is +# manipulated directly using tcl scripts (using the [hexio_write] command). +# +do_not_use_codec + # If the VACUUM statement is disabled in the current build, skip all # the tests in this file. # diff --git a/test/wal.test b/test/wal.test index 1db08b5..1aa70e0 100644 --- a/test/wal.test +++ b/test/wal.test @@ -19,6 +19,8 @@ source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl +set testprefix wal + ifcapable !wal {finish_test ; return } proc reopen_db {} { @@ -287,8 +289,8 @@ do_test wal-4.6.1 { INSERT INTO t2 VALUES('y', 'z'); ROLLBACK TO save; COMMIT; - SELECT * FROM t2; } + execsql { SELECT * FROM t2 } } {w x} @@ -463,6 +465,7 @@ do_multiclient_test tn { # do_test wal-10.$tn.1 { execsql { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = wal; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); @@ -542,7 +545,7 @@ do_multiclient_test tn { } {1 2 3 4 5 6 7 8 9 10} do_test wal-10.$tn.12 { catchsql { PRAGMA wal_checkpoint } - } {0 {}} ;# Reader no longer block checkpoints + } {0 {0 13 13}} ;# Reader no longer block checkpoints do_test wal-10.$tn.13 { execsql { INSERT INTO t1 VALUES(11, 12) } sql2 {SELECT * FROM t1} @@ -552,7 +555,7 @@ do_multiclient_test tn { # do_test wal-10.$tn.14 { catchsql { PRAGMA wal_checkpoint } - } {0 {}} + } {0 {0 15 13}} # The following series of test cases used to verify another blocking # case in WAL - a case which no longer blocks. @@ -562,10 +565,10 @@ do_multiclient_test tn { } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-10.$tn.16 { catchsql { PRAGMA wal_checkpoint } - } {0 {}} + } {0 {0 15 15}} do_test wal-10.$tn.17 { execsql { PRAGMA wal_checkpoint } - } {} + } {0 15 15} do_test wal-10.$tn.18 { sql3 { BEGIN; SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} @@ -588,13 +591,13 @@ do_multiclient_test tn { # do_test wal-10.$tn.23 { execsql { PRAGMA wal_checkpoint } - } {} + } {0 17 17} do_test wal-10.$tn.24 { sql2 { BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-10.$tn.25 { execsql { PRAGMA wal_checkpoint } - } {} + } {0 17 17} do_test wal-10.$tn.26 { catchsql { INSERT INTO t1 VALUES(15, 16) } } {0 {}} @@ -611,11 +614,11 @@ do_multiclient_test tn { do_test wal-10.$tn.29 { execsql { INSERT INTO t1 VALUES(19, 20) } catchsql { PRAGMA wal_checkpoint } - } {0 {}} + } {0 {0 6 0}} do_test wal-10.$tn.30 { code3 { sqlite3_finalize $::STMT } execsql { PRAGMA wal_checkpoint } - } {} + } {0 6 0} # At one point, if a reader failed to upgrade to a writer because it # was reading an old snapshot, the write-locks were not being released. @@ -654,7 +657,7 @@ do_multiclient_test tn { } {a b c d} do_test wal-10.$tn.36 { catchsql { PRAGMA wal_checkpoint } - } {0 {}} + } {0 {0 16 16}} do_test wal-10.$tn.36 { sql3 { INSERT INTO t1 VALUES('e', 'f') } sql2 { SELECT * FROM t1 } @@ -662,7 +665,7 @@ do_multiclient_test tn { do_test wal-10.$tn.37 { sql2 COMMIT execsql { PRAGMA wal_checkpoint } - } {} + } {0 18 18} } #------------------------------------------------------------------------- @@ -797,8 +800,8 @@ do_test wal-12.5 { UPDATE t1 SET y = 1 WHERE x = 'A'; PRAGMA wal_checkpoint; UPDATE t1 SET y = 0 WHERE x = 'A'; - SELECT * FROM t2; } + execsql { SELECT * FROM t2 } } {B 2} do_test wal-12.6 { file copy -force test.db test2.db @@ -845,6 +848,7 @@ do_test wal-13.1.2 { sqlite3 db test.db execsql { SELECT * FROM t2 } } {B 2} +breakpoint do_test wal-13.1.3 { db close file exists test.db-wal @@ -1029,14 +1033,14 @@ catch { db close } foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} { 1 {sqlite3_wal_checkpoint db} SQLITE_OK 1 1 2 {sqlite3_wal_checkpoint db ""} SQLITE_OK 1 1 - 3 {db eval "PRAGMA wal_checkpoint"} {} 1 1 + 3 {db eval "PRAGMA wal_checkpoint"} {0 10 10} 1 1 4 {sqlite3_wal_checkpoint db main} SQLITE_OK 1 0 5 {sqlite3_wal_checkpoint db aux} SQLITE_OK 0 1 6 {sqlite3_wal_checkpoint db temp} SQLITE_OK 0 0 - 7 {db eval "PRAGMA main.wal_checkpoint"} {} 1 0 - 8 {db eval "PRAGMA aux.wal_checkpoint"} {} 0 1 - 9 {db eval "PRAGMA temp.wal_checkpoint"} {} 0 0 + 7 {db eval "PRAGMA main.wal_checkpoint"} {0 10 10} 1 0 + 8 {db eval "PRAGMA aux.wal_checkpoint"} {0 16 16} 0 1 + 9 {db eval "PRAGMA temp.wal_checkpoint"} {0 -1 -1} 0 0 } { do_test wal-16.$tn.1 { file delete -force test2.db test2.db-wal test2.db-journal @@ -1400,10 +1404,8 @@ do_test wal-20.2 { } {0} do_test wal-20.3 { close $::buddy - execsql { - PRAGMA wal_checkpoint; - SELECT count(*) FROM t1; - } + execsql { PRAGMA wal_checkpoint } + execsql { SELECT count(*) FROM t1 } } {16384} do_test wal-20.4 { db close @@ -1437,8 +1439,8 @@ do_test wal-21.2 { INSERT INTO t1 SELECT randomblob(900), randomblob(900) FROM t1; ROLLBACK TO s; COMMIT; - SELECT * FROM t1; } + execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-21.3 { execsql { PRAGMA integrity_check } @@ -1504,6 +1506,55 @@ do_test wal-23.4 { set ::log } [list SQLITE_OK "Recovered $nPage frames from WAL file $walfile"] + +ifcapable autovacuum { + # This block tests that if the size of a database is reduced by a + # transaction (because of an incremental or auto-vacuum), that no + # data is written to the WAL file for the truncated pages as part + # of the commit. e.g. if a transaction reduces the size of a database + # to N pages, data for page N+1 should not be written to the WAL file + # when committing the transaction. At one point such data was being + # written. + # + catch {db close} + forcedelete test.db + sqlite3 db test.db + do_execsql_test 24.1 { + PRAGMA auto_vacuum = 2; + PRAGMA journal_mode = WAL; + PRAGMA page_size = 1024; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(randomblob(5000)); + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + } {wal} + do_test 24.2 { + execsql { + DELETE FROM t1; + PRAGMA wal_checkpoint; + } + db close + sqlite3 db test.db + file exists test.db-wal + } 0 + do_test 24.3 { + file size test.db + } [expr 84 * 1024] + do_test 24.4 { + execsql { + PRAGMA cache_size = 200; + PRAGMA incremental_vacuum; + PRAGMA wal_checkpoint; + } + file size test.db + } [expr 3 * 1024] + do_test 24.5 { + file size test.db-wal + } 2128 +} + db close sqlite3_shutdown test_sqlite3_log diff --git a/test/wal2.test b/test/wal2.test index e31fe79..8223433 100644 --- a/test/wal2.test +++ b/test/wal2.test @@ -343,12 +343,13 @@ file delete -force test.db test.db-wal test.db-journal do_test wal2-4.1 { sqlite3 db test.db execsql { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; CREATE TABLE data(x); INSERT INTO data VALUES('need xShmOpen to see this'); PRAGMA wal_checkpoint; } -} {wal} +} {wal 0 5 5} do_test wal2-4.2 { db close testvfs tvfs -noshm 1 @@ -622,6 +623,7 @@ set READMARK1_WRITE { foreach {tn sql res expected_locks} { 2 { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE t1(x); @@ -707,6 +709,7 @@ tvfs delete do_test wal2-6.5.1 { sqlite3 db test.db execsql { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = wal; PRAGMA locking_mode = exclusive; CREATE TABLE t2(a, b); @@ -714,7 +717,7 @@ do_test wal2-6.5.1 { INSERT INTO t2 VALUES('I', 'II'); PRAGMA journal_mode; } -} {wal exclusive wal} +} {wal exclusive 0 3 3 wal} do_test wal2-6.5.2 { execsql { PRAGMA locking_mode = normal; @@ -725,7 +728,7 @@ do_test wal2-6.5.2 { } {normal exclusive I II III IV} do_test wal2-6.5.3 { execsql { PRAGMA wal_checkpoint } -} {} +} {0 4 4} db close proc lock_control {method filename handle spec} { @@ -809,9 +812,9 @@ do_test wal2-8.1.2 { CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(8188*1020)); CREATE TABLE t2(y); + PRAGMA wal_checkpoint; } execsql { - PRAGMA wal_checkpoint; SELECT rootpage>=8192 FROM sqlite_master WHERE tbl_name = 't2'; } } {1} @@ -1154,7 +1157,7 @@ if {$::tcl_platform(platform) == "unix"} { } catch { db close } } -} +} #------------------------------------------------------------------------- # Test that "PRAGMA checkpoint_fullsync" appears to be working. @@ -1166,6 +1169,7 @@ foreach {tn sql reslist} { } { faultsim_delete_and_reopen + execsql {PRAGMA auto_vacuum = 0} execsql $sql do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal} @@ -1182,7 +1186,7 @@ foreach {tn sql reslist} { INSERT INTO t1 VALUES(5, 6); COMMIT; -- 1 wal sync PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync - } {10} + } {10 0 5 5 0 2 2} do_test wal2-14.$tn.3 { list $sqlite_sync_count $sqlite_fullsync_count diff --git a/test/wal3.test b/test/wal3.test index 2ed39b8..bd29615 100644 --- a/test/wal3.test +++ b/test/wal3.test @@ -413,6 +413,7 @@ testvfs T -default 1 do_test wal3-6.1.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db + execsql { PRAGMA auto_vacuum = off } execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); @@ -427,7 +428,7 @@ do_test wal3-6.1.2 { } {o t t f} do_test wal3-6.1.3 { execsql { PRAGMA wal_checkpoint } db2 -} {} +} {0 7 7} # At this point the log file has been fully checkpointed. However, # connection [db3] holds a lock that prevents the log from being wrapped. @@ -494,6 +495,7 @@ do_test wal3-6.2.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db sqlite3 db2 test.db + execsql { PRAGMA auto_vacuum = off } execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); @@ -515,7 +517,7 @@ proc lock_callback {method file handle spec} { } do_test wal3-6.2.2 { execsql { PRAGMA wal_checkpoint } -} {} +} {0 7 7} do_test wal3-6.2.3 { set ::R } {h h l b} @@ -617,6 +619,7 @@ do_test wal3-8.1 { sqlite3 db test.db sqlite3 db2 test.db execsql { + PRAGMA auto_vacuum = off; PRAGMA journal_mode = WAL; CREATE TABLE b(c); INSERT INTO b VALUES('Tehran'); @@ -624,7 +627,7 @@ do_test wal3-8.1 { INSERT INTO b VALUES('Markazi'); PRAGMA wal_checkpoint; } -} {wal} +} {wal 0 9 9} do_test wal3-8.2 { execsql { SELECT * FROM b } } {Tehran Qom Markazi} diff --git a/test/wal5.test b/test/wal5.test new file mode 100644 index 0000000..ad6bcfc --- /dev/null +++ b/test/wal5.test @@ -0,0 +1,346 @@ +# 2010 April 13 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the operation of "blocking-checkpoint" +# operations. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/wal_common.tcl +ifcapable !wal {finish_test ; return } + +set testprefix wal5 + +proc db_page_count {{file test.db}} { expr [file size $file] / 1024 } +proc wal_page_count {{file test.db}} { wal_frame_count ${file}-wal 1024 } + + +# A checkpoint may be requested either using the C API or by executing +# an SQL PRAGMA command. To test both methods, all tests in this file are +# run twice - once using each method to request checkpoints. +# +foreach {testprefix do_wal_checkpoint} { + + wal5-pragma { + proc do_wal_checkpoint { dbhandle args } { + array set a $args + foreach key [array names a] { + if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" } + } + + set sql "PRAGMA " + if {[info exists a(-db)]} { append sql "$a(-db)." } + append sql "wal_checkpoint" + if {[info exists a(-mode)]} { append sql " = $a(-mode)" } + + uplevel [list $dbhandle eval $sql] + } + } + + wal5-capi { + proc do_wal_checkpoint { dbhandle args } { + set a(-mode) passive + array set a $args + foreach key [array names a] { + if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" } + } + + if {$a(-mode)!="restart" && $a(-mode)!="full"} { set a(-mode) passive } + + set cmd [list sqlite3_wal_checkpoint_v2 $dbhandle $a(-mode)] + if {[info exists a(-db)]} { lappend sql $a(-db) } + + uplevel $cmd + } + } +} { + + eval $do_wal_checkpoint + + do_multiclient_test tn { + + set ::nBusyHandler 0 + set ::busy_handler_script "" + proc busyhandler {n} { + incr ::nBusyHandler + eval $::busy_handler_script + return 0 + } + + proc reopen_all {} { + code1 {db close} + code2 {db2 close} + code3 {db3 close} + + code1 {sqlite3 db test.db} + code2 {sqlite3 db2 test.db} + code3 {sqlite3 db3 test.db} + + sql1 { PRAGMA synchronous = NORMAL } + code1 { db busy busyhandler } + } + + do_test 1.$tn.1 { + reopen_all + sql1 { + PRAGMA page_size = 1024; + PRAGMA auto_vacuum = 0; + CREATE TABLE t1(x, y); + PRAGMA journal_mode = WAL; + INSERT INTO t1 VALUES(1, zeroblob(1200)); + INSERT INTO t1 VALUES(2, zeroblob(1200)); + INSERT INTO t1 VALUES(3, zeroblob(1200)); + } + expr [file size test.db] / 1024 + } {2} + + # Have connection 2 grab a read-lock on the current snapshot. + do_test 1.$tn.2 { sql2 { BEGIN; SELECT x FROM t1 } } {1 2 3} + + # Attempt a checkpoint. + do_test 1.$tn.3 { + code1 { do_wal_checkpoint db } + list [db_page_count] [wal_page_count] + } {5 9} + + # Write to the db again. The log cannot wrap because of the lock still + # held by connection 2. The busy-handler has not yet been invoked. + do_test 1.$tn.4 { + sql1 { INSERT INTO t1 VALUES(4, zeroblob(1200)) } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {5 12 0} + + # Now do a blocking-checkpoint. Set the busy-handler up so that connection + # 2 releases its lock on the 6th invocation. The checkpointer should then + # proceed to checkpoint the entire log file. Next write should go to the + # start of the log file. + # + set ::busy_handler_script { if {$n==5} { sql2 COMMIT } } + do_test 1.$tn.5 { + code1 { do_wal_checkpoint db -mode restart } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {6 12 6} + do_test 1.$tn.6 { + set ::nBusyHandler 0 + sql1 { INSERT INTO t1 VALUES(5, zeroblob(1200)) } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {6 12 0} + + do_test 1.$tn.7 { + reopen_all + list [db_page_count] [wal_page_count] $::nBusyHandler + } {7 0 0} + + do_test 1.$tn.8 { sql2 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5} + do_test 1.$tn.9 { + sql1 { INSERT INTO t1 VALUES(6, zeroblob(1200)) } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {7 5 0} + do_test 1.$tn.10 { sql3 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5 6} + + set ::busy_handler_script { + if {$n==5} { sql2 COMMIT } + if {$n==6} { set ::db_file_size [db_page_count] } + if {$n==7} { sql3 COMMIT } + } + do_test 1.$tn.11 { + code1 { do_wal_checkpoint db -mode restart } + list [db_page_count] [wal_page_count] $::nBusyHandler + } {10 5 8} + do_test 1.$tn.12 { set ::db_file_size } 10 + } + + #------------------------------------------------------------------------- + # This block of tests explores checkpoint operations on more than one + # database file. + # + proc setup_and_attach_aux {} { + sql1 { ATTACH 'test.db2' AS aux } + sql2 { ATTACH 'test.db2' AS aux } + sql3 { ATTACH 'test.db2' AS aux } + sql1 { + PRAGMA aux.auto_vacuum = 0; + PRAGMA main.auto_vacuum = 0; + PRAGMA main.page_size=1024; PRAGMA main.journal_mode=WAL; + PRAGMA aux.page_size=1024; PRAGMA aux.journal_mode=WAL; + } + } + + proc file_page_counts {} { + list [db_page_count test.db ] \ + [wal_page_count test.db ] \ + [db_page_count test.db2] \ + [wal_page_count test.db2] + } + + # Test that executing "PRAGMA wal_checkpoint" checkpoints all attached + # databases, not just the main db. In capi mode, check that this is + # true if a NULL pointer is passed to wal_checkpoint_v2() in place of a + # database name. + do_multiclient_test tn { + setup_and_attach_aux + do_test 2.1.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + CREATE TABLE aux.t2(a, b); + INSERT INTO t2 VALUES(1, 2); + } + } {} + do_test 2.2.$tn.2 { file_page_counts } {1 5 1 5} + do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 5 5} + do_test 2.1.$tn.4 { file_page_counts } {2 5 2 5} + } + + do_multiclient_test tn { + setup_and_attach_aux + do_test 2.2.$tn.1 { + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + CREATE TABLE aux.t2(a, b); + INSERT INTO t2 VALUES(1, 2); + INSERT INTO t2 VALUES(3, 4); + } + } {} + do_test 2.2.$tn.2 { file_page_counts } {1 5 1 7} + do_test 2.2.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2} + do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 5 5} + do_test 2.2.$tn.5 { file_page_counts } {2 5 2 7} + } + + do_multiclient_test tn { + setup_and_attach_aux + do_test 2.3.$tn.1 { + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + CREATE TABLE aux.t2(a, b); + INSERT INTO t2 VALUES(1, 2); + } + } {} + do_test 2.3.$tn.2 { file_page_counts } {1 5 1 5} + do_test 2.3.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2} + do_test 2.3.$tn.4 { sql1 { INSERT INTO t1 VALUES(3, 4) } } {} + do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {} + do_test 2.3.$tn.6 { file_page_counts } {1 7 1 7} + do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 7 5} + do_test 2.3.$tn.8 { file_page_counts } {1 7 2 7} + } + + # Check that checkpoints block on the correct locks. And respond correctly + # if they cannot obtain those locks. There are three locks that a checkpoint + # may block on (in the following order): + # + # 1. The writer lock: FULL and RESTART checkpoints block until any writer + # process releases its lock. + # + # 2. Readers using part of the log file. FULL and RESTART checkpoints block + # until readers using part (but not all) of the log file have finished. + # + # 3. Readers using any of the log file. After copying data into the + # database file, RESTART checkpoints block until readers using any part + # of the log file have finished. + # + # This test case involves running a checkpoint while there exist other + # processes holding all three types of locks. + # + foreach {tn1 checkpoint busy_on ckpt_expected expected} { + 1 PASSIVE - {0 5 5} - + 2 TYPO - {0 5 5} - + + 3 FULL - {0 7 7} 2 + 4 FULL 1 {1 5 5} 1 + 5 FULL 2 {1 7 5} 2 + 6 FULL 3 {0 7 7} 2 + + 7 RESTART - {0 7 7} 3 + 8 RESTART 1 {1 5 5} 1 + 9 RESTART 2 {1 7 5} 2 + 10 RESTART 3 {1 7 7} 3 + + } { + do_multiclient_test tn { + setup_and_attach_aux + + proc busyhandler {x} { + set ::max_busyhandler $x + if {$::busy_on!="-" && $x==$::busy_on} { return 1 } + switch -- $x { + 1 { sql2 "COMMIT ; BEGIN ; SELECT * FROM t1" } + 2 { sql3 "COMMIT" } + 3 { sql2 "COMMIT" } + } + return 0 + } + set ::max_busyhandler - + + do_test 2.4.$tn1.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + sql2 { BEGIN; INSERT INTO t1 VALUES(3, 4) } + sql3 { BEGIN; SELECT * FROM t1 } + } {1 2} + + do_test 2.4.$tn1.$tn.2 { + code1 { db busy busyhandler } + code1 { do_wal_checkpoint db -mode [string tolower $checkpoint] } + } $ckpt_expected + do_test 2.4.$tn1.$tn.3 { set ::max_busyhandler } $expected + } + } + + + do_multiclient_test tn { + + code1 $do_wal_checkpoint + code2 $do_wal_checkpoint + code3 $do_wal_checkpoint + + do_test 3.$tn.1 { + sql1 { + PRAGMA auto_vacuum = 0; + PRAGMA journal_mode = WAL; + PRAGMA synchronous = normal; + CREATE TABLE t1(x, y); + } + + sql2 { PRAGMA journal_mode } + sql3 { PRAGMA journal_mode } + } {wal} + + do_test 3.$tn.2 { code2 { do_wal_checkpoint db2 } } {0 2 2} + + do_test 3.$tn.3 { code2 { do_wal_checkpoint db2 } } {0 2 2} + + do_test 3.$tn.4 { code3 { do_wal_checkpoint db3 } } {0 2 2} + + code1 {db close} + code2 {db2 close} + code3 {db3 close} + + code1 {sqlite3 db test.db} + code2 {sqlite3 db2 test.db} + code3 {sqlite3 db3 test.db} + + do_test 3.$tn.5 { sql3 { PRAGMA journal_mode } } {wal} + + do_test 3.$tn.6 { code3 { do_wal_checkpoint db3 } } {0 0 0} + } +} + + +finish_test + diff --git a/test/wal_common.tcl b/test/wal_common.tcl index a5b165d..917ad59 100644 --- a/test/wal_common.tcl +++ b/test/wal_common.tcl @@ -18,7 +18,9 @@ proc wal_file_size {nFrame pgsz} { } proc wal_frame_count {zFile pgsz} { + if {[file exists $zFile]==0} { return 0 } set f [file size $zFile] + if {$f < 32} { return 0 } expr {($f - 32) / ($pgsz+24)} } diff --git a/test/walfault.test b/test/walfault.test index d0c907e..3a90f9c 100644 --- a/test/walfault.test +++ b/test/walfault.test @@ -118,6 +118,7 @@ do_faultsim_test walfault-3 -prep { DELETE FROM abc; PRAGMA wal_checkpoint; } + set {} {} } -test { faultsim_test_result {0 {}} } @@ -132,6 +133,7 @@ if {[permutation] != "inmemory_journal"} { faultsim_restore_and_reopen } -body { execsql { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('a', 'b'); @@ -139,7 +141,7 @@ if {[permutation] != "inmemory_journal"} { SELECT * FROM t1; } } -test { - faultsim_test_result {0 {wal a b}} + faultsim_test_result {0 {wal 0 7 7 a b}} faultsim_integrity_check } } @@ -409,6 +411,7 @@ do_faultsim_test walfault-11 -faults shmerr* -prep { db eval { SELECT count(*) FROM abc } sqlite3 db2 test.db -vfs shmfault db2 eval { PRAGMA wal_checkpoint } + set {} {} } -test { faultsim_test_result {0 {}} } @@ -521,6 +524,7 @@ do_faultsim_test walfault-13.3 -prep { do_test walfault-14-pre { faultsim_delete_and_reopen execsql { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE abc(a PRIMARY KEY); @@ -534,11 +538,11 @@ do_faultsim_test walfault-14 -prep { faultsim_restore_and_reopen } -body { db eval { - PRAGMA wal_checkpoint; + PRAGMA wal_checkpoint = full; INSERT INTO abc VALUES(randomblob(1500)); } } -test { - faultsim_test_result {0 {}} + faultsim_test_result {0 {0 10 10}} faultsim_integrity_check set nRow [db eval {SELECT count(*) FROM abc}] if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" } diff --git a/test/where3.test b/test/where3.test index c032f1f..ab75fde 100644 --- a/test/where3.test +++ b/test/where3.test @@ -222,17 +222,18 @@ do_execsql_test where3-3.0 { CREATE INDEX t301c ON t301(c); INSERT INTO t301 VALUES(1,2,3); CREATE TABLE t302(x, y); + INSERT INTO t302 VALUES(4,5); ANALYZE; explain query plan SELECT * FROM t302, t301 WHERE t302.x=5 AND t301.a=t302.y; } { - 0 0 0 {SCAN TABLE t302 (~0 rows)} + 0 0 0 {SCAN TABLE t302 (~1 rows)} 0 1 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } do_execsql_test where3-3.1 { explain query plan SELECT * FROM t301, t302 WHERE t302.x=5 AND t301.a=t302.y; } { - 0 0 1 {SCAN TABLE t302 (~0 rows)} + 0 0 1 {SCAN TABLE t302 (~1 rows)} 0 1 0 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } @@ -341,5 +342,4 @@ do_execsql_test where3-5.3 { 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } - finish_test diff --git a/test/where9.test b/test/where9.test index 1ffc8ef..9a18011 100644 --- a/test/where9.test +++ b/test/where9.test @@ -472,7 +472,7 @@ ifcapable explain { do_execsql_test where9-5.3 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c>=31031 OR d IS NULL) } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?) (~165000 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?) (~125000 rows)} } } diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index c3216f6..fa99f2d 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -51,7 +51,7 @@ puts $out [subst \ {/****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version $VERSION. By combining all the individual C code files into this -** single large file, the entire code can be compiled as a one translation +** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single @@ -294,6 +294,7 @@ foreach file { notify.c fts3.c + fts3_aux.c fts3_expr.c fts3_hash.c fts3_porter.c diff --git a/tool/omittest.tcl b/tool/omittest.tcl index ec1508c..f1963ff 100644 --- a/tool/omittest.tcl +++ b/tool/omittest.tcl @@ -8,10 +8,12 @@ This Tcl script is used to test the various compile time options available for omitting code (the SQLITE_OMIT_xxx options). It should be invoked as follows: -