Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch ttmrichter-skins Excluding Merge-Ins
This is equivalent to a diff from 861a885c74 to 1a6876db8d
2010-11-07
| ||
10:23 | Merge from trunk. Leaf check-in: 1a6876db8d user: michael tags: ttmrichter-skins | |
2010-11-06
| ||
23:59 | Add an undocumented --noverify option to the rebuild command to skip the verify_before_commit() sanity check. check-in: 6a11af1782 user: drh tags: trunk | |
2010-10-08
| ||
17:08 | EDITED www/server.wiki fixed formating and created ticket (I don't see the ticket in commit) check-in: 3f49a84995 user: navratil tags: ttmrichter-skins | |
2010-09-16
| ||
14:00 | Committing a merge. check-in: be5fd10aaf user: michael tags: ttmrichter-skins | |
2010-08-13
| ||
07:07 | Trying to figure out how to get merge conflicts to actually show up. check-in: d8f8932ff2 user: michael tags: ttmrichter-skins | |
03:49 | Merged with trunk. Leaf check-in: 861a885c74 user: michael tags: ttmrichter | |
03:30 | Added build for FreeBSD using clang. check-in: b775af5045 user: michael tags: ttmrichter | |
2010-08-12
| ||
19:39 | Reimplement the reconstruct command that was removed in the GPL to BSD license change. This resolves ticket [dfe1fc608a]. check-in: 3332895df8 user: bcsmith tags: trunk | |
Changes to Makefile.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # intended to direct the compilation below. # -include config.mk # Configure if present. ifndef CONFIG_MK_COMPLETE include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. endif | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | # intended to direct the compilation below. # -include config.mk # Configure if present. ifndef CONFIG_MK_COMPLETE include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. endif #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # #TCC = gcc -O6 #TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage TCC = gcc -g -Os -Wall # To add support for HTTPS TCC += -DFOSSIL_ENABLE_SSL #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library. There are no # other dependencies. We sometimes add the -static option here # so that we can build a static executable that will run in a # chroot jail. # LIB = -lz $(LDFLAGS) # If using HTTPS: LIB += -lcrypto -lssl #### Tcl shell for use in running the fossil testsuite. # TCLSH = tclsh # You should not need to change anything below this line ############################################################################### # # Automatic platform-specific options. HOST_OS!= uname -s LIB.SunOS= -lsocket -lnsl LIB += $(LIB.$(HOST_OS)) TCC.DragonFly += -DUSE_PREAD TCC.FreeBSD += -DUSE_PREAD TCC.NetBSD += -DUSE_PREAD TCC.OpenBSD += -DUSE_PREAD TCC += $(TCC.$(HOST_OS)) include $(SRCDIR)/main.mk |
Added skins/default1/footer.html.
> > > > | 1 2 3 4 | <div class="footer"> Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/default1/header.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <html> <head> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$baseurl/timeline.rss"> <link rel="stylesheet" href="$baseurl/style.css?blackwhite" type="text/css" media="screen"> </head> <body> <div class="header"> <div class="logo"> <img src="$baseurl/logo" alt="logo"> </div> <div class="title"><small>$<project_name></small><br>$<title></div> <div class="status"><nobr><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></nobr></div> </div> <div class="mainmenu"><th1> html "<a href=''$baseurl$index_page''>Home</a> " if {[anycap jor]} { html "<a href=''$baseurl/timeline''>Timeline</a> " } if {[hascap oh]} { html "<a href=''$baseurl/dir?ci=tip''>Files</a> " } if {[hascap o]} { html "<a href=''$baseurl/leaves''>Leaves</a> " html "<a href=''$baseurl/brlist''>Branches</a> " html "<a href=''$baseurl/taglist''>Tags</a> " } if {[hascap r]} { html "<a href=''$baseurl/reportlist''>Tickets</a> " } if {[hascap j]} { html "<a href=''$baseurl/wiki''>Wiki</a> " } if {[hascap s]} { html "<a href=''$baseurl/setup''>Admin</a> " } elseif {[hascap a]} { html "<a href=''$baseurl/setup_ulist''>Users</a> " } if {[info exists login]} { html "<a href=''$baseurl/login''>Logout</a> " } else { html "<a href=''$baseurl/login''>Login</a> " } </th1></div> |
Added skins/default1/info.txt.
> > > > > | 1 2 3 4 5 | Title: Plain Gray Description: A black-and-white theme with the project title in a bar across the top and no logo image. Author: Unknown. |
Added skins/default1/style.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0px; background-color: white; font-family: sans-serif; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-row; text-align: center; /* vertical-align: bottom;*/ font-size: 2em; font-weight: bold; background-color: #707070; color: #ffffff; min-width: 200px; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 1.5em; font-weight: bold; text-align: center; padding: 0 0 0 10px; color: #404040; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: #404040; font-size: 0.8em; font-weight: bold; min-width: 200px; } /* The header across the top of the page */ div.header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath the header */ div.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #404040; color: white; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu { padding: 3px 10px 3px 0px; font-size: 0.9em; text-align: center; background-color: #606060; color: white; } div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover { color: #404040; background-color: white; } /* All page content from the bottom of the menu or submenu down to the footer */ div.content { padding: 0ex 0ex 0ex 0ex; } /* Hyperlink colors */ div.content a { color: #604000; } div.content a:link { color: #604000;} div.content a:visited { color: #600000; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; background-color: #404040; color: white; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #a0a0a0; border: 2px #505050 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; } /* The footer at the very bottom of the page */ div.footer { font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #404040; color: white; } /* The label/value pairs on (for example) the vinfo page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Added skins/default2/footer.html.
> > > > | 1 2 3 4 | <div class="footer"> Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/default2/header.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <html> <head> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$baseurl/timeline.rss"> <link rel="stylesheet" href="$baseurl/style.css?tan" type="text/css" media="screen"> </head> <body> <div class="header"> <div class="title">$<title></div> <div class="status"> <div class="logo"><nobr>$<project_name></nobr></div><br/> <nobr><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></nobr></div> </div> <div class="mainmenu"><th1> html "<a href=''$baseurl$index_page''>Home</a> " if {[anycap jor]} { html "<a href=''$baseurl/timeline''>Timeline</a> " } if {[hascap oh]} { html "<a href=''$baseurl/dir?ci=tip''>Files</a> " } if {[hascap o]} { html "<a href=''$baseurl/leaves''>Leaves</a> " html "<a href=''$baseurl/brlist''>Branches</a> " html "<a href=''$baseurl/taglist''>Tags</a> " } if {[hascap r]} { html "<a href=''$baseurl/reportlist''>Tickets</a> " } if {[hascap j]} { html "<a href=''$baseurl/wiki''>Wiki</a> " } if {[hascap s]} { html "<a href=''$baseurl/setup''>Admin</a> " } elseif {[hascap a]} { html "<a href=''$baseurl/setup_ulist''>Users</a> " } if {[info exists login]} { html "<a href=''$baseurl/login''>Logout</a> " } else { html "<a href=''$baseurl/login''>Login</a> " } </th1></div> |
Added skins/default2/info.txt.
> > > > | 1 2 3 4 | Title: Khaki, No Logo Description: A tan theme with the project title above the user identification and no logo image. Author: Unknown |
Added skins/default2/style.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* General settings for the entire page */ body { margin: 0ex 0ex; padding: 0px; background-color: #fef3bc; font-family: sans-serif; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: inline; text-align: center; vertical-align: bottom; font-weight: bold; font-size: 2.5em; color: #a09048; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: left; padding: 0 0 0 5px; color: #a09048; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: #a09048; padding: 5px 5px 0 0; font-size: 0.8em; font-weight: bold; } /* The header across the top of the page */ div.header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ div.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #a09048; color: black; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu { padding: 3px 10px 3px 0px; font-size: 0.9em; text-align: center; background-color: #c0af58; color: white; } div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover { color: #a09048; background-color: white; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 1ex 5px; } div.content a { color: #706532; } div.content a:link { color: #706532; } div.content a:visited { color: #704032; } div.content a:hover { background-color: white; color: #706532; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 3px 3px 0 3px; font-size: 1.2em; font-weight: bold; background-color: #a09048; color: white; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #e1d498; border: 2px #a09048 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; } /* The footer at the very bottom of the page */ div.footer { font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #a09048; color: white; } /* Hyperlink colors */ div.footer a { color: white; } div.footer a:link { color: white; } div.footer a:visited { color: white; } div.footer a:hover { background-color: white; color: #558195; } /* <verbatim> blocks */ pre.verbatim { background-color: #f5f5f5; padding: 0.5em; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Added skins/default3/footer.html.
> > > > > | 1 2 3 4 5 | </div> <div class="footer"> Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/default3/header.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <html> <head> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$baseurl/timeline.rss"> <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css" media="screen"> </head> <body> <div class="header"> <div class="logo"> <!-- <img src="$baseurl/logo" alt="logo"> --> <br><nobr>$<project_name></nobr> </div> <div class="title">$<title></div> <div class="status"><nobr><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></nobr></div> </div> <div class="mainmenu"><ul><th1> html "<li><a href=''$baseurl$index_page''>Home</a></li>" if {[anycap jor]} { html "<li><a href=''$baseurl/timeline''>Timeline</a></li>" } if {[hascap oh]} { html "<li><a href=''$baseurl/dir?ci=tip''>Files</a></li>" } if {[hascap o]} { html "<li><a href=''$baseurl/leaves''>Leaves</a></li>" html "<li><a href=''$baseurl/brlist''>Branches</a></li>" html "<li><a href=''$baseurl/taglist''>Tags</a></li>" } if {[hascap r]} { html "<li><a href=''$baseurl/reportlist''>Tickets</a></li>" } if {[hascap j]} { html "<li><a href=''$baseurl/wiki''>Wiki</a></li>" } if {[hascap s]} { html "<li><a href=''$baseurl/setup''>Admin</a></li>" } elseif {[hascap a]} { html "<li><a href=''$baseurl/setup_ulist''>Users</a></li>" } if {[info exists login]} { html "<li><a href=''$baseurl/login''>Logout</a></li>" } else { html "<li><a href=''$baseurl/login''>Login</a></li>" } </th1></ul></div> <div id="container"> |
Added skins/default3/info.txt.
> > > > > | 1 2 3 4 5 | Title: Black & White, Menu on Left, No Logo Description: Black letters on a white or cream background with the main menu stuck on the left-hand side. Author: Unknown |
Added skins/default3/style.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* General settings for the entire page */ body { margin:0px 0px 0px 0px; padding:0px; font-family:verdana, arial, helvetica, "sans serif"; color:#333; background-color:white; } /* consistent colours */ h2 { color: #333; } h3 { color: #333; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-cell; text-align: left; vertical-align: bottom; font-weight: bold; color: #333; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: center; color: #333; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; padding-right: 10px; text-align: right; vertical-align: bottom; padding-bottom: 5px; color: #333; font-size: 0.8em; font-weight: bold; } /* The header across the top of the page */ div.header { margin:10px 0px 10px 0px; padding:1px 0px 0px 20px; border-style:solid; border-color:black; border-width:1px 0px; background-color:#eee; } /* The main menu bar that appears at the top left of the page beneath ** the header. Width must be co-ordinated with the container below */ div.mainmenu { float: left; margin-left: 10px; margin-right: 10px; font-size: 0.9em; font-weight: bold; padding:5px; background-color:#eee; border:1px solid #999; width:8em; } /* Main menu is now a list */ div.mainmenu ul { padding: 0; list-style:none; } div.mainmenu a, div.mainmenu a:visited{ padding: 1px 10px 1px 10px; color: #333; text-decoration: none; } div.mainmenu a:hover { color: #eee; background-color: #333; } /* Container for the sub-menu and content so they don''t spread ** out underneath the main menu */ #container { padding-left: 9em; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu { padding: 3px 10px 3px 10px; font-size: 0.9em; text-align: center; border:1px solid #999; border-width:1px 0px; background-color: #eee; color: #333; } div.submenu a, div.submenu a:visited { padding: 3px 10px 3px 10px; color: #333; text-decoration: none; } div.submenu a:hover { color: #eee; background-color: #333; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { float: right; padding: 2ex 1ex 0ex 2ex; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; border-style:solid; border-color:#999; border-width:1px 0px; background-color: #eee; color: #333; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #eee; border: 2px #999 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; color: #333 } /* The footer at the very bottom of the page */ div.footer { font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #eee; color: #555; } /* <verbatim> blocks */ pre.verbatim { background-color: #f5f5f5; padding: 0.5em; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Changes to src/add.c.
︙ | ︙ | |||
34 35 36 37 38 39 40 | */ static void add_one_file(const char *zName, int vid, Blob *pOmit){ Blob pathname; const char *zPath; file_tree_name(zName, &pathname, 1); zPath = blob_str(&pathname); | < | < | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | */ static void add_one_file(const char *zName, int vid, Blob *pOmit){ Blob pathname; const char *zPath; file_tree_name(zName, &pathname, 1); zPath = blob_str(&pathname); if( strcmp(zPath, "_FOSSIL_")==0 || strcmp(zPath, "_FOSSIL_-journal")==0 || strcmp(zPath, "_FOSSIL_-wal")==0 || strcmp(zPath, "_FOSSIL_-shm")==0 || strcmp(zPath, ".fos")==0 || strcmp(zPath, ".fos-journal")==0 || strcmp(zPath, ".fos-wal")==0 || strcmp(zPath, ".fos-shm")==0 || blob_compare(&pathname, pOmit)==0 ){ fossil_warning("cannot add %s", zPath); }else{ if( !file_is_simple_pathname(zPath) ){ fossil_fatal("filename contains illegal characters: %s", zPath); } #if defined(_WIN32) if( db_exists("SELECT 1 FROM vfile" " WHERE pathname=%Q COLLATE nocase", zPath) ){ db_multi_exec("UPDATE vfile SET deleted=0" " WHERE pathname=%Q COLLATE nocase", zPath); } #else if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){ |
︙ | ︙ | |||
150 151 152 153 154 155 156 | fossil_panic("no checkout to add to"); } db_begin_transaction(); if( !file_tree_name(g.zRepositoryName, &repo, 0) ){ blob_zero(&repo); } db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | fossil_panic("no checkout to add to"); } db_begin_transaction(); if( !file_tree_name(g.zRepositoryName, &repo, 0) ){ blob_zero(&repo); } db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); #if defined(_WIN32) db_multi_exec( "CREATE INDEX IF NOT EXISTS vfile_pathname " " ON vfile(pathname COLLATE nocase)" ); #endif for(i=2; i<g.argc; i++){ char *zName; |
︙ | ︙ |
Changes to src/allrepo.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | ** string is returned even if no quoting is needed. */ static char *quoteFilename(const char *zFilename){ int i, c; int needQuote = 0; for(i=0; (c = zFilename[i])!=0; i++){ if( c=='"' ) return 0; | | | | | > > > | > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | ** string is returned even if no quoting is needed. */ static char *quoteFilename(const char *zFilename){ int i, c; int needQuote = 0; for(i=0; (c = zFilename[i])!=0; i++){ if( c=='"' ) return 0; if( fossil_isspace(c) ) needQuote = 1; if( c=='\\' && zFilename[i+1]==0 ) return 0; if( c=='$' ) return 0; } if( needQuote ){ return mprintf("\"%s\"", zFilename); }else{ return mprintf("%s", zFilename); } } /* ** COMMAND: all ** ** Usage: %fossil all (list|ls|pull|push|rebuild|sync) ** ** The ~/.fossil file records the location of all repositories for a ** user. This command performs certain operations on all repositories ** that can be useful before or after a period of disconnected operation. ** ** On Win32 systems, the file is named "_fossil" and is located in ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. ** ** Available operations are: ** ** ignore Arguments are repositories that should be ignored ** by subsequent list, pull, push, rebuild, and sync. ** ** list | ls Display the location of all repositories ** ** pull Run a "pull" operation on all repositories ** ** push Run a "push" on all repositories ** ** rebuild Rebuild on all repositories ** ** sync Run a "sync" on all repositories ** ** Respositories are automatically added to the set of known repositories ** when one of the following commands against the repository: clone, info, ** pull, push, or sync. Even previously ignored repositories are added back ** to the list of repositories by these commands. */ void all_cmd(void){ int n; Stmt q; const char *zCmd; char *zSyscmd; char *zFossil; |
︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 | zCmd = "push -autourl -R"; }else if( strncmp(zCmd, "pull", n)==0 ){ zCmd = "pull -autourl -R"; }else if( strncmp(zCmd, "rebuild", n)==0 ){ zCmd = "rebuild"; }else if( strncmp(zCmd, "sync", n)==0 ){ zCmd = "sync -autourl -R"; }else{ fossil_fatal("\"all\" subcommand should be one of: " | > > > > > > > > > | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | zCmd = "push -autourl -R"; }else if( strncmp(zCmd, "pull", n)==0 ){ zCmd = "pull -autourl -R"; }else if( strncmp(zCmd, "rebuild", n)==0 ){ zCmd = "rebuild"; }else if( strncmp(zCmd, "sync", n)==0 ){ zCmd = "sync -autourl -R"; }else if( strncmp(zCmd, "ignore", n)==0 ){ int j; db_begin_transaction(); for(j=3; j<g.argc; j++){ db_multi_exec("DELETE FROM global_config WHERE name GLOB 'repo:%q'", g.argv[j]); } db_end_transaction(0); return; }else{ fossil_fatal("\"all\" subcommand should be one of: " "ignore list ls push pull rebuild sync"); } zFossil = quoteFilename(g.argv[0]); nMissing = 0; db_prepare(&q, "SELECT DISTINCT substr(name, 6) COLLATE nocase" " FROM global_config" " WHERE substr(name, 1, 5)=='repo:' ORDER BY 1" |
︙ | ︙ | |||
123 124 125 126 127 128 129 | printf("%s\n", zFilename); continue; } zQFilename = quoteFilename(zFilename); zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename); printf("%s\n", zSyscmd); fflush(stdout); | | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | printf("%s\n", zFilename); continue; } zQFilename = quoteFilename(zFilename); zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename); printf("%s\n", zSyscmd); fflush(stdout); fossil_system(zSyscmd); free(zSyscmd); free(zQFilename); } /* If any repositories whose names appear in the ~/.fossil file could not ** be found, remove those names from the ~/.fossil file. */ |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
73 74 75 76 77 78 79 | for(i=0; zFilename[i]; i++){ if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ zFilename = &zFilename[i+1]; i = -1; } } if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ | | | | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | for(i=0; zFilename[i]; i++){ if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ zFilename = &zFilename[i+1]; i = -1; } } if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename); }else{ zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); } @ @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; if( zComment && zComment[0] ){ @ %w(zComment)<br /> } if( zPage==0 && zTkt==0 ){ if( zSrc==0 || zSrc[0]==0 ){ zSrc = "Deleted from"; }else { zSrc = "Added to"; } |
︙ | ︙ | |||
249 250 251 252 253 254 255 | for(i=n=0; zName[i]; i++){ if( zName[i]=='/' || zName[i]=='\\' ) n = i; } zName += n; if( zName[0]==0 ) zName = "unknown"; blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID); zComment = PD("comment", ""); | | | | | | | | | | | | | | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | for(i=n=0; zName[i]; i++){ if( zName[i]=='/' || zName[i]=='\\' ) n = i; } zName += n; if( zName[0]==0 ) zName = "unknown"; blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID); zComment = PD("comment", ""); while( fossil_isspace(zComment[0]) ) zComment++; n = strlen(zComment); while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; } if( n>0 ){ blob_appendf(&manifest, "C %F\n", zComment); } zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&manifest, "D %s\n", zDate); blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); md5sum_blob(&manifest, &cksum); blob_appendf(&manifest, "Z %b\n", &cksum); rid = content_put(&manifest, 0, 0); manifest_crosslink(rid, &manifest); db_end_transaction(0); cgi_redirect(zFrom); } style_header("Add Attachment"); @ <h2>Add Attachment To %s(zTargetType)</h2> @ <form action="%s(g.zBaseURL)/attachadd" method="post" @ enctype="multipart/form-data"><div> @ File to Attach: @ <input type="file" name="f" size="60" /><br /> @ Description:<br /> @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br /> if( zTkt ){ @ <input type="hidden" name="tkt" value="%h(zTkt)" /> }else{ @ <input type="hidden" name="page" value="%h(zPage)" /> } @ <input type="hidden" name="from" value="%h(zFrom)" /> @ <input type="submit" name="ok" value="Add Attachment" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </div></form> style_footer(); } /* ** WEBPAGE: attachdelete ** |
︙ | ︙ | |||
347 348 349 350 351 352 353 | blob_appendf(&manifest, "Z %b\n", &cksum); rid = content_put(&manifest, 0, 0); manifest_crosslink(rid, &manifest); db_end_transaction(0); cgi_redirect(zFrom); } style_header("Delete Attachment"); | | | | | | | | | | | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | blob_appendf(&manifest, "Z %b\n", &cksum); rid = content_put(&manifest, 0, 0); manifest_crosslink(rid, &manifest); db_end_transaction(0); cgi_redirect(zFrom); } style_header("Delete Attachment"); @ <form action="%s(g.zBaseURL)/attachdelete" method="post"><div> @ <p>Confirm that you want to delete the attachment named @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br /></p> if( zTkt ){ @ <input type="hidden" name="tkt" value="%h(zTkt)" /> }else{ @ <input type="hidden" name="page" value="%h(zPage)" /> } @ <input type="hidden" name="file" value="%h(zFile)" /> @ <input type="hidden" name="from" value="%h(zFrom)" /> @ <input type="submit" name="confirm" value="Delete" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </div></form> style_footer(); } |
Changes to src/bag.c.
︙ | ︙ | |||
84 85 86 87 88 89 90 | int i; Bag old; int nDel = 0; /* Number of deleted entries */ int nLive = 0; /* Number of live entries */ old = *p; assert( newSize>old.cnt ); | | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | int i; Bag old; int nDel = 0; /* Number of deleted entries */ int nLive = 0; /* Number of live entries */ old = *p; assert( newSize>old.cnt ); p->a = fossil_malloc( sizeof(p->a[0])*newSize ); p->sz = newSize; memset(p->a, 0, sizeof(p->a[0])*newSize ); for(i=0; i<old.sz; i++){ int e = old.a[i]; if( e>0 ){ unsigned h = bag_hash(e)%newSize; while( p->a[h] ){ |
︙ | ︙ |
Changes to src/blob.c.
︙ | ︙ | |||
70 71 72 73 74 75 76 | #define blob_is_reset(x) #endif /* ** We find that the built-in isspace() function does not work for ** some international character sets. So here is a substitute. */ | | > > > > > > > > > > > > > > > > > | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | #define blob_is_reset(x) #endif /* ** We find that the built-in isspace() function does not work for ** some international character sets. So here is a substitute. */ int fossil_isspace(char c){ return c==' ' || (c<='\r' && c>='\t'); } /* ** Other replacements for ctype.h functions. */ int fossil_islower(char c){ return c>='a' && c<='z'; } int fossil_isupper(char c){ return c>='A' && c<='Z'; } int fossil_isdigit(char c){ return c>='0' && c<='9'; } int fossil_tolower(char c){ return fossil_isupper(c) ? c - 'A' + 'a' : c; } int fossil_isalpha(char c){ return (c>='a' && c<='z') || (c>='A' && c<='Z'); } int fossil_isalnum(char c){ return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); } /* ** COMMAND: test-isspace */ void isspace_cmd(void){ int i; for(i=0; i<=255; i++){ if( i==' ' || i=='\n' || i=='\t' || i=='\v' || i=='\f' || i=='\r' ){ assert( fossil_isspace((char)i) ); }else{ assert( !fossil_isspace((char)i) ); } } printf("All 256 characters OK\n"); } /* ** This routine is called if a blob operation fails because we |
︙ | ︙ | |||
117 118 119 120 121 122 123 | if( newSize==0 ){ free(pBlob->aData); pBlob->aData = 0; pBlob->nAlloc = 0; pBlob->nUsed = 0; pBlob->iCursor = 0; }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){ | | < | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | if( newSize==0 ){ free(pBlob->aData); pBlob->aData = 0; pBlob->nAlloc = 0; pBlob->nUsed = 0; pBlob->iCursor = 0; }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){ char *pNew = fossil_realloc(pBlob->aData, newSize); pBlob->aData = pNew; pBlob->nAlloc = newSize; if( pBlob->nUsed>pBlob->nAlloc ){ pBlob->nUsed = pBlob->nAlloc; } } } |
︙ | ︙ | |||
143 144 145 146 147 148 149 | ** A reallocation function for when the initial string is in unmanaged ** space. Copy the string to memory obtained from malloc(). */ static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ if( newSize==0 ){ *pBlob = empty_blob; }else{ | | < | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | ** A reallocation function for when the initial string is in unmanaged ** space. Copy the string to memory obtained from malloc(). */ static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ if( newSize==0 ){ *pBlob = empty_blob; }else{ char *pNew = fossil_malloc( newSize ); if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; memcpy(pNew, pBlob->aData, pBlob->nUsed); pBlob->aData = pNew; pBlob->xRealloc = blobReallocMalloc; pBlob->nAlloc = newSize; } } |
︙ | ︙ | |||
427 428 429 430 431 432 433 | ** ** All this does is reduce the length counter. This routine does ** not insert a new zero terminator. */ int blob_trim(Blob *p){ char *z = p->aData; int n = p->nUsed; | | | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | ** ** All this does is reduce the length counter. This routine does ** not insert a new zero terminator. */ int blob_trim(Blob *p){ char *z = p->aData; int n = p->nUsed; while( n>0 && fossil_isspace(z[n-1]) ){ n--; } p->nUsed = n; return n; } /* ** Extract a single token from pFrom and use it to initialize pTo. ** Return the number of bytes in the token. If no token is found, |
︙ | ︙ | |||
450 451 452 453 454 455 456 | ** pTo will be an ephermeral blob. If pFrom changes, it might alter ** pTo as well. */ int blob_token(Blob *pFrom, Blob *pTo){ char *aData = pFrom->aData; int n = pFrom->nUsed; int i = pFrom->iCursor; | | | | | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | ** pTo will be an ephermeral blob. If pFrom changes, it might alter ** pTo as well. */ int blob_token(Blob *pFrom, Blob *pTo){ char *aData = pFrom->aData; int n = pFrom->nUsed; int i = pFrom->iCursor; while( i<n && fossil_isspace(aData[i]) ){ i++; } pFrom->iCursor = i; while( i<n && !fossil_isspace(aData[i]) ){ i++; } blob_extract(pFrom, i-pFrom->iCursor, pTo); while( i<n && fossil_isspace(aData[i]) ){ i++; } pFrom->iCursor = i; return pTo->nUsed; } /* ** Extract everything from the current cursor to the end of the blob ** into a new blob. The new blob is an ephemerial reference to the |
︙ | ︙ | |||
521 522 523 524 525 526 527 | ** the integer value in *pValue. */ int blob_is_int(Blob *pBlob, int *pValue){ const char *z = blob_buffer(pBlob); int i, n, c, v; n = blob_size(pBlob); v = 0; | | | 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 | ** the integer value in *pValue. */ int blob_is_int(Blob *pBlob, int *pValue){ const char *z = blob_buffer(pBlob); int i, n, c, v; n = blob_size(pBlob); v = 0; for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){ v = v*10 + c - '0'; } if( i==n ){ *pValue = v; return 1; }else{ return 0; |
︙ | ︙ | |||
610 611 612 613 614 615 616 | if( zFilename==0 || zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ return blob_read_from_channel(pBlob, stdin, -1); } size = file_size(zFilename); blob_zero(pBlob); if( size<0 ){ | | | 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 | if( zFilename==0 || zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ return blob_read_from_channel(pBlob, stdin, -1); } size = file_size(zFilename); blob_zero(pBlob); if( size<0 ){ fossil_fatal("no such file: %s", zFilename); } if( size==0 ){ return 0; } blob_resize(pBlob, size); in = fopen(zFilename, "rb"); if( in==0 ){ |
︙ | ︙ | |||
658 659 660 661 662 663 664 | zName = zBuf; strcpy(zName, zFilename); } nName = file_simplify_name(zName, nName); for(i=1; i<nName; i++){ if( zName[i]=='/' ){ zName[i] = 0; | | | | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 | zName = zBuf; strcpy(zName, zFilename); } nName = file_simplify_name(zName, nName); for(i=1; i<nName; i++){ if( zName[i]=='/' ){ zName[i] = 0; #if defined(_WIN32) /* ** On Windows, local path looks like: C:/develop/project/file.txt ** The if stops us from trying to create a directory of a drive letter ** C: in this example. */ if( !(i==2 && zName[1]==':') ){ #endif if( file_mkdir(zName, 1) ){ fossil_fatal_recursive("unable to create directory %s", zName); return 0; } #if defined(_WIN32) } #endif zName[i] = '/'; } } out = fopen(zName, "wb"); if( out==0 ){ |
︙ | ︙ | |||
854 855 856 857 858 859 860 | blob_reset(&b1); blob_reset(&b2); blob_reset(&b3); } printf("ok\n"); } | | | 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | blob_reset(&b1); blob_reset(&b2); blob_reset(&b3); } printf("ok\n"); } #if defined(_WIN32) /* ** Convert every \n character in the given blob into \r\n. */ void blob_add_cr(Blob *p){ char *z = p->aData; int j = p->nUsed; int i, n; |
︙ | ︙ | |||
894 895 896 897 898 899 900 | z = p->aData; for(i=j=0; z[i]; i++){ if( z[i]!='\r' ) z[j++] = z[i]; } z[j] = 0; p->nUsed = j; } | > > > > > > > > > > > > > > > > > > > > > | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 | z = p->aData; for(i=j=0; z[i]; i++){ if( z[i]!='\r' ) z[j++] = z[i]; } z[j] = 0; p->nUsed = j; } /* ** Shell-escape the given string. Append the result to a blob. */ void shell_escape(Blob *pBlob, const char *zIn){ int n = blob_size(pBlob); int k = strlen(zIn); int i, c; char *z; for(i=0; (c = zIn[i])!=0; i++){ if( fossil_isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){ blob_appendf(pBlob, "\"%s\"", zIn); z = blob_buffer(pBlob); for(i=n+1; i<=n+k; i++){ if( z[i]=='"' ) z[i] = '_'; } return; } } blob_append(pBlob, zIn, -1); } |
Changes to src/branch.c.
︙ | ︙ | |||
33 34 35 36 37 38 39 | char *zUuid; /* Artifact ID of origin */ Stmt q; /* Generic query */ const char *zBranch; /* Name of the new branch */ char *zDate; /* Date that branch was created */ char *zComment; /* Check-in comment for the new branch */ const char *zColor; /* Color of the new branch */ Blob branch; /* manifest for the new branch */ | < | > > > > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | char *zUuid; /* Artifact ID of origin */ Stmt q; /* Generic query */ const char *zBranch; /* Name of the new branch */ char *zDate; /* Date that branch was created */ char *zComment; /* Check-in comment for the new branch */ const char *zColor; /* Color of the new branch */ Blob branch; /* manifest for the new branch */ Manifest *pParent; /* Parsed parent manifest */ Blob mcksum; /* Self-checksum on the manifest */ const char *zDateOvrd; /* Override date string */ const char *zUserOvrd; /* Override user name */ noSign = find_option("nosign","",0)!=0; zColor = find_option("bgcolor","c",1); zDateOvrd = find_option("date-override",0,1); zUserOvrd = find_option("user-override",0,1); verify_all_options(); if( g.argc<5 ){ usage("new BRANCH-NAME CHECK-IN ?-bgcolor COLOR?"); } db_find_and_open_repository(1); noSign = db_get_int("omitsign", 0)|noSign; |
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 | user_select(); db_begin_transaction(); rootid = name_to_rid(g.argv[4]); if( rootid==0 ){ fossil_fatal("unable to locate check-in off of which to branch"); } /* Create a manifest for the new branch */ blob_zero(&branch); zComment = mprintf("Create new branch named \"%h\"", zBranch); blob_appendf(&branch, "C %F\n", zComment); | > > > > > > > > | < < < < < | < | | | | < | < < | | > > | | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | user_select(); db_begin_transaction(); rootid = name_to_rid(g.argv[4]); if( rootid==0 ){ fossil_fatal("unable to locate check-in off of which to branch"); } pParent = manifest_get(rootid, CFTYPE_MANIFEST); if( pParent==0 ){ fossil_fatal("%s is not a valid check-in", g.argv[4]); } /* Create a manifest for the new branch */ blob_zero(&branch); if( pParent->zBaseline ){ blob_appendf(&branch, "B %s\n", pParent->zBaseline); } zComment = mprintf("Create new branch named \"%h\"", zBranch); blob_appendf(&branch, "C %F\n", zComment); zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); zDate[10] = 'T'; blob_appendf(&branch, "D %s\n", zDate); /* Copy all of the content from the parent into the branch */ for(i=0; i<pParent->nFile; ++i){ blob_appendf(&branch, "F %F", pParent->aFile[i].zName); if( pParent->aFile[i].zUuid ){ blob_appendf(&branch, " %s", pParent->aFile[i].zUuid); if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){ blob_appendf(&branch, " %s", pParent->aFile[i].zPerm); } } blob_append(&branch, "\n", 1); } zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); blob_appendf(&branch, "P %s\n", zUuid); blob_appendf(&branch, "R %s\n", pParent->zRepoCksum); manifest_destroy(pParent); /* Add the symbolic branch name and the "branch" tag to identify ** this as a new branch */ if( zColor!=0 ){ blob_appendf(&branch, "T *bgcolor * %F\n", zColor); } blob_appendf(&branch, "T *branch * %F\n", zBranch); |
︙ | ︙ | |||
118 119 120 121 122 123 124 | rootid); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); blob_appendf(&branch, "T -%F *\n", zTag); } db_finalize(&q); | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | rootid); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); blob_appendf(&branch, "T -%F *\n", zTag); } db_finalize(&q); blob_appendf(&branch, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); md5sum_blob(&branch, &mcksum); blob_appendf(&branch, "Z %b\n", &mcksum); if( !noSign && clearsign(&branch, &branch) ){ Blob ans; blob_zero(&ans); prompt_user("unable to sign manifest. continue (y/N)? ", &ans); if( blob_str(&ans)[0]!='y' ){ |
︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 | ** WEBPAGE: brlist ** ** Show a timeline of all branches */ void brlist_page(void){ Stmt q; int cnt; login_check_credentials(); if( !g.okRead ){ login_needed(); return; } | > | > > > > > > | > | > | > > | | | | | | | < < < < < < < < < < | < < < < < < < < | | | | | | | | | | > > | > > > | < < | | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | ** WEBPAGE: brlist ** ** Show a timeline of all branches */ void brlist_page(void){ Stmt q; int cnt; int showClosed = P("closed")!=0; login_check_credentials(); if( !g.okRead ){ login_needed(); return; } style_header(showClosed ? "Closed Branches" : "Open Branches"); style_submenu_element("Timeline", "Timeline", "brtimeline"); if( showClosed ){ style_submenu_element("Open","Open","brlist"); }else{ style_submenu_element("Closed","Closed","brlist?closed"); } login_anonymous_available(); compute_leaves(0, 1); style_sidebox_begin("Nomenclature:", "33%"); @ <ol> @ <li> An <div class="sideboxDescribed"><a href="brlist"> @ open branch</a></div> is a branch that has one or @ more <a href="leaves">open leaves.</a> @ The presence of open leaves presumably means @ that the branch is still being extended with new check-ins.</li> @ <li> A <div class="sideboxDescribed"><a href="brlist?closed"> @ closed branch</a></div> is a branch with only @ <div class="sideboxDescribed"><a href="leaves?closed"> @ closed leaves</a></div>. @ Closed branches are fixed and do not change (unless they are first @ reopened)</li> @ </ol> style_sidebox_end(); cnt = 0; if( !showClosed ){ db_prepare(&q, "SELECT DISTINCT value FROM tagxref" " WHERE tagid=%d AND value NOT NULL" " AND rid IN leaves" " ORDER BY value /*sort*/", TAG_BRANCH ); }else{ db_prepare(&q, "SELECT value FROM tagxref" " WHERE tagid=%d AND value NOT NULL" " EXCEPT " "SELECT value FROM tagxref" " WHERE tagid=%d AND value NOT NULL" " AND rid IN leaves" " ORDER BY value /*sort*/", TAG_BRANCH, TAG_BRANCH ); } while( db_step(&q)==SQLITE_ROW ){ const char *zBr = db_column_text(&q, 0); if( cnt==0 ){ if( showClosed ){ @ <h2>Closed Branches:</h2> }else{ @ <h2>Open Branches:</h2> } @ <ul> cnt++; } if( g.okHistory ){ @ <li><a href="%s(g.zBaseURL)/timeline?r=%T(zBr)">%h(zBr)</a></li> }else{ @ <li><b>%h(zBr)</b></li> } } if( cnt ){ @ </ul> } db_finalize(&q); @ <script type="text/JavaScript"> @ function xin(id){ @ } @ function xout(id){ @ } @ </script> style_footer(); } |
︙ | ︙ | |||
344 345 346 347 348 349 350 | "%s AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d AND srcid!=0)" " ORDER BY event.mtime DESC", timeline_query_for_www(), TAG_BRANCH ); www_print_timeline(&q, 0, brtimeline_extra); db_finalize(&q); | < | | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | "%s AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d AND srcid!=0)" " ORDER BY event.mtime DESC", timeline_query_for_www(), TAG_BRANCH ); www_print_timeline(&q, 0, brtimeline_extra); db_finalize(&q); @ <script type="text/JavaScript"> @ function xin(id){ @ } @ function xout(id){ @ } @ </script> style_footer(); } |
Changes to src/browse.c.
︙ | ︙ | |||
69 70 71 72 73 74 75 | ** to the "dir" page for the directory. ** ** There is no hyperlink on the file element of the path. ** ** The computed string is appended to the pOut blob. pOut should ** have already been initialized. */ | | > > > > | | > > | | | > > > | | > > > > | < | > > > > | > | | > > | > < > > > | > | > > | > > > > > | | > > > < < < < < | | | | | | | | | | | > > | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | ** to the "dir" page for the directory. ** ** There is no hyperlink on the file element of the path. ** ** The computed string is appended to the pOut blob. pOut should ** have already been initialized. */ void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){ int i, j; char *zSep = ""; for(i=0; zPath[i]; i=j){ for(j=i; zPath[j] && zPath[j]!='/'; j++){} if( zPath[j] && g.okHistory ){ if( zCI ){ blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&name=%#T\">%#h</a>", zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]); }else{ blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]); } }else{ blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); } zSep = "/"; while( zPath[j]=='/' ){ j++; } } } /* ** WEBPAGE: dir ** ** Query parameters: ** ** name=PATH Directory to display. Required. ** ci=LABEL Show only files in this check-in. Optional. */ void page_dir(void){ const char *zD = P("name"); int nD = zD ? strlen(zD)+1 : 0; int mxLen; int nCol, nRow; int cnt, i; char *zPrefix; Stmt q; const char *zCI = P("ci"); int rid = 0; char *zUuid = 0; Blob dirname; Manifest *pM = 0; const char *zSubdirLink; login_check_credentials(); if( !g.okHistory ){ login_needed(); return; } style_header("File List"); sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, pathelementFunc, 0, 0); /* If the name= parameter is an empty string, make it a NULL pointer */ if( zD && strlen(zD)==0 ){ zD = 0; } /* If a specific check-in is requested, fetch and parse it. If the ** specific check-in does not exist, clear zCI. zCI==0 will cause all ** files from all check-ins to be displayed. */ if( zCI ){ pM = manifest_get_by_name(zCI, &rid); if( pM ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); }else{ zCI = 0; } } /* Compute the title of the page */ blob_zero(&dirname); if( zD ){ blob_append(&dirname, "in directory ", -1); hyperlinked_path(zD, &dirname, zCI); zPrefix = mprintf("%h/", zD); }else{ blob_append(&dirname, "in the top-level directory", -1); zPrefix = ""; } if( zCI ){ char zShort[20]; memcpy(zShort, zUuid, 10); zShort[10] = 0; @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>] @ %s(blob_str(&dirname))</h2> zSubdirLink = mprintf("%s/dir?ci=%S&name=%T", g.zTop, zUuid, zPrefix); if( zD ){ style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid); style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD); }else{ style_submenu_element("All", "All", "%s/dir", g.zBaseURL); } }else{ int hasTrunk; @ <h2>The union of all files from all check-ins @ %s(blob_str(&dirname))</h2> hasTrunk = db_exists( "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'", TAG_BRANCH); zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); if( zD ){ style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL); style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip", g.zBaseURL, zD); if( hasTrunk ){ style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk", g.zBaseURL,zD); } }else{ style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL); if( hasTrunk ){ style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL); } } } /* Compute the temporary table "localfiles" containing the names ** of all files and subdirectories in the zD[] directory. ** ** Subdirectory names begin with "/". This causes them to sort ** first and it also gives us an easy way to distinguish files ** from directories in the loop that follows. */ db_multi_exec( "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" ); if( zCI ){ Stmt ins; ManifestFile *pFile; ManifestFile *pPrev = 0; int nPrev = 0; int c; db_prepare(&ins, "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)" ); manifest_file_rewind(pM); while( (pFile = manifest_file_next(pM,0))!=0 ){ if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue; if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){ continue; } db_bind_text(&ins, ":x", &pFile->zName[nD]); db_bind_text(&ins, ":u", pFile->zUuid); db_step(&ins); db_reset(&ins); pPrev = pFile; for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} if( c=='/' ) nPrev++; } db_finalize(&ins); }else if( zD ){ db_multi_exec( "INSERT OR IGNORE INTO localfiles" " SELECT pathelement(name,%d), NULL FROM filename" " WHERE name GLOB '%q/*'", nD, zD ); }else{ db_multi_exec( "INSERT OR IGNORE INTO localfiles" " SELECT pathelement(name,0), NULL FROM filename" ); } /* Generate a multi-column table listing the contents of zD[] ** directory. */ mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); nCol = 4; nRow = (cnt+nCol-1)/nCol; db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); @ <table class="browser"><tr><td class="browser"><ul class="browser"> i = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zFN; if( i==nRow ){ @ </ul></td><td class="browser"><ul class="browser"> i = 0; } i++; zFN = db_column_text(&q, 0); if( zFN[0]=='/' ){ zFN++; @ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li> }else if( zCI ){ const char *zUuid = db_column_text(&q, 1); @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></li> }else{ @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN) @ </a></li> } } db_finalize(&q); manifest_destroy(pM); @ </ul></td></tr></table> style_footer(); } |
Changes to src/captcha.c.
︙ | ︙ | |||
67 68 69 70 71 72 73 | /* ** Render an 8-character hexadecimal string as ascii art. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | /* ** Render an 8-character hexadecimal string as ascii art. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ char *z = fossil_malloc( 500 ); int i, j, k, m; k = 0; for(i=0; i<6; i++){ for(j=0; j<8; j++){ unsigned char v = hexValue(zPw[j]); v = (aFont1[v] >> ((5-i)*4)) & 0xf; |
︙ | ︙ | |||
200 201 202 203 204 205 206 | /* ** Render an 8-digit hexadecimal string as ascii arg. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ | | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | /* ** Render an 8-digit hexadecimal string as ascii arg. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ char *z = fossil_malloc( 300 ); int i, j, k, m; const char *zChar; k = 0; for(i=0; i<4; i++){ for(j=0; j<8; j++){ unsigned char v = hexValue(zPw[j]); |
︙ | ︙ | |||
357 358 359 360 361 362 363 | /* ** Render an 8-digit hexadecimal string as ascii arg. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ | | | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | /* ** Render an 8-digit hexadecimal string as ascii arg. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ char *z = fossil_malloc( 600 ); int i, j, k, m; const char *zChar; k = 0; for(i=0; i<6; i++){ for(j=0; j<8; j++){ unsigned char v = hexValue(zPw[j]); |
︙ | ︙ | |||
397 398 399 400 401 402 403 | printf("%s:\n%s", zHex, z); free(z); } } /* ** Compute a seed value for a captcha. The seed is public and is sent | | | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | printf("%s:\n%s", zHex, z); free(z); } } /* ** Compute a seed value for a captcha. The seed is public and is sent ** as a hidden parameter with the page that contains the captcha. Knowledge ** of the seed is insufficient for determining the captcha without additional ** information held only on the server and never revealed. */ unsigned int captcha_seed(void){ unsigned int x; sqlite3_randomness(sizeof(x), &x); x &= 0x7fffffff; |
︙ | ︙ |
Changes to src/cgi.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains C functions and procedures that provide useful ** services to CGI programs. There are procedures for parsing and ** dispensing QUERY_STRING parameters and cookies, the "mprintf()" ** formatting function and its cousins, and routines to encode and ** decode strings in HTML or HTTP. */ #include "config.h" | | | | | > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | ** This file contains C functions and procedures that provide useful ** services to CGI programs. There are procedures for parsing and ** dispensing QUERY_STRING parameters and cookies, the "mprintf()" ** formatting function and its cousins, and routines to encode and ** decode strings in HTML or HTTP. */ #include "config.h" #ifdef _WIN32 # include <windows.h> /* for Sleep once server works again */ # if defined(__MINGW32__) # define sleep Sleep /* windows does not have sleep, but Sleep */ # include <ws2tcpip.h> # endif #else # include <sys/socket.h> # include <netinet/in.h> # include <arpa/inet.h> # include <sys/times.h> # include <sys/time.h> # include <sys/wait.h> # include <sys/select.h> #endif #ifdef __EMX__ typedef int socklen_t; #endif #include <time.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #if defined (__POCC__) # undef INTERFACE #endif #include "cgi.h" #if INTERFACE /* ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter ** or cookie "x", or NULL if there is no such parameter or cookie. PD("x","y") ** does the same except "y" is returned in place of NULL if there is not match. |
︙ | ︙ | |||
381 382 383 384 385 386 387 | ** ** zName and zValue are not copied and must not change or be ** deallocated after this routine returns. */ void cgi_set_parameter_nocopy(const char *zName, const char *zValue){ if( nAllocQP<=nUsedQP ){ nAllocQP = nAllocQP*2 + 10; | | < | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | ** ** zName and zValue are not copied and must not change or be ** deallocated after this routine returns. */ void cgi_set_parameter_nocopy(const char *zName, const char *zValue){ if( nAllocQP<=nUsedQP ){ nAllocQP = nAllocQP*2 + 10; aParamQP = fossil_realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) ); } aParamQP[nUsedQP].zName = zName; aParamQP[nUsedQP].zValue = zValue; if( g.fHttpTrace ){ fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue); } aParamQP[nUsedQP].seq = seqQP++; |
︙ | ︙ | |||
457 458 459 460 461 462 463 | ** should not be deallocated or changed again after this routine ** returns or it will corrupt the parameter table. */ static void add_param_list(char *z, int terminator){ while( *z ){ char *zName; char *zValue; | | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 | ** should not be deallocated or changed again after this routine ** returns or it will corrupt the parameter table. */ static void add_param_list(char *z, int terminator){ while( *z ){ char *zName; char *zValue; while( fossil_isspace(*z) ){ z++; } zName = z; while( *z && *z!='=' && *z!=terminator ){ z++; } if( *z=='=' ){ *z = 0; z++; zValue = z; while( *z && *z!=terminator ){ z++; } if( *z ){ *z = 0; z++; } dehttpize(zValue); }else{ if( *z ){ *z++ = 0; } zValue = ""; } if( fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(zName, zValue); } } } /* ** *pz is a string that consists of multiple lines of text. This |
︙ | ︙ | |||
569 570 571 572 573 574 575 | ** '\000' characters are inserted in z[] at the end of each token. ** This routine returns the total number of tokens on the line, 6 ** in the example above. */ static int tokenize_line(char *z, int mxArg, char **azArg){ int i = 0; while( *z ){ | | | | 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | ** '\000' characters are inserted in z[] at the end of each token. ** This routine returns the total number of tokens on the line, 6 ** in the example above. */ static int tokenize_line(char *z, int mxArg, char **azArg){ int i = 0; while( *z ){ while( fossil_isspace(*z) || *z==';' ){ z++; } if( *z=='"' && z[1] ){ *z = 0; z++; if( i<mxArg-1 ){ azArg[i++] = z; } while( *z && *z!='"' ){ z++; } if( *z==0 ) break; *z = 0; z++; }else{ if( i<mxArg-1 ){ azArg[i++] = z; } while( *z && !fossil_isspace(*z) && *z!=';' && *z!='"' ){ z++; } if( *z && *z!='"' ){ *z = 0; z++; } } } azArg[i] = 0; |
︙ | ︙ | |||
615 616 617 618 619 620 621 | zBoundry = get_line_from_string(&z, &len); if( zBoundry==0 ) return; while( (zLine = get_line_from_string(&z, &len))!=0 ){ if( zLine[0]==0 ){ int nContent = 0; zValue = get_bounded_content(&z, &len, zBoundry, &nContent); | | | | | | 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 | zBoundry = get_line_from_string(&z, &len); if( zBoundry==0 ) return; while( (zLine = get_line_from_string(&z, &len))!=0 ){ if( zLine[0]==0 ){ int nContent = 0; zValue = get_bounded_content(&z, &len, zBoundry, &nContent); if( zName && zValue && fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(zName, zValue); if( showBytes ){ cgi_set_parameter_nocopy(mprintf("%s:bytes", zName), mprintf("%d",nContent)); } } zName = 0; showBytes = 0; }else{ nArg = tokenize_line(zLine, sizeof(azArg)/sizeof(azArg[0]), azArg); for(i=0; i<nArg; i++){ int c = fossil_tolower(azArg[i][0]); int n = strlen(azArg[i]); if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){ i++; }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){ zName = azArg[++i]; }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){ char *z = azArg[++i]; if( zName && z && fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z); } showBytes = 1; }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){ char *z = azArg[++i]; if( zName && z && fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z); } } } } } } |
︙ | ︙ | |||
675 676 677 678 679 680 681 | len = atoi(PD("CONTENT_LENGTH", "0")); g.zContentType = zType = P("CONTENT_TYPE"); if( len>0 && zType ){ blob_zero(&g.cgiIn); if( strcmp(zType,"application/x-www-form-urlencoded")==0 || strncmp(zType,"multipart/form-data",19)==0 ){ | | < | 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 | len = atoi(PD("CONTENT_LENGTH", "0")); g.zContentType = zType = P("CONTENT_TYPE"); if( len>0 && zType ){ blob_zero(&g.cgiIn); if( strcmp(zType,"application/x-www-form-urlencoded")==0 || strncmp(zType,"multipart/form-data",19)==0 ){ z = fossil_malloc( len+1 ); len = fread(z, 1, len, g.httpIn); z[len] = 0; if( zType[0]=='a' ){ add_param_list(z, '&'); }else{ process_multipart_form_data(z, len); } |
︙ | ︙ | |||
767 768 769 770 771 772 773 | } } /* If no match is found and the name begins with an upper-case ** letter, then check to see if there is an environment variable ** with the given name. */ | | | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | } } /* If no match is found and the name begins with an upper-case ** letter, then check to see if there is an environment variable ** with the given name. */ if( fossil_isupper(zName[0]) ){ const char *zValue = getenv(zName); if( zValue ){ cgi_set_parameter_nocopy(zName, zValue); CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue)); return zValue; } } |
︙ | ︙ | |||
849 850 851 852 853 854 855 | cgi_parameter("",""); /* Force the parameters into sorted order */ for(i=0; i<nUsedQP; i++){ cgi_printf("%s = %s <br />\n", htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1)); } } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 851 852 853 854 855 856 857 858 859 860 861 862 863 864 | cgi_parameter("",""); /* Force the parameters into sorted order */ for(i=0; i<nUsedQP; i++){ cgi_printf("%s = %s <br />\n", htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1)); } } /* ** This routine works like "printf" except that it has the ** extra formatting capabilities such as %h and %t. */ void cgi_printf(const char *zFormat, ...){ va_list ap; va_start(ap,zFormat); |
︙ | ︙ | |||
1045 1046 1047 1048 1049 1050 1051 | */ static char *extract_token(char *zInput, char **zLeftOver){ char *zResult = 0; if( zInput==0 ){ if( zLeftOver ) *zLeftOver = 0; return 0; } | | | | | | 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 | */ static char *extract_token(char *zInput, char **zLeftOver){ char *zResult = 0; if( zInput==0 ){ if( zLeftOver ) *zLeftOver = 0; return 0; } while( fossil_isspace(*zInput) ){ zInput++; } zResult = zInput; while( *zInput && !fossil_isspace(*zInput) ){ zInput++; } if( *zInput ){ *zInput = 0; zInput++; while( fossil_isspace(*zInput) ){ zInput++; } } if( zLeftOver ){ *zLeftOver = zInput; } return zResult; } /* ** This routine handles a single HTTP request which is coming in on ** standard input and which replies on standard output. ** ** The HTTP request is read from standard input and is used to initialize ** environment variables as per CGI. The cgi_init() routine to complete ** the setup. Once all the setup is finished, this procedure returns ** and subsequent code handles the actual generation of the webpage. */ void cgi_handle_http_request(const char *zIpAddr){ char *z, *zToken; int i; struct sockaddr_in remoteName; socklen_t size = sizeof(struct sockaddr_in); char zLine[2000]; /* A single line of input. */ g.fullHttpReply = 1; if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request(); } zToken = extract_token(zLine, &z); |
︙ | ︙ | |||
1098 1099 1100 1101 1102 1103 1104 | cgi_setenv("REQUEST_URI", zToken); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); if( zIpAddr==0 && getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, | | | | | | < > | < < < < > > > > > > > > > > > > > > > > > > > | | | > > > | > | 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 | cgi_setenv("REQUEST_URI", zToken); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); if( zIpAddr==0 && getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, &size)>=0 ){ zIpAddr = inet_ntoa(remoteName.sin_addr); } if( zIpAddr ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = mprintf("%s", zIpAddr); } /* Get all the optional fields that follow the first line. */ while( fgets(zLine,sizeof(zLine),g.httpIn) ){ char *zFieldName; char *zVal; zFieldName = extract_token(zLine,&zVal); if( zFieldName==0 || *zFieldName==0 ) break; while( fossil_isspace(*zVal) ){ zVal++; } i = strlen(zVal); while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } zVal[i] = 0; for(i=0; zFieldName[i]; i++){ zFieldName[i] = fossil_tolower(zFieldName[i]); } if( strcmp(zFieldName,"content-length:")==0 ){ cgi_setenv("CONTENT_LENGTH", zVal); }else if( strcmp(zFieldName,"content-type:")==0 ){ cgi_setenv("CONTENT_TYPE", zVal); }else if( strcmp(zFieldName,"cookie:")==0 ){ cgi_setenv("HTTP_COOKIE", zVal); }else if( strcmp(zFieldName,"https:")==0 ){ cgi_setenv("HTTPS", zVal); }else if( strcmp(zFieldName,"host:")==0 ){ cgi_setenv("HTTP_HOST", zVal); }else if( strcmp(zFieldName,"if-none-match:")==0 ){ cgi_setenv("HTTP_IF_NONE_MATCH", zVal); }else if( strcmp(zFieldName,"if-modified-since:")==0 ){ cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal); } #if 0 else if( strcmp(zFieldName,"referer:")==0 ){ cgi_setenv("HTTP_REFERER", zVal); }else if( strcmp(zFieldName,"user-agent:")==0 ){ cgi_setenv("HTTP_USER_AGENT", zVal); } #endif } cgi_init(); } #if INTERFACE /* ** Bitmap values for the flags parameter to cgi_http_server(). */ #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ #endif /* INTERFACE */ /* ** Maximum number of child processes that we can have running ** at one time before we start slowing things down. */ #define MAX_PARALLEL 2 /* ** Implement an HTTP server daemon listening on port iPort. ** ** As new connections arrive, fork a child and let child return ** out of this procedure call. The child will handle the request. ** The parent never returns from this procedure. ** ** Return 0 to each child as it runs. If unable to establish a ** listening socket, return non-zero. */ int cgi_http_server(int mnPort, int mxPort, char *zBrowser, int flags){ #if defined(_WIN32) /* Use win32_http_server() instead */ fossil_exit(1); #else int listener = -1; /* The server socket */ int connection; /* A socket for each individual connection */ fd_set readfds; /* Set of file descriptors for select() */ socklen_t lenaddr; /* Length of the inaddr structure */ int child; /* PID of the child process */ int nchildren = 0; /* Number of child processes */ struct timeval delay; /* How long to wait inside select() */ struct sockaddr_in inaddr; /* The socket address */ int opt = 1; /* setsockopt flag */ int iPort = mnPort; while( iPort<=mxPort ){ memset(&inaddr, 0, sizeof(inaddr)); inaddr.sin_family = AF_INET; if( flags & HTTP_SERVER_LOCALHOST ){ inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); }else{ inaddr.sin_addr.s_addr = htonl(INADDR_ANY); } inaddr.sin_port = htons(iPort); listener = socket(AF_INET, SOCK_STREAM, 0); if( listener<0 ){ iPort++; continue; } |
︙ | ︙ | |||
1222 1223 1224 1225 1226 1227 1228 | /* Slow down if connections are arriving too fast */ sleep( nchildren-MAX_PARALLEL ); } delay.tv_sec = 60; delay.tv_usec = 0; FD_ZERO(&readfds); FD_SET( listener, &readfds); | | > | < | 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 | /* Slow down if connections are arriving too fast */ sleep( nchildren-MAX_PARALLEL ); } delay.tv_sec = 60; delay.tv_usec = 0; FD_ZERO(&readfds); FD_SET( listener, &readfds); select( listener+1, &readfds, 0, 0, &delay); if( FD_ISSET(listener, &readfds) ){ lenaddr = sizeof(inaddr); connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr); if( connection>=0 ){ child = fork(); if( child!=0 ){ if( child>0 ) nchildren++; close(connection); }else{ close(0); |
︙ | ︙ |
Changes to src/checkin.c.
︙ | ︙ | |||
213 214 215 216 217 218 219 | int nTerm = 0; int i; int cTerm; if( zGlobList==0 || zGlobList[0]==0 ) return "0"; blob_zero(&expr); while( zGlobList[0] ){ | | | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | int nTerm = 0; int i; int cTerm; if( zGlobList==0 || zGlobList[0]==0 ) return "0"; blob_zero(&expr); while( zGlobList[0] ){ while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++; if( zGlobList[0]==0 ) break; if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){ cTerm = zGlobList[0]; zGlobList++; }else{ cTerm = ','; } for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){} if( cTerm==',' ){ while( i>0 && fossil_isspace(zGlobList[i-1]) ){ i--; } } blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList); zSep = " OR "; if( cTerm!=',' && zGlobList[i] ) i++; zGlobList += i; if( zGlobList[0] ) zGlobList++; nTerm++; |
︙ | ︙ | |||
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | void extra_cmd(void){ Blob path; Blob repo; Stmt q; int n; const char *zIgnoreFlag = find_option("ignore",0,1); int allFlag = find_option("dotfiles",0,0)!=0; db_must_be_within_tree(); db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); n = strlen(g.zLocalRoot); blob_init(&path, g.zLocalRoot, n-1); if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } vfile_scan(0, &path, blob_size(&path), allFlag); db_prepare(&q, "SELECT x FROM sfile" | > | > > | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | void extra_cmd(void){ Blob path; Blob repo; Stmt q; int n; const char *zIgnoreFlag = find_option("ignore",0,1); int allFlag = find_option("dotfiles",0,0)!=0; int outputManifest = db_get_boolean("manifest",0); db_must_be_within_tree(); db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); n = strlen(g.zLocalRoot); blob_init(&path, g.zLocalRoot, n-1); if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } vfile_scan(0, &path, blob_size(&path), allFlag); db_prepare(&q, "SELECT x FROM sfile" " WHERE x NOT IN ('%s','%s','_FOSSIL_'," "'_FOSSIL_-journal','.fos','.fos-journal'," "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal'," "'.fos-shm')" " AND NOT %s" " ORDER BY 1", outputManifest ? "manifest" : "_FOSSIL_", outputManifest ? "manifest.uuid" : "_FOSSIL_", glob_expr("x", zIgnoreFlag) ); if( file_tree_name(g.zRepositoryName, &repo, 0) ){ db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); } while( db_step(&q)==SQLITE_ROW ){ printf("%s\n", db_column_text(&q, 0)); |
︙ | ︙ | |||
399 400 401 402 403 404 405 | if( zEditor==0 ){ zEditor = getenv("VISUAL"); } if( zEditor==0 ){ zEditor = getenv("EDITOR"); } if( zEditor==0 ){ | | | | | | | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | if( zEditor==0 ){ zEditor = getenv("VISUAL"); } if( zEditor==0 ){ zEditor = getenv("EDITOR"); } if( zEditor==0 ){ #if defined(_WIN32) zEditor = "notepad"; #else zEditor = "ed"; #endif } zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'", g.zLocalRoot); #if defined(_WIN32) blob_add_cr(&text); #endif blob_write_to_file(&text, zFile); zCmd = mprintf("%s \"%s\"", zEditor, zFile); printf("%s\n", zCmd); if( fossil_system(zCmd) ){ fossil_panic("editor aborted"); } blob_reset(&text); blob_read_from_file(&text, zFile); blob_remove_cr(&text); unlink(zFile); free(zFile); blob_zero(pComment); while( blob_line(&text, &line) ){ int i, n; char *z; n = blob_size(&line); z = blob_buffer(&line); for(i=0; i<n && fossil_isspace(z[i]); i++){} if( i<n && z[i]=='#' ) continue; if( i<n || blob_size(pComment)>0 ){ blob_appendf(pComment, "%b", &line); } } blob_reset(&text); zComment = blob_str(pComment); i = strlen(zComment); while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; } blob_resize(pComment, i); } /* ** Populate the Global.aCommitFile[] based on the command line arguments ** to a [commit] command. Global.aCommitFile is an array of integers ** sized at (N+1), where N is the number of arguments passed to [commit]. |
︙ | ︙ | |||
459 460 461 462 463 464 465 | ** to mean "all files". */ void select_commit_files(void){ if( g.argc>2 ){ int ii; Blob b; blob_zero(&b); | | | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | ** to mean "all files". */ void select_commit_files(void){ if( g.argc>2 ){ int ii; Blob b; blob_zero(&b); g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); for(ii=2; ii<g.argc; ii++){ int iId; file_tree_name(g.argv[ii], &b, 1); iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); if( iId<0 ){ fossil_fatal("fossil knows nothing about: %s", g.argv[ii]); |
︙ | ︙ | |||
511 512 513 514 515 516 517 | b = db_exists( "SELECT 1 FROM event" " WHERE datetime(mtime)>=%Q" " AND type='ci' AND objid=%d", zDate, rid ); if( b ){ | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | < < | | | > > > | | > > | | | < | > > > > | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 | b = db_exists( "SELECT 1 FROM event" " WHERE datetime(mtime)>=%Q" " AND type='ci' AND objid=%d", zDate, rid ); if( b ){ fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)" " Use -f to override.", zUuid, zDate); } #endif } /* ** zDate should be a valid date string. Convert this string into the ** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date, ** print a fatal error and quit. */ char *date_in_standard_format(const char *zInputDate){ char *zDate = db_text(0, "SELECT datetime(%Q)", zInputDate); if( zDate[0]==0 ){ fossil_fatal("unrecognized date format (%s): use \"YYYY-MM-DD HH:MM:SS\"", zInputDate); } assert( strlen(zDate)==19 ); assert( zDate[10]==' ' ); zDate[10] = 'T'; return zDate; } /* ** Create a manifest. */ static void create_manifest( Blob *pOut, /* Write the manifest here */ const char *zBaselineUuid, /* UUID of baseline, or zero */ Manifest *pBaseline, /* Make it a delta manifest if not zero */ Blob *pComment, /* Check-in comment text */ int vid, /* blob-id of the parent manifest */ int verifyDate, /* Verify that child is younger */ Blob *pCksum, /* Repository checksum. May be 0 */ const char *zDateOvrd, /* Date override. If 0 then use 'now' */ const char *zUserOvrd, /* User override. If 0 then use g.zLogin */ const char *zBranch, /* Branch name. May be 0 */ const char *zBgColor, /* Background color. May be 0 */ int *pnFBcard /* Number of generated B- and F-cards */ ){ char *zDate; /* Date of the check-in */ char *zParentUuid; /* UUID of parent check-in */ Blob filename; /* A single filename */ int nBasename; /* Size of base filename */ Stmt q; /* Query of files changed */ Stmt q2; /* Query of merge parents */ Blob mcksum; /* Manifest checksum */ ManifestFile *pFile; /* File from the baseline */ int nFBcard = 0; /* Number of B-cards and F-cards */ assert( pBaseline==0 || pBaseline->zBaseline==0 ); assert( pBaseline==0 || zBaselineUuid!=0 ); blob_zero(pOut); zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); if( pBaseline ){ blob_appendf(pOut, "B %s\n", zBaselineUuid); manifest_file_rewind(pBaseline); pFile = manifest_file_next(pBaseline, 0); nFBcard++; }else{ pFile = 0; } blob_appendf(pOut, "C %F\n", blob_str(pComment)); zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); blob_appendf(pOut, "D %s\n", zDate); zDate[10] = ' '; db_prepare(&q, "SELECT pathname, uuid, origname, blob.rid, isexe" " FROM vfile JOIN blob ON vfile.mrid=blob.rid" " WHERE NOT deleted AND vfile.vid=%d" " ORDER BY 1", vid); blob_zero(&filename); blob_appendf(&filename, "%s", g.zLocalRoot); nBasename = blob_size(&filename); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zUuid = db_column_text(&q, 1); const char *zOrig = db_column_text(&q, 2); int frid = db_column_int(&q, 3); int isexe = db_column_int(&q, 4); const char *zPerm; int cmp; blob_append(&filename, zName, -1); #if !defined(_WIN32) /* For unix, extract the "executable" permission bit directly from ** the filesystem. On windows, the "executable" bit is retained ** unchanged from the original. */ isexe = file_isexe(blob_str(&filename)); #endif if( isexe ){ zPerm = " x"; }else{ zPerm = ""; } if( !g.markPrivate ) content_make_public(frid); while( pFile && strcmp(pFile->zName,zName)<0 ){ blob_appendf(pOut, "F %F\n", pFile->zName); pFile = manifest_file_next(pBaseline, 0); nFBcard++; } cmp = 1; if( pFile==0 || (cmp = strcmp(pFile->zName,zName))!=0 || strcmp(pFile->zUuid, zUuid)!=0 ){ blob_resize(&filename, nBasename); if( zOrig==0 || strcmp(zOrig,zName)==0 ){ blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm); }else{ if( zPerm[0]==0 ){ zPerm = " w"; } blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig); } nFBcard++; } if( cmp==0 ) pFile = manifest_file_next(pBaseline,0); } blob_reset(&filename); db_finalize(&q); while( pFile ){ blob_appendf(pOut, "F %F\n", pFile->zName); pFile = manifest_file_next(pBaseline, 0); nFBcard++; } blob_appendf(pOut, "P %s", zParentUuid); if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate); free(zParentUuid); db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); db_bind_int(&q2, ":id", 0); while( db_step(&q2)==SQLITE_ROW ){ char *zMergeUuid; int mid = db_column_int(&q2, 0); if( !g.markPrivate && content_is_private(mid) ) continue; zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); if( zMergeUuid ){ blob_appendf(pOut, " %s", zMergeUuid); if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate); free(zMergeUuid); } } db_finalize(&q2); free(zDate); blob_appendf(pOut, "\n"); if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum); if( zBranch && zBranch[0] ){ Stmt q; if( zBgColor && zBgColor[0] ){ blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor); } blob_appendf(pOut, "T *branch * %F\n", zBranch); blob_appendf(pOut, "T *sym-%F *\n", zBranch); /* Cancel all other symbolic tags */ db_prepare(&q, "SELECT tagname FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" " AND tagtype>0 AND tagname GLOB 'sym-*'" " AND tagname!='sym-'||%Q" " ORDER BY tagname", vid, zBranch); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); blob_appendf(pOut, "T -%F *\n", zTag); } db_finalize(&q); } blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); md5sum_blob(pOut, &mcksum); blob_appendf(pOut, "Z %b\n", &mcksum); if( pnFBcard ) *pnFBcard = nFBcard; } /* ** COMMAND: ci ** COMMAND: commit ** ** Usage: %fossil commit ?OPTIONS? ?FILE...? ** ** Create a new version containing all of the changes in the current ** checkout. You will be prompted to enter a check-in comment unless ** the comment has been specified on the command-line using "-m" or a ** file containing the comment using -M. The editor defined in the ** "editor" fossil option (see %fossil help set) will be used, or from ** the "VISUAL" or "EDITOR" environment variables (in that order) if ** no editor is set. ** ** All files that have changed will be committed unless some subset of ** files is specified on the command line. ** ** The --branch option followed by a branch name causes the new check-in ** to be placed in the named branch. The --bgcolor option can be followed ** by a color name (ex: '#ffc0c0') to specify the background color of ** entries in the new branch when shown in the web timeline interface. ** ** A check-in is not permitted to fork unless the --force or -f ** option appears. A check-in is not allowed against a closed check-in. ** ** The --private option creates a private check-in that is never synced. ** Children of private check-ins are automatically private. ** ** Options: ** ** --comment|-m COMMENT-TEXT ** --message-file|-M COMMENT-FILE ** --branch NEW-BRANCH-NAME ** --bgcolor COLOR ** --nosign ** --force|-f ** --private ** --baseline ** --delta ** */ void commit_cmd(void){ int hasChanges; /* True if unsaved changes exist */ int vid; /* blob-id of parent version */ int nrid; /* blob-id of a modified file */ int nvid; /* Blob-id of the new check-in */ Blob comment; /* Check-in comment */ const char *zComment; /* Check-in comment */ Stmt q; /* Query to find files that have been modified */ char *zUuid; /* UUID of the new check-in */ int noSign = 0; /* True to omit signing the manifest using GPG */ int isAMerge = 0; /* True if checking in a merge */ int forceFlag = 0; /* Force a fork */ int forceDelta = 0; /* Force a delta-manifest */ int forceBaseline = 0; /* Force a baseline-manifest */ char *zManifestFile; /* Name of the manifest file */ int useCksum; /* True if checksums should be computed and verified */ int outputManifest; /* True to output "manifest" and "manifest.uuid" */ int testRun; /* True for a test run. Debugging only */ const char *zBranch; /* Create a new branch with this name */ const char *zBgColor; /* Set background color when branching */ const char *zDateOvrd; /* Override date string */ const char *zUserOvrd; /* Override user name */ const char *zComFile; /* Read commit message from this file */ Blob manifest; /* Manifest in baseline form */ Blob muuid; /* Manifest uuid */ Blob cksum1, cksum2; /* Before and after commit checksums */ Blob cksum1b; /* Checksum recorded in the manifest */ int szD; /* Size of the delta manifest */ int szB; /* Size of the baseline manifest */ url_proxy_options(); noSign = find_option("nosign",0,0)!=0; forceDelta = find_option("delta",0,0)!=0; forceBaseline = find_option("baseline",0,0)!=0; if( forceDelta && forceBaseline ){ fossil_fatal("cannot use --delta and --baseline together"); } testRun = find_option("test",0,0)!=0; zComment = find_option("comment","m",1); forceFlag = find_option("force", "f", 0)!=0; zBranch = find_option("branch","b",1); zBgColor = find_option("bgcolor",0,1); zComFile = find_option("message-file", "M", 1); if( find_option("private",0,0) ){ g.markPrivate = 1; if( zBranch==0 ) zBranch = "private"; if( zBgColor==0 ) zBgColor = "#fec084"; /* Orange */ } zDateOvrd = find_option("date-override",0,1); zUserOvrd = find_option("user-override",0,1); db_must_be_within_tree(); noSign = db_get_boolean("omitsign", 0)|noSign; if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } useCksum = db_get_boolean("repo-cksum", 1); outputManifest = db_get_boolean("manifest", 0); verify_all_options(); /* So that older versions of Fossil (that do not understand delta- ** manifest) can continue to use this repository, do not create a new ** delta-manifest unless this repository already contains one or more ** delta-manifets, or unless the delta-manifest is explicitly requested ** by the --delta option. */ if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ forceBaseline = 1; } /* Get the ID of the parent manifest artifact */ vid = db_lget_int("checkout", 0); if( content_is_private(vid) ){ g.markPrivate = 1; } /* ** Autosync if autosync is enabled and this is not a private check-in. */ if( !g.markPrivate ){ autosync(AUTOSYNC_PULL); } /* Require confirmation to continue with the check-in if there is ** clock skew */ if( g.clockSkewSeen ){ Blob ans; blob_zero(&ans); prompt_user("continue in spite of time skew (y/N)? ", &ans); if( blob_str(&ans)[0]!='y' ){ fossil_exit(1); } } /* There are two ways this command may be executed. If there are ** no arguments following the word "commit", then all modified files ** in the checked out directory are committed. If one or more arguments ** follows "commit", then only those files are committed. ** ** After the following function call has returned, the Global.aCommitFile[] |
︙ | ︙ | |||
637 638 639 640 641 642 643 644 645 | /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } db_begin_transaction(); db_record_repository_filename(0); | > < | | | 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 | /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } hasChanges = unsaved_changes(); db_begin_transaction(); db_record_repository_filename(0); if( hasChanges==0 && !isAMerge && !forceFlag ){ fossil_fatal("nothing has changed"); } /* If one or more files that were named on the command line have not ** been modified, bail out now. */ if( g.aCommitFile ){ Blob unmodified; |
︙ | ︙ | |||
676 677 678 679 680 681 682 | */ if( db_exists("SELECT 1 FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, vid) ){ fossil_fatal("cannot commit against a closed leaf"); } | | | 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | */ if( db_exists("SELECT 1 FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, vid) ){ fossil_fatal("cannot commit against a closed leaf"); } if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1); if( zComment ){ blob_zero(&comment); blob_append(&comment, zComment, -1); }else if( zComFile ){ blob_zero(&comment); blob_read_from_file(&comment, zComFile); }else{ |
︙ | ︙ | |||
731 732 733 734 735 736 737 | content_deltify(rid, nrid, 0); } db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); } db_finalize(&q); | | < | < < < < < < < < < | < < < < < < < < < < < < < < < < < < | < > > > | | > | > | > > > > > > | < | < | < < > > > > | | | | < < < < < < | < < < < < | > | > > > | > > > > > > | < < | > | | < | < < < < < < | < < < | < | < < < < > > > > > > > > > > | | | | > > | | | | | | > > | | | | | | | | | | | | | | | | | | | | | | | | > > > > > | 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 | content_deltify(rid, nrid, 0); } db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); } db_finalize(&q); /* Create the new manifest */ if( blob_size(&comment)==0 ){ blob_append(&comment, "(no comment)", -1); } if( forceDelta ){ blob_zero(&manifest); }else{ create_manifest(&manifest, 0, 0, &comment, vid, !forceFlag, useCksum ? &cksum1 : 0, zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB); } /* See if a delta-manifest would be more appropriate */ if( !forceBaseline ){ const char *zBaselineUuid; Manifest *pParent; Manifest *pBaseline; pParent = manifest_get(vid, CFTYPE_MANIFEST); if( pParent && pParent->zBaseline ){ zBaselineUuid = pParent->zBaseline; pBaseline = manifest_get_by_name(zBaselineUuid, 0); }else{ zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); pBaseline = pParent; } if( pBaseline ){ Blob delta; create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid, !forceFlag, useCksum ? &cksum1 : 0, zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD); /* ** At this point, two manifests have been constructed, either of ** which would work for this checkin. The first manifest (held ** in the "manifest" variable) is a baseline manifest and the second ** (held in variable named "delta") is a delta manifest. The ** question now is: which manifest should we use? ** ** Let B be the number of F-cards in the baseline manifest and ** let D be the number of F-cards in the delta manifest, plus one for ** the B-card. (B is held in the szB variable and D is held in the ** szD variable.) Assume that all delta manifests adds X new F-cards. ** Then to minimize the total number of F- and B-cards in the repository, ** we should use the delta manifest if and only if: ** ** D*D < B*X - X*X ** ** X is an unknown here, but for most repositories, we will not be ** far wrong if we assume X=3. */ if( forceDelta || (szD*szD)<(szB*3-9) ){ blob_reset(&manifest); manifest = delta; }else{ blob_reset(&delta); } }else if( forceDelta ){ fossil_panic("unable to find a baseline-manifest for the delta"); } } if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ Blob ans; blob_zero(&ans); prompt_user("unable to sign manifest. continue (y/N)? ", &ans); if( blob_str(&ans)[0]!='y' ){ fossil_exit(1); } } /* If the --test option is specified, output the manifest file ** and rollback the transaction. */ if( testRun ){ blob_write_to_file(&manifest, ""); } if( outputManifest ){ zManifestFile = mprintf("%smanifest", g.zLocalRoot); blob_write_to_file(&manifest, zManifestFile); blob_reset(&manifest); blob_read_from_file(&manifest, zManifestFile); free(zManifestFile); } nvid = content_put(&manifest, 0, 0); if( nvid==0 ){ fossil_panic("trouble committing manifest: %s", g.zErrMsg); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); manifest_crosslink(nvid, &manifest); content_deltify(vid, nvid, 0); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); printf("New_Version: %s\n", zUuid); if( outputManifest ){ zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot); blob_zero(&muuid); blob_appendf(&muuid, "%s\n", zUuid); blob_write_to_file(&muuid, zManifestFile); free(zManifestFile); blob_reset(&muuid); } /* Update the vfile and vmerge tables */ db_multi_exec( "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND file_is_selected(id);" "DELETE FROM vmerge WHERE file_is_selected(id) OR id=0;" "UPDATE vfile SET vid=%d;" "UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL" " WHERE file_is_selected(id);" , vid, nvid ); db_lset_int("checkout", nvid); if( useCksum ){ /* Verify that the repository checksum matches the expected checksum ** calculated before the checkin started (and stored as the R record ** of the manifest file). */ vfile_aggregate_checksum_repository(nvid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ fossil_panic("tree checksum does not match repository after commit"); } /* Verify that the manifest checksum matches the expected checksum */ vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); if( blob_compare(&cksum1, &cksum1b) ){ fossil_panic("manifest checksum does not agree with manifest: " "%b versus %b", &cksum1, &cksum1b); } if( blob_compare(&cksum1, &cksum2) ){ fossil_panic("tree checksum does not match manifest after commit: " "%b versus %b", &cksum1, &cksum2); } /* Verify that the commit did not modify any disk images. */ vfile_aggregate_checksum_disk(nvid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ fossil_panic("tree checksums before and after commit do not match"); } } /* Clear the undo/redo stack */ undo_reset(); /* Commit */ db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); if( testRun ){ db_end_transaction(1); exit(1); } db_end_transaction(0); if( !g.markPrivate ){ autosync(AUTOSYNC_PUSH); } if( count_nonbranch_children(vid)>1 ){ printf("**** warning: a fork has occurred *****\n"); } } |
Changes to src/checkout.c.
︙ | ︙ | |||
76 77 78 79 80 81 82 | return vid; } /* ** Load a vfile from a record ID. */ void load_vfile_from_rid(int vid){ | < < < | < > > | > > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > > < < < < > | < | > | | | | | | | | | > | < | | | > > | | < < < | | > | < | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | return vid; } /* ** Load a vfile from a record ID. */ void load_vfile_from_rid(int vid){ if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ return; } vfile_build(vid); } /* ** Set or clear the vfile.isexe flag for a file. */ static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){ static Stmt s; db_static_prepare(&s, "UPDATE vfile SET isexe=:isexe" " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe" ); db_bind_int(&s, ":isexe", onoff); db_bind_int(&s, ":vid", vid); db_bind_text(&s, ":path", zFilename); db_step(&s); db_reset(&s); } /* ** Set or clear the execute permission bit (as appropriate) for all ** files in the current check-out. */ void checkout_set_all_exe(int vid){ Blob filename; int baseLen; Manifest *pManifest; ManifestFile *pFile; /* Check the EXE permission status of all files */ pManifest = manifest_get(vid, CFTYPE_MANIFEST); if( pManifest==0 ) return; blob_zero(&filename); blob_appendf(&filename, "%s/", g.zLocalRoot); baseLen = blob_size(&filename); manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest, 0))!=0 ){ int isExe; blob_append(&filename, pFile->zName, -1); isExe = pFile->zPerm && strstr(pFile->zPerm, "x"); file_setexe(blob_str(&filename), isExe); set_or_clear_isexe(pFile->zName, vid, isExe); blob_resize(&filename, baseLen); } blob_reset(&filename); manifest_destroy(pManifest); } /* ** If the "manifest" setting is true, then automatically generate ** files named "manifest" and "manifest.uuid" containing, respectively, ** the text of the manifest and the artifact ID of the manifest. */ void manifest_to_disk(int vid){ char *zManFile; Blob manifest; Blob hash; if( db_get_boolean("manifest",0) ){ blob_zero(&manifest); content_get(vid, &manifest); zManFile = mprintf("%smanifest", g.zLocalRoot); blob_write_to_file(&manifest, zManFile); free(zManFile); blob_zero(&hash); sha1sum_blob(&manifest, &hash); zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); blob_append(&hash, "\n", 1); blob_write_to_file(&hash, zManFile); free(zManFile); blob_reset(&hash); }else{ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ zManFile = mprintf("%smanifest", g.zLocalRoot); unlink(zManFile); free(zManFile); } if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){ zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); unlink(zManFile); free(zManFile); } } } /* ** COMMAND: checkout ** COMMAND: co ** ** Usage: %fossil checkout VERSION ?-f|--force? ?--keep? |
︙ | ︙ | |||
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | if( !keepFlag ){ uncheckout(prior); } db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); if( !keepFlag ){ vfile_to_disk(vid, 0, 1, promptFlag); } manifest_to_disk(vid); db_lset_int("checkout", vid); undo_reset(); db_multi_exec("DELETE FROM vmerge"); if( !keepFlag ){ vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b); vfile_aggregate_checksum_disk(vid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ printf("WARNING: manifest checksum does not agree with disk\n"); } | > | | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | if( !keepFlag ){ uncheckout(prior); } db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); if( !keepFlag ){ vfile_to_disk(vid, 0, 1, promptFlag); } checkout_set_all_exe(vid); manifest_to_disk(vid); db_lset_int("checkout", vid); undo_reset(); db_multi_exec("DELETE FROM vmerge"); if( !keepFlag ){ vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b); vfile_aggregate_checksum_disk(vid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ printf("WARNING: manifest checksum does not agree with disk\n"); } if( blob_size(&cksum1b) && blob_compare(&cksum1, &cksum1b) ){ printf("WARNING: manifest checksum does not agree with manifest\n"); } } db_end_transaction(0); } /* |
︙ | ︙ |
Changes to src/clearsign.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 | return 0; } zRand = db_text(0, "SELECT hex(randomblob(10))"); zOut = mprintf("out-%s", zRand); zIn = mprintf("in-%z", zRand); blob_write_to_file(pIn, zOut); zCmd = mprintf("%s %s %s", zBase, zIn, zOut); | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | return 0; } zRand = db_text(0, "SELECT hex(randomblob(10))"); zOut = mprintf("out-%s", zRand); zIn = mprintf("in-%z", zRand); blob_write_to_file(pIn, zOut); zCmd = mprintf("%s %s %s", zBase, zIn, zOut); rc = fossil_system(zCmd); free(zCmd); if( rc==0 ){ if( pOut==pIn ){ blob_reset(pIn); } blob_zero(pOut); blob_read_from_file(pOut, zIn); |
︙ | ︙ |
Changes to src/comformat.c.
︙ | ︙ | |||
36 37 38 39 40 41 42 | int tlen = lineLength - indent; int si, sk, i, k; int doIndent = 0; char zBuf[400]; int lineCnt = 0; for(;;){ | | | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | int tlen = lineLength - indent; int si, sk, i, k; int doIndent = 0; char zBuf[400]; int lineCnt = 0; for(;;){ while( fossil_isspace(zText[0]) ){ zText++; } if( zText[0]==0 ){ if( doIndent==0 ){ printf("\n"); lineCnt = 1; } return lineCnt; } for(sk=si=i=k=0; zText[i] && k<tlen; i++){ char c = zText[i]; if( fossil_isspace(c) ){ si = i; sk = k; if( k==0 || zBuf[k-1]!=' ' ){ zBuf[k++] = ' '; } }else{ zBuf[k] = c; if( c=='-' && k>0 && fossil_isalpha(zBuf[k-1]) ){ si = i+1; sk = k+1; } k++; } } if( doIndent ){ |
︙ | ︙ |
Changes to src/config.h.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** A common header file used by all modules. */ /* ** System header files used by all modules */ #include <unistd.h> #include <stdio.h> #include <stdlib.h> | > > > > > > > > > > > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** A common header file used by all modules. */ /* The following macros are necessary for large-file support under ** some linux distributions, and possibly other unixes as well. */ #define _LARGE_FILE 1 #ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 #endif #define _LARGEFILE_SOURCE 1 #ifndef _RC_COMPILE_ /* ** System header files used by all modules */ #include <unistd.h> #include <stdio.h> #include <stdlib.h> /* #include <ctype.h> // do not use - causes problems */ #include <string.h> #include <stdarg.h> #include <assert.h> #endif #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) # if defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) typedef int socklen_t; # endif # ifndef _WIN32 # define _WIN32 # endif #else # include <sys/types.h> # include <signal.h> # include <pwd.h> #endif /* ** Define the compiler variant, used to compile the project */ #if !defined(COMPILER_NAME) # if defined(__DMC__) # define COMPILER_NAME "dmc" # elif defined(__POCC__) # if defined(_M_X64) # define COMPILER_NAME "pellesc64" # else # define COMPILER_NAME "pellesc32" # endif # elif defined(_MSC_VER) # define COMPILER_NAME "msc" # elif defined(__MINGW32__) # define COMPILER_NAME "mingw32" # elif defined(_WIN32) # define COMPILER_NAME "win32" # elif defined(__GNUC__) # define COMPILER_NAME "gcc-" __VERSION__ # else # define COMPILER_NAME "unknown" # endif #endif #ifndef _RC_COMPILE_ #include "sqlite3.h" /* ** Typedef for a 64-bit integer */ typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; /* ** Unsigned character type */ typedef unsigned char u8; /* In the timeline, check-in messages are truncated at the first space ** that is more than MX_CKIN_MSG from the beginning, or at the first ** paragraph break that is more than MN_CKIN_MSG from the beginning. */ #define MN_CKIN_MSG 100 #define MX_CKIN_MSG 300 /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers ** that vary from one machine to the next. */ #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define FOSSIL_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define FOSSIL_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) #elif !defined(__GNUC__) /* Works for compilers other than LLVM */ # define FOSSIL_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define FOSSIL_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #else /* Generates a warning - but it always works */ # define FOSSIL_INT_TO_PTR(X) ((void*)(X)) # define FOSSIL_PTR_TO_INT(X) ((int)(X)) #endif /* Unset the following to disable internationalization code. */ #ifndef FOSSIL_I18N # define FOSSIL_I18N 1 #endif #if FOSSIL_I18N # include <locale.h> # include <langinfo.h> #endif #ifndef CODESET # undef FOSSIL_I18N # define FOSSIL_I18N 0 #endif #endif /* _RC_COMPILE_ */ |
Changes to src/configure.c.
︙ | ︙ | |||
72 73 74 75 76 77 78 79 80 81 82 83 84 85 | { "css", CONFIGSET_SKIN }, { "header", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "project-name", CONFIGSET_PROJ }, { "project-description", CONFIGSET_PROJ }, { "index-page", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "ticket-table", CONFIGSET_TKT }, { "ticket-common", CONFIGSET_TKT }, { "ticket-newpage", CONFIGSET_TKT }, { "ticket-viewpage", CONFIGSET_TKT }, | > | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | { "css", CONFIGSET_SKIN }, { "header", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "project-name", CONFIGSET_PROJ }, { "project-description", CONFIGSET_PROJ }, { "manifest", CONFIGSET_PROJ }, { "index-page", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "ticket-table", CONFIGSET_TKT }, { "ticket-common", CONFIGSET_TKT }, { "ticket-newpage", CONFIGSET_TKT }, { "ticket-viewpage", CONFIGSET_TKT }, |
︙ | ︙ | |||
452 453 454 455 456 457 458 | zPw = 0; g.dontKeepUrl = 1; }else{ zServer = db_get("last-sync-url", 0); if( zServer==0 ){ fossil_fatal("no server specified"); } | | > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | zPw = 0; g.dontKeepUrl = 1; }else{ zServer = db_get("last-sync-url", 0); if( zServer==0 ){ fossil_fatal("no server specified"); } zPw = unobscure(db_get("last-sync-pw", 0)); } url_parse(zServer); if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw); user_select(); url_enable_proxy("via proxy: "); if( strncmp(zMethod, "push", n)==0 ){ client_sync(0,0,0,0,mask); }else{ client_sync(0,0,0,mask,0); } }else if( strncmp(zMethod, "reset", n)==0 ){ |
︙ | ︙ |
Changes to src/content.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** Procedures store and retrieve records from the repository */ #include "config.h" #include "content.h" #include <assert.h> | < < < < < < < < < < < > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | ** ** Procedures store and retrieve records from the repository */ #include "config.h" #include "content.h" #include <assert.h> /* ** The artifact retrival cache */ static struct { i64 szTotal; /* Total size of all entries in the cache */ int n; /* Current number of eache entries */ int nAlloc; /* Number of slots allocated in a[] */ int nextAge; /* Age counter for implementing LRU */ int skipCnt; /* Used to limit entries expelled from cache */ struct cacheLine { /* One instance of this for each cache entry */ int rid; /* Artifact id */ int age; /* Age. Newer is larger */ Blob content; /* Content of the artifact */ } *a; /* The positive cache */ Bag inCache; /* Set of artifacts currently in cache */ /* ** The missing artifact cache. ** ** Artifacts whose record ID are in missingCache cannot be retrieved ** either because they are phantoms or because they are a delta that ** depends on a phantom. Artifacts whose content we are certain is ** available are in availableCache. If an artifact is in neither cache ** then its current availablity is unknown. */ Bag missing; /* Cache of artifacts that are incomplete */ Bag available; /* Cache of artifacts that are complete */ } contentCache; /* ** Remove the oldest element from the content cache */ static void content_cache_expire_oldest(void){ int i; int mnAge = contentCache.nextAge; int mn = -1; for(i=0; i<contentCache.n; i++){ if( contentCache.a[i].age<mnAge ){ mnAge = contentCache.a[i].age; mn = i; } } if( mn>=0 ){ bag_remove(&contentCache.inCache, contentCache.a[mn].rid); contentCache.szTotal -= blob_size(&contentCache.a[mn].content); blob_reset(&contentCache.a[mn].content); contentCache.n--; contentCache.a[mn] = contentCache.a[contentCache.n]; } } /* ** Add an entry to the content cache */ void content_cache_insert(int rid, Blob *pBlob){ struct cacheLine *p; if( contentCache.n>500 || contentCache.szTotal>50000000 ){ content_cache_expire_oldest(); } if( contentCache.n>=contentCache.nAlloc ){ contentCache.nAlloc = contentCache.nAlloc*2 + 10; contentCache.a = fossil_realloc(contentCache.a, contentCache.nAlloc*sizeof(contentCache.a[0])); } p = &contentCache.a[contentCache.n++]; p->rid = rid; p->age = contentCache.nextAge++; contentCache.szTotal += blob_size(pBlob); p->content = *pBlob; blob_zero(pBlob); bag_insert(&contentCache.inCache, rid); } /* ** Clear the content cache. */ void content_clear_cache(void){ int i; for(i=0; i<contentCache.n; i++){ blob_reset(&contentCache.a[i].content); } bag_clear(&contentCache.missing); bag_clear(&contentCache.available); bag_clear(&contentCache.inCache); contentCache.n = 0; contentCache.szTotal = 0; } /* ** Return the srcid associated with rid. Or return 0 if rid is ** original content and not a delta. */ static int findSrcid(int rid){ |
︙ | ︙ | |||
93 94 95 96 97 98 99 | /* ** Check to see if content is available for artifact "rid". Return ** true if it is. Return false if rid is a phantom or depends on ** a phantom. */ int content_is_available(int rid){ int srcid; | > > | | | | | | | | | | | | | | | | < < < > | | < | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | /* ** Check to see if content is available for artifact "rid". Return ** true if it is. Return false if rid is a phantom or depends on ** a phantom. */ int content_is_available(int rid){ int srcid; int depth = 0; /* Limit to recursion depth */ while( depth++ < 10000000 ){ if( bag_find(&contentCache.missing, rid) ){ return 0; } if( bag_find(&contentCache.available, rid) ){ return 1; } if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){ bag_insert(&contentCache.missing, rid); return 0; } srcid = findSrcid(rid); if( srcid==0 ){ bag_insert(&contentCache.available, rid); return 1; } rid = srcid; } fossil_panic("delta-loop in repository"); return 0; } /* ** Mark artifact rid as being available now. Update the cache to ** show that everything that was formerly unavailable because rid ** was missing is now available. */ |
︙ | ︙ | |||
161 162 163 164 165 166 167 | blob_uncompress(pBlob, pBlob); rc = 1; } db_reset(&q); return rc; } | < < < | | < < > | | < | | < < < < < | | | | | | > > > > > | | > | | | > | < < < < | > | | < > | < > | > | > | | | | < < | < < | < < | < < < < < < < < < < < | | < < < < < < < | > < | < | < < | | > > > > > | < | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | blob_uncompress(pBlob, pBlob); rc = 1; } db_reset(&q); return rc; } /* ** Extract the content for ID rid and put it into the ** uninitialized blob. Return 1 on success. If the record ** is a phantom, zero pBlob and return 0. */ int content_get(int rid, Blob *pBlob){ int rc; int i; int nextRid; assert( g.repositoryOpen ); blob_zero(pBlob); if( rid==0 ) return 0; /* Early out if we know the content is not available */ if( bag_find(&contentCache.missing, rid) ){ return 0; } /* Look for the artifact in the cache first */ if( bag_find(&contentCache.inCache, rid) ){ for(i=0; i<contentCache.n; i++){ if( contentCache.a[i].rid==rid ){ blob_copy(pBlob, &contentCache.a[i].content); contentCache.a[i].age = contentCache.nextAge++; return 1; } } } nextRid = findSrcid(rid); if( nextRid==0 ){ rc = content_of_blob(rid, pBlob); }else{ int n = 1; int nAlloc = 10; int *a = 0; int mx; Blob delta, next; a = fossil_malloc( sizeof(a[0])*nAlloc ); a[0] = rid; a[1] = nextRid; n = 1; while( !bag_find(&contentCache.inCache, nextRid) && (nextRid = findSrcid(nextRid))>0 ){ n++; if( n>=nAlloc ){ nAlloc = nAlloc*2 + 10; a = fossil_realloc(a, nAlloc*sizeof(a[0])); } a[n] = nextRid; } mx = n; rc = content_get(a[n], pBlob); n--; while( rc && n>=0 ){ rc = content_of_blob(a[n], &delta); if( rc ){ blob_delta_apply(pBlob, &delta, &next); blob_reset(&delta); if( (mx-n)%8==0 ){ content_cache_insert(a[n+1], pBlob); }else{ blob_reset(pBlob); } *pBlob = next; } n--; } free(a); if( !rc ) blob_reset(pBlob); } if( rc==0 ){ bag_insert(&contentCache.missing, rid); }else{ bag_insert(&contentCache.available, rid); } return rc; } /* ** COMMAND: artifact ** ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? ** ** Extract an artifact by its SHA1 hash and write the results on ** standard output, or if the optional 4th argument is given, in ** the named output file. ** ** Options: ** ** -R|--repository FILE Extract artifacts from repository FILE */ void artifact_cmd(void){ int rid; Blob content; const char *zFile; db_find_and_open_repository(1); if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?"); zFile = g.argc==4 ? g.argv[3] : "-"; rid = name_to_rid(g.argv[2]); content_get(rid, &content); blob_write_to_file(&content, zFile); } /* ** COMMAND: test-content-rawget |
︙ | ︙ | |||
314 315 316 317 318 319 320 321 322 323 324 325 326 327 | rid = name_to_rid(g.argv[2]); blob_zero(&content); db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid); blob_uncompress(&content, &content); blob_write_to_file(&content, zFile); } /* ** When a record is converted from a phantom to a real record, ** if that record has other records that are derived by delta, ** then call manifest_crosslink() on those other records. */ void after_dephantomize(int rid, int linkFlag){ Stmt q; | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < | < > | | | | > > > | | | > | > > > > > | < < < > | | > > > > > > | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | rid = name_to_rid(g.argv[2]); blob_zero(&content); db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid); blob_uncompress(&content, &content); blob_write_to_file(&content, zFile); } /* ** The following flag is set to disable the automatic calls to ** manifest_crosslink() when a record is dephantomized. This ** flag can be set (for example) when doing a clone when we know ** that rebuild will be run over all records at the conclusion ** of the operation. */ static int ignoreDephantomizations = 0; /* ** When a record is converted from a phantom to a real record, ** if that record has other records that are derived by delta, ** then call manifest_crosslink() on those other records. ** ** If the formerly phantom record or any of the other records ** derived by delta from the former phantom are a baseline manifest, ** then also invoke manifest_crosslink() on the delta-manifests ** associated with that baseline. ** ** Tail recursion is used to minimize stack depth. */ void after_dephantomize(int rid, int linkFlag){ Stmt q; int nChildAlloc = 0; int *aChild = 0; Blob content; if( ignoreDephantomizations ) return; while( rid ){ int nChildUsed = 0; int i; /* Parse the object rid itself */ if( linkFlag ){ content_get(rid, &content); manifest_crosslink(rid, &content); blob_reset(&content); } /* Parse all delta-manifests that depend on baseline-manifest rid */ db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); while( db_step(&q)==SQLITE_ROW ){ int child = db_column_int(&q, 0); if( nChildUsed>=nChildAlloc ){ nChildAlloc = nChildAlloc*2 + 10; aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); } aChild[nChildUsed++] = child; } db_finalize(&q); for(i=0; i<nChildUsed; i++){ content_get(aChild[i], &content); manifest_crosslink(aChild[i], &content); blob_reset(&content); } if( nChildUsed ){ db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); } /* Recursively dephantomize all artifacts that are derived by ** delta from artifact rid */ nChildUsed = 0; db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); while( db_step(&q)==SQLITE_ROW ){ int child = db_column_int(&q, 0); if( nChildUsed>=nChildAlloc ){ nChildAlloc = nChildAlloc*2 + 10; aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); } aChild[nChildUsed++] = child; } db_finalize(&q); for(i=1; i<nChildUsed; i++){ after_dephantomize(aChild[i], 1); } /* Tail recursion for the common case where only a single artifact ** is derived by delta from rid... */ rid = nChildUsed>0 ? aChild[0] : 0; linkFlag = 1; } free(aChild); } /* ** Turn dephantomization processing on or off. */ void content_enable_dephantomize(int onoff){ ignoreDephantomizations = !onoff; } /* ** Write content into the database. Return the record ID. If the ** content is already in the database, just return the record ID. ** ** If srcId is specified, then pBlob is delta content from |
︙ | ︙ |
Changes to src/db.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ** (2) The "repository" database ** ** (3) A local checkout database named "_FOSSIL_" or ".fos" ** and located at the root of the local copy of the source tree. ** */ #include "config.h" #include <sqlite3.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "db.h" #if INTERFACE | > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | ** (2) The "repository" database ** ** (3) A local checkout database named "_FOSSIL_" or ".fos" ** and located at the root of the local copy of the source tree. ** */ #include "config.h" #if ! defined(_WIN32) # include <pwd.h> #endif #include <sqlite3.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "db.h" #if INTERFACE |
︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 | void db_force_rollback(void){ static int busy = 0; if( busy || g.db==0 ) return; busy = 1; undo_rollback(); if( nBegin ){ sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); if( isNewRepo ){ db_close(); unlink(g.zRepositoryName); } } | > < | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | void db_force_rollback(void){ static int busy = 0; if( busy || g.db==0 ) return; busy = 1; undo_rollback(); if( nBegin ){ sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); nBegin = 0; if( isNewRepo ){ db_close(); unlink(g.zRepositoryName); } } busy = 0; } /* ** Install a commit hook. Hooks are installed in sequence order. ** It is an error to install the same commit hook more than once. ** |
︙ | ︙ | |||
532 533 534 535 536 537 538 | }else{ z = 0; } db_finalize(&s); return z; } | | | | 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 | }else{ z = 0; } db_finalize(&s); return z; } #if defined(_WIN32) extern char *sqlite3_win32_mbcs_to_utf8(const char*); #endif /* ** Initialize a new database file with the given schema. If anything ** goes wrong, call db_err() to exit. */ void db_init_database( const char *zFileName, /* Name of database file to create */ const char *zSchema, /* First part of schema */ ... /* Additional SQL to run. Terminate with NULL. */ ){ sqlite3 *db; int rc; const char *zSql; va_list ap; #if defined(_WIN32) zFileName = sqlite3_win32_mbcs_to_utf8(zFileName); #endif rc = sqlite3_open(zFileName, &db); if( rc!=SQLITE_OK ){ db_err(sqlite3_errmsg(db)); } sqlite3_busy_timeout(db, 5000); |
︙ | ︙ | |||
585 586 587 588 589 590 591 | */ static sqlite3 *openDatabase(const char *zDbName){ int rc; const char *zVfs; sqlite3 *db; zVfs = getenv("FOSSIL_VFS"); | | | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 | */ static sqlite3 *openDatabase(const char *zDbName){ int rc; const char *zVfs; sqlite3 *db; zVfs = getenv("FOSSIL_VFS"); #if defined(_WIN32) zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); #endif rc = sqlite3_open_v2( zDbName, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, zVfs ); |
︙ | ︙ | |||
613 614 615 616 617 618 619 | */ void db_open_or_attach(const char *zDbName, const char *zLabel){ if( !g.db ){ g.db = openDatabase(zDbName); g.zRepoDb = "main"; db_connection_init(); }else{ | | | 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 | */ void db_open_or_attach(const char *zDbName, const char *zLabel){ if( !g.db ){ g.db = openDatabase(zDbName); g.zRepoDb = "main"; db_connection_init(); }else{ #if defined(_WIN32) zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); #endif db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); g.zRepoDb = mprintf("%s", zLabel); } } |
︙ | ︙ | |||
637 638 639 640 641 642 643 | ** connection so that we can join between the various databases. In that ** case, invoke this routine with useAttach as 1. */ void db_open_config(int useAttach){ char *zDbName; const char *zHome; if( g.configOpen ) return; | | | 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 | ** connection so that we can join between the various databases. In that ** case, invoke this routine with useAttach as 1. */ void db_open_config(int useAttach){ char *zDbName; const char *zHome; if( g.configOpen ) return; #if defined(_WIN32) zHome = getenv("LOCALAPPDATA"); if( zHome==0 ){ zHome = getenv("APPDATA"); if( zHome==0 ){ zHome = getenv("HOMEPATH"); } } |
︙ | ︙ | |||
659 660 661 662 663 664 665 | fossil_fatal("cannot locate home directory - " "please set the HOME environment variable"); } #endif if( file_isdir(zHome)!=1 ){ fossil_fatal("invalid home directory: %s", zHome); } | | | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 | fossil_fatal("cannot locate home directory - " "please set the HOME environment variable"); } #endif if( file_isdir(zHome)!=1 ){ fossil_fatal("invalid home directory: %s", zHome); } #ifndef _WIN32 if( access(zHome, W_OK) ){ fossil_fatal("home directory %s must be writeable", zHome); } #endif g.zHome = mprintf("%/", zHome); #if defined(_WIN32) /* . filenames give some window systems problems and many apps problems */ zDbName = mprintf("%//_fossil", zHome); #else zDbName = mprintf("%s/.fossil", zHome); #endif if( file_size(zDbName)<1024*3 ){ db_init_database(zDbName, zConfigSchema, (char*)0); |
︙ | ︙ | |||
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 | db_open_repository(0); } /* ** Close the database connection. */ void db_close(void){ if( g.db==0 ) return; while( pAllStmt ){ db_finalize(pAllStmt); } db_end_transaction(1); g.repositoryOpen = 0; g.localOpen = 0; g.configOpen = 0; sqlite3_close(g.db); g.db = 0; } /* ** Create a new empty repository database with the given name. ** ** Only the schema is initialized. The required VAR tables entries | > > > > > > > > > > | 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 | db_open_repository(0); } /* ** Close the database connection. */ void db_close(void){ sqlite3_stmt *pStmt; if( g.db==0 ) return; while( pAllStmt ){ db_finalize(pAllStmt); } db_end_transaction(1); pStmt = 0; while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); } g.repositoryOpen = 0; g.localOpen = 0; g.configOpen = 0; sqlite3_wal_checkpoint(g.db, 0); sqlite3_close(g.db); g.db = 0; if( g.dbConfig ){ sqlite3_close(g.dbConfig); g.dbConfig = 0; } } /* ** Create a new empty repository database with the given name. ** ** Only the schema is initialized. The required VAR tables entries |
︙ | ︙ | |||
932 933 934 935 936 937 938 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ const char *zUser; zUser = db_get("default-user", 0); if( zUser==0 ){ zUser = zDefaultUser; } if( zUser==0 ){ | | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ const char *zUser; zUser = db_get("default-user", 0); if( zUser==0 ){ zUser = zDefaultUser; } if( zUser==0 ){ #if defined(_WIN32) zUser = getenv("USERNAME"); #else zUser = getenv("USER"); #endif } if( zUser==0 ){ zUser = "root"; |
︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 | db_create_default_users(0, zDefaultUser); user_select(); if( zInitialDate ){ int rid; blob_zero(&manifest); blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n"); | | < | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 | db_create_default_users(0, zDefaultUser); user_select(); if( zInitialDate ){ int rid; blob_zero(&manifest); blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n"); zDate = date_in_standard_format(zInitialDate); blob_appendf(&manifest, "D %s\n", zDate); blob_appendf(&manifest, "P\n"); md5sum_init(); blob_appendf(&manifest, "R %s\n", md5sum_finish(0)); blob_appendf(&manifest, "T *branch * trunk\n"); blob_appendf(&manifest, "T *sym-trunk *\n"); blob_appendf(&manifest, "U %F\n", g.zLogin); |
︙ | ︙ | |||
1251 1252 1253 1254 1255 1256 1257 | ** If g.useAttach that means the ~/.fossil database was opened with ** the useAttach flag set to 1. In that case no connection swap is required ** so this routine is a no-op. */ void db_swap_connections(void){ if( !g.useAttach ){ sqlite3 *dbTemp = g.db; | < | 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 | ** If g.useAttach that means the ~/.fossil database was opened with ** the useAttach flag set to 1. In that case no connection swap is required ** so this routine is a no-op. */ void db_swap_connections(void){ if( !g.useAttach ){ sqlite3 *dbTemp = g.db; g.db = g.dbConfig; g.dbConfig = dbTemp; } } /* ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the |
︙ | ︙ | |||
1372 1373 1374 1375 1376 1377 1378 | } void db_lset_int(const char *zName, int value){ db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); } /* ** Record the name of a local repository in the global_config() database. | | | 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 | } void db_lset_int(const char *zName, int value){ db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); } /* ** Record the name of a local repository in the global_config() database. ** The repository filename %s is recorded as an entry with a "name" field ** of the following form: ** ** repo:%s ** ** The value field is set to 1. */ void db_record_repository_filename(const char *zName){ |
︙ | ︙ | |||
1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 | }else{ printf("%-20s\n", zName); } db_finalize(&q); } /* ** COMMAND: settings ** COMMAND: unset ** %fossil settings ?PROPERTY? ?VALUE? ?-global? ** %fossil unset PROPERTY ?-global? ** ** The "settings" command with no arguments lists all properties and their ** values. With just a property name it shows the value of that property. ** With a value argument it changes the property for the current repository. ** ** The "unset" command clears a property setting. ** ** ** auto-captcha If enabled, the Login page provides a button to ** fill in the captcha password. Default: on ** ** auto-shun If enabled, automatically pull the shunning list ** from a server to which the client autosyncs. ** ** autosync If enabled, automatically pull prior to commit ** or update and automatically push after commit or ** tag or branch creation. If the value is "pullonly" ** then only pull operations occur automatically. ** ** binary-glob The VALUE is a comma-separated list of GLOB patterns ** that should be treated as binary files for merging ** purposes. Example: *.xml ** ** clearsign When enabled, fossil will attempt to sign all commits ** with gpg. When disabled (the default), commits will | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 | }else{ printf("%-20s\n", zName); } db_finalize(&q); } /* ** define all settings, which can be controlled via the set/unset ** command. var is the name of the internal configuration name for db_(un)set. ** If var is 0, the settings name is used. ** width is the length for the edit field on the behavior page, 0 ** is used for on/off checkboxes. ** The behaviour page doesn't use a special layout. It lists all ** set-commands and displays the 'set'-help as info. */ #if INTERFACE struct stControlSettings { char const *name; /* Name of the setting */ char const *var; /* Internal variable name used by db_set() */ int width; /* Width of display. 0 for boolean values */ char const *def; /* Default value */ }; #endif /* INTERFACE */ struct stControlSettings const ctrlSettings[] = { { "auto-captcha", "autocaptcha", 0, "on" }, { "auto-shun", 0, 0, "on" }, { "autosync", 0, 0, "on" }, { "binary-glob", 0, 32, "" }, { "clearsign", 0, 0, "off" }, { "diff-command", 0, 16, "" }, { "dont-push", 0, 0, "off" }, { "editor", 0, 16, "" }, { "gdiff-command", 0, 16, "gdiff" }, { "ignore-glob", 0, 40, "" }, { "http-port", 0, 16, "8080" }, { "localauth", 0, 0, "off" }, { "manifest", 0, 0, "off" }, { "mtime-changes", 0, 0, "on" }, { "pgp-command", 0, 32, "gpg --clearsign -o " }, { "proxy", 0, 32, "off" }, { "repo-cksum", 0, 0, "on" }, { "ssh-command", 0, 32, "" }, { "web-browser", 0, 32, "" }, { 0,0,0,0 } }; /* ** COMMAND: settings ** COMMAND: unset ** %fossil settings ?PROPERTY? ?VALUE? ?-global? ** %fossil unset PROPERTY ?-global? ** ** The "settings" command with no arguments lists all properties and their ** values. With just a property name it shows the value of that property. ** With a value argument it changes the property for the current repository. ** ** The "unset" command clears a property setting. ** ** ** auto-captcha If enabled, the Login page provides a button to ** fill in the captcha password. Default: on ** ** auto-shun If enabled, automatically pull the shunning list ** from a server to which the client autosyncs. ** Default: on ** ** autosync If enabled, automatically pull prior to commit ** or update and automatically push after commit or ** tag or branch creation. If the value is "pullonly" ** then only pull operations occur automatically. ** Default: on ** ** binary-glob The VALUE is a comma-separated list of GLOB patterns ** that should be treated as binary files for merging ** purposes. Example: *.xml ** ** clearsign When enabled, fossil will attempt to sign all commits ** with gpg. When disabled (the default), commits will ** be unsigned. Default: off ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** |
︙ | ︙ | |||
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 | ** Example: *.o,*.obj,*.exe ** ** localauth If enabled, require that HTTP connections from ** 127.0.0.1 be authenticated by password. If ** false, all HTTP requests from localhost have ** unrestricted access to the repository. ** ** mtime-changes Use file modification times (mtimes) to detect when ** files have been modified. (Default "on".) ** ** pgp-command Command used to clear-sign manifests at check-in. ** The default is "gpg --clearsign -o ". ** ** proxy URL of the HTTP proxy. If undefined or "off" then ** the "http_proxy" environment variable is consulted. ** If the http_proxy environment variable is undefined ** then a direct HTTP connection is used. ** ** web-browser A shell command used to launch your preferred ** web browser when given a URL as an argument. ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. */ void setting_cmd(void){ | > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < | | > | | | > > > > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 | ** Example: *.o,*.obj,*.exe ** ** localauth If enabled, require that HTTP connections from ** 127.0.0.1 be authenticated by password. If ** false, all HTTP requests from localhost have ** unrestricted access to the repository. ** ** manifest If enabled, automatically create files "manifest" and ** "manifest.uuid" in every checkout. The SQLite and ** Fossil repositories both require this. Default: off. ** ** mtime-changes Use file modification times (mtimes) to detect when ** files have been modified. (Default "on".) ** ** pgp-command Command used to clear-sign manifests at check-in. ** The default is "gpg --clearsign -o ". ** ** proxy URL of the HTTP proxy. If undefined or "off" then ** the "http_proxy" environment variable is consulted. ** If the http_proxy environment variable is undefined ** then a direct HTTP connection is used. ** ** repo-cksum Compute checksums over all files in each checkout ** as a double-check of correctness. Defaults to "on". ** Disable on large repositories for a performance ** improvement. ** ** ssh-command Command used to talk to a remote machine with ** the "ssh://" protocol. ** ** web-browser A shell command used to launch your preferred ** web browser when given a URL as an argument. ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. */ void setting_cmd(void){ int i; int globalFlag = find_option("global","g",0)!=0; int unsetFlag = g.argv[1][0]=='u'; db_open_config(1); db_find_and_open_repository(0); if( !g.repositoryOpen ){ globalFlag = 1; } if( unsetFlag && g.argc!=3 ){ usage("PROPERTY ?-global?"); } if( g.argc==2 ){ for(i=0; ctrlSettings[i].name; i++){ print_setting(ctrlSettings[i].name); } }else if( g.argc==3 || g.argc==4 ){ const char *zName = g.argv[2]; int isManifest; int n = strlen(zName); for(i=0; ctrlSettings[i].name; i++){ if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break; } if( !ctrlSettings[i].name ){ fossil_fatal("no such setting: %s", zName); } isManifest = strcmp(ctrlSettings[i].name, "manifest")==0; if( isManifest && globalFlag ){ fossil_fatal("cannot set 'manifest' globally"); } if( unsetFlag ){ db_unset(ctrlSettings[i].name, globalFlag); }else if( g.argc==4 ){ db_set(ctrlSettings[i].name, g.argv[3], globalFlag); }else{ isManifest = 0; print_setting(ctrlSettings[i].name); } if( isManifest ){ manifest_to_disk(db_lget_int("checkout", 0)); } }else{ usage("?PROPERTY? ?VALUE?"); } } /* ** The input in a a timespan measured in days. Return a string which ** describes that timespan in units of seconds, minutes, hours, days, ** or years, depending on its duration. */ char *db_timespan_name(double rSpan){ if( rSpan<0 ) rSpan = -rSpan; rSpan *= 24.0*3600.0; /* Convert units to seconds */ if( rSpan<120.0 ){ return sqlite3_mprintf("%.1f seconds", rSpan); } rSpan /= 60.0; /* Convert units to minutes */ if( rSpan<90.0 ){ return sqlite3_mprintf("%.1f minutes", rSpan); } rSpan /= 60.0; /* Convert units to hours */ if( rSpan<=48.0 ){ return sqlite3_mprintf("%.1f hours", rSpan); } rSpan /= 24.0; /* Convert units to days */ if( rSpan<=365.0 ){ return sqlite3_mprintf("%.1f days", rSpan); } rSpan /= 356.24; /* Convert units to years */ return sqlite3_mprintf("%.1f years", rSpan); } /* ** COMMAND: test-timespan ** %fossil test-timespan TIMESTAMP ** ** Print the approximate span of time from now to TIMESTAMP. */ void test_timespan_cmd(void){ double rDiff; if( g.argc!=3 ) usage("TIMESTAMP"); sqlite3_open(":memory:", &g.db); rDiff = db_double(0.0, "SELECT julianday('now') - julianday(%Q)", g.argv[2]); printf("Time differences: %s\n", db_timespan_name(rDiff)); sqlite3_close(g.db); g.db = 0; } |
Changes to src/delta.c.
︙ | ︙ | |||
193 194 195 196 197 198 199 | } /* ** Compute a 32-bit checksum on the N-byte buffer. Return the result. */ static unsigned int checksum(const char *zIn, size_t N){ const unsigned char *z = (const unsigned char *)zIn; | | > > > | | | | | > > > > | | | | | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | } /* ** Compute a 32-bit checksum on the N-byte buffer. Return the result. */ static unsigned int checksum(const char *zIn, size_t N){ const unsigned char *z = (const unsigned char *)zIn; unsigned sum0 = 0; unsigned sum1 = 0; unsigned sum2 = 0; unsigned sum3 = 0; while(N >= 16){ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]); z += 16; N -= 16; } while(N >= 4){ sum0 += z[0]; sum1 += z[1]; sum2 += z[2]; sum3 += z[3]; z += 4; N -= 4; } sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); switch(N){ case 3: sum3 += (z[2] << 8); case 2: sum3 += (z[1] << 16); case 1: sum3 += (z[0] << 24); default: ; } return sum3; } /* ** Create a new delta. ** ** The delta is written into a preallocated buffer, zDelta, which ** should be at least 60 bytes longer than the target file, zOut. |
︙ | ︙ | |||
315 316 317 318 319 320 321 | return zDelta - zOrigDelta; } /* Compute the hash table used to locate matching sections in the ** source file. */ nHash = lenSrc/NHASH; | | < | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | return zDelta - zOrigDelta; } /* Compute the hash table used to locate matching sections in the ** source file. */ nHash = lenSrc/NHASH; collide = fossil_malloc( nHash*2*sizeof(int) ); landmark = &collide[nHash]; memset(landmark, -1, nHash*sizeof(int)); memset(collide, -1, nHash*sizeof(int)); for(i=0; i<lenSrc-NHASH; i+=NHASH){ int hv; hash_init(&h, &zSrc[i]); hv = hash_32bit(&h) % nHash; |
︙ | ︙ | |||
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | int lenSrc, /* Length of the source file */ const char *zDelta, /* Delta to apply to the pattern */ int lenDelta, /* Length of the delta */ char *zOut /* Write the output into this preallocated buffer */ ){ unsigned int limit; unsigned int total = 0; char *zOrigOut = zOut; limit = getInt(&zDelta, &lenDelta); if( *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } zDelta++; lenDelta--; | > > | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 | int lenSrc, /* Length of the source file */ const char *zDelta, /* Delta to apply to the pattern */ int lenDelta, /* Length of the delta */ char *zOut /* Write the output into this preallocated buffer */ ){ unsigned int limit; unsigned int total = 0; #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST char *zOrigOut = zOut; #endif limit = getInt(&zDelta, &lenDelta); if( *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } zDelta++; lenDelta--; |
︙ | ︙ | |||
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | zDelta += cnt; lenDelta -= cnt; break; } case ';': { zDelta++; lenDelta--; zOut[0] = 0; if( cnt!=checksum(zOrigOut, total) ){ /* ERROR: bad checksum */ return -1; } if( total!=limit ){ /* ERROR: generated size does not match predicted size */ return -1; } return total; } default: { | > > | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 | zDelta += cnt; lenDelta -= cnt; break; } case ';': { zDelta++; lenDelta--; zOut[0] = 0; #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST if( cnt!=checksum(zOrigOut, total) ){ /* ERROR: bad checksum */ return -1; } #endif if( total!=limit ){ /* ERROR: generated size does not match predicted size */ return -1; } return total; } default: { |
︙ | ︙ |
Changes to src/descendants.c.
︙ | ︙ | |||
316 317 318 319 320 321 322 | style_submenu_element("Open", "Open", "leaves"); } style_header("Leaves"); login_anonymous_available(); compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1); style_sidebox_begin("Nomenclature:", "33%"); @ <ol> | > | > | > | | | | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | style_submenu_element("Open", "Open", "leaves"); } style_header("Leaves"); login_anonymous_available(); compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1); style_sidebox_begin("Nomenclature:", "33%"); @ <ol> @ <li> A <div class="sideboxDescribed">leaf</div> @ is a check-in with no descendants.</li> @ <li> An <div class="sideboxDescribed">open leaf</div> @ is a leaf that does not have a "closed" tag @ and is thus assumed to still be in use.</li> @ <li> A <div class="sideboxDescribed">closed leaf</div> @ has a "closed" tag and is thus assumed to @ be historical and no longer in active use.</li> @ </ol> style_sidebox_end(); if( showAll ){ @ <h1>All leaves, both open and closed:</h1> }else if( showClosed ){ @ <h1>Closed leaves:</h1> }else{ @ <h1>Open leaves:</h1> } db_prepare(&q, "%s" " AND blob.rid IN leaves" " ORDER BY event.mtime DESC", timeline_query_for_www() ); www_print_timeline(&q, TIMELINE_LEAFONLY, leaves_extra); db_finalize(&q); @ <br /> @ <script type="text/JavaScript"> @ function xin(id){ @ } @ function xout(id){ @ } @ </script> style_footer(); } |
Changes to src/diff.c.
︙ | ︙ | |||
63 64 65 66 67 68 69 | }; /* ** Return an array of DLine objects containing a pointer to the ** start of each line and a hash of that line. The lower ** bits of the hash store the length of each line. ** | | > > | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | }; /* ** Return an array of DLine objects containing a pointer to the ** start of each line and a hash of that line. The lower ** bits of the hash store the length of each line. ** ** Trailing whitespace is removed from each line. 2010-08-20: Not any ** more. If trailing whitespace is ignored, the "patch" command gets ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b] ** ** Return 0 if the file is binary or contains a line that is ** too long. */ static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){ int nLine, i, j, k, x; unsigned int h, h2; DLine *a; /* Count the number of lines. Allocate space to hold ** the returned array. */ |
︙ | ︙ | |||
92 93 94 95 96 97 98 | } j = 0; } } if( j>LENGTH_MASK ){ return 0; } | | < > > > > > | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | } j = 0; } } if( j>LENGTH_MASK ){ return 0; } a = fossil_malloc( nLine*sizeof(a[0]) ); memset(a, 0, nLine*sizeof(a[0]) ); if( n==0 ){ *pnLine = 0; return a; } /* Fill in the array */ for(i=0; i<nLine; i++){ a[i].z = z; for(j=0; z[j] && z[j]!='\n'; j++){} k = j; while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; } for(h=0, x=0; x<k; x++){ h = h ^ (h<<2) ^ z[x]; } a[i].h = h = (h<<LENGTH_MASK_SZ) | k;; h2 = h % nLine; a[i].iNext = a[h2].iHash; a[h2].iHash = i+1; |
︙ | ︙ | |||
136 137 138 139 140 141 142 | blob_append(pOut, "\n", 1); } /* ** Expand the size of aEdit[] array to hold nEdit elements. */ static void expandEdit(DContext *p, int nEdit){ | < | < < < < < < | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | blob_append(pOut, "\n", 1); } /* ** Expand the size of aEdit[] array to hold nEdit elements. */ static void expandEdit(DContext *p, int nEdit){ p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int)); p->nEditAlloc = nEdit; } /* ** Append a new COPY/DELETE/INSERT triple. */ static void appendTriple(DContext *p, int nCopy, int nDel, int nIns){ |
︙ | ︙ | |||
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | m = R[r+nr*3]; if( m>nContext ) m = nContext; for(j=0; j<m; j++){ appendDiffLine(pOut, " ", &B[b+j]); } } } /* ** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[] ** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence ** of lines in these two blocks that are exactly the same. Return ** the bounds of the matching sequence. */ static void longestCommonSequence( DContext *p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ double bestScore = -1e30; /* Best score seen so far */ int i, j; /* Loop counters */ int iSX, iSY, iEX, iEY; /* Current match */ double score; /* Current score */ int skew; /* How lopsided is the match */ int dist; /* Distance of match from center */ int mid; /* Center of the span */ int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ int iSXp, iSYp, iEXp, iEYp; /* Previous match */ iSXb = iSXp = iS1; iEXb = iEXp = iS1; iSYb = iSYp = iS2; iEYb = iEYp = iS2; mid = (iE1 + iS1)/2; for(i=iS1; i<iE1; i++){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | m = R[r+nr*3]; if( m>nContext ) m = nContext; for(j=0; j<m; j++){ appendDiffLine(pOut, " ", &B[b+j]); } } } /* ** Compute the optimal longest common subsequence (LCS) using an ** exhaustive search. This version of the LCS is only used for ** shorter input strings since runtime is O(N*N) where N is the ** input string length. */ static void optimalLCS( DContext *p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ int mxLength = 0; /* Length of longest common subsequence */ int i, j; /* Loop counters */ int k; /* Length of a candidate subsequence */ int iSXb = iS1; /* Best match so far */ int iSYb = iS2; /* Best match so far */ for(i=iS1; i<iE1-mxLength; i++){ for(j=iS2; j<iE2-mxLength; j++){ if( !same_dline(&p->aFrom[i], &p->aTo[j]) ) continue; if( mxLength && !same_dline(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){ continue; } k = 1; while( i+k<iE1 && j+k<iE2 && same_dline(&p->aFrom[i+k],&p->aTo[j+k]) ){ k++; } if( k>mxLength ){ iSXb = i; iSYb = j; mxLength = k; } } } *piSX = iSXb; *piEX = iSXb + mxLength; *piSY = iSYb; *piEY = iSYb + mxLength; } /* ** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[] ** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence ** of lines in these two blocks that are exactly the same. Return ** the bounds of the matching sequence. ** ** If there are two or more possible answers of the same length, the ** returned sequence should be the one closest to the center of the ** input range. ** ** Ideally, the common sequence should be the longest possible common ** sequence. However, an exact computation of LCS is O(N*N) which is ** way too slow for larger files. So this routine uses an O(N) ** heuristic approximation based on hashing that usually works about ** as well. But if the O(N) algorithm doesn't get a good solution ** and N is not too large, we fall back to an exact solution by ** calling optimalLCS(). */ static void longestCommonSequence( DContext *p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ double bestScore = -1e30; /* Best score seen so far */ int i, j; /* Loop counters */ int iSX, iSY, iEX, iEY; /* Current match */ double score; /* Current score */ int skew; /* How lopsided is the match */ int dist; /* Distance of match from center */ int mid; /* Center of the span */ int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ int iSXp, iSYp, iEXp, iEYp; /* Previous match */ iSXb = iSXp = iS1; iEXb = iEXp = iS1; iSYb = iSYp = iS2; iEYb = iEYp = iS2; mid = (iE1 + iS1)/2; for(i=iS1; i<iE1; i++){ |
︙ | ︙ | |||
352 353 354 355 356 357 358 | }else{ iSXp = iSX; iSYp = iSY; iEXp = iEX; iEYp = iEY; } } | > > > > > | | | | > | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | }else{ iSXp = iSX; iSYp = iSY; iEXp = iEX; iEYp = iEY; } } if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){ /* If no common sequence is found using the hashing heuristic and ** the input is not too big, use the expensive exact solution */ optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY); }else{ *piSX = iSXb; *piSY = iSYb; *piEX = iEXb; *piEY = iEYb; } /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n", iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */ } /* ** Do a single step in the difference. Compute a sequence of ** copy/delete/insert steps that will convert lines iS1 through iE1-1 of |
︙ | ︙ | |||
470 471 472 473 474 475 476 | ** file is encountered, 0 is returned and pOut is written with ** text "cannot compute difference between binary files". */ int *text_diff( Blob *pA_Blob, /* FROM file */ Blob *pB_Blob, /* TO file */ Blob *pOut, /* Write unified diff here if not NULL */ | | > | > | > | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 | ** file is encountered, 0 is returned and pOut is written with ** text "cannot compute difference between binary files". */ int *text_diff( Blob *pA_Blob, /* FROM file */ Blob *pB_Blob, /* TO file */ Blob *pOut, /* Write unified diff here if not NULL */ int nContext, /* Amount of context to unified diff */ int ignoreEolWs /* Ignore whitespace at the end of lines */ ){ DContext c; /* Prepare the input files */ memset(&c, 0, sizeof(c)); c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), &c.nFrom, ignoreEolWs); c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), &c.nTo, ignoreEolWs); if( c.aFrom==0 || c.aTo==0 ){ free(c.aFrom); free(c.aTo); if( pOut ){ blob_appendf(pOut, "cannot compute difference between binary files\n"); } return 0; |
︙ | ︙ | |||
520 521 522 523 524 525 526 | int i; int *R; if( g.argc<4 ) usage("FILE1 FILE2 ..."); blob_read_from_file(&a, g.argv[2]); for(i=3; i<g.argc; i++){ if( i>3 ) printf("-------------------------------\n"); blob_read_from_file(&b, g.argv[i]); | | | | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 | int i; int *R; if( g.argc<4 ) usage("FILE1 FILE2 ..."); blob_read_from_file(&a, g.argv[2]); for(i=3; i<g.argc; i++){ if( i>3 ) printf("-------------------------------\n"); blob_read_from_file(&b, g.argv[i]); R = text_diff(&a, &b, 0, 0, 0); for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ printf(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]); } /* free(R); */ blob_reset(&b); } } /* ** COMMAND: test-udiff */ void test_udiff_cmd(void){ Blob a, b, out; if( g.argc!=4 ) usage("FILE1 FILE2"); blob_read_from_file(&a, g.argv[2]); blob_read_from_file(&b, g.argv[3]); blob_zero(&out); text_diff(&a, &b, &out, 3, 0); blob_write_to_file(&out, "-"); } /************************************************************************** ** The basic difference engine is above. What follows is the annotation ** engine. Both are in the same file since they share many components. */ |
︙ | ︙ | |||
572 573 574 575 576 577 578 | ** to be annotated. The annotator takes control of the input Blob and ** will release it when it is finished with it. */ static int annotation_start(Annotator *p, Blob *pInput){ int i; memset(p, 0, sizeof(*p)); | | | < | 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | ** to be annotated. The annotator takes control of the input Blob and ** will release it when it is finished with it. */ static int annotation_start(Annotator *p, Blob *pInput){ int i; memset(p, 0, sizeof(*p)); p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1); if( p->c.aTo==0 ){ return 1; } p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo ); for(i=0; i<p->c.nTo; i++){ p->aOrig[i].z = p->c.aTo[i].z; p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; p->aOrig[i].zSrc = 0; } p->nOrig = p->c.nTo; return 0; |
︙ | ︙ | |||
600 601 602 603 604 605 606 | */ static int annotation_step(Annotator *p, Blob *pParent, char *zPName){ int i, j; int lnTo; /* Prepare the parent file to be diffed */ p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent), | | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 | */ static int annotation_step(Annotator *p, Blob *pParent, char *zPName){ int i, j; int lnTo; /* Prepare the parent file to be diffed */ p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent), &p->c.nFrom, 1); if( p->c.aFrom==0 ){ return 1; } /* Compute the differences going from pParent to the file being ** annotated. */ diff_all(&p->c); |
︙ | ︙ | |||
755 756 757 758 759 760 761 | /* ** COMMAND: annotate ** ** %fossil annotate FILENAME ** ** Output the text of a file with markings to show when each line of | | | 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 | /* ** COMMAND: annotate ** ** %fossil annotate FILENAME ** ** Output the text of a file with markings to show when each line of ** the file was last modified. */ void annotate_cmd(void){ int fnid; /* Filename ID */ int fid; /* File instance ID */ int mid; /* Manifest where file was checked in */ Blob treename; /* FILENAME translated to canonical form */ char *zFilename; /* Cannonical filename */ |
︙ | ︙ |
Changes to src/diffcmd.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code used to implement the "diff" command */ #include "config.h" #include "diffcmd.h" #include <assert.h> /* | | | < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < | > | | > > > > | > > | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | ** This file contains code used to implement the "diff" command */ #include "config.h" #include "diffcmd.h" #include <assert.h> /* ** Diff option flags */ #define DIFF_NEWFILE 0x01 /* Treat non-existing fails as empty files */ #define DIFF_NOEOLWS 0x02 /* Ignore whitespace at the end of lines */ /* ** Show the difference between two files, one in memory and one on disk. ** ** The difference is the set of edits needed to transform pFile1 into ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. */ static void diff_file( Blob *pFile1, /* In memory content to compare from */ const char *zFile2, /* On disk content to compare to */ const char *zName, /* Display name of the file */ const char *zDiffCmd, /* Command for comparison */ int ignoreEolWs /* Ignore whitespace at end of line */ ){ if( zDiffCmd==0 ){ Blob out; /* Diff output text */ Blob file2; /* Content of zFile2 */ const char *zName2; /* Name of zFile2 for display */ /* Read content of zFile2 into memory */ blob_zero(&file2); if( file_size(zFile2)<0 ){ zName2 = "/dev/null"; }else{ blob_read_from_file(&file2, zFile2); zName2 = zName; } /* Compute and output the differences */ blob_zero(&out); text_diff(pFile1, &file2, &out, 5, ignoreEolWs); printf("--- %s\n+++ %s\n", zName, zName2); printf("%s\n", blob_str(&out)); /* Release memory resources */ blob_reset(&file2); blob_reset(&out); }else{ int cnt = 0; |
︙ | ︙ | |||
112 113 114 115 116 117 118 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, blob_str(&nameFile1)); blob_append(&cmd, " ", 1); shell_escape(&cmd, zFile2); /* Run the external diff command */ | | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, blob_str(&nameFile1)); blob_append(&cmd, " ", 1); shell_escape(&cmd, zFile2); /* Run the external diff command */ fossil_system(blob_str(&cmd)); /* Delete the temporary file and clean up memory used */ unlink(blob_str(&nameFile1)); blob_reset(&nameFile1); blob_reset(&cmd); } } |
︙ | ︙ | |||
134 135 136 137 138 139 140 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. */ static void diff_file_mem( Blob *pFile1, /* In memory content to compare from */ Blob *pFile2, /* In memory content to compare to */ const char *zName, /* Display name of the file */ | | > | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. */ static void diff_file_mem( Blob *pFile1, /* In memory content to compare from */ Blob *pFile2, /* In memory content to compare to */ const char *zName, /* Display name of the file */ const char *zDiffCmd, /* Command for comparison */ int ignoreEolWs /* Ignore whitespace at end of lines */ ){ if( zDiffCmd==0 ){ Blob out; /* Diff output text */ blob_zero(&out); text_diff(pFile1, pFile2, &out, 5, ignoreEolWs); printf("--- %s\n+++ %s\n", zName, zName); printf("%s\n", blob_str(&out)); /* Release memory resources */ blob_reset(&out); }else{ Blob cmd; |
︙ | ︙ | |||
165 166 167 168 169 170 171 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, zTemp1); blob_append(&cmd, " ", 1); shell_escape(&cmd, zTemp2); /* Run the external diff command */ | | | > > > > | | > > > > > > > > | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, zTemp1); blob_append(&cmd, " ", 1); shell_escape(&cmd, zTemp2); /* Run the external diff command */ fossil_system(blob_str(&cmd)); /* Delete the temporary file and clean up memory used */ unlink(zTemp1); unlink(zTemp2); blob_reset(&cmd); } } /* ** Do a diff against a single file named in g.argv[2] from version zFrom ** against the same file on disk. */ static void diff_one_against_disk( const char *zFrom, /* Name of file */ const char *zDiffCmd, /* Use this "diff" command */ int ignoreEolWs /* Ignore whitespace changes at end of lines */ ){ Blob fname; Blob content; file_tree_name(g.argv[2], &fname, 1); historical_version_of_file(zFrom, blob_str(&fname), &content, 0); diff_file(&content, g.argv[2], g.argv[2], zDiffCmd, ignoreEolWs); blob_reset(&content); blob_reset(&fname); } /* ** Run a diff between the version zFrom and files on disk. zFrom might ** be NULL which means to simply show the difference between the edited ** files on disk and the check-out on which they are based. */ static void diff_all_against_disk( const char *zFrom, /* Version to difference from */ const char *zDiffCmd, /* Use this diff command. NULL for built-in */ int diffFlags /* Flags controlling diff output */ ){ int vid; Blob sql; Stmt q; int ignoreEolWs; /* Ignore end-of-line whitespace */ int asNewFile; /* Treat non-existant files as empty files */ ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0; asNewFile = (diffFlags & DIFF_NEWFILE)!=0; vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 1); blob_zero(&sql); db_begin_transaction(); if( zFrom ){ int rid = name_to_rid(zFrom); if( !is_a_version(rid) ){ |
︙ | ︙ | |||
244 245 246 247 248 249 250 251 252 253 254 255 256 257 | } db_prepare(&q, blob_str(&sql)); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); int isDeleted = db_column_int(&q, 1); int isChnged = db_column_int(&q,2); int isNew = db_column_int(&q,3); char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); if( isDeleted ){ printf("DELETED %s\n", zPathname); }else if( access(zFullName, 0) ){ printf("MISSING %s\n", zPathname); }else if( isNew ){ printf("ADDED %s\n", zPathname); | > > > > > > | < < | > > > > | > > > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > > | > > | > | | | | | | > > > | | > > > | | | | < < | < < < < | < < | | | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | } db_prepare(&q, blob_str(&sql)); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); int isDeleted = db_column_int(&q, 1); int isChnged = db_column_int(&q,2); int isNew = db_column_int(&q,3); int srcid = db_column_int(&q, 4); char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); char *zToFree = zFullName; int showDiff = 1; if( isDeleted ){ printf("DELETED %s\n", zPathname); if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; } }else if( access(zFullName, 0) ){ printf("MISSING %s\n", zPathname); if( !asNewFile ){ showDiff = 0; } }else if( isNew ){ printf("ADDED %s\n", zPathname); srcid = 0; if( !asNewFile ){ showDiff = 0; } }else if( isChnged==3 ){ printf("ADDED_BY_MERGE %s\n", zPathname); srcid = 0; if( !asNewFile ){ showDiff = 0; } } if( showDiff ){ Blob content; if( srcid>0 ){ content_get(srcid, &content); }else{ blob_zero(&content); } printf("Index: %s\n=======================================" "============================\n", zPathname ); diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs); blob_reset(&content); } free(zToFree); } db_finalize(&q); db_end_transaction(1); /* ROLLBACK */ } /* ** Output the differences between two versions of a single file. ** zFrom and zTo are the check-ins containing the two file versions. ** The filename is contained in g.argv[2]. */ static void diff_one_two_versions( const char *zFrom, const char *zTo, const char *zDiffCmd, int ignoreEolWs ){ char *zName; Blob fname; Blob v1, v2; file_tree_name(g.argv[2], &fname, 1); zName = blob_str(&fname); historical_version_of_file(zFrom, zName, &v1, 0); historical_version_of_file(zTo, zName, &v2, 0); diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs); blob_reset(&v1); blob_reset(&v2); blob_reset(&fname); } /* ** Show the difference between two files identified by ManifestFile ** entries. */ static void diff_manifest_entry( struct ManifestFile *pFrom, struct ManifestFile *pTo, const char *zDiffCmd, int ignoreEolWs ){ Blob f1, f2; int rid; const char *zName = pFrom ? pFrom->zName : pTo->zName; printf("Index: %s\n=======================================" "============================\n", zName); if( pFrom ){ rid = uuid_to_rid(pFrom->zUuid, 0); content_get(rid, &f1); }else{ blob_zero(&f1); } if( pTo ){ rid = uuid_to_rid(pTo->zUuid, 0); content_get(rid, &f2); }else{ blob_zero(&f2); } diff_file_mem(&f1, &f2, zName, zDiffCmd, ignoreEolWs); blob_reset(&f1); blob_reset(&f2); } /* ** Output the differences between two check-ins. */ static void diff_all_two_versions( const char *zFrom, const char *zTo, const char *zDiffCmd, int diffFlags ){ Manifest *pFrom, *pTo; ManifestFile *pFromFile, *pToFile; int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0; int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0; pFrom = manifest_get_by_name(zFrom, 0); manifest_file_rewind(pFrom); pFromFile = manifest_file_next(pFrom,0); pTo = manifest_get_by_name(zTo, 0); manifest_file_rewind(pTo); pToFile = manifest_file_next(pTo,0); while( pFromFile || pToFile ){ int cmp; if( pFromFile==0 ){ cmp = +1; }else if( pToFile==0 ){ cmp = -1; }else{ cmp = strcmp(pFromFile->zName, pToFile->zName); } if( cmp<0 ){ printf("DELETED %s\n", pFromFile->zName); if( asNewFlag ){ diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs); } pFromFile = manifest_file_next(pFrom,0); }else if( cmp>0 ){ printf("ADDED %s\n", pToFile->zName); if( asNewFlag ){ diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs); } pToFile = manifest_file_next(pTo,0); }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ /* No changes */ pFromFile = manifest_file_next(pFrom,0); pToFile = manifest_file_next(pTo,0); }else{ printf("CHANGED %s\n", pFromFile->zName); diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs); pFromFile = manifest_file_next(pFrom,0); pToFile = manifest_file_next(pTo,0); } } manifest_destroy(pFrom); manifest_destroy(pTo); } /* ** COMMAND: diff ** COMMAND: gdiff ** ** Usage: %fossil diff|gdiff ?options? ?FILE? |
︙ | ︙ | |||
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | ** no "--to" option then the (possibly edited) files in the current check-out ** are used. ** ** The "-i" command-line option forces the use of the internal diff logic ** rather than any external diff program that might be configured using ** the "setting" command. If no external diff program is configured, then ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". */ void diff_cmd(void){ int isGDiff; /* True for gdiff. False for normal diff */ int isInternDiff; /* True for internal diff */ const char *zFrom; /* Source version number */ const char *zTo; /* Target version number */ const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ isGDiff = g.argv[1][0]=='g'; isInternDiff = find_option("internal","i",0)!=0; zFrom = find_option("from", "r", 1); zTo = find_option("to", 0, 1); if( zTo==0 ){ db_must_be_within_tree(); verify_all_options(); | > > > > > > > > | | | | | | | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | ** no "--to" option then the (possibly edited) files in the current check-out ** are used. ** ** The "-i" command-line option forces the use of the internal diff logic ** rather than any external diff program that might be configured using ** the "setting" command. If no external diff program is configured, then ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". ** ** The "-N" or "--new-file" option causes the complete text of added or ** deleted files to be displayed. */ void diff_cmd(void){ int isGDiff; /* True for gdiff. False for normal diff */ int isInternDiff; /* True for internal diff */ int hasNFlag; /* True if -N or --new-file flag is used */ const char *zFrom; /* Source version number */ const char *zTo; /* Target version number */ const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ int diffFlags = 0; /* Flags to control the DIFF */ isGDiff = g.argv[1][0]=='g'; isInternDiff = find_option("internal","i",0)!=0; zFrom = find_option("from", "r", 1); zTo = find_option("to", 0, 1); hasNFlag = find_option("new-file","N",0)!=0; if( hasNFlag ) diffFlags |= DIFF_NEWFILE; if( zTo==0 ){ db_must_be_within_tree(); verify_all_options(); if( !isInternDiff ){ zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); } if( g.argc==3 ){ diff_one_against_disk(zFrom, zDiffCmd, 0); }else{ diff_all_against_disk(zFrom, zDiffCmd, diffFlags); } }else if( zFrom==0 ){ fossil_fatal("must use --from if --to is present"); }else{ db_find_and_open_repository(1); verify_all_options(); if( !isInternDiff ){ zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); } if( g.argc==3 ){ diff_one_two_versions(zFrom, zTo, zDiffCmd, 0); }else{ diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags); } } } |
Changes to src/doc.c.
︙ | ︙ | |||
288 289 290 291 292 293 294 | z = zName; for(i=0; zName[i]; i++){ if( zName[i]=='.' ) z = &zName[i+1]; } len = strlen(z); if( len<sizeof(zSuffix)-1 ){ strcpy(zSuffix, z); | | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | z = zName; for(i=0; zName[i]; i++){ if( zName[i]=='.' ) z = &zName[i+1]; } len = strlen(z); if( len<sizeof(zSuffix)-1 ){ strcpy(zSuffix, z); for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]); first = 0; last = sizeof(aMime)/sizeof(aMime[0]); while( first<=last ){ int c; i = (first+last)/2; c = strcmp(zSuffix, aMime[i].zSuffix); if( c==0 ) return aMime[i].zMimetype; |
︙ | ︙ | |||
386 387 388 389 390 391 392 | " WHERE vid=%d AND fname=%Q", vid, zName); if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ goto doc_not_found; } if( rid==0 ){ Stmt s; | | | | < < | | > | | | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | " WHERE vid=%d AND fname=%Q", vid, zName); if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ goto doc_not_found; } if( rid==0 ){ Stmt s; Manifest *pM; ManifestFile *pFile; /* Add the vid baseline to the cache */ if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ db_multi_exec("DELETE FROM vcache"); } pM = manifest_get(vid, CFTYPE_MANIFEST); if( pM==0 ){ goto doc_not_found; } db_prepare(&s, "INSERT INTO vcache(vid,fname,rid)" " SELECT %d, :fname, rid FROM blob" " WHERE uuid=:uuid", vid ); manifest_file_rewind(pM); while( (pFile = manifest_file_next(pM,0))!=0 ){ db_bind_text(&s, ":fname", pFile->zName); db_bind_text(&s, ":uuid", pFile->zUuid); db_step(&s); db_reset(&s); } db_finalize(&s); manifest_destroy(pM); /* Try again to find the file */ rid = db_int(0, "SELECT rid FROM vcache" " WHERE vid=%d AND fname=%Q", vid, zName); } if( rid==0 ){ goto doc_not_found; |
︙ | ︙ |
Changes to src/encode.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 | case '&': count += 5; break; case '"': count += 6; break; default: count++; break; } i++; } i = 0; | | < | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | case '&': count += 5; break; case '"': count += 6; break; default: count++; break; } i++; } i = 0; zOut = fossil_malloc( count+1 ); while( n-->0 && (c = *zIn)!=0 ){ switch( c ){ case '<': zOut[i++] = '&'; zOut[i++] = 'l'; zOut[i++] = 't'; zOut[i++] = ';'; |
︙ | ︙ | |||
98 99 100 101 102 103 104 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ int c; int i = 0; int count = 0; char *zOut; int other; # define IsSafeChar(X) \ | | > | < | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ int c; int i = 0; int count = 0; char *zOut; int other; # define IsSafeChar(X) \ (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) if( zIn==0 ) return 0; if( n<0 ) n = strlen(zIn); other = encodeSlash ? 'a' : '/'; while( i<n && (c = zIn[i])!=0 ){ if( IsSafeChar(c) || c==' ' ){ count++; }else{ count += 3; } i++; } i = 0; zOut = fossil_malloc( count+1 ); while( n-->0 && (c = *zIn)!=0 ){ if( IsSafeChar(c) ){ zOut[i++] = c; }else if( c==' ' ){ zOut[i++] = '+'; }else{ zOut[i++] = '%'; |
︙ | ︙ | |||
231 232 233 234 235 236 237 | if( nIn<0 ) nIn = strlen(zIn); for(i=n=0; i<nIn; i++){ c = zIn[i]; if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v' || c=='\\' ) n++; } n += nIn; | | | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | if( nIn<0 ) nIn = strlen(zIn); for(i=n=0; i<nIn; i++){ c = zIn[i]; if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v' || c=='\\' ) n++; } n += nIn; zOut = fossil_malloc( n+1 ); if( zOut ){ for(i=j=0; i<nIn; i++){ int c = zIn[i]; if( c==0 ){ zOut[j++] = '\\'; zOut[j++] = '0'; }else if( c=='\\' ){ zOut[j++] = '\\'; zOut[j++] = '\\'; }else if( fossil_isspace(c) ){ zOut[j++] = '\\'; switch( c ){ case '\n': c = 'n'; break; case ' ': c = 's'; break; case '\t': c = 't'; break; case '\r': c = 'r'; break; case '\v': c = 'v'; break; |
︙ | ︙ | |||
266 267 268 269 270 271 272 | } /* ** Decode a fossilized string in-place. */ void defossilize(char *z){ int i, j, c; | | > | | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | } /* ** Decode a fossilized string in-place. */ void defossilize(char *z){ int i, j, c; for(i=0; (c=z[i])!=0 && c!='\\'; i++){} if( c==0 ) return; for(j=i; (c=z[i])!=0; i++){ if( c=='\\' && z[i+1] ){ i++; switch( z[i] ){ case 'n': c = '\n'; break; case 's': c = ' '; break; case 't': c = '\t'; break; case 'r': c = '\r'; break; |
︙ | ︙ | |||
307 308 309 310 311 312 313 | char *encode64(const char *zData, int nData){ char *z64; int i, n; if( nData<=0 ){ nData = strlen(zData); } | | | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | char *encode64(const char *zData, int nData){ char *z64; int i, n; if( nData<=0 ){ nData = strlen(zData); } z64 = fossil_malloc( (nData*4)/3 + 8 ); for(i=n=0; i+2<nData; i+=3){ z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ]; z64[n++] = zBase[ zData[i+2] & 0x3f ]; } if( i+1<nData ){ |
︙ | ︙ | |||
368 369 370 371 372 373 374 | if( !isInit ){ for(i=0; i<128; i++){ trans[i] = 0; } for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } isInit = 1; } n64 = strlen(z64); while( n64>0 && z64[n64-1]=='=' ) n64--; | | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | if( !isInit ){ for(i=0; i<128; i++){ trans[i] = 0; } for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } isInit = 1; } n64 = strlen(z64); while( n64>0 && z64[n64-1]=='=' ) n64--; zData = fossil_malloc( (n64*3)/4 + 4 ); for(i=j=0; i+3<n64; i+=4){ a = trans[z64[i] & 0x7f]; b = trans[z64[i+1] & 0x7f]; c = trans[z64[i+2] & 0x7f]; d = trans[z64[i+3] & 0x7f]; zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03); zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f); |
︙ | ︙ | |||
503 504 505 506 507 508 509 | */ void canonical16(char *z, int n){ while( *z && n-- ){ *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; z++; } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 | */ void canonical16(char *z, int n){ while( *z && n-- ){ *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; z++; } } /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ static const unsigned char aObscurer[16] = { 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 }; /* ** Obscure plain text so that it is not easily readable. ** ** This is used for storing sensitive information (such as passwords) in a ** way that prevents their exposure through idle browsing. This is not ** encryption. Anybody who really wants the password can still get it. ** ** The text is XOR-ed with a repeating pattern then converted to hex. ** Space to hold the returned string is obtained from malloc and should ** be freed by the caller. */ char *obscure(const char *zIn){ int n, i; unsigned char salt; char *zOut; if( zIn==0 ) return 0; n = strlen(zIn); zOut = fossil_malloc( n*2+3 ); sqlite3_randomness(1, &salt); zOut[n+1] = (char)salt; for(i=0; i<n; i++) zOut[i+n+2] = zIn[i]^aObscurer[i&0x0f]^salt; encode16((unsigned char*)&zOut[n+1], (unsigned char*)zOut, n+1); return zOut; } /* ** Undo the obscuring of text performed by obscure(). Or, if the input is ** not hexadecimal (meaning the input is not the output of obscure()) then ** do the equivalent of strdup(). ** ** The result is memory obtained from malloc that should be freed by the caller. */ char *unobscure(const char *zIn){ int n, i; unsigned char salt; char *zOut; if( zIn==0 ) return 0; n = strlen(zIn); zOut = fossil_malloc( n + 1 ); if( n<2 || decode16((unsigned char*)zIn, &salt, 2) || decode16((unsigned char*)&zIn[2], (unsigned char*)zOut, n-2) ){ memcpy(zOut, zIn, n+1); }else{ n = n/2 - 1; for(i=0; i<n; i++) zOut[i] = zOut[i]^aObscurer[i&0x0f]^salt; zOut[n] = 0; } return zOut; } /* ** Command to test obscure() and unobscure(). These commands are also useful ** utilities for decoding passwords found in the database. ** ** COMMAND: test-obscure */ void test_obscure_cmd(void){ int i; char *z, *z2; for(i=2; i<g.argc; i++){ z = obscure(g.argv[i]); z2 = unobscure(z); printf("OBSCURE: %s -> %s (%s)\n", g.argv[i], z, z2); free(z); free(z2); z = unobscure(g.argv[i]); printf("UNOBSCURE: %s -> %s\n", g.argv[i], z); free(z); } } |
Added src/event.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | /* ** Copyright (c) 2010 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to do formatting of event messages: ** ** Milestones ** Blog posts ** New articles ** Process checkpoints ** Announcements */ #include <assert.h> #include <ctype.h> #include "config.h" #include "event.h" /* ** Output a hyperlink to an event given its tagid. */ void hyperlink_to_event_tagid(int tagid){ char *zEventId; char zShort[12]; zEventId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d", tagid); sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zEventId); if( g.okHistory ){ @ [<a href="%s(g.zTop)/event?name=%s(zEventId)">%s(zShort)</a>] }else{ @ [%s(zShort)] } free(zEventId); } /* ** WEBPAGE: event ** URL: /event ** PARAMETERS: ** ** name=EVENTID // Identify the event to display EVENTID must be complete ** detail=BOOLEAN // Show details if TRUE. Default is FALSE. Optional. ** aid=ARTIFACTID // Which specific version of the event. Optional. ** ** Display an existing event identified by EVENTID */ void event_page(void){ int rid = 0; /* rid of the event artifact */ char *zUuid; /* UUID corresponding to rid */ const char *zEventId; /* Event identifier */ char *zETime; /* Time of the event */ char *zATime; /* Time the artifact was created */ int specRid; /* rid specified by aid= parameter */ int prevRid, nextRid; /* Previous or next edits of this event */ Manifest *pEvent; /* Parsed event artifact */ Blob fullbody; /* Complete content of the event body */ Blob title; /* Title extracted from the event body */ Blob tail; /* Event body that comes after the title */ Stmt q1; /* Query to search for the event */ int showDetail; /* True to show details */ /* wiki-read privilege is needed in order to read events. */ login_check_credentials(); if( !g.okRdWiki ){ login_needed(); return; } zEventId = P("name"); if( zEventId==0 ){ fossil_redirect_home(); return; } zUuid = (char*)P("aid"); specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0; rid = nextRid = prevRid = 0; db_prepare(&q1, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB 'event-%q*')" " ORDER BY mtime DESC", zEventId ); while( db_step(&q1)==SQLITE_ROW ){ nextRid = rid; rid = db_column_int(&q1, 0); if( specRid==0 || specRid==rid ){ if( db_step(&q1)==SQLITE_ROW ){ prevRid = db_column_int(&q1, 0); } break; } } db_finalize(&q1); if( rid==0 || (specRid!=0 && specRid!=rid) ){ style_header("No Such Event"); @ Cannot locate specified event style_footer(); return; } zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); showDetail = atoi(PD("detail","0")); /* Extract the event content. */ pEvent = manifest_get(rid, CFTYPE_EVENT); if( pEvent==0 ){ fossil_panic("Object #%d is not an event", rid); } blob_init(&fullbody, pEvent->zWiki, -1); if( wiki_find_title(&fullbody, &title, &tail) ){ style_header(blob_str(&title)); }else{ style_header("Event %S", zEventId); tail = fullbody; } if( g.okWrWiki && g.okWrite && nextRid==0 ){ style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s", g.zTop, zEventId); } zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate); style_submenu_element("Context", "Context", "%s/timeline?c=%T", g.zTop, zETime); if( g.okHistory ){ if( showDetail ){ style_submenu_element("Plain", "Plain", "%s/event?name=%s&aid=%s", g.zTop, zEventId, zUuid); if( nextRid ){ char *zNext; zNext = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nextRid); style_submenu_element("Next", "Next", "%s/event?name=%s&aid=%s&detail=1", g.zTop, zEventId, zNext); free(zNext); } if( prevRid ){ char *zPrev; zPrev = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", prevRid); style_submenu_element("Prev", "Prev", "%s/event?name=%s&aid=%s&detail=1", g.zTop, zEventId, zPrev); free(zPrev); } }else{ style_submenu_element("Detail", "Detail", "%s/event?name=%s&aid=%s&detail=1", g.zTop, zEventId, zUuid); } } if( showDetail && g.okHistory ){ int i; const char *zClr = 0; Blob comment; zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate); @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>] @ entered by user <b>%h(pEvent->zUser)</b> on @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p> @ <blockquote> for(i=0; i<pEvent->nTag; i++){ if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){ zClr = pEvent->aTag[i].zValue; } } if( zClr && zClr[0]==0 ) zClr = 0; if( zClr ){ @ <div style="background-color: %h(zClr);"> }else{ @ <div> } blob_init(&comment, pEvent->zComment, -1); wiki_convert(&comment, 0, WIKI_INLINE); blob_reset(&comment); @ </div> @ </blockquote><hr /> } wiki_convert(&tail, 0, 0); style_footer(); manifest_destroy(pEvent); } /* ** WEBPAGE: eventedit ** URL: /eventedit?name=EVENTID ** ** Edit an event. If name is omitted, create a new event. */ void eventedit_page(void){ char *zTag; int rid = 0; Blob event; const char *zEventId; char *zHtmlPageName; int n; const char *z; char *zBody = (char*)P("w"); char *zETime = (char*)P("t"); const char *zComment = P("c"); const char *zTags = P("g"); const char *zClr; if( zBody ){ zBody = mprintf("%s", zBody); } login_check_credentials(); zEventId = P("name"); if( zEventId==0 ){ zEventId = db_text(0, "SELECT lower(hex(randomblob(20)))"); }else{ int nEventId = strlen(zEventId); if( nEventId!=40 || !validate16(zEventId, 40) ){ fossil_redirect_home(); return; } } zTag = mprintf("event-%s", zEventId); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " ORDER BY mtime DESC", zTag ); free(zTag); /* Need both check-in and wiki-write or wiki-create privileges in order ** to edit/create an event. */ if( !g.okWrite || (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){ login_needed(); return; } /* Figure out the color */ if( rid ){ zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid); }else{ zClr = ""; } zClr = PD("clr",zClr); if( strcmp(zClr,"##")==0 ) zClr = PD("cclr",""); /* If editing an existing event, extract the key fields to use as ** a starting point for the edit. */ if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){ Manifest *pEvent; pEvent = manifest_get(rid, CFTYPE_EVENT); if( pEvent && pEvent->type==CFTYPE_EVENT ){ if( zBody==0 ) zBody = pEvent->zWiki; if( zETime==0 ){ zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate); } if( zComment==0 ) zComment = pEvent->zComment; } if( zTags==0 ){ zTags = db_text(0, "SELECT group_concat(substr(tagname,5),', ')" " FROM tagxref, tag" " WHERE tagxref.rid=%d" " AND tagxref.tagid=tag.tagid" " AND tag.tagname GLOB 'sym-*'", rid ); } } zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime); if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){ char *zDate; Blob cksum; int nrid; blob_zero(&event); db_begin_transaction(); login_verify_csrf_secret(); blob_appendf(&event, "C %F\n", zComment); zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&event, "D %s\n", zDate); free(zDate); zETime[10] = 'T'; blob_appendf(&event, "E %s %s\n", zETime, zEventId); zETime[10] = ' '; if( rid ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&event, "P %s\n", zUuid); free(zUuid); } if( zClr && zClr[0] ){ blob_appendf(&event, "T +bgcolor * %F\n", zClr); } if( zTags && zTags[0] ){ Blob tags, one; int i, j; Stmt q; char *zBlob; /* Load the tags string into a blob */ blob_zero(&tags); blob_append(&tags, zTags, -1); /* Collapse all sequences of whitespace and "," characters into ** a single space character */ zBlob = blob_str(&tags); for(i=j=0; zBlob[i]; i++, j++){ if( fossil_isspace(zBlob[i]) || zBlob[i]==',' ){ while( fossil_isspace(zBlob[i+1]) ){ i++; } zBlob[j] = ' '; }else{ zBlob[j] = zBlob[i]; } } blob_resize(&tags, j); /* Parse out each tag and load it into a temporary table for sorting */ db_multi_exec("CREATE TEMP TABLE newtags(x);"); while( blob_token(&tags, &one) ){ db_multi_exec("INSERT INTO newtags VALUES(%B)", &one); } blob_reset(&tags); /* Extract the tags in sorted order and make an entry in the ** artifact for each. */ db_prepare(&q, "SELECT x FROM newtags ORDER BY x"); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&event, "T +sym-%F *\n", db_column_text(&q, 0)); } db_finalize(&q); } if( g.zLogin ){ blob_appendf(&event, "U %F\n", g.zLogin); } blob_appendf(&event, "W %d\n%s\n", strlen(zBody), zBody); md5sum_blob(&event, &cksum); blob_appendf(&event, "Z %b\n", &cksum); blob_reset(&cksum); nrid = content_put(&event, 0, 0); db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); manifest_crosslink(nrid, &event); blob_reset(&event); content_deltify(rid, nrid, 0); db_end_transaction(0); cgi_redirectf("event?name=%T", zEventId); } if( P("cancel")!=0 ){ cgi_redirectf("event?name=%T", zEventId); return; } if( zBody==0 ){ zBody = mprintf("<i>Event Text</i>"); } zHtmlPageName = mprintf("Edit Event %S", zEventId); style_header(zHtmlPageName); if( P("preview")!=0 ){ Blob title, tail, com; @ <p><b>Timeline comment preview:</b></p> @ <blockquote> @ <table border="0"> if( zClr && zClr[0] ){ @ <tr><td style="background-color: %h(zClr);"> }else{ @ <tr><td> } blob_zero(&com); blob_append(&com, zComment, -1); wiki_convert(&com, 0, WIKI_INLINE); @ </td></tr></table> @ </blockquote> @ <p><b>Page content preview:</b><p> @ <blockquote> blob_zero(&event); blob_append(&event, zBody, -1); if( wiki_find_title(&event, &title, &tail) ){ @ <h2 align="center">%h(blob_str(&title))</h2> wiki_convert(&tail, 0, 0); }else{ wiki_convert(&event, 0, 0); } @ </blockquote><hr /> blob_reset(&event); } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } if( n<20 ) n = 20; if( n>40 ) n = 40; @ <form method="post" action="%s(g.zBaseURL)/eventedit"><div> login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zEventId)" /> @ <table border="0" cellspacing="10"> @ <tr><td align="right" valign="top"><b>Event Time:</b></td> @ <td valign="top"> @ <input type="text" name="t" size="25" value="%h(zETime)" /> @ </td></tr> @ <tr><td align="right" valign="top"><b>Timeline Comment:</b></td> @ <td valign="top"> @ <textarea name="c" class="eventedit" cols="80" @ rows="3" wrap="virtual">%h(zComment)</textarea> @ </td></tr> @ <tr><td align="right" valign="top"><b>Background Color:</b></td> @ <td valign="top"> render_color_chooser(0, zClr, 0, "clr", "cclr"); @ </td></tr> @ <tr><td align="right" valign="top"><b>Tags:</b></td> @ <td valign="top"> @ <input type="text" name="g" size="40" value="%h(zTags)" /> @ </td></tr> @ <tr><td align="right" valign="top"><b>Page Content:</b></td> @ <td valign="top"> @ <textarea name="w" class="eventedit" cols="80" @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> @ </td></tr> @ <tr><td colspan="2"> @ <input type="submit" name="preview" value="Preview Your Changes" /> @ <input type="submit" name="submit" value="Apply These Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </td></tr></table> @ </div></form> style_footer(); } |
Changes to src/file.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 28 | #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "file.h" /* ** The file status information from the most recent stat() call. */ | > > > > > > > | > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "file.h" /* ** The file status information from the most recent stat() call. ** ** Use _stati64 rather than stat on windows, in order to handle files ** larger than 2GB. */ #if defined(_WIN32) && defined(__MSVCRT__) static struct _stati64 fileStat; # define stat _stati64 #else static struct stat fileStat; #endif static int fileStatValid = 0; /* ** Fill in the fileStat variable for the file named zFilename. ** If zFilename==0, then use the previous value of fileStat if ** there is a previous value. ** |
︙ | ︙ | |||
81 82 83 84 85 86 87 | /* ** Return TRUE if the named file is an executable. Return false ** for directories, devices, fifos, symlinks, etc. */ int file_isexe(const char *zFilename){ if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0; | | > > > | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | /* ** Return TRUE if the named file is an executable. Return false ** for directories, devices, fifos, symlinks, etc. */ int file_isexe(const char *zFilename){ if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0; #if defined(_WIN32) # if defined(__DMC__) || defined(_MSC_VER) # define S_IXUSR _S_IEXEC # endif return ((S_IXUSR)&fileStat.st_mode)!=0; #else return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0; #endif } |
︙ | ︙ | |||
143 144 145 146 147 148 149 | fclose(out); } /* ** Set or clear the execute bit on a file. */ void file_setexe(const char *zFilename, int onoff){ | | | | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | fclose(out); } /* ** Set or clear the execute bit on a file. */ void file_setexe(const char *zFilename, int onoff){ #if !defined(_WIN32) struct stat buf; if( stat(zFilename, &buf)!=0 ) return; if( onoff ){ if( (buf.st_mode & 0111)!=0111 ){ chmod(zFilename, buf.st_mode | 0111); } }else{ if( (buf.st_mode & 0111)!=0 ){ chmod(zFilename, buf.st_mode & ~0111); } } #endif /* _WIN32 */ } /* ** Create the directory named in the argument, if it does not already ** exist. If forceFlag is 1, delete any prior non-directory object ** with the same name. ** ** Return the number of errors. */ int file_mkdir(const char *zName, int forceFlag){ int rc = file_isdir(zName); if( rc==2 ){ if( !forceFlag ) return 1; unlink(zName); } if( rc!=1 ){ #if defined(_WIN32) return mkdir(zName); #else return mkdir(zName, 0755); #endif } return 0; } |
︙ | ︙ | |||
229 230 231 232 233 234 235 | ** * removing /A/../ ** ** Changes are made in-place. Return the new name length. */ int file_simplify_name(char *z, int n){ int i, j; if( n<0 ) n = strlen(z); | | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | ** * removing /A/../ ** ** Changes are made in-place. Return the new name length. */ int file_simplify_name(char *z, int n){ int i, j; if( n<0 ) n = strlen(z); #if defined(_WIN32) for(i=0; i<n; i++){ if( z[i]=='\\' ) z[i] = '/'; } #endif while( n>1 && z[n-1]=='/' ){ n--; } for(i=j=0; i<n; i++){ if( z[i]=='/' ){ |
︙ | ︙ | |||
264 265 266 267 268 269 270 | ** Make the name absolute if it is relative. ** Remove redundant / characters ** Remove all /./ path elements. ** Convert /A/../ to just / */ void file_canonical_name(const char *zOrigName, Blob *pOut){ if( zOrigName[0]=='/' | | | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | ** Make the name absolute if it is relative. ** Remove redundant / characters ** Remove all /./ path elements. ** Convert /A/../ to just / */ void file_canonical_name(const char *zOrigName, Blob *pOut){ if( zOrigName[0]=='/' #if defined(_WIN32) || zOrigName[0]=='\\' || (strlen(zOrigName)>3 && zOrigName[1]==':' && (zOrigName[2]=='\\' || zOrigName[2]=='/')) #endif ){ blob_set(pOut, zOrigName); blob_materialize(pOut); |
︙ | ︙ | |||
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | blob_appendf(pOut, "%//%/", zPwd, zOrigName); } blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); } /* ** COMMAND: test-canonical-name ** ** Test the operation of the canonical name generator. */ void cmd_test_canonical_name(void){ int i; Blob x; blob_zero(&x); for(i=2; i<g.argc; i++){ | > > > | > > > > > | | 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | blob_appendf(pOut, "%//%/", zPwd, zOrigName); } blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); } /* ** COMMAND: test-canonical-name ** Usage: %fossil test-canonical-name FILENAME... ** ** Test the operation of the canonical name generator. ** Also test Fossil's ability to measure attributes of a file. */ void cmd_test_canonical_name(void){ int i; Blob x; blob_zero(&x); for(i=2; i<g.argc; i++){ const char *zName = g.argv[i]; file_canonical_name(zName, &x); printf("%s\n", blob_buffer(&x)); blob_reset(&x); printf(" file_size = %lld\n", file_size(zName)); printf(" file_mtime = %lld\n", file_mtime(zName)); printf(" file_isfile = %d\n", file_isfile(zName)); printf(" file_isexe = %d\n", file_isexe(zName)); printf(" file_isdir = %d\n", file_isdir(zName)); } } /* ** Return TRUE if the given filename is canonical. ** ** Canonical names are full pathnames using "/" not "\" and which ** contain no "/./" or "/../" terms. */ int file_is_canonical(const char *z){ int i; if( z[0]!='/' #if defined(_WIN32) && (z[0]==0 || z[1]!=':' || z[2]!='/') #endif ) return 0; for(i=0; z[i]; i++){ if( z[i]=='\\' ) return 0; if( z[i]=='/' ){ |
︙ | ︙ |
Changes to src/finfo.c.
︙ | ︙ | |||
129 130 131 132 133 134 135 | " AND event.objid=mlink.mid" " ORDER BY event.mtime DESC /*sort*/", TAG_BRANCH, zFilename ); blob_zero(&title); blob_appendf(&title, "History of "); | | | | | | | | > | | | > | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | " AND event.objid=mlink.mid" " ORDER BY event.mtime DESC /*sort*/", TAG_BRANCH, zFilename ); blob_zero(&title); blob_appendf(&title, "History of "); hyperlinked_path(zFilename, &title, 0); @ <h2>%b(&title)</h2> blob_reset(&title); pGraph = graph_init(); @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> @ <table class="timelineTable"> while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zCom = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); int fpid = db_column_int(&q, 3); int frid = db_column_int(&q, 4); const char *zPUuid = db_column_text(&q, 5); const char *zUuid = db_column_text(&q, 6); const char *zCkin = db_column_text(&q,7); const char *zBgClr = db_column_text(&q, 8); const char *zBr = db_column_text(&q, 9); int gidx; char zTime[10]; char zShort[20]; char zShortCkin[20]; if( zBr==0 ) zBr = "trunk"; gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr); if( memcmp(zDate, zPrevDate, 10) ){ sprintf(zPrevDate, "%.10s", zDate); @ <tr><td> @ <div class="divider">%s(zPrevDate)</div> @ </td></tr> } memcpy(zTime, &zDate[11], 5); zTime[5] = 0; @ <tr><td class="timelineTime"> @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td> @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td> if( zBgClr && zBgClr[0] ){ @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> }else{ @ <td class="timelineTableCell"> } sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid); sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin); if( zUuid ){ if( g.okHistory ){ @ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%S(zUuid)]</a> }else{ @ [%S(zUuid)] } @ part of check-in }else{ @ <b>Deleted</b> by check-in } hyperlink_to_uuid(zShortCkin); @ %h(zCom) (user: hyperlink_to_user(zUser, zDate, ""); @ branch: %h(zBr)) if( g.okHistory && zUuid ){ const char *z = zFilename; if( fpid ){ @ <a href="%s(g.zTop)/fdiff?v1=%s(zPUuid)&v2=%s(zUuid)">[diff]</a> } @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&filename=%h(z)"> @ [annotate]</a> } @ </td></tr> } db_finalize(&q); if( pGraph ){ graph_finish(pGraph, 1); if( pGraph->nErr ){ graph_free(pGraph); pGraph = 0; }else{ @ <tr><td></td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div> @ </td></tr> } } @ </table> timeline_output_graph_javascript(pGraph); style_footer(); } |
Changes to src/graph.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "graph.h" #include <assert.h> #if INTERFACE | | | | > | | | | | | < | | < | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | */ #include "config.h" #include "graph.h" #include <assert.h> #if INTERFACE #define GR_MAX_PARENT 10 /* Most parents for any one node */ #define GR_MAX_RAIL 32 /* Max number of "rails" to display */ /* The graph appears vertically beside a timeline. Each row in the ** timeline corresponds to a row in the graph. */ struct GraphRow { int rid; /* The rid for the check-in */ int nParent; /* Number of parents */ int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/ char *zBranch; /* Branch name */ char *zBgClr; /* Background Color */ GraphRow *pNext; /* Next row down in the list of all rows */ GraphRow *pPrev; /* Previous row */ int idx; /* Row index. First is 1. 0 used for "none" */ int idxTop; /* Direct descendent highest up on the graph */ GraphRow *pChild; /* Child immediately above this node */ u8 isDup; /* True if this is duplicate of a prior entry */ int iRail; /* Which rail this check-in appears on. 0-based.*/ int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */ int bDescender; /* Raiser from bottom of graph to here. */ u32 mergeIn; /* Merge in from other rails */ int mergeOut; /* Merge out to this rail */ int mergeUpto; /* Draw the merge rail up to this level */ u32 railInUse; /* Mask of occupied rails */ }; /* Context while building a graph */ struct GraphContext { int nErr; /* Number of errors encountered */ int mxRail; /* Number of rails required to render the graph */ GraphRow *pFirst; /* First row in the list */ GraphRow *pLast; /* Last row in the list */ int nBranch; /* Number of distinct branches */ char **azBranch; /* Names of the branches */ int nRow; /* Number of rows */ int nHash; /* Number of slots in apHash[] */ GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ }; #endif /* ** Malloc for zeroed space. Panic if unable to provide the ** requested space. */ void *safeMalloc(int nByte){ void *p = fossil_malloc(nByte); memset(p, 0, nByte); return p; } /* ** Create and initialize a GraphContext */ |
︙ | ︙ | |||
101 102 103 104 105 106 107 | for(i=0; i<p->nBranch; i++) free(p->azBranch[i]); free(p->azBranch); free(p->apHash); free(p); } /* | | | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | for(i=0; i<p->nBranch; i++) free(p->azBranch[i]); free(p->azBranch); free(p->apHash); free(p); } /* ** Insert a row into the hash table. pRow->rid is the key. Keys must ** be unique. If there is already another row with the same rid, ** overwrite the prior entry if and only if the overwrite flag is set. */ static void hashInsert(GraphContext *p, GraphRow *pRow, int overwrite){ int h; h = pRow->rid % p->nHash; while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){ h++; if( h>=p->nHash ) h = 0; |
︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | return p->apHash[h]; } /* ** Return the canonical pointer for a given branch name. ** Multiple calls to this routine with equivalent strings ** will return the same pointer. ** ** Note: also used for background color names. */ static char *persistBranchName(GraphContext *p, const char *zBranch){ int i; for(i=0; i<p->nBranch; i++){ if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i]; } p->nBranch++; | > > > > | < | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | return p->apHash[h]; } /* ** Return the canonical pointer for a given branch name. ** Multiple calls to this routine with equivalent strings ** will return the same pointer. ** ** The returned value is a pointer to a (readonly) string that ** has the useful property that strings can be checked for ** equality by comparing pointers. ** ** Note: also used for background color names. */ static char *persistBranchName(GraphContext *p, const char *zBranch){ int i; for(i=0; i<p->nBranch; i++){ if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i]; } p->nBranch++; p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch); p->azBranch[p->nBranch-1] = mprintf("%s", zBranch); return p->azBranch[p->nBranch-1]; } /* ** Add a new row to the graph context. Rows are added from top to bottom. */ int graph_add_row( GraphContext *p, /* The context to which the row is added */ int rid, /* RID for the check-in */ int nParent, /* Number of parents */ int *aParent, /* Array of parents */ const char *zBranch, /* Branch for this check-in */ |
︙ | ︙ | |||
177 178 179 180 181 182 183 | if( p->pFirst==0 ){ p->pFirst = pRow; }else{ p->pLast->pNext = pRow; } p->pLast = pRow; p->nRow++; | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | if( p->pFirst==0 ){ p->pFirst = pRow; }else{ p->pLast->pNext = pRow; } p->pLast = pRow; p->nRow++; pRow->idx = pRow->idxTop = p->nRow; return pRow->idx; } /* ** Return the index of a rail currently not in use for any row between ** top and bottom, inclusive. */ |
︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 | iBest = i; } } } if( iBestDist>1000 ) p->nErr++; return iBest; } /* ** Compute the complete graph */ void graph_finish(GraphContext *p, int omitDescenders){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | iBest = i; } } } if( iBestDist>1000 ) p->nErr++; return iBest; } /* ** Assign all children of node pBottom to the same rail as pBottom. */ static void assignChildrenToRail(GraphRow *pBottom){ int iRail = pBottom->iRail; GraphRow *pCurrent; GraphRow *pPrior; u32 mask = 1<<iRail; pBottom->iRail = iRail; pBottom->railInUse |= mask; pPrior = pBottom; for(pCurrent=pBottom->pChild; pCurrent; pCurrent=pCurrent->pChild){ assert( pPrior->idx > pCurrent->idx ); assert( pCurrent->iRail<0 ); pCurrent->iRail = iRail; pCurrent->railInUse |= mask; pPrior->aiRaiser[iRail] = pCurrent->idx; while( pPrior->idx > pCurrent->idx ){ pPrior->railInUse |= mask; pPrior = pPrior->pPrev; assert( pPrior!=0 ); } if( pCurrent->pPrev ){ pCurrent->pPrev->railInUse |= mask; } } } /* ** Compute the complete graph */ void graph_finish(GraphContext *p, int omitDescenders){ GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent; int i; u32 mask; u32 inUse; int hasDup = 0; /* True if one or more isDup entries */ const char *zTrunk; if( p==0 || p->pFirst==0 || p->nErr ) return; |
︙ | ︙ | |||
246 247 248 249 250 251 252 | hasDup = 1; pDup->isDup = 1; } hashInsert(p, pRow, 1); } p->mxRail = -1; | | > > > > > > > > > | > > > | < < | < < | | | > | > | | > > > | | < < < < | | > < < > | > > > > | | < < < | < | > > | > > | | | < > | | | | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | hasDup = 1; pDup->isDup = 1; } hashInsert(p, pRow, 1); } p->mxRail = -1; /* Purge merge-parents that are out-of-graph. ** ** Each node has one primary parent and zero or more "merge" parents. ** A merge parent is a prior checkin from which changes were merged into ** the current check-in. If a merge parent is not in the visible section ** of this graph, then no arrows will be drawn for it, so remove it from ** the aParent[] array. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ for(i=1; i<pRow->nParent; i++){ if( hashFind(p, pRow->aParent[i])==0 ){ pRow->aParent[i] = pRow->aParent[--pRow->nParent]; i--; } } } /* Find the pChild pointer for each node. ** ** The pChild points to node directly above on the same rail. ** The pChild must be in the same branch. Leaf nodes have a NULL ** pChild. ** ** In the case of a fork, choose the pChild that results in the ** longest rail. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->isDup ) continue; if( pRow->nParent==0 ) continue; pParent = hashFind(p, pRow->aParent[0]); if( pParent==0 ) continue; if( pParent->zBranch!=pRow->zBranch ) continue; if( pParent->idx <= pRow->idx ) continue; if( pRow->idxTop < pParent->idxTop ){ pParent->pChild = pRow; pParent->idxTop = pRow->idxTop; } } /* Identify rows where the primary parent is off screen. Assign ** each to a rail and draw descenders to the bottom of the screen. ** ** Strive to put the "trunk" branch on far left. */ zTrunk = persistBranchName(p, "trunk"); for(i=0; i<2; i++){ for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ if( i==0 ){ if( pRow->zBranch!=zTrunk ) continue; }else { if( pRow->iRail>=0 ) continue; } if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ if( omitDescenders ){ pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0, 0); }else{ pRow->iRail = ++p->mxRail; } mask = 1<<(pRow->iRail); if( omitDescenders ){ if( pRow->pNext ) pRow->pNext->railInUse |= mask; }else{ pRow->bDescender = pRow->nParent>0; for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } } assignChildrenToRail(pRow); } } } /* Assign rails to all rows that are still unassigned. */ inUse = (1<<(p->mxRail+1))-1; for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ int parentRid; if( pRow->iRail>=0 ){ if( pRow->pChild==0 ) inUse &= ~(1<<pRow->iRail); continue; } if( pRow->isDup ){ pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0); pDesc = pRow; pParent = 0; }else{ assert( pRow->nParent>0 ); parentRid = pRow->aParent[0]; pParent = hashFind(p, parentRid); if( pParent==0 ){ /* Time skew */ pRow->iRail = ++p->mxRail; pRow->railInUse = 1<<pRow->iRail; continue; } pRow->iRail = findFreeRail(p, 0, pParent->idx, inUse, pParent->iRail); pParent->aiRaiser[pRow->iRail] = pRow->idx; } mask = 1<<pRow->iRail; if( pRow->pPrev ) pRow->pPrev->railInUse |= mask; if( pRow->pNext ) pRow->pNext->railInUse |= mask; if( pRow->pChild==0 ){ inUse &= ~mask; }else{ inUse |= mask; assignChildrenToRail(pRow); } if( pParent ){ for(pLoop=pParent; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){ pLoop->railInUse |= mask; } } } /* ** Insert merge rails and merge arrows */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ for(i=1; i<pRow->nParent; i++){ int parentRid = pRow->aParent[i]; pDesc = hashFind(p, parentRid); if( pDesc==0 ) continue; if( pDesc->mergeOut<0 ){ int iTarget = (pRow->iRail + pDesc->iRail)/2; pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx, 0, iTarget); pDesc->mergeUpto = pRow->idx; mask = 1<<pDesc->mergeOut; pDesc->railInUse |= mask; for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } } pRow->mergeIn |= 1<<pDesc->mergeOut; } } /* |
︙ | ︙ | |||
396 397 398 399 400 401 402 | pRow->mergeIn |= 1<<pDesc->mergeOut; } } /* ** Find the maximum rail number. */ | < | 441 442 443 444 445 446 447 448 449 450 451 452 453 | pRow->mergeIn |= 1<<pDesc->mergeOut; } } /* ** Find the maximum rail number. */ p->mxRail = 0; for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut; } } |
Changes to src/http.c.
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | Blob pw; /* The nonce with user password appended */ Blob sig; /* The signature field */ blob_zero(pLogin); if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){ return; /* If no login card for users "nobody" and "anonymous" */ } blob_zero(&nonce); blob_zero(&pw); sha1sum_blob(pPayload, &nonce); blob_copy(&pw, &nonce); zLogin = g.urlUser; if( g.urlPasswd ){ zPw = g.urlPasswd; }else if( g.cgiOutput ){ /* Password failure while doing a sync from the web interface */ cgi_printf("*** incorrect or missing password for user %h\n", zLogin); zPw = 0; }else{ /* Password failure while doing a sync from the command-line interface */ url_prompt_for_password(); zPw = g.urlPasswd; | > > > | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | Blob pw; /* The nonce with user password appended */ Blob sig; /* The signature field */ blob_zero(pLogin); if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){ return; /* If no login card for users "nobody" and "anonymous" */ } if( g.urlIsSsh ){ return; /* If no login card for SSH: */ } blob_zero(&nonce); blob_zero(&pw); sha1sum_blob(pPayload, &nonce); blob_copy(&pw, &nonce); zLogin = g.urlUser; if( g.urlPasswd ){ zPw = g.urlPasswd; }else if( g.cgiOutput ){ /* Password failure while doing a sync from the web interface */ cgi_printf("*** incorrect or missing password for user %h\n", zLogin); zPw = 0; }else{ /* Password failure while doing a sync from the command-line interface */ url_prompt_for_password(); zPw = g.urlPasswd; if( !g.dontKeepUrl ) db_set("last-sync-pw", obscure(zPw), 0); } /* The login card wants the SHA1 hash of the password, so convert the ** password to its SHA1 hash it it isn't already a SHA1 hash. ** ** Except, if the password begins with "*" then use the characters ** after the "*" as a cleartext password. Put an "*" at the beginning |
︙ | ︙ | |||
98 99 100 101 102 103 104 | blob_zero(pHdr); i = strlen(g.urlPath); if( i>0 && g.urlPath[i-1]=='/' ){ zSep = ""; }else{ zSep = "/"; } | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | blob_zero(pHdr); i = strlen(g.urlPath); if( i>0 && g.urlPath[i-1]=='/' ){ zSep = ""; }else{ zSep = "/"; } blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep); if( g.urlProxyAuth ){ blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); } blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); if( g.fHttpTrace ){ blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 144 145 | Blob hdr; /* The HTTP request header */ int closeConnection; /* True to close the connection when done */ int iLength; /* Length of the reply payload */ int rc; /* Result code */ int iHttpVersion; /* Which version of HTTP protocol server uses */ char *zLine; /* A single line of the reply header */ int i; /* Loop counter */ if( transport_open() ){ fossil_fatal(transport_errmsg()); } /* Construct the login card and prepare the complete payload */ blob_zero(&login); | > | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | Blob hdr; /* The HTTP request header */ int closeConnection; /* True to close the connection when done */ int iLength; /* Length of the reply payload */ int rc; /* Result code */ int iHttpVersion; /* Which version of HTTP protocol server uses */ char *zLine; /* A single line of the reply header */ int i; /* Loop counter */ int isError = 0; /* True if the reply is an error message */ if( transport_open() ){ fossil_fatal(transport_errmsg()); } /* Construct the login card and prepare the complete payload */ blob_zero(&login); |
︙ | ︙ | |||
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | /* ** Read and interpret the server reply */ closeConnection = 1; iLength = -1; while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ if( strncasecmp(zLine, "http/1.", 7)==0 ){ if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; if( rc!=200 && rc!=302 ){ int ii; for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} while( zLine[ii]==' ' ) ii++; fossil_fatal("server says: %s\n", &zLine[ii]); goto write_err; } if( iHttpVersion==0 ){ closeConnection = 1; }else{ closeConnection = 0; } }else if( strncasecmp(zLine, "content-length:", 15)==0 ){ | > | | | > > > > > > > > > > > > > > > > > > > | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | /* ** Read and interpret the server reply */ closeConnection = 1; iLength = -1; while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ /* printf("[%s]\n", zLine); fflush(stdout); */ if( strncasecmp(zLine, "http/1.", 7)==0 ){ if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; if( rc!=200 && rc!=302 ){ int ii; for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} while( zLine[ii]==' ' ) ii++; fossil_fatal("server says: %s\n", &zLine[ii]); goto write_err; } if( iHttpVersion==0 ){ closeConnection = 1; }else{ closeConnection = 0; } }else if( strncasecmp(zLine, "content-length:", 15)==0 ){ for(i=15; fossil_isspace(zLine[i]); i++){} iLength = atoi(&zLine[i]); }else if( strncasecmp(zLine, "connection:", 11)==0 ){ char c; for(i=11; fossil_isspace(zLine[i]); i++){} c = zLine[i]; if( c=='c' || c=='C' ){ closeConnection = 1; }else if( c=='k' || c=='K' ){ closeConnection = 0; } }else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){ int i, j; for(i=9; zLine[i] && zLine[i]==' '; i++){} if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); j = strlen(zLine) - 1; while( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ){ j -= 4; zLine[j] = 0; } fossil_print("redirect to %s\n", &zLine[i]); url_parse(&zLine[i]); transport_close(); http_exchange(pSend, pReply, useLogin); return; }else if( strncasecmp(zLine, "content-type: text/html", 23)==0 ){ isError = 1; } } if( rc!=200 ){ fossil_fatal("\"location:\" missing from 302 redirect reply"); goto write_err; } /* ** Extract the reply payload that follows the header */ if( iLength<0 ){ fossil_fatal("server did not reply"); goto write_err; } blob_zero(pReply); blob_resize(pReply, iLength); iLength = transport_receive(blob_buffer(pReply), iLength); blob_resize(pReply, iLength); if( isError ){ char *z; int i, j; z = blob_str(pReply); for(i=j=0; z[i]; i++, j++){ if( z[i]=='<' ){ while( z[i] && z[i]!='>' ) i++; if( z[i]==0 ) break; } z[j] = z[i]; } z[j] = 0; fossil_fatal("server sends error: %s", z); } if( g.fHttpTrace ){ /*printf("HTTP RECEIVE:\n%s\n=======================\n",blob_str(pReply));*/ }else{ blob_uncompress(pReply, pReply); } /* ** Close the connection to the server if appropriate. ** |
︙ | ︙ |
Changes to src/http_socket.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | ** ** Low-level sockets are abstracted out into this module because they ** are handled different on Unix and windows. */ #include "config.h" #include "http_socket.h" | | | > > | > | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | ** ** Low-level sockets are abstracted out into this module because they ** are handled different on Unix and windows. */ #include "config.h" #include "http_socket.h" #if defined(_WIN32) # include <windows.h> /* for Sleep once server works again */ # define sleep Sleep /* windows does not have sleep, but Sleep */ # if defined(__MINGW32__) # include <ws2tcpip.h> # endif #else # include <arpa/inet.h> # include <sys/socket.h> # include <netdb.h> # include <netinet/in.h> #endif #include <assert.h> #include <sys/types.h> #include <signal.h> /* ** There can only be a single socket connection open at a time. ** State information about that socket is stored in the following ** local variables: */ static int socketIsInit = 0; /* True after global initialization */ #if defined(_WIN32) static WSADATA socketInfo; /* Windows socket initialize data */ #endif static int iSocket = -1; /* The socket on which we talk to the server */ static char *socketErrMsg = 0; /* Text of most recent socket error */ /* |
︙ | ︙ | |||
82 83 84 85 86 87 88 | /* ** Call this routine once before any other use of the socket interface. ** This routine does initial configuration of the socket module. */ void socket_global_init(void){ if( socketIsInit==0 ){ | | | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | /* ** Call this routine once before any other use of the socket interface. ** This routine does initial configuration of the socket module. */ void socket_global_init(void){ if( socketIsInit==0 ){ #if defined(_WIN32) if( WSAStartup(MAKEWORD(2,0), &socketInfo)!=0 ){ fossil_panic("can't initialize winsock"); } #endif socketIsInit = 1; } } /* ** Call this routine to shutdown the socket module prior to program ** exit. */ void socket_global_shutdown(void){ if( socketIsInit ){ #if defined(_WIN32) WSACleanup(); #endif socket_clear_errmsg(); socketIsInit = 0; } } /* ** Close the currently open socket. If no socket is open, this routine ** is a no-op. */ void socket_close(void){ if( iSocket>=0 ){ #if defined(_WIN32) closesocket(iSocket); #else close(iSocket); #endif iSocket = -1; } } |
︙ | ︙ | |||
169 170 171 172 173 174 175 | return 1; } if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort); socket_close(); return 1; } | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | return 1; } if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort); socket_close(); return 1; } #if !defined(_WIN32) signal(SIGPIPE, SIG_IGN); #endif return 0; } /* ** Send content out over the open socket connection. |
︙ | ︙ |
Changes to src/http_ssl.c.
︙ | ︙ | |||
88 89 90 91 92 93 94 95 96 97 98 99 100 101 | void ssl_global_init(void){ if( sslIsInit==0 ){ SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); sslCtx = SSL_CTX_new(SSLv23_client_method()); sslIsInit = 1; } } /* ** Call this routine to shutdown the SSL module prior to program exit. */ | > | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | void ssl_global_init(void){ if( sslIsInit==0 ){ SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); sslCtx = SSL_CTX_new(SSLv23_client_method()); X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); sslIsInit = 1; } } /* ** Call this routine to shutdown the SSL module prior to program exit. */ |
︙ | ︙ | |||
126 127 128 129 130 131 132 | ** g.urlPort TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int ssl_open(void){ X509 *cert; int hasSavedCertificate = 0; | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | ** g.urlPort TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int ssl_open(void){ X509 *cert; int hasSavedCertificate = 0; char *connStr ; ssl_global_init(); /* Get certificate for current server from global config and * (if we have it in config) add it to certificate store. */ cert = ssl_get_certificate(); if ( cert!=NULL ){ |
︙ | ︙ | |||
148 149 150 151 152 153 154 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); if( iBio==NULL ) { ssl_set_errmsg("SSL: cannot open SSL (%s)", ERR_reason_error_string(ERR_get_error())); return 1; } | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); if( iBio==NULL ) { ssl_set_errmsg("SSL: cannot open SSL (%s)", ERR_reason_error_string(ERR_get_error())); return 1; } connStr = mprintf("%s:%d", g.urlName, g.urlPort); BIO_set_conn_hostname(iBio, connStr); free(connStr); if( BIO_do_connect(iBio)<=0 ){ ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); ssl_close(); |
︙ | ︙ | |||
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | ssl_set_errmsg("No SSL certificate was presented by the peer"); ssl_close(); return 1; } if( SSL_get_verify_result(ssl) != X509_V_OK ){ char *desc, *prompt; BIO *mem; mem = BIO_new(BIO_s_mem()); X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE); BIO_puts(mem, "\n\nIssued By:\n\n"); X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE); BIO_write(mem, "", 1); // null-terminate mem buffer BIO_get_mem_data(mem, &desc); | > > < < | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | ssl_set_errmsg("No SSL certificate was presented by the peer"); ssl_close(); return 1; } if( SSL_get_verify_result(ssl) != X509_V_OK ){ char *desc, *prompt; char *warning = ""; Blob ans; BIO *mem; mem = BIO_new(BIO_s_mem()); X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE); BIO_puts(mem, "\n\nIssued By:\n\n"); X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE); BIO_write(mem, "", 1); // null-terminate mem buffer BIO_get_mem_data(mem, &desc); if( hasSavedCertificate ){ warning = "WARNING: Certificate doesn't match the " "saved certificate for this host!"; } prompt = mprintf("\nUnknown SSL certificate:\n\n%s\n\n%s\n" "Accept certificate [a=always/y/N]? ", desc, warning); BIO_free(mem); prompt_user(prompt, &ans); free(prompt); if( blob_str(&ans)[0]!='y' && blob_str(&ans)[0]!='a' ) { X509_free(cert); ssl_set_errmsg("SSL certificate declined"); ssl_close(); return 1; |
︙ | ︙ |
Changes to src/http_transport.c.
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | int nRcvd; /* Number of bytes received */ FILE *pFile; /* File I/O for FILE: */ char *zOutFile; /* Name of outbound file for FILE: */ char *zInFile; /* Name of inbound file for FILE: */ } transport = { 0, 0, 0, 0, 0, 0, 0 }; /* ** Return the current transport error message. */ const char *transport_errmsg(void){ #ifdef FOSSIL_ENABLE_SSL if( g.urlIsHttps ){ | > > > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | int nRcvd; /* Number of bytes received */ FILE *pFile; /* File I/O for FILE: */ char *zOutFile; /* Name of outbound file for FILE: */ char *zInFile; /* Name of inbound file for FILE: */ } transport = { 0, 0, 0, 0, 0, 0, 0 }; /* ** Information about the connection to the SSH subprocess when ** using the ssh:// sync method. */ static int sshPid; /* Process id of ssh subprocess */ static int sshIn; /* From ssh subprocess to this process */ static FILE *sshOut; /* From this to ssh subprocess */ /* ** Return the current transport error message. */ const char *transport_errmsg(void){ #ifdef FOSSIL_ENABLE_SSL if( g.urlIsHttps ){ |
︙ | ︙ | |||
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | if( pnSent ) *pnSent = transport.nSent; if( pnRcvd ) *pnRcvd = transport.nRcvd; if( resetFlag ){ transport.nSent = 0; transport.nRcvd = 0; } } /* ** Open a connection to the server. The server is defined by the following ** global variables: ** ** g.urlName Name of the server. Ex: www.fossil-scm.org ** g.urlPort TCP/IP port. Ex: 80 ** g.urlIsHttps Use TLS for the connection ** ** Return the number of errors. */ int transport_open(void){ int rc = 0; if( transport.isOpen==0 ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | if( pnSent ) *pnSent = transport.nSent; if( pnRcvd ) *pnRcvd = transport.nRcvd; if( resetFlag ){ transport.nSent = 0; transport.nRcvd = 0; } } /* ** Read text from sshIn. Zero-terminate and remove trailing ** whitespace. */ static void sshin_read(char *zBuf, int szBuf){ int got; zBuf[0] = 0; got = read(sshIn, zBuf, szBuf-1); while( got>=0 ){ zBuf[got] = 0; if( got==0 || !fossil_isspace(zBuf[got-1]) ) break; got--; } } /* ** Default SSH command */ #ifdef __MINGW32__ static char zDefaultSshCmd[] = "ssh -T"; #else static char zDefaultSshCmd[] = "ssh -e none -T"; #endif /* ** Global initialization of the transport layer */ void transport_global_startup(void){ if( g.urlIsSsh ){ /* Only SSH requires a global initialization. For SSH we need to create ** and run an SSH command to talk to the remote machine. */ const char *zSsh; /* The base SSH command */ Blob zCmd; /* The SSH command */ char *zHost; /* The host name to contact */ char zIn[200]; /* An input line received back from remote */ zSsh = db_get("ssh-command", zDefaultSshCmd); blob_init(&zCmd, zSsh, -1); if( g.urlPort!=g.urlDfltPort ){ #ifdef __MINGW32__ blob_appendf(&zCmd, " -P %d", g.urlPort); #else blob_appendf(&zCmd, " -p %d", g.urlPort); #endif } if( g.urlUser && g.urlUser[0] ){ zHost = mprintf("%s@%s", g.urlUser, g.urlName); #ifdef __MINGW32__ /* Only win32 (and specifically PLINK.EXE support the -pw option */ if( g.urlPasswd && g.urlPasswd[0] ){ Blob pw; blob_zero(&pw); if( g.urlPasswd[0]=='*' ){ char *zPrompt; zPrompt = mprintf("Password for [%s]: ", zHost); prompt_for_password(zPrompt, &pw, 0); free(zPrompt); }else{ blob_init(&pw, g.urlPasswd, -1); } blob_append(&zCmd, " -pw ", -1); shell_escape(&zCmd, blob_str(&pw)); blob_reset(&pw); } #endif }else{ zHost = mprintf("%s", g.urlName); } blob_append(&zCmd, " ", 1); shell_escape(&zCmd, zHost); free(zHost); /* printf("%s\n", blob_str(&zCmd)); */ popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); if( sshPid==0 ){ fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd); } blob_reset(&zCmd); /* Send an "echo" command to the other side to make sure that the ** connection is up and working. */ fprintf(sshOut, "echo test\n"); fflush(sshOut); sshin_read(zIn, sizeof(zIn)); if( memcmp(zIn, "test", 4)!=0 ){ pclose2(sshIn, sshOut, sshPid); fossil_fatal("ssh connection failed: [%s]", zIn); } } } /* ** Open a connection to the server. The server is defined by the following ** global variables: ** ** g.urlName Name of the server. Ex: www.fossil-scm.org ** g.urlPort TCP/IP port. Ex: 80 ** g.urlIsHttps Use TLS for the connection ** ** Return the number of errors. */ int transport_open(void){ int rc = 0; if( transport.isOpen==0 ){ if( g.urlIsSsh ){ Blob cmd; blob_zero(&cmd); shell_escape(&cmd, g.urlFossil); blob_append(&cmd, " test-http ", -1); shell_escape(&cmd, g.urlPath); /* fprintf(stdout, "%s\n", blob_str(&cmd)); */ fprintf(sshOut, "%s\n", blob_str(&cmd)); fflush(sshOut); blob_reset(&cmd); }else if( g.urlIsHttps ){ #ifdef FOSSIL_ENABLE_SSL rc = ssl_open(); if( rc==0 ) transport.isOpen = 1; #else socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); rc = 1; #endif |
︙ | ︙ | |||
113 114 115 116 117 118 119 | void transport_close(void){ if( transport.isOpen ){ free(transport.pBuf); transport.pBuf = 0; transport.nAlloc = 0; transport.nUsed = 0; transport.iCursor = 0; | | > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | void transport_close(void){ if( transport.isOpen ){ free(transport.pBuf); transport.pBuf = 0; transport.nAlloc = 0; transport.nUsed = 0; transport.iCursor = 0; if( g.urlIsSsh ){ /* No-op */ }else if( g.urlIsHttps ){ #ifdef FOSSIL_ENABLE_SSL ssl_close(); #endif }else if( g.urlIsFile ){ if( transport.pFile ){ fclose(transport.pFile); transport.pFile = 0; |
︙ | ︙ | |||
140 141 142 143 144 145 146 | /* ** Send content over the wire. */ void transport_send(Blob *toSend){ char *z = blob_buffer(toSend); int n = blob_size(toSend); transport.nSent += n; | | > > > > > | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | /* ** Send content over the wire. */ void transport_send(Blob *toSend){ char *z = blob_buffer(toSend); int n = blob_size(toSend); transport.nSent += n; if( g.urlIsSsh ){ int sent; sent = fwrite(z, 1, n, sshOut); fflush(sshOut); /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */ }else if( g.urlIsHttps ){ #ifdef FOSSIL_ENABLE_SSL int sent; while( n>0 ){ sent = ssl_send(0, z, n); /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ if( sent<=0 ) break; n -= sent; |
︙ | ︙ | |||
168 169 170 171 172 173 174 | } /* ** This routine is called when the outbound message is complete and ** it is time to being recieving a reply. */ void transport_flip(void){ | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < | < | < | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | } /* ** This routine is called when the outbound message is complete and ** it is time to being recieving a reply. */ void transport_flip(void){ if( g.urlIsSsh ){ fprintf(sshOut, "\n\n"); }else if( g.urlIsFile ){ char *zCmd; fclose(transport.pFile); zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", g.argv[0], g.urlName, transport.zOutFile, transport.zInFile ); fossil_system(zCmd); free(zCmd); transport.pFile = fopen(transport.zInFile, "rb"); } } /* ** This routine is called when the inbound message has been received ** and it is time to start sending again. */ void transport_rewind(void){ if( g.urlIsFile ){ transport_close(); } } /* ** Read N bytes of content directly from the wire and write into ** the buffer. */ static int transport_fetch(char *zBuf, int N){ int got; if( sshIn ){ int x; int wanted = N; got = 0; while( wanted>0 ){ x = read(sshIn, &zBuf[got], wanted); if( x<=0 ) break; got += x; wanted -= x; } }else if( g.urlIsHttps ){ #ifdef FOSSIL_ENABLE_SSL got = ssl_receive(0, zBuf, N); #else got = 0; #endif }else if( g.urlIsFile ){ got = fread(zBuf, 1, N, transport.pFile); }else{ got = socket_receive(0, zBuf, N); } /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ return got; } /* ** Read N bytes of content from the wire and store in the supplied buffer. ** Return the number of bytes actually received. */ int transport_receive(char *zBuf, int N){ int onHand; /* Bytes current held in the transport buffer */ int nByte = 0; /* Bytes of content received */ onHand = transport.nUsed - transport.iCursor; /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */ if( onHand>0 ){ int toMove = onHand; if( toMove>N ) toMove = N; /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); transport.iCursor += toMove; if( transport.iCursor>=transport.nUsed ){ transport.nUsed = 0; transport.iCursor = 0; } N -= toMove; zBuf += toMove; nByte += toMove; } if( N>0 ){ int got = transport_fetch(zBuf, N); if( got>0 ){ nByte += got; transport.nRcvd += got; } } return nByte; } /* ** Load up to N new bytes of content into the transport.pBuf buffer. ** The buffer itself might be moved. And the transport.iCursor value ** might be reset to 0. */ static void transport_load_buffer(int N){ int i, j; if( transport.nAlloc==0 ){ transport.nAlloc = N; transport.pBuf = fossil_malloc( N ); transport.iCursor = 0; transport.nUsed = 0; } if( transport.iCursor>0 ){ for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){ transport.pBuf[i] = transport.pBuf[j]; } transport.nUsed -= transport.iCursor; transport.iCursor = 0; } if( transport.nUsed + N > transport.nAlloc ){ char *pNew; transport.nAlloc = transport.nUsed + N; pNew = fossil_realloc(transport.pBuf, transport.nAlloc); transport.pBuf = pNew; } if( N>0 ){ i = transport_fetch(&transport.pBuf[transport.nUsed], N); if( i>0 ){ transport.nUsed += i; } } } /* |
︙ | ︙ | |||
298 299 300 301 302 303 304 | transport.pBuf[i] = 0; transport.iCursor = i; break; } } if( transport.pBuf[i]=='\n' ){ transport.iCursor = i+1; | | > > > > > > | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | transport.pBuf[i] = 0; transport.iCursor = i; break; } } if( transport.pBuf[i]=='\n' ){ transport.iCursor = i+1; while( i>=iStart && fossil_isspace(transport.pBuf[i]) ){ transport.pBuf[i] = 0; i--; } break; } i++; } /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ return &transport.pBuf[iStart]; } void transport_global_shutdown(void){ if( g.urlIsSsh && sshPid ){ printf("Closing SSH tunnel: "); fflush(stdout); pclose2(sshIn, sshOut, sshPid); sshPid = 0; } if( g.urlIsHttps ){ #ifdef FOSSIL_ENABLE_SSL ssl_global_shutdown(); #endif }else{ socket_global_shutdown(); } } |
Changes to src/info.c.
︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 72 73 74 75 76 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", rid ); /* 01234567890123 */ printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : ""); free(zUuid); free(zDate); } db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid " " WHERE cid=%d", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); zDate = db_text("", "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", | > > > > > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", rid ); /* 01234567890123 */ printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : ""); free(zUuid); free(zDate); } if( zUuid && showComment ){ zComment = db_text(0, "SELECT coalesce(ecomment,comment) || ' (user: ' || coalesce(euser,user,'?') || ')' FROM event WHERE objid=%d", rid ); } db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid " " WHERE cid=%d", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); zDate = db_text("", "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
︙ | ︙ | |||
94 95 96 97 98 99 100 | db_finalize(&q); zTags = info_tags_of_checkin(rid, 0); if( zTags && zTags[0] ){ printf("tags: %s\n", zTags); } free(zTags); if( zComment ){ | | > | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | db_finalize(&q); zTags = info_tags_of_checkin(rid, 0); if( zTags && zTags[0] ){ printf("tags: %s\n", zTags); } free(zTags); if( zComment ){ printf("comment: "); comment_print(zComment, 14, 79); free(zComment); } } /* ** COMMAND: info |
︙ | ︙ | |||
133 134 135 136 137 138 139 | if( g.argc==2 ){ int vid; /* 012345678901234 */ db_record_repository_filename(0); printf("project-name: %s\n", db_get("project-name", "<unnamed>")); printf("repository: %s\n", db_lget("repository", "")); printf("local-root: %s\n", g.zLocalRoot); | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | if( g.argc==2 ){ int vid; /* 012345678901234 */ db_record_repository_filename(0); printf("project-name: %s\n", db_get("project-name", "<unnamed>")); printf("repository: %s\n", db_lget("repository", "")); printf("local-root: %s\n", g.zLocalRoot); #if defined(_WIN32) if( g.zHome ){ printf("user-home: %s\n", g.zHome); } #endif printf("project-code: %s\n", db_get("project-code", "")); printf("server-code: %s\n", db_get("server-code", "")); vid = db_lget_int("checkout", 0); |
︙ | ︙ | |||
185 186 187 188 189 190 191 | cnt++; if( cnt==1 ){ @ <div class="section">Tags And Properties</div> @ <ul> } @ <li> if( tagtype==0 ){ | | | | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | cnt++; if( cnt==1 ){ @ <div class="section">Tags And Properties</div> @ <ul> } @ <li> if( tagtype==0 ){ @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled }else if( zValue ){ @ <span class="infoTag">%h(zTagname)=%h(zValue)</span> }else { @ <span class="infoTag">%h(zTagname)</span> } if( tagtype==2 ){ if( zOrigUuid && zOrigUuid[0] ){ @ inherited from hyperlink_to_uuid(zOrigUuid); }else{ @ propagates to descendants |
︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | }else{ @ added by } hyperlink_to_uuid(zSrcUuid); @ on hyperlink_to_date(zDate,0); } } db_finalize(&q); if( cnt ){ @ </ul> } } /* ** Append the difference between two RIDs to the output */ | > | > > > > | > > > > > | > > > | | 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | }else{ @ added by } hyperlink_to_uuid(zSrcUuid); @ on hyperlink_to_date(zDate,0); } @ </li> } db_finalize(&q); if( cnt ){ @ </ul> } } /* ** Append the difference between two RIDs to the output */ static void append_diff(const char *zFrom, const char *zTo){ int fromid; int toid; Blob from, to, out; if( zFrom ){ fromid = uuid_to_rid(zFrom, 0); content_get(fromid, &from); }else{ blob_zero(&from); } if( zTo ){ toid = uuid_to_rid(zTo, 0); content_get(toid, &to); }else{ blob_zero(&to); } blob_zero(&out); text_diff(&from, &to, &out, 5, 1); @ %h(blob_str(&out)) blob_reset(&from); blob_reset(&to); blob_reset(&out); } /* |
︙ | ︙ | |||
254 255 256 257 258 259 260 | if( zNew==0 ){ @ <p>Deleted %h(zName)</p> }else if( zOld==0 ){ @ <p>Added %h(zName)</p> }else{ @ <p>Changes to %h(zName)</p> } | < < < < | < < < < < | > > > > > | | | | | | > > > > > > > > > > | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | if( zNew==0 ){ @ <p>Deleted %h(zName)</p> }else if( zOld==0 ){ @ <p>Added %h(zName)</p> }else{ @ <p>Changes to %h(zName)</p> } if( showDiff ){ @ <blockquote><pre> append_diff(zOld, zNew); @ </pre></blockquote> } }else{ if( zOld && zNew ){ @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> }else if( zOld ){ @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> }else{ @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a> } if( showDiff ){ @ <blockquote><pre> append_diff(zOld, zNew); @ </pre></blockquote> }else if( zOld && zNew ){ @ @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> } @ </p> } } /* ** WEBPAGE: vinfo ** WEBPAGE: ci |
︙ | ︙ | |||
338 339 340 341 342 343 344 | zEComment = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", TAG_COMMENT, rid); zUser = db_column_text(&q, 2); zComment = db_column_text(&q, 3); zDate = db_column_text(&q,1); @ <div class="section">Overview</div> | | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | zEComment = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", TAG_COMMENT, rid); zUser = db_column_text(&q, 2); zComment = db_column_text(&q, 3); zDate = db_column_text(&q,1); @ <div class="section">Overview</div> @ <table class="label-value"> @ <tr><th>SHA1 Hash:</th><td>%s(zUuid) if( g.okSetup ){ @ (Record ID: %d(rid)) } @ </td></tr> @ <tr><th>Date:</th><td> hyperlink_to_date(zDate, "</td></tr>"); |
︙ | ︙ | |||
381 382 383 384 385 386 387 | @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr> } db_finalize(&q); } if( g.okHistory ){ const char *zProjName = db_get("project-name", "unnamed"); @ <tr><th>Timelines:</th><td> | | | | > | | > | | 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr> } db_finalize(&q); } if( g.okHistory ){ const char *zProjName = db_get("project-name", "unnamed"); @ <tr><th>Timelines:</th><td> @ <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a> @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a> @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&p=%S(zUuid)">both</a> db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag " " WHERE rid=%d AND tagtype>0 " " AND tag.tagid=tagxref.tagid " " AND +tag.tagname GLOB 'sym-*'", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zTagName = db_column_text(&q, 0); @ | <a href="%s(g.zTop)/timeline?r=%T(zTagName)">%h(zTagName)</a> } db_finalize(&q); @ </td></tr> @ <tr><th>Other Links:</th> @ <td> @ <a href="%s(g.zTop)/dir?ci=%S(zUuid)">files</a> if( g.okZip ){ @ | <a href="%s(g.zTop)/zip/%s(zProjName)-%S(zUuid).zip?uuid=%s(zUuid)"> @ ZIP archive</a> } @ | <a href="%s(g.zTop)/artifact/%S(zUuid)">manifest</a> if( g.okWrite ){ @ | <a href="%s(g.zTop)/ci_edit?r=%S(zUuid)">edit</a> } @ </td> @ </tr> } @ </table> }else{ style_header("Check-in Information"); login_anonymous_available(); } db_finalize(&q); showTags(rid, ""); @ <div class="section">Changes</div> |
︙ | ︙ | |||
513 514 515 516 517 518 519 | }else{ style_header("Wiki Information"); rid = 0; } db_finalize(&q); showTags(rid, "wiki-*"); if( rid ){ | < | < < | < | | | | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | }else{ style_header("Wiki Information"); rid = 0; } db_finalize(&q); showTags(rid, "wiki-*"); if( rid ){ Manifest *pWiki; pWiki = manifest_get(rid, CFTYPE_WIKI); if( pWiki ){ Blob wiki; blob_init(&wiki, pWiki->zWiki, -1); @ <div class="section">Content</div> wiki_convert(&wiki, 0, 0); blob_reset(&wiki); } manifest_destroy(pWiki); } style_footer(); } /* ** Show a webpage error message */ |
︙ | ︙ | |||
550 551 552 553 554 555 556 | style_footer(); } /* ** Find an checkin based on query parameter zParam and parse its ** manifest. Return the number of errors. */ | | < | | < < | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 | style_footer(); } /* ** Find an checkin based on query parameter zParam and parse its ** manifest. Return the number of errors. */ static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){ int rid; *pRid = rid = name_to_rid_www(zParam); if( rid==0 ){ webpage_error("Missing \"%s\" query parameter.", zParam); return 0; } if( !is_a_version(rid) ){ webpage_error("Artifact %s is not a checkin.", P(zParam)); return 0; } return manifest_get(rid, CFTYPE_MANIFEST); } /* ** Output a description of a check-in */ void checkin_description(int rid){ Stmt q; |
︙ | ︙ | |||
598 599 600 601 602 603 604 | } db_finalize(&q); } /* ** WEBPAGE: vdiff | | | | | > | > | > | > > | | | | | | | | | | | | | | | | | | | | | 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 | } db_finalize(&q); } /* ** WEBPAGE: vdiff ** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN ** ** Show all differences between two checkins. */ void vdiff_page(void){ int ridFrom, ridTo; int showDetail = 0; Manifest *pFrom, *pTo; ManifestFile *pFileFrom, *pFileTo; login_check_credentials(); if( !g.okRead ){ login_needed(); return; } login_anonymous_available(); pFrom = vdiff_parse_manifest("from", &ridFrom); if( pFrom==0 ) return; pTo = vdiff_parse_manifest("to", &ridTo); if( pTo==0 ) return; showDetail = atoi(PD("detail","0")); style_header("Check-in Differences"); @ <h2>Difference From:</h2><blockquote> checkin_description(ridFrom); @ </blockquote><h2>To:</h2><blockquote> checkin_description(ridTo); @ </blockquote><hr /><p> manifest_file_rewind(pFrom); pFileFrom = manifest_file_next(pFrom, 0); manifest_file_rewind(pTo); pFileTo = manifest_file_next(pTo, 0); while( pFileFrom || pFileTo ){ int cmp; if( pFileFrom==0 ){ cmp = +1; }else if( pFileTo==0 ){ cmp = -1; }else{ cmp = strcmp(pFileFrom->zName, pFileTo->zName); } if( cmp<0 ){ append_file_change_line(pFileFrom->zName, pFileFrom->zUuid, 0, 0); pFileFrom = manifest_file_next(pFrom, 0); }else if( cmp>0 ){ append_file_change_line(pFileTo->zName, 0, pFileTo->zUuid, 0); pFileTo = manifest_file_next(pTo, 0); }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ /* No changes */ pFileFrom = manifest_file_next(pFrom, 0); pFileTo = manifest_file_next(pTo, 0); }else{ append_file_change_line(pFileFrom->zName, pFileFrom->zUuid, pFileTo->zUuid, showDetail); pFileFrom = manifest_file_next(pFrom, 0); pFileTo = manifest_file_next(pTo, 0); } } manifest_destroy(pFrom); manifest_destroy(pTo); style_footer(); } /* ** Write a description of an object to the www reply. ** |
︙ | ︙ | |||
759 760 761 762 763 764 765 | if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_appendf(pDownloadName, "%s.wiki", zPagename); } } db_finalize(&q); if( nWiki==0 ){ db_prepare(&q, | | > > > > | > | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_appendf(pDownloadName, "%s.wiki", zPagename); } } db_finalize(&q); if( nWiki==0 ){ db_prepare(&q, "SELECT datetime(mtime), user, comment, type, uuid, tagid" " FROM event, blob" " WHERE event.objid=%d" " AND blob.rid=%d", rid, rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zCom = db_column_text(&q, 2); const char *zType = db_column_text(&q, 3); const char *zUuid = db_column_text(&q, 4); if( cnt>0 ){ @ Also } if( zType[0]=='w' ){ @ Wiki edit }else if( zType[0]=='t' ){ @ Ticket change }else if( zType[0]=='c' ){ @ Manifest of check-in }else if( zType[0]=='e' ){ @ Instance of event hyperlink_to_event_tagid(db_column_int(&q, 5)); }else{ @ Control file referencing } if( zType[0]!='e' ){ hyperlink_to_uuid(zUuid); } @ - %w(zCom) by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate, "."); if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_appendf(pDownloadName, "%.10s.txt", zUuid); } cnt++; |
︙ | ︙ | |||
862 863 864 865 866 867 868 | login_check_credentials(); if( !g.okRead ){ login_needed(); return; } v1 = name_to_rid_www("v1"); v2 = name_to_rid_www("v2"); if( v1==0 || v2==0 ) fossil_redirect_home(); style_header("Diff"); @ <h2>Differences From:</h2> | | | | | | | | 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 | login_check_credentials(); if( !g.okRead ){ login_needed(); return; } v1 = name_to_rid_www("v1"); v2 = name_to_rid_www("v2"); if( v1==0 || v2==0 ) fossil_redirect_home(); style_header("Diff"); @ <h2>Differences From:</h2> @ <blockquote><p> object_description(v1, 1, 0); @ </p></blockquote> @ <h2>To:</h2> @ <blockquote><p> object_description(v2, 1, 0); @ </p></blockquote> @ <hr /> @ <blockquote><pre> content_get(v1, &c1); content_get(v2, &c2); blob_zero(&diff); text_diff(&c1, &c2, &diff, 4, 1); blob_reset(&c1); blob_reset(&c2); @ %h(blob_str(&diff)) @ </pre></blockquote> blob_reset(&diff); style_footer(); } |
︙ | ︙ | |||
976 977 978 979 980 981 982 | rid = name_to_rid_www("name"); login_check_credentials(); if( !g.okRead ){ login_needed(); return; } if( rid==0 ) fossil_redirect_home(); if( g.okAdmin ){ const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ | | | | | < | < > | | | | | | > > | 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 | rid = name_to_rid_www("name"); login_check_credentials(); if( !g.okRead ){ login_needed(); return; } if( rid==0 ) fossil_redirect_home(); if( g.okAdmin ){ const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } style_header("Hex Artifact Content"); zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid); @ <h2>Artifact %s(zUuid):</h2> @ <blockquote><p> blob_zero(&downloadName); object_description(rid, 0, &downloadName); style_submenu_element("Download", "Download", "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); @ </p></blockquote> @ <hr /> content_get(rid, &content); @ <blockquote><pre> hexdump(&content); @ </pre></blockquote> style_footer(); } /* ** Look for "ci" and "filename" query parameters. If found, try to ** use them to extract the record ID of an artifact for the file. */ int artifact_from_ci_and_filename(void){ const char *zFilename; const char *zCI; int cirid; Manifest *pManifest; ManifestFile *pFile; zCI = P("ci"); if( zCI==0 ) return 0; zFilename = P("filename"); if( zFilename==0 ) return 0; cirid = name_to_rid_www("ci"); pManifest = manifest_get(cirid, CFTYPE_MANIFEST); if( pManifest==0 ) return 0; manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ if( strcmp(zFilename, pFile->zName)==0 ){ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); manifest_destroy(pManifest); return rid; } } return 0; } /* |
︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 | login_check_credentials(); if( !g.okRead ){ login_needed(); return; } if( rid==0 ) fossil_redirect_home(); if( g.okAdmin ){ const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ | | | | | | | | < | | | | < < < | | | > | | | | | | | 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 | login_check_credentials(); if( !g.okRead ){ login_needed(); return; } if( rid==0 ) fossil_redirect_home(); if( g.okAdmin ){ const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } style_header("Artifact Content"); zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); @ <h2>Artifact %s(zUuid)</h2> @ <blockquote><p> blob_zero(&downloadName); object_description(rid, 0, &downloadName); style_submenu_element("Download", "Download", "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); zMime = mimetype_from_name(blob_str(&downloadName)); if( zMime ){ if( strcmp(zMime, "text/html")==0 ){ if( P("txt") ){ style_submenu_element("Html", "Html", "%s/artifact?name=%s", g.zTop, zUuid); }else{ renderAsHtml = 1; style_submenu_element("Text", "Text", "%s/artifact?name=%s&txt=1", g.zTop, zUuid); } }else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){ if( P("txt") ){ style_submenu_element("Wiki", "Wiki", "%s/artifact?name=%s", g.zTop, zUuid); }else{ renderAsWiki = 1; style_submenu_element("Text", "Text", "%s/artifact?name=%s&txt=1", g.zTop, zUuid); } } } @ </p></blockquote> @ <hr /> content_get(rid, &content); if( renderAsWiki ){ wiki_convert(&content, 0, 0); }else if( renderAsHtml ){ @ <div> cgi_append_content(blob_buffer(&content), blob_size(&content)); @ </div> }else{ zMime = mimetype_from_content(&content); @ <blockquote> if( zMime==0 ){ @ <pre> @ %h(blob_str(&content)) @ </pre> style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid); }else if( strncmp(zMime, "image/", 6)==0 ){ @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&m=%s(zMime)"></img> style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid); }else{ @ <pre> hexdump(&content); @ </pre> } @ </blockquote> } style_footer(); } /* ** WEBPAGE: tinfo ** URL: /tinfo?name=ARTIFACTID ** ** Show the details of a ticket change control artifact. */ void tinfo_page(void){ int rid; char *zDate; const char *zUuid; char zTktName[20]; Manifest *pTktChng; login_check_credentials(); if( !g.okRdTkt ){ login_needed(); return; } rid = name_to_rid_www("name"); if( rid==0 ){ fossil_redirect_home(); } zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( g.okAdmin ){ if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } pTktChng = manifest_get(rid, CFTYPE_TICKET); if( pTktChng==0 ){ fossil_redirect_home(); } style_header("Ticket Change Details"); zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate); memcpy(zTktName, pTktChng->zTicketUuid, 10); zTktName[10] = 0; if( g.okHistory ){ @ <h2>Changes to ticket @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2> @ @ <p>By %h(pTktChng->zUser) on %s(zDate). See also: @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket @ history</a></p> }else{ @ <h2>Changes to ticket %s(zTktName)</h2> @ @ <p>By %h(pTktChng->zUser) on %s(zDate). @ </p> } @ @ <ol> free(zDate); ticket_output_change_artifact(pTktChng); manifest_destroy(pTktChng); style_footer(); } /* ** WEBPAGE: info ** URL: info/ARTIFACTID |
︙ | ︙ | |||
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ ci_page(); }else { artifact_page(); } } /* ** WEBPAGE: ci_edit ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER ** ** Present a dialog for updating properties of a baseline: ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ ci_page(); }else { artifact_page(); } } /* ** Generate HTML that will present the user with a selection of ** potential background colors for timeline entries. */ void render_color_chooser( int fPropagate, /* Default value for propagation */ const char *zDefaultColor, /* The current default color */ const char *zIdPropagate, /* ID of form element checkbox. NULL for none */ const char *zId, /* The ID of the form element */ const char *zIdCustom /* ID of text box for custom color */ ){ static const struct SampleColors { const char *zCName; const char *zColor; } aColor[] = { { "(none)", "" }, { "#f2dcdc", "#f2dcdc" }, { "#f0ffc0", "#f0ffc0" }, { "#bde5d6", "#bde5d6" }, { "#c0ffc0", "#c0ffc0" }, { "#c0fff0", "#c0fff0" }, { "#c0f0ff", "#c0f0ff" }, { "#d0c0ff", "#d0c0ff" }, { "#ffc0ff", "#ffc0ff" }, { "#ffc0d0", "#ffc0d0" }, { "#fff0c0", "#fff0c0" }, { "#c0c0c0", "#c0c0c0" }, { "custom", "##" }, }; int nColor = sizeof(aColor)/sizeof(aColor[0])-1; int stdClrFound = 0; int i; @ <table border="0" cellpadding="0" cellspacing="1"> if( zIdPropagate ){ @ <tr><td colspan="6" align="left"> if( fPropagate ){ @ <input type="checkbox" name="%s(zIdPropagate)" checked="checked" /> }else{ @ <input type="checkbox" name="%s(zIdPropagate)" /> } @ Propagate color to descendants</td></tr> } @ <tr> for(i=0; i<nColor; i++){ if( aColor[i].zColor[0] ){ @ <td style="background-color: %h(aColor[i].zColor);"> }else{ @ <td> } if( strcmp(zDefaultColor, aColor[i].zColor)==0 ){ @ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)" @ checked="checked" /> stdClrFound=1; }else{ @ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)" /> } @ %h(aColor[i].zCName)</td> if( (i%6)==5 && i+1<nColor ){ @ </tr><tr> } } @ </tr><tr> if (stdClrFound){ @ <td colspan="6"> @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" /> }else{ @ <td style="background-color: %h(zDefaultColor);" colspan="6"> @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" @ checked="checked" /> } @ %h(aColor[i].zCName) @ <input type="text" name="%s(zIdCustom)" @ id="%s(zIdCustom)" class="checkinUserColor" @ value="%h(stdClrFound?"":zDefaultColor)" /> @ </td> @ </tr> @ </table> } /* ** WEBPAGE: ci_edit ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER ** ** Present a dialog for updating properties of a baseline: ** |
︙ | ︙ | |||
1270 1271 1272 1273 1274 1275 1276 | const char *zNewBrFlag; const char *zNewBranch; const char *zCloseFlag; int fPropagateColor; char *zUuid; Blob comment; Stmt q; | < < < < < < < < < < < < < < < < < < < | 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 | const char *zNewBrFlag; const char *zNewBranch; const char *zCloseFlag; int fPropagateColor; char *zUuid; Blob comment; Stmt q; login_check_credentials(); if( !g.okWrite ){ login_needed(); return; } rid = name_to_rid(P("r")); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); zComment = db_text(0, "SELECT coalesce(ecomment,comment)" " FROM event WHERE objid=%d", rid); |
︙ | ︙ | |||
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 | zDate = db_text(0, "SELECT datetime(mtime)" " FROM event WHERE objid=%d", rid); if( zDate==0 ) fossil_redirect_home(); zNewDate = PD("dt",zDate); zColor = db_text("", "SELECT bgcolor" " FROM event WHERE objid=%d", rid); zNewColor = PD("clr",zColor); fPropagateColor = P("pclr")!=0; zNewTagFlag = P("newtag") ? " checked" : ""; zNewTag = PD("tagname",""); zNewBrFlag = P("newbr") ? " checked" : ""; zNewBranch = PD("brname",""); zCloseFlag = P("close") ? " checked" : ""; if( P("apply") ){ | > > > | 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 | zDate = db_text(0, "SELECT datetime(mtime)" " FROM event WHERE objid=%d", rid); if( zDate==0 ) fossil_redirect_home(); zNewDate = PD("dt",zDate); zColor = db_text("", "SELECT bgcolor" " FROM event WHERE objid=%d", rid); zNewColor = PD("clr",zColor); if( strcmp(zNewColor,"##")==0 ){ zNewColor = P("clrcust"); } fPropagateColor = P("pclr")!=0; zNewTagFlag = P("newtag") ? " checked" : ""; zNewTag = PD("tagname",""); zNewBrFlag = P("newbr") ? " checked" : ""; zNewBranch = PD("brname",""); zCloseFlag = P("close") ? " checked" : ""; if( P("apply") ){ |
︙ | ︙ | |||
1424 1425 1426 1427 1428 1429 1430 | if( P("preview") ){ Blob suffix; int nTag = 0; @ <b>Preview:</b> @ <blockquote> @ <table border=0> if( zNewColor && zNewColor[0] ){ | | | 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 | if( P("preview") ){ Blob suffix; int nTag = 0; @ <b>Preview:</b> @ <blockquote> @ <table border=0> if( zNewColor && zNewColor[0] ){ @ <tr><td style="background-color: %h(zNewColor);"> }else{ @ <tr><td> } wiki_convert(&comment, 0, WIKI_INLINE); blob_zero(&suffix); blob_appendf(&suffix, "(user: %h", zNewUser); db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag" |
︙ | ︙ | |||
1449 1450 1451 1452 1453 1454 1455 | nTag++; } db_finalize(&q); blob_appendf(&suffix, ")"); @ %s(blob_str(&suffix)) @ </td></tr></table> @ </blockquote> | | | | | | < < | < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 | nTag++; } db_finalize(&q); blob_appendf(&suffix, ")"); @ %s(blob_str(&suffix)) @ </td></tr></table> @ </blockquote> @ <hr /> blob_reset(&suffix); } @ <p>Make changes to attributes of check-in @ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p> @ <form action="%s(g.zBaseURL)/ci_edit" method="post"><div> login_insert_csrf_secret(); @ <input type="hidden" name="r" value="%S(zUuid)" /> @ <table border="0" cellspacing="10"> @ <tr><td align="right" valign="top"><b>User:</b></td> @ <td valign="top"> @ <input type="text" name="u" size="20" value="%h(zNewUser)" /> @ </td></tr> @ <tr><td align="right" valign="top"><b>Comment:</b></td> @ <td valign="top"> @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea> @ </td></tr> @ <tr><td align="right" valign="top"><b>Check-in Time:</b></td> @ <td valign="top"> @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> @ </td></tr> @ <tr><td align="right" valign="top"><b>Background Color:</b></td> @ <td valign="top"> render_color_chooser(fPropagateColor, zNewColor, "pclr", "clr", "clrcust"); @ </td></tr> @ <tr><td align="right" valign="top"><b>Tags:</b></td> @ <td valign="top"> @ <input type="checkbox" name="newtag"%s(zNewTagFlag) /> @ Add the following new tag name to this check-in: @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)" /> db_prepare(&q, "SELECT tag.tagid, tagname FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)" " ELSE tagname END", rid ); while( db_step(&q)==SQLITE_ROW ){ int tagid = db_column_int(&q, 0); const char *zTagName = db_column_text(&q, 1); char zLabel[30]; sprintf(zLabel, "c%d", tagid); if( P(zLabel) ){ @ <br /><input type="checkbox" name="c%d(tagid)" checked="checked" /> }else{ @ <br /><input type="checkbox" name="c%d(tagid)" /> } if( strncmp(zTagName, "sym-", 4)==0 ){ @ Cancel tag <b>%h(&zTagName[4])</b> }else{ @ Cancel special tag <b>%h(zTagName)</b> } } db_finalize(&q); @ </td></tr> @ <tr><td align="right" valign="top"><b>Branching:</b></td> @ <td valign="top"> @ <input type="checkbox" name="newbr"%s(zNewBrFlag) /> @ Make this check-in the start of a new branch named: @ <input type="text" style="width:15;" name="brname" value="%h(zNewBranch)" /> @ </td></tr> if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid) ){ @ <tr><td align="right" valign="top"><b>Leaf Closure:</b></td> @ <td valign="top"> @ <input type="checkbox" name="close"%s(zCloseFlag) /> @ Mark this leaf as "closed" so that it no longer appears on the @ "leaves" page and is no longer labeled as a "<b>Leaf</b>". @ </td></tr> } @ <tr><td colspan="2"> @ <input type="submit" name="preview" value="Preview" /> @ <input type="submit" name="apply" value="Apply Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </td></tr> @ </table> @ </div></form> style_footer(); } |
Changes to src/login.c.
︙ | ︙ | |||
35 36 37 38 39 40 41 | ** not really the point. The anonymous login keeps search-engine ** crawlers and site download tools like wget from walking change ** logs and downloading diffs of very version of the archive that ** has ever existed, and things like that. */ #include "config.h" #include "login.h" | | > | > | | | | < | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | ** not really the point. The anonymous login keeps search-engine ** crawlers and site download tools like wget from walking change ** logs and downloading diffs of very version of the archive that ** has ever existed, and things like that. */ #include "config.h" #include "login.h" #if defined(_WIN32) # include <windows.h> /* for Sleep */ # if defined(__MINGW32__) || defined(_MSC_VER) # define sleep Sleep /* windows does not have sleep, but Sleep */ # endif #endif #include <time.h> /* ** Return the name of the login cookie */ static char *login_cookie_name(void){ static char *zCookieName = 0; if( zCookieName==0 ){ unsigned int h = 0; const char *z = g.zBaseURL; while( *z ){ h = (h<<3) ^ (h>>26) ^ *(z++); } zCookieName = mprintf("fossil_login_%08x", h); } return zCookieName; } /* ** Redirect to the page specified by the "g" query parameter. ** Or if there is no "g" query parameter, redirect to the homepage. |
︙ | ︙ | |||
149 150 151 152 153 154 155 | if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin); if( db_int(1, "SELECT 0 FROM user" " WHERE uid=%d AND (pw=%Q OR pw=%Q)", g.userUid, zPasswd, zSha1Pw) ){ sleep(1); zErrMsg = | | | | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin); if( db_int(1, "SELECT 0 FROM user" " WHERE uid=%d AND (pw=%Q OR pw=%Q)", g.userUid, zPasswd, zSha1Pw) ){ sleep(1); zErrMsg = @ <p><span class="loginError"> @ You entered an incorrect old password while attempting to change @ your password. Your password is unchanged. @ </span></p> ; }else if( strcmp(zNew1,zNew2)!=0 ){ zErrMsg = @ <p><span class="loginError"> @ The two copies of your new passwords do not match. @ Your password is unchanged. @ </span></p> ; }else{ char *zNewPw = sha1_shared_secret(zNew1, g.zLogin); db_multi_exec( "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid ); redirect_to_g(); |
︙ | ︙ | |||
202 203 204 205 206 207 208 | " AND login NOT IN ('anonymous','nobody','developer','reader')" " AND (pw=%Q OR pw=%Q)", zUsername, zPasswd, zSha1Pw ); if( uid<=0 ){ sleep(1); zErrMsg = | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | " AND login NOT IN ('anonymous','nobody','developer','reader')" " AND (pw=%Q OR pw=%Q)", zUsername, zPasswd, zSha1Pw ); if( uid<=0 ){ sleep(1); zErrMsg = @ <p><span class="loginError"> @ You entered an unknown user or an incorrect password. @ </span></p> ; }else{ char *zCookie; const char *zCookieName = login_cookie_name(); const char *zExpire = db_get("cookie-expire","8766"); int expires = atoi(zExpire)*3600; const char *zIpAddr = PD("REMOTE_ADDR","nil"); zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid); cgi_set_cookie(zCookieName, zCookie, 0, expires); db_multi_exec( "UPDATE user SET cookie=%Q, ipaddr=%Q, " " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", zCookie, zIpAddr, expires, uid ); redirect_to_g(); } } style_header("Login/Logout"); @ %s(zErrMsg) @ <form action="login" method="post"> if( P("g") ){ @ <input type="hidden" name="g" value="%h(P("g"))" /> } @ <table class="login_out"> @ <tr> @ <td class="login_out_label">User ID:</td> if( anonFlag ){ @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td> }else{ @ <td><input type="text" id="u" name="u" value="" size="30" /></td> } @ </tr> @ <tr> @ <td class="login_out_label">Password:</td> @ <td><input type="password" id="p" name="p" value="" size="30" /></td> @ </tr> if( g.zLogin==0 ){ zAnonPw = db_text(0, "SELECT pw FROM user" " WHERE login='anonymous'" " AND cap!=''"); } @ <tr> @ <td></td> @ <td><input type="submit" name="in" value="Login" /></td> @ </tr> @ </table> @ <script type="text/JavaScript">document.getElementById('u').focus()</script> if( g.zLogin==0 ){ @ <p>Enter }else{ @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p> @ <p>To change your login to a different user, enter } @ your user-id and password at the left and press the @ "Login" button. Your user name will be stored in a browser cookie. @ You must configure your web browser to accept cookies in order for @ the login to take.</p> if( zAnonPw ){ unsigned int uSeed = captcha_seed(); char const *zDecoded = captcha_decode(uSeed); int bAutoCaptcha = db_get_boolean("auto-captcha", 1); char *zCaptcha = captcha_render(zDecoded); @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> @ Visitors may enter <b>anonymous</b> as the user-ID with @ the 8-character hexadecimal password shown below:</p> @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %s(zCaptcha) @ </pre></td></tr></table> if( bAutoCaptcha ) { @ <input type="button" value="Fill out captcha" @ onclick="document.getElementById('u').value='anonymous'; @ document.getElementById('p').value='%s(zDecoded)';" /> } @ </div> free(zCaptcha); } if( g.zLogin ){ @ <hr /> @ <p>To log off the system (and delete your login cookie) @ press the following button:<br /> @ <input type="submit" name="out" value="Logout" /></p> } @ </form> if( g.okPassword ){ @ <hr /> @ <p>To change your password, enter your old password and your @ new password twice below then press the "Change Password" @ button.</p> @ <form action="login" method="post"> @ <table> @ <tr><td class="login_out_label">Old Password:</td> @ <td><input type="password" name="p" size="30" /></td></tr> @ <tr><td class="login_out_label">New Password:</td> @ <td><input type="password" name="n1" size="30" /></td></tr> @ <tr><td class="login_out_label">Repeat New Password:</td> @ <td><input type="password" name="n2" size="30" /></td></tr> @ <tr><td></td> @ <td><input type="submit" value="Change Password" /></td></tr> @ </table> @ </form> } style_footer(); } |
︙ | ︙ | |||
349 350 351 352 353 354 355 | g.noPswd = 1; strcpy(g.zCsrfToken, "localhost"); } /* Check the login cookie to see if it matches a known valid user. */ if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ | | | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | g.noPswd = 1; strcpy(g.zCsrfToken, "localhost"); } /* Check the login cookie to see if it matches a known valid user. */ if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ if( fossil_isdigit(zCookie[0]) ){ /* Cookies of the form "uid/randomness". There must be a ** corresponding entry in the user table. */ uid = db_int(0, "SELECT uid FROM user" " WHERE uid=%d" " AND cookie=%Q" " AND ipaddr=%Q" |
︙ | ︙ | |||
592 593 594 595 596 597 598 | */ void login_anonymous_available(void){ if( !g.okHistory && db_exists("SELECT 1 FROM user" " WHERE login='anonymous'" " AND cap LIKE '%%h%%'") ){ const char *zUrl = PD("REQUEST_URI", "index"); | | | | | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | */ void login_anonymous_available(void){ if( !g.okHistory && db_exists("SELECT 1 FROM user" " WHERE login='anonymous'" " AND cap LIKE '%%h%%'") ){ const char *zUrl = PD("REQUEST_URI", "index"); @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br /> @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a> @ to enable hyperlinks.</p> } } /* ** While rendering a form, call this routine to add the Anti-CSRF token ** as a hidden element of the form. */ void login_insert_csrf_secret(void){ @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" /> } /* ** Before using the results of a form, first call this routine to verify ** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token ** is missing or is incorrect, that indicates a cross-site scripting attach ** so emits an error message and abort. |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Th_Interp *interp; /* The TH1 interpreter */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ int markPrivate; /* All new artifacts are private if true */ int urlIsFile; /* True if a "file:" url */ int urlIsHttps; /* True if a "https:" url */ char *urlName; /* Hostname for http: or filename for file: */ char *urlHostname; /* The HOST: parameter on http headers */ char *urlProtocol; /* "http" or "https" */ int urlPort; /* TCP port number for http: or https: */ int urlDfltPort; /* The default port for the given protocol */ char *urlPath; /* Pathname for http: */ char *urlUser; /* User id for http: */ char *urlPasswd; /* Password for http: */ char *urlCanonical; /* Canonical representation of the URL */ char *urlProxyAuth; /* Proxy-Authorizer: string */ int dontKeepUrl; /* Do not persist the URL */ const char *zLogin; /* Login name. "" if not logged in. */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ /* Information used to populate the RCVFROM table */ | > > > | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | Th_Interp *interp; /* The TH1 interpreter */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ int markPrivate; /* All new artifacts are private if true */ int clockSkewSeen; /* True if clocks on client and server out of sync */ int urlIsFile; /* True if a "file:" url */ int urlIsHttps; /* True if a "https:" url */ int urlIsSsh; /* True if an "ssh:" url */ char *urlName; /* Hostname for http: or filename for file: */ char *urlHostname; /* The HOST: parameter on http headers */ char *urlProtocol; /* "http" or "https" */ int urlPort; /* TCP port number for http: or https: */ int urlDfltPort; /* The default port for the given protocol */ char *urlPath; /* Pathname for http: */ char *urlUser; /* User id for http: */ char *urlPasswd; /* Password for http: */ char *urlCanonical; /* Canonical representation of the URL */ char *urlProxyAuth; /* Proxy-Authorizer: string */ char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ int dontKeepUrl; /* Do not persist the URL */ const char *zLogin; /* Login name. "" if not logged in. */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ /* Information used to populate the RCVFROM table */ |
︙ | ︙ | |||
218 219 220 221 222 223 224 225 226 227 228 229 | /* ** This procedure runs first. */ int main(int argc, char **argv){ const char *zCmdName = "unknown"; int idx; int rc; sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); g.now = time(0); g.argc = argc; g.argv = argv; | > | > > | | | | | | | > > > > | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | /* ** This procedure runs first. */ int main(int argc, char **argv){ const char *zCmdName = "unknown"; int idx; int rc; int mightBeCgi; sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); g.now = time(0); g.argc = argc; g.argv = argv; mightBeCgi = getenv("GATEWAY_INTERFACE")!=0; if( argc<2 ){ if( mightBeCgi ){ zCmdName = "cgi"; }else{ fprintf(stderr, "Usage: %s COMMAND ...\n" "\"%s help\" for a list of available commands\n" "\"%s help COMMAND\" for specific details\n", argv[0], argv[0], argv[0]); fossil_exit(1); } }else{ g.fQuiet = find_option("quiet", 0, 0)!=0; g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; g.fHttpTrace = find_option("httptrace", 0, 0)!=0; g.zLogin = find_option("user", "U", 1); zCmdName = argv[1]; } rc = name_search(zCmdName, aCommand, count(aCommand), &idx); if( rc==1 && mightBeCgi ){ rc = name_search("cgi", aCommand, count(aCommand), &idx); } if( rc==1 ){ fprintf(stderr,"%s: unknown command: %s\n" "%s: use \"help\" for more information\n", argv[0], zCmdName, argv[0]); fossil_exit(1); }else if( rc==2 ){ fprintf(stderr,"%s: ambiguous command prefix: %s\n" |
︙ | ︙ | |||
286 287 288 289 290 291 292 | static int once = 1; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput && once ){ once = 0; | | | | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | static int once = 1; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput && once ){ once = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } db_force_rollback(); fossil_exit(1); } void fossil_fatal(const char *zFormat, ...){ char *z; va_list ap; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } db_force_rollback(); fossil_exit(1); } |
︙ | ︙ | |||
331 332 333 334 335 336 337 | if( mainInFatalError ) return; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ g.cgiOutput = 0; | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | if( mainInFatalError ) return; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } db_force_rollback(); fossil_exit(1); } /* Print a warning message */ void fossil_warning(const char *zFormat, ...){ char *z; va_list ap; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ cgi_printf("<p class=\"generalError\">%h</p>", z); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } } /* ** Malloc and free routines that cannot fail */ void *fossil_malloc(size_t n){ void *p = malloc(n); if( p==0 ) fossil_panic("out of memory"); return p; } void fossil_free(void *p){ free(p); } void *fossil_realloc(void *p, size_t n){ p = realloc(p, n); if( p==0 ) fossil_panic("out of memory"); return p; } /* ** This function implements a cross-platform "system()" interface. */ int fossil_system(const char *zOrigCmd){ int rc; #if defined(_WIN32) /* On windows, we have to put double-quotes around the entire command. ** Who knows why - this is just the way windows works. */ char *zNewCmd = mprintf("\"%s\"", zOrigCmd); rc = system(zNewCmd); free(zNewCmd); #else /* On unix, evaluate the command directly. */ rc = system(zOrigCmd); #endif return rc; } /* ** Return a name for an SQLite error code */ static const char *sqlite_error_code_name(int iCode){ static char zCode[30]; switch( iCode & 0xff ){ |
︙ | ︙ | |||
586 587 588 589 590 591 592 593 594 595 596 597 598 599 | }else{ putchar(*z); z++; } } putchar('\n'); } /* ** Set the g.zBaseURL value to the full URL for the toplevel of ** the fossil tree. Set g.zTop to g.zBaseURL without the ** leading "http://" and the host and port. */ void set_base_url(void){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 | }else{ putchar(*z); z++; } } putchar('\n'); } /* ** WEBPAGE: help ** URL: /help?cmd=CMD */ void help_page(void){ const char * zCmd = P("cmd"); style_header("Command line help %s%s",zCmd?" - ":"",zCmd?zCmd:""); if( zCmd ){ int rc, idx; char *z, *s, *d; @ <h1>%s(zCmd)</h1> rc = name_search(zCmd, aCommand, count(aCommand), &idx); if( rc==1 ){ @ unknown command: %s(zCmd) }else if( rc==2 ){ @ ambiguous command prefix: %s(zCmd) }else{ z = (char*)aCmdHelp[idx]; if( z==0 ){ @ no help available for the %s(aCommand[idx].zName) command }else{ z=s=d=mprintf("%s",z); while( *s ){ if( *s=='%' && strncmp(s, "%fossil", 7)==0 ){ s++; }else{ *d++ = *s++; } } *d = 0; @ <pre>%s(z)</pre> free(z); } } @ <hr/><a href="help">available commands</a> in fossil @ version %s(MANIFEST_VERSION" "MANIFEST_DATE) UTC }else{ int i; @ <h1>Available commands</h1> for(i=0; i<count(aCommand); i++){ if( strncmp(aCommand[i].zName,"test",4)==0 ) continue; @ <kbd><a href="help?cmd=%s(aCommand[i].zName)"> @ %s(aCommand[i].zName)</a></kbd> } @ <hr/>fossil version %s(MANIFEST_VERSION" "MANIFEST_DATE) UTC } style_footer(); } /* ** Set the g.zBaseURL value to the full URL for the toplevel of ** the fossil tree. Set g.zTop to g.zBaseURL without the ** leading "http://" and the host and port. */ void set_base_url(void){ |
︙ | ︙ | |||
628 629 630 631 632 633 634 | ** zRepo might be a directory itself. In that case chroot into ** the directory zRepo. ** ** Assume the user-id and group-id of the repository, or if zRepo ** is a directory, of that directory. */ static char *enter_chroot_jail(char *zRepo){ | | | 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 | ** zRepo might be a directory itself. In that case chroot into ** the directory zRepo. ** ** Assume the user-id and group-id of the repository, or if zRepo ** is a directory, of that directory. */ static char *enter_chroot_jail(char *zRepo){ #if !defined(_WIN32) if( getuid()==0 ){ int i; struct stat sStat; Blob dir; char *zDir; file_canonical_name(zRepo, &dir); |
︙ | ︙ | |||
700 701 702 703 704 705 706 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); /* To avoid mischief, make sure the repository basename contains no ** characters other than alphanumerics, "-", and "_". */ for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ | | | 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); /* To avoid mischief, make sure the repository basename contains no ** characters other than alphanumerics, "-", and "_". */ for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ if( !fossil_isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; } if( zRepo[0]=='/' && zRepo[1]=='/' ) zRepo++; if( file_size(zRepo)<1024 ){ if( zNotFound ){ cgi_redirect(zNotFound); }else{ |
︙ | ︙ | |||
806 807 808 809 810 811 812 | if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){ zFile = g.argv[2]; }else{ zFile = g.argv[1]; } g.httpOut = stdout; g.httpIn = stdin; | | | 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 | if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){ zFile = g.argv[2]; }else{ zFile = g.argv[1]; } g.httpOut = stdout; g.httpIn = stdin; #if defined(_WIN32) /* Set binary mode on windows to avoid undesired translations ** between \n and \r\n. */ setmode(_fileno(g.httpOut), _O_BINARY); setmode(_fileno(g.httpIn), _O_BINARY); #endif #ifdef __EMX__ /* Similar hack for OS/2 */ |
︙ | ︙ | |||
912 913 914 915 916 917 918 919 | ** not select a valid repository and the --notfound option is available, ** then the server redirects (HTTP code 302) to the URL of --notfound. */ void cmd_http(void){ const char *zIpAddr; const char *zNotFound; zNotFound = find_option("notfound", 0, 1); if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ | > | < > > | > > > > > > | | 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 | ** not select a valid repository and the --notfound option is available, ** then the server redirects (HTTP code 302) to the URL of --notfound. */ void cmd_http(void){ const char *zIpAddr; const char *zNotFound; zNotFound = find_option("notfound", 0, 1); g.cgiOutput = 1; if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ fossil_fatal("no repository specified"); } g.fullHttpReply = 1; if( g.argc==6 ){ g.httpIn = fopen(g.argv[3], "rb"); g.httpOut = fopen(g.argv[4], "wb"); zIpAddr = g.argv[5]; }else{ g.httpIn = stdin; g.httpOut = stdout; zIpAddr = 0; } find_server_repository(0); g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); cgi_handle_http_request(zIpAddr); process_one_web_page(zNotFound); } /* ** Note that the following command is used by ssh:// processing. ** ** COMMAND: test-http ** Works like the http command but gives setup permission to all users. */ void cmd_test_http(void){ login_set_capabilities("s"); g.httpIn = stdin; g.httpOut = stdout; find_server_repository(0); g.cgiOutput = 1; g.fullHttpReply = 1; cgi_handle_http_request(0); process_one_web_page(0); } #if !defined(_WIN32) #if !defined(__DARWIN__) && !defined(__APPLE__) /* ** Search for an executable on the PATH environment variable. ** Return true (1) if found and false (0) if not found. */ static int binaryOnPath(const char *zBinary){ const char *zPath = getenv("PATH"); |
︙ | ︙ | |||
980 981 982 983 984 985 986 | ** Open a socket and begin listening and responding to HTTP requests on ** TCP port 8080, or on any other TCP port defined by the -P or ** --port option. The optional argument is the name of the repository. ** The repository argument may be omitted if the working directory is ** within an open checkout. ** ** The "ui" command automatically starts a web browser after initializing | | > > | > | | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 | ** Open a socket and begin listening and responding to HTTP requests on ** TCP port 8080, or on any other TCP port defined by the -P or ** --port option. The optional argument is the name of the repository. ** The repository argument may be omitted if the working directory is ** within an open checkout. ** ** The "ui" command automatically starts a web browser after initializing ** the web server. The "ui" command also binds to 127.0.0.1 and so will ** only process HTTP traffic from the local machine. ** ** In the "server" command, the REPOSITORY can be a directory (aka folder) ** that contains one or more respositories with names ending in ".fossil". ** In that case, the first element of the URL is used to select among the ** various repositories. */ void cmd_webserver(void){ int iPort, mxPort; /* Range of TCP ports allowed */ const char *zPort; /* Value of the --port option */ char *zBrowser; /* Name of web browser program */ char *zBrowserCmd = 0; /* Command to launch the web browser */ int isUiCmd; /* True if command is "ui", not "server' */ const char *zNotFound; /* The --notfound option or NULL */ int flags = 0; /* Server flags */ #if defined(_WIN32) const char *zStopperFile; /* Name of file used to terminate server */ zStopperFile = find_option("stopper", 0, 1); #endif g.thTrace = find_option("th-trace", 0, 0)!=0; if( g.thTrace ){ blob_zero(&g.thLog); } zPort = find_option("port", "P", 1); zNotFound = find_option("notfound", 0, 1); if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); isUiCmd = g.argv[1][0]=='u'; if( isUiCmd ) flags |= HTTP_SERVER_LOCALHOST; find_server_repository(isUiCmd); if( zPort ){ iPort = mxPort = atoi(zPort); }else{ iPort = db_get_int("http-port", 8080); mxPort = iPort+100; } #if !defined(_WIN32) /* Unix implementation */ if( isUiCmd ){ #if !defined(__DARWIN__) && !defined(__APPLE__) zBrowser = db_get("web-browser", 0); if( zBrowser==0 ){ static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; int i; |
︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 | } #else zBrowser = db_get("web-browser", "open"); #endif zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); } db_close(); | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 | } #else zBrowser = db_get("web-browser", "open"); #endif zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); } db_close(); if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){ fossil_fatal("unable to listen on TCP socket %d", iPort); } g.httpIn = stdin; g.httpOut = stdout; if( g.fHttpTrace || g.fSqlTrace ){ fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); } g.cgiOutput = 1; find_server_repository(isUiCmd); g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); cgi_handle_http_request(0); process_one_web_page(zNotFound); #else /* Win32 implementation */ if( isUiCmd ){ zBrowser = db_get("web-browser", "start"); zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); } db_close(); win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags); #endif } /* ** COMMAND: sqlite3 ** ** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS? ** ** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS. ** If DATABASE is omitted, then the repository that serves the working ** directory is opened. ** ** WARNING: Careless use of this command can corrupt a Fossil repository ** in ways that are unrecoverable. Be sure you know what you are doing before ** running any SQL commands that modifies the repository database. */ void sqlite3_cmd(void){ extern int sqlite3_shell(int, char**); sqlite3_shell(g.argc-1, g.argv+1); } /* ** This routine is called by the patched sqlite3 command-line shell in order ** to load the name and database connection for the open Fossil database. */ void fossil_open(sqlite3 **pDb, const char **pzRepoName){ db_must_be_within_tree(); *pDb = 0; *pzRepoName = g.zRepositoryName; } |
Changes to src/main.mk.
︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/info.c \ $(SRCDIR)/login.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/name.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ | > > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/info.c \ $(SRCDIR)/login.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/name.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ |
︙ | ︙ | |||
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | delta_.c \ deltacmd_.c \ descendants_.c \ diff_.c \ diffcmd_.c \ doc_.c \ encode_.c \ file_.c \ finfo_.c \ graph_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ info_.c \ login_.c \ main_.c \ manifest_.c \ md5_.c \ merge_.c \ merge3_.c \ name_.c \ pivot_.c \ pqueue_.c \ printf_.c \ rebuild_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ | > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | delta_.c \ deltacmd_.c \ descendants_.c \ diff_.c \ diffcmd_.c \ doc_.c \ encode_.c \ event_.c \ file_.c \ finfo_.c \ graph_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ info_.c \ login_.c \ main_.c \ manifest_.c \ md5_.c \ merge_.c \ merge3_.c \ name_.c \ pivot_.c \ popen_.c \ pqueue_.c \ printf_.c \ rebuild_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ |
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/info.o \ $(OBJDIR)/login.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/name.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ | > > | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/info.o \ $(OBJDIR)/login.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/name.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ |
︙ | ︙ | |||
256 257 258 259 260 261 262 | $(TCLSH) test/tester.tcl $(APPNAME) VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' $(SRCDIR)/../manifest.uuid >VERSION.h awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' $(SRCDIR)/../manifest.uuid >>VERSION.h awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}' $(SRCDIR)/../manifest >>VERSION.h | > > | | | | | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | $(TCLSH) test/tester.tcl $(APPNAME) VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' $(SRCDIR)/../manifest.uuid >VERSION.h awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' $(SRCDIR)/../manifest.uuid >>VERSION.h awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}' $(SRCDIR)/../manifest >>VERSION.h EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(APPNAME): headers $(OBJ) $(EXTRAOBJ) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build # an executable named "manifest" out of the file named "manifest.c" # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h event.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h page_index.h: $(TRANS_SRC) mkindex ./mkindex $(TRANS_SRC) >$@ headers: page_index.h makeheaders VERSION.h ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h touch headers headers: Makefile Makefile: add_.c: $(SRCDIR)/add.c translate ./translate $(SRCDIR)/add.c >add_.c $(OBJDIR)/add.o: add_.c add.h $(SRCDIR)/config.h |
︙ | ︙ | |||
445 446 447 448 449 450 451 452 453 454 455 456 457 458 | encode_.c: $(SRCDIR)/encode.c translate ./translate $(SRCDIR)/encode.c >encode_.c $(OBJDIR)/encode.o: encode_.c encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c encode_.c encode.h: headers file_.c: $(SRCDIR)/file.c translate ./translate $(SRCDIR)/file.c >file_.c $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c file_.c file.h: headers | > > > > > > > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | encode_.c: $(SRCDIR)/encode.c translate ./translate $(SRCDIR)/encode.c >encode_.c $(OBJDIR)/encode.o: encode_.c encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c encode_.c encode.h: headers event_.c: $(SRCDIR)/event.c translate ./translate $(SRCDIR)/event.c >event_.c $(OBJDIR)/event.o: event_.c event.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/event.o -c event_.c event.h: headers file_.c: $(SRCDIR)/file.c translate ./translate $(SRCDIR)/file.c >file_.c $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c file_.c file.h: headers |
︙ | ︙ | |||
557 558 559 560 561 562 563 564 565 566 567 568 569 570 | pivot_.c: $(SRCDIR)/pivot.c translate ./translate $(SRCDIR)/pivot.c >pivot_.c $(OBJDIR)/pivot.o: pivot_.c pivot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c pivot.h: headers pqueue_.c: $(SRCDIR)/pqueue.c translate ./translate $(SRCDIR)/pqueue.c >pqueue_.c $(OBJDIR)/pqueue.o: pqueue_.c pqueue.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c pqueue.h: headers | > > > > > > > | 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | pivot_.c: $(SRCDIR)/pivot.c translate ./translate $(SRCDIR)/pivot.c >pivot_.c $(OBJDIR)/pivot.o: pivot_.c pivot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c pivot.h: headers popen_.c: $(SRCDIR)/popen.c translate ./translate $(SRCDIR)/popen.c >popen_.c $(OBJDIR)/popen.o: popen_.c popen.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/popen.o -c popen_.c popen.h: headers pqueue_.c: $(SRCDIR)/pqueue.c translate ./translate $(SRCDIR)/pqueue.c >pqueue_.c $(OBJDIR)/pqueue.o: pqueue_.c pqueue.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c pqueue.h: headers |
︙ | ︙ | |||
770 771 772 773 774 775 776 777 778 779 780 781 782 | $(OBJDIR)/zip.o: zip_.c zip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/zip.o -c zip_.c zip.h: headers $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o: $(SRCDIR)/th.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o | > > > | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 | $(OBJDIR)/zip.o: zip_.c zip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/zip.o -c zip_.c zip.h: headers $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o $(OBJDIR)/th.o: $(SRCDIR)/th.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o |
Changes to src/makeheaders.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> | > | > > | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) # ifndef WIN32 # define WIN32 # endif # include <string.h> #else # include <unistd.h> #endif /* ** Macros for debugging. */ #ifdef DEBUG static int debugMask = 0; |
︙ | ︙ | |||
2139 2140 2141 2142 2143 2144 2145 | ** and EXPORT_INTERFACE and LOCAL_INTERFACE */ zArg = &zCmd[2]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } | | | | | 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 | ** and EXPORT_INTERFACE and LOCAL_INTERFACE */ zArg = &zCmd[2]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){ PushIfMacro(0,0,0,pToken->nLine,PS_Interface); }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){ PushIfMacro(0,0,0,pToken->nLine,PS_Export); }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){ PushIfMacro(0,0,0,pToken->nLine,PS_Local); }else{ PushIfMacro(0,zArg,nArg,pToken->nLine,0); } }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ /* ** Push an #ifdef. */ zArg = &zCmd[5]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); PushIfMacro("defined",zArg,nArg,pToken->nLine,0); }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){ /* ** Push an #ifndef. */ zArg = &zCmd[6]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ /* ** Invert the #if on the top of the stack */ if( ifStack==0 ){ fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename, |
︙ | ︙ | |||
2795 2796 2797 2798 2799 2800 2801 | nErr++; } }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){ if( report ) fprintf(report,"error!\n"); fprintf(stderr, "%s: Can't overwrite this file because it wasn't previously\n" "%*s generated by 'makeheaders'.\n", | | | 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 | nErr++; } }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){ if( report ) fprintf(report,"error!\n"); fprintf(stderr, "%s: Can't overwrite this file because it wasn't previously\n" "%*s generated by 'makeheaders'.\n", pFile->zHdr, (int)strlen(pFile->zHdr), ""); nErr++; }else if( strcmp(zOldVersion,zNewVersion)!=0 ){ if( report ) fprintf(report,"updated\n"); if( WriteFile(pFile->zHdr,zNewVersion) ){ fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr); nErr++; } |
︙ | ︙ | |||
2950 2951 2952 2953 2954 2955 2956 | } } if( nLabel==0 ) continue; zLabel[nLabel] = 0; InsertExtraDecl(pDecl); zDecl = pDecl->zDecl; if( zDecl==0 ) zDecl = pDecl->zFwd; | | | | | | 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 | } } if( nLabel==0 ) continue; zLabel[nLabel] = 0; InsertExtraDecl(pDecl); zDecl = pDecl->zDecl; if( zDecl==0 ) zDecl = pDecl->zFwd; printf("%s %s %s %p %d %d %d %d %d\n", pDecl->zName, zLabel, pDecl->zFile, pDecl->pComment, pDecl->pComment ? pDecl->pComment->nText+1 : 0, pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0, zDecl ? (int)strlen(zDecl) : 0, pDecl->pComment ? pDecl->pComment->nLine : 0, pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0 ); if( pDecl->pComment ){ printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText); } if( pDecl->zIf ){ |
︙ | ︙ |
Changes to src/makemake.tcl.
︙ | ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | delta deltacmd descendants diff diffcmd doc encode file finfo graph http http_socket http_transport info login main manifest md5 merge merge3 name pivot pqueue printf rebuild report rss schema search | > > | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | delta deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search |
︙ | ︙ | |||
78 79 80 81 82 83 84 | zip http_ssl } # Name of the final application # set name fossil | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | zip http_ssl } # Name of the final application # set name fossil if { 0 == $argc } { puts {# DO NOT EDIT # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl >main.mk" # to regenerate this file. # # This file is included by linux-gcc.mk or linux-mingw.mk or possible |
︙ | ︙ | |||
144 145 146 147 148 149 150 | $(SRCDIR)/../manifest.uuid >VERSION.h awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' \ $(SRCDIR)/../manifest.uuid >>VERSION.h awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\ substr($$2,1,10),substr($$2,12)}' \ $(SRCDIR)/../manifest >>VERSION.h | > > > > > > | | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | $(SRCDIR)/../manifest.uuid >VERSION.h awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' \ $(SRCDIR)/../manifest.uuid >>VERSION.h awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\ substr($$2,1,10),substr($$2,12)}' \ $(SRCDIR)/../manifest >>VERSION.h EXTRAOBJ = \ $(OBJDIR)/sqlite3.o \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o $(APPNAME): headers $(OBJ) $(EXTRAOBJ) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build # an executable named "manifest" out of the file named "manifest.c" # $(SRCDIR)/../manifest: # noop |
︙ | ︙ | |||
197 198 199 200 201 202 203 204 205 206 207 208 | set opt {-DSQLITE_OMIT_LOAD_EXTENSION=1} append opt " -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4" #append opt " -DSQLITE_ENABLE_FTS3=1" append opt " -Dlocaltime=fossil_localtime" append opt " -DSQLITE_ENABLE_LOCKING_STYLE=0" puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | set opt {-DSQLITE_OMIT_LOAD_EXTENSION=1} append opt " -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4" #append opt " -DSQLITE_ENABLE_FTS3=1" append opt " -Dlocaltime=fossil_localtime" append opt " -DSQLITE_ENABLE_LOCKING_STYLE=0" puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" puts "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c" set opt {-Dmain=sqlite3_shell} append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" puts "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n" puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" exit } if { "dmc" == [lindex $argv 0] } { puts {# DO NOT EDIT # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run # "tclsh src/makemake.tcl dmc > win/Makefile.dmc" # to regenerate this file. B = .. SRCDIR = $B\src OBJDIR = . O = .obj E = .exe # Maybe DMDIR, SSL or INCL needs adjustment DMDIR = c:\DM INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = DMCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp I18N = -DFOSSIL_I18N=0 CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 } puts -nonewline "SRC = " foreach s [lsort $src] { puts -nonewline "${s}_.c " } puts "\n" puts -nonewline "OBJ = " foreach s [lsort $src] { puts -nonewline "\$(OBJDIR)\\$s\$O " } puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O " puts { APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) $(DMDIR)\bin\link @link $(OBJDIR)\link: $B\win\Makefile.dmc} puts -nonewline "\t+echo " foreach s [lsort $src] { puts -nonewline "$s " } puts "sqlite3 th th_lang > \$@" puts "\t+echo fossil >> \$@" puts "\t+echo fossil >> \$@" puts "\t+echo \$(LIBS) >> \$@\n\n" puts { translate$E: $(SRCDIR)\translate.c $(BCC) -o$@ $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** version$E: $B\win\version.c $(BCC) -o$@ $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) -o$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) -o$@ -c $** VERSION.h : version$E $B\manifest.uuid $B\manifest +$** > $@ page_index.h: mkindex$E $(SRC) +$** > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E } foreach s [lsort $src] { puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h" puts "\t\$(TCC) -o\$@ -c ${s}_.c\n" puts "${s}_.c : \$(SRCDIR)\\$s.c" puts "\t+translate\$E \$** > \$@\n" } puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E " foreach s [lsort $src] { puts -nonewline "${s}_.c:$s.h " } puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h" puts "\t@copy /Y nul: headers" exit } if { "msc" == [lindex $argv 0] } { puts {# DO NOT EDIT # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run # "tclsh src/makemake.tcl msc > win/Makefile.msc" # to regenerate this file. B = .. SRCDIR = $B\src OBJDIR = . O = .obj E = .exe # Maybe MSCDIR, SSL, ZLIB, or INCL needs adjustment MSCDIR = c:\msc # Uncomment below for SSL support SSL = SSLLIB = #SSL = -DFOSSIL_ENABLE_SSL=1 #SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib # zlib options # When using precompiled from http://zlib.net/zlib125-dll.zip #ZINCDIR = C:\zlib125-dll\include #ZLIBDIR = C:\zlib125-dll\lib #ZLIB = zdll.lib ZINCDIR = $(MSCDIR)\extra\include ZLIBDIR = $(MSCDIR)\extra\lib ZLIB = zlib.lib INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR) MSCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp I18N = -DFOSSIL_I18N=0 CFLAGS = -nologo -MT -O2 BCC = $(CC) $(CFLAGS) TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(ZLIB) ws2_32.lib $(SSLLIB) LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR) } puts -nonewline "SRC = " foreach s [lsort $src] { puts -nonewline "${s}_.c " } puts "\n" puts -nonewline "OBJ = " foreach s [lsort $src] { puts -nonewline "\$(OBJDIR)\\$s\$O " } puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O " puts { APPNAME = $(OBJDIR)\fossil$(E) all: $(OBJDIR) $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\linkopts cd $(OBJDIR) link -LINK -OUT:$@ $(LIBDIR) @linkopts $(OBJDIR)\linkopts: $B\win\Makefile.msc} puts -nonewline "\techo " foreach s [lsort $src] { puts -nonewline "$s " } puts "sqlite3 th th_lang > \$@" puts "\techo \$(LIBS) >> \$@\n\n" puts { $(OBJDIR): @-mkdir $@ translate$E: $(SRCDIR)\translate.c $(BCC) $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) $** version$E: $B\win\version.c $(BCC) $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) /Fo$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) /Fo$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : version$E $B\manifest.uuid $B\manifest $** > $@ page_index.h: mkindex$E $(SRC) $** > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map -del headers linkopts realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E } foreach s [lsort $src] { puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h" puts "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" puts "${s}_.c : \$(SRCDIR)\\$s.c" puts "\ttranslate\$E \$** > \$@\n" } puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E " foreach s [lsort $src] { puts -nonewline "${s}_.c:$s.h " } puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h" puts "\t@copy /Y nul: headers" } |
Added src/makeskins.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | /* ** Copyright (c) 2010 Michael T. Richter, assigned to the Fossil SCM project. ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** ttmrichter@gmail.com ** ******************************************************************************* ** ** This utility is a preprocessor that takes raw CSS and HTML files, along with ** plain text descriptions, and generates the skins.c file that is used to embed ** the distributed default skins into Fossil. ** ** The intent of the utility is to make adding new skins to a Fossil build ** easier without involving error-prone C programming. ** ** This utility must be run BEFORE the translate program is executed on the C ** source files. (This is in retrospect obvious since it generates one of the ** said C files.) */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> int main(int argc, char **argv){ if( argc==3 ){ /* ** 1. Validate the arguments: <skins> and <src> directory. ** 2. Open the <src>/skins.c file. ** 3. Write the initial boilerplate. ** 4. Walk the directory structure of <skins>. ** 5. For each one: ** a) Store the title and author information. (info.txt) ** b) Write out the description as a comment. (info.txt) ** c) Write out the CSS information. (style.css) ** d) Write out the header information. (header.html) ** e) Write out the footer information. (footer.html) ** 6. Write out the built-in skins table. ** 7. Write the trailing boilerplate. */ }else{ /* error -- need a pair of directories */ } return 0; } |
Changes to src/manifest.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #include "manifest.h" #include <assert.h> #if INTERFACE /* ** Types of control files */ #define CFTYPE_MANIFEST 1 #define CFTYPE_CLUSTER 2 #define CFTYPE_CONTROL 3 #define CFTYPE_WIKI 4 #define CFTYPE_TICKET 5 #define CFTYPE_ATTACHMENT 6 /* ** A parsed manifest or cluster. */ struct Manifest { Blob content; /* The original content blob */ int type; /* Type of artifact. One of CFTYPE_xxxxx */ char *zComment; /* Decoded comment. The C card. */ double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ char *zTicketUuid; /* UUID for a ticket. K card. */ char *zAttachName; /* Filename of an attachment. A card. */ char *zAttachSrc; /* UUID of document being attached. A card. */ char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ int nFile; /* Number of F cards */ int nFileAlloc; /* Slots allocated in aFile[] */ | > > > > > > > > > > > > > > > > > > < | < < < < | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | #include "manifest.h" #include <assert.h> #if INTERFACE /* ** Types of control files */ #define CFTYPE_ANY 0 #define CFTYPE_MANIFEST 1 #define CFTYPE_CLUSTER 2 #define CFTYPE_CONTROL 3 #define CFTYPE_WIKI 4 #define CFTYPE_TICKET 5 #define CFTYPE_ATTACHMENT 6 #define CFTYPE_EVENT 7 /* ** A single F-card within a manifest */ struct ManifestFile { char *zName; /* Name of a file */ char *zUuid; /* UUID of the file */ char *zPerm; /* File permissions */ char *zPrior; /* Prior name if the name was changed */ }; /* ** A parsed manifest or cluster. */ struct Manifest { Blob content; /* The original content blob */ int type; /* Type of artifact. One of CFTYPE_xxxxx */ int rid; /* The blob-id for this manifest */ char *zBaseline; /* Baseline manifest. The B card. */ Manifest *pBaseline; /* The actual baseline manifest */ char *zComment; /* Decoded comment. The C card. */ double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ double rEventDate; /* Date of an event. E card. */ char *zEventId; /* UUID for an event. E card. */ char *zTicketUuid; /* UUID for a ticket. K card. */ char *zAttachName; /* Filename of an attachment. A card. */ char *zAttachSrc; /* UUID of document being attached. A card. */ char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ int nFile; /* Number of F cards */ int nFileAlloc; /* Slots allocated in aFile[] */ int iFile; /* Index of current file in iterator */ ManifestFile *aFile; /* One entry for each F-card */ int nParent; /* Number of parents. */ int nParentAlloc; /* Slots allocated in azParent[] */ char **azParent; /* UUIDs of parents. One for each P card argument */ int nCChild; /* Number of cluster children */ int nCChildAlloc; /* Number of closts allocated in azCChild[] */ char **azCChild; /* UUIDs of referenced objects in a cluster. M cards */ int nTag; /* Number of T Cards */ |
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 | struct { char *zName; /* Key or field name */ char *zValue; /* Value of the field */ } *aField; /* One for each J card */ }; #endif /* ** Clear the memory allocated in a manifest object */ | > > > > > > > > > > > | > | | | | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | struct { char *zName; /* Key or field name */ char *zValue; /* Value of the field */ } *aField; /* One for each J card */ }; #endif /* ** A cache of parsed manifests. This reduces the number of ** calls to manifest_parse() when doing a rebuild. */ #define MX_MANIFEST_CACHE 6 static struct { int nxAge; int aAge[MX_MANIFEST_CACHE]; Manifest *apManifest[MX_MANIFEST_CACHE]; } manifestCache; /* ** Clear the memory allocated in a manifest object */ void manifest_destroy(Manifest *p){ if( p ){ blob_reset(&p->content); free(p->aFile); free(p->azParent); free(p->azCChild); free(p->aTag); free(p->aField); if( p->pBaseline ) manifest_destroy(p->pBaseline); fossil_free(p); } } /* ** Add an element to the manifest cache using LRU replacement. */ void manifest_cache_insert(Manifest *p){ while( p ){ int i; Manifest *pBaseline = p->pBaseline; p->pBaseline = 0; for(i=0; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.apManifest[i]==0 ) break; } if( i>=MX_MANIFEST_CACHE ){ int oldest = 0; int oldestAge = manifestCache.aAge[0]; for(i=1; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.aAge[i]<oldestAge ){ oldest = i; oldestAge = manifestCache.aAge[i]; } } manifest_destroy(manifestCache.apManifest[oldest]); i = oldest; } manifestCache.aAge[i] = ++manifestCache.nxAge; manifestCache.apManifest[i] = p; p = pBaseline; } } /* ** Try to extract a line from the manifest cache. Return 1 if found. ** Return 0 if not found. */ static Manifest *manifest_cache_find(int rid){ int i; Manifest *p; for(i=0; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){ p = manifestCache.apManifest[i]; manifestCache.apManifest[i] = 0; return p; } } return 0; } /* ** Clear the manifest cache. */ void manifest_cache_clear(void){ int i; for(i=0; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.apManifest[i] ){ manifest_destroy(manifestCache.apManifest[i]); } } memset(&manifestCache, 0, sizeof(manifestCache)); } #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM # define md5sum_init(X) # define md5sum_step_text(X,Y) #endif /* ** Remove the PGP signature from the artifact, if there is one. */ static void remove_pgp_signature(char **pz, int *pn){ char *z = *pz; int n = *pn; int i; if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return; for(i=34; i<n && (z[i-1]!='\n' || z[i-2]!='\n'); i++){} if( i>=n ) return; z += i; n -= i; *pz = z; for(i=n-1; i>=0; i--){ if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){ n = i+1; break; } } *pn = n; return; } /* ** Verify the Z-card checksum on the artifact, if there is such a ** checksum. Return 0 if there is no Z-card. Return 1 if the Z-card ** exists and is correct. Return 2 if the Z-card exists and has the wrong ** value. ** ** 0123456789 123456789 123456789 123456789 ** Z aea84f4f863865a8d59d0384e4d2a41c */ static int verify_z_card(const char *z, int n){ if( n<35 ) return 0; if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0; md5sum_init(); md5sum_step_text(z, n-35); if( memcmp(&z[n-33], md5sum_finish(0), 32)==0 ){ return 1; }else{ return 2; } } /* ** A structure used for rapid parsing of the Manifest file */ typedef struct ManifestText ManifestText; struct ManifestText { char *z; /* The first character of the next token */ char *zEnd; /* One character beyond the end of the manifest */ int atEol; /* True if z points to the start of a new line */ }; /* ** Return a pointer to the next token. The token is zero-terminated. ** Return NULL if there are no more tokens on the current line. */ static char *next_token(ManifestText *p, int *pLen){ char *z; char *zStart; int c; if( p->atEol ) return 0; zStart = z = p->z; while( (c=(*z))!=' ' && c!='\n' ){ z++; } *z = 0; p->z = &z[1]; p->atEol = c=='\n'; if( pLen ) *pLen = z - zStart; return zStart; } /* ** Return the card-type for the next card. Or, return 0 if there are no ** more cards or if we are not at the end of the current card. */ static char next_card(ManifestText *p){ char c; if( !p->atEol || p->z>=p->zEnd ) return 0; c = p->z[0]; if( p->z[1]==' ' ){ p->z += 2; p->atEol = 0; }else if( p->z[1]=='\n' ){ p->z += 2; p->atEol = 1; }else{ c = 0; } return c; } /* ** Parse a blob into a Manifest object. The Manifest object ** takes over the input blob and will free it when the ** Manifest object is freed. Zeros are inserted into the blob ** as string terminators so that blob should not be used again. |
︙ | ︙ | |||
119 120 121 122 123 124 125 | ** The file consists of zero or more cards, one card per line. ** (Except: the content of the W card can extend of multiple lines.) ** Each card is divided into tokens by a single space character. ** The first token is a single upper-case letter which is the card type. ** The card type determines the other parameters to the card. ** Cards must occur in lexicographical order. */ | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | | < | > < < < < < < < < < < < < < < < < < < | | | > > | < < < < | < | > > > > > > > > > > > > > > > < | | < | > > > > | > > > > > > > > > > | > | > | < < > | | < < < < < | < | < > > > > > | | | < < > | < < < | | | < | | < < < | | | | < < < | | < | < | < | | | < < | < | < > | < | | < | < | < | < | | < | | < < < < | < < | < | | | < < | > < < > | < | | | > > > < | < | | | | > < < | < | | < < < < | | | > > | > > > > > > > > > > > > > > | | < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | < > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 | ** The file consists of zero or more cards, one card per line. ** (Except: the content of the W card can extend of multiple lines.) ** Each card is divided into tokens by a single space character. ** The first token is a single upper-case letter which is the card type. ** The card type determines the other parameters to the card. ** Cards must occur in lexicographical order. */ static Manifest *manifest_parse(Blob *pContent, int rid){ Manifest *p; int seenZ = 0; int i, lineNo=0; ManifestText x; char cPrevType = 0; char cType; char *z; int n; char *zUuid; int sz = 0; /* Every control artifact ends with a '\n' character. Exit early ** if that is not the case for this artifact. */ z = blob_buffer(pContent); n = blob_size(pContent); if( n<=0 || z[n-1]!='\n' ){ blob_reset(pContent); return 0; } /* Strip off the PGP signature if there is one. Then verify the ** Z-card. */ remove_pgp_signature(&z, &n); if( verify_z_card(z, n)==0 ){ blob_reset(pContent); return 0; } /* Verify that the first few characters of the artifact look like ** a control artifact. */ if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){ blob_reset(pContent); return 0; } /* Allocate a Manifest object to hold the parsed control artifact. */ p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); memcpy(&p->content, pContent, sizeof(p->content)); p->rid = rid; blob_zero(pContent); pContent = &p->content; /* Begin parsing, card by card. */ x.z = z; x.zEnd = &z[n]; x.atEol = 1; while( (cType = next_card(&x))!=0 && cType>=cPrevType ){ lineNo++; switch( cType ){ /* ** A <filename> <target> ?<source>? ** ** Identifies an attachment to either a wiki page or a ticket. ** <source> is the artifact that is the attachment. <source> ** is omitted to delete an attachment. <target> is the name of ** a wiki page or ticket to which that attachment is connected. */ case 'A': { char *zName, *zTarget, *zSrc; int nTarget = 0, nSrc = 0; zName = next_token(&x, 0); zTarget = next_token(&x, &nTarget); zSrc = next_token(&x, &nSrc); if( zName==0 || zTarget==0 ) goto manifest_syntax_error; if( p->zAttachName!=0 ) goto manifest_syntax_error; defossilize(zName); if( !file_is_simple_pathname(zName) ){ goto manifest_syntax_error; } defossilize(zTarget); if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ goto manifest_syntax_error; } if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ goto manifest_syntax_error; } p->zAttachName = (char*)file_tail(zName); p->zAttachSrc = zSrc; p->zAttachTarget = zTarget; break; } /* ** B <uuid> ** ** A B-line gives the UUID for the baselinen of a delta-manifest. */ case 'B': { if( p->zBaseline ) goto manifest_syntax_error; p->zBaseline = next_token(&x, &sz); if( p->zBaseline==0 ) goto manifest_syntax_error; if( sz!=UUID_SIZE ) goto manifest_syntax_error; if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error; break; } /* ** C <comment> ** ** Comment text is fossil-encoded. There may be no more than ** one C line. C lines are required for manifests and are ** disallowed on all other control files. */ case 'C': { if( p->zComment!=0 ) goto manifest_syntax_error; p->zComment = next_token(&x, 0); if( p->zComment==0 ) goto manifest_syntax_error; defossilize(p->zComment); break; } /* ** D <timestamp> ** ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS ** There can be no more than 1 D line. D lines are required ** for all control files except for clusters. */ case 'D': { if( p->rDate>0.0 ) goto manifest_syntax_error; p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0)); if( p->rDate<=0.0 ) goto manifest_syntax_error; break; } /* ** E <timestamp> <uuid> ** ** An "event" card that contains the timestamp of the event in the ** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event. ** The event timestamp is distinct from the D timestamp. The D ** timestamp is when the artifact was created whereas the E timestamp ** is when the specific event is said to occur. */ case 'E': { if( p->rEventDate>0.0 ) goto manifest_syntax_error; p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); if( p->rEventDate<=0.0 ) goto manifest_syntax_error; p->zEventId = next_token(&x, &sz); if( sz!=UUID_SIZE ) goto manifest_syntax_error; if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; break; } /* ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? ** ** Identifies a file in a manifest. Multiple F lines are ** allowed in a manifest. F lines are not allowed in any ** other control file. The filename and old-name are fossil-encoded. */ case 'F': { char *zName, *zPerm, *zPriorName; zName = next_token(&x,0); if( zName==0 ) goto manifest_syntax_error; defossilize(zName); if( !file_is_simple_pathname(zName) ){ goto manifest_syntax_error; } zUuid = next_token(&x, &sz); if( p->zBaseline==0 || zUuid!=0 ){ if( sz!=UUID_SIZE ) goto manifest_syntax_error; if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; } zPerm = next_token(&x,0); zPriorName = next_token(&x,0); if( zPriorName ){ defossilize(zPriorName); if( !file_is_simple_pathname(zPriorName) ){ goto manifest_syntax_error; } } if( p->nFile>=p->nFileAlloc ){ p->nFileAlloc = p->nFileAlloc*2 + 10; p->aFile = fossil_realloc(p->aFile, p->nFileAlloc*sizeof(p->aFile[0]) ); } i = p->nFile++; p->aFile[i].zName = zName; p->aFile[i].zUuid = zUuid; p->aFile[i].zPerm = zPerm; p->aFile[i].zPrior = zPriorName; if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){ goto manifest_syntax_error; } break; } /* ** J <name> ?<value>? ** ** Specifies a name value pair for ticket. If the first character ** of <name> is "+" then the <value> is appended to any preexisting ** value. If <value> is omitted then it is understood to be an ** empty string. */ case 'J': { char *zName, *zValue; zName = next_token(&x,0); zValue = next_token(&x,0); if( zName==0 ) goto manifest_syntax_error; if( zValue==0 ) zValue = ""; defossilize(zValue); if( p->nField>=p->nFieldAlloc ){ p->nFieldAlloc = p->nFieldAlloc*2 + 10; p->aField = fossil_realloc(p->aField, p->nFieldAlloc*sizeof(p->aField[0]) ); } i = p->nField++; p->aField[i].zName = zName; p->aField[i].zValue = zValue; if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){ goto manifest_syntax_error; } break; } /* ** K <uuid> ** ** A K-line gives the UUID for the ticket which this control file ** is amending. */ case 'K': { if( p->zTicketUuid!=0 ) goto manifest_syntax_error; p->zTicketUuid = next_token(&x, &sz); if( sz!=UUID_SIZE ) goto manifest_syntax_error; if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error; break; } /* ** L <wikititle> ** ** The wiki page title is fossil-encoded. There may be no more than ** one L line. */ case 'L': { if( p->zWikiTitle!=0 ) goto manifest_syntax_error; p->zWikiTitle = next_token(&x,0); if( p->zWikiTitle==0 ) goto manifest_syntax_error; defossilize(p->zWikiTitle); if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ goto manifest_syntax_error; } break; } /* ** M <uuid> ** ** An M-line identifies another artifact by its UUID. M-lines ** occur in clusters only. */ case 'M': { zUuid = next_token(&x, &sz); if( zUuid==0 ) goto manifest_syntax_error; if( sz!=UUID_SIZE ) goto manifest_syntax_error; if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; if( p->nCChild>=p->nCChildAlloc ){ p->nCChildAlloc = p->nCChildAlloc*2 + 10; p->azCChild = fossil_realloc(p->azCChild , p->nCChildAlloc*sizeof(p->azCChild[0]) ); } i = p->nCChild++; p->azCChild[i] = zUuid; if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){ goto manifest_syntax_error; } break; } /* ** P <uuid> ... ** ** Specify one or more other artifacts where are the parents of ** this artifact. The first parent is the primary parent. All ** others are parents by merge. */ case 'P': { while( (zUuid = next_token(&x, &sz))!=0 ){ if( sz!=UUID_SIZE ) goto manifest_syntax_error; if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; if( p->nParent>=p->nParentAlloc ){ p->nParentAlloc = p->nParentAlloc*2 + 5; p->azParent = fossil_realloc(p->azParent, p->nParentAlloc*sizeof(char*)); } i = p->nParent++; p->azParent[i] = zUuid; } break; } /* ** R <md5sum> ** ** Specify the MD5 checksum over the name and content of all files ** in the manifest. */ case 'R': { if( p->zRepoCksum!=0 ) goto manifest_syntax_error; p->zRepoCksum = next_token(&x, &sz); if( sz!=32 ) goto manifest_syntax_error; if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error; break; } /* ** T (+|*|-)<tagname> <uuid> ?<value>? ** ** Create or cancel a tag or property. The tagname is fossil-encoded. ** The first character of the name must be either "+" to create a ** singleton tag, "*" to create a propagating tag, or "-" to create ** anti-tag that undoes a prior "+" or blocks propagation of of ** a "*". ** ** The tag is applied to <uuid>. If <uuid> is "*" then the tag is ** applied to the current manifest. If <value> is provided then ** the tag is really a property with the given value. ** ** Tags are not allowed in clusters. Multiple T lines are allowed. */ case 'T': { char *zName, *zValue; zName = next_token(&x, 0); if( zName==0 ) goto manifest_syntax_error; zUuid = next_token(&x, &sz); if( zUuid==0 ) goto manifest_syntax_error; zValue = next_token(&x, 0); if( zValue ) defossilize(zValue); if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ /* A valid uuid */ }else if( sz==1 && zUuid[0]=='*' ){ zUuid = 0; }else{ goto manifest_syntax_error; } defossilize(zName); if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){ goto manifest_syntax_error; } if( validate16(&zName[1], strlen(&zName[1])) ){ /* Do not allow tags whose names look like UUIDs */ goto manifest_syntax_error; } if( p->nTag>=p->nTagAlloc ){ p->nTagAlloc = p->nTagAlloc*2 + 10; p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); } i = p->nTag++; p->aTag[i].zName = zName; p->aTag[i].zUuid = zUuid; p->aTag[i].zValue = zValue; if( i>0 && strcmp(p->aTag[i-1].zName, zName)>=0 ){ goto manifest_syntax_error; } break; } /* ** U ?<login>? ** ** Identify the user who created this control file by their ** login. Only one U line is allowed. Prohibited in clusters. ** If the user name is omitted, take that to be "anonymous". */ case 'U': { if( p->zUser!=0 ) goto manifest_syntax_error; p->zUser = next_token(&x, 0); if( p->zUser==0 ){ p->zUser = "anonymous"; }else{ defossilize(p->zUser); } break; } /* ** W <size> ** ** The next <size> bytes of the file contain the text of the wiki ** page. There is always an extra \n before the start of the next ** record. */ case 'W': { char *zSize; int size, c; Blob wiki; zSize = next_token(&x, 0); if( zSize==0 ) goto manifest_syntax_error; if( x.atEol==0 ) goto manifest_syntax_error; for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ size = size*10 + c - '0'; } if( size<0 ) goto manifest_syntax_error; if( p->zWiki!=0 ) goto manifest_syntax_error; blob_zero(&wiki); if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error; p->zWiki = x.z; x.z += size; if( x.z[0]!='\n' ) goto manifest_syntax_error; x.z[0] = 0; x.z++; break; } /* ** Z <md5sum> ** ** MD5 checksum on this control file. The checksum is over all ** lines (other than PGP-signature lines) prior to the current ** line. This must be the last record. ** ** This card is required for all control file types except for ** Manifest. It is not required for manifest only for historical ** compatibility reasons. */ case 'Z': { zUuid = next_token(&x, &sz); if( sz!=32 ) goto manifest_syntax_error; if( !validate16(zUuid, 32) ) goto manifest_syntax_error; seenZ = 1; break; } default: { goto manifest_syntax_error; } } } if( x.z<x.zEnd ) goto manifest_syntax_error; if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){ if( p->nCChild>0 ) goto manifest_syntax_error; if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->nField>0 ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWiki ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( p->zEventId ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zAttachName ) goto manifest_syntax_error; p->type = CFTYPE_MANIFEST; }else if( p->nCChild>0 ){ if( p->rDate>0.0 ) goto manifest_syntax_error; if( p->zComment!=0 ) goto manifest_syntax_error; if( p->zUser!=0 ) goto manifest_syntax_error; if( p->nTag>0 ) goto manifest_syntax_error; if( p->nParent>0 ) goto manifest_syntax_error; if( p->nField>0 ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWiki ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( p->zEventId ) goto manifest_syntax_error; if( p->zAttachName ) goto manifest_syntax_error; if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_CLUSTER; }else if( p->nField>0 ){ if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->zWiki ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( p->zEventId ) goto manifest_syntax_error; if( p->nCChild>0 ) goto manifest_syntax_error; if( p->nTag>0 ) goto manifest_syntax_error; if( p->zTicketUuid==0 ) goto manifest_syntax_error; if( p->zUser==0 ) goto manifest_syntax_error; if( p->zAttachName ) goto manifest_syntax_error; if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_TICKET; }else if( p->zEventId ){ if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->nCChild>0 ) goto manifest_syntax_error; if( p->zTicketUuid!=0 ) goto manifest_syntax_error; if( p->zWikiTitle!=0 ) goto manifest_syntax_error; if( p->zWiki==0 ) goto manifest_syntax_error; if( p->zAttachName ) goto manifest_syntax_error; for(i=0; i<p->nTag; i++){ if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error; if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error; } if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_EVENT; }else if( p->zWiki!=0 ){ if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->nCChild>0 ) goto manifest_syntax_error; if( p->nTag>0 ) goto manifest_syntax_error; if( p->zTicketUuid!=0 ) goto manifest_syntax_error; if( p->zWikiTitle==0 ) goto manifest_syntax_error; if( p->zAttachName ) goto manifest_syntax_error; if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_WIKI; }else if( p->nTag>0 ){ if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->nParent>0 ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zAttachName ) goto manifest_syntax_error; if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_CONTROL; }else if( p->zAttachName ){ if( p->nCChild>0 ) goto manifest_syntax_error; if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_ATTACHMENT; }else{ if( p->nCChild>0 ) goto manifest_syntax_error; if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->nParent>0 ) goto manifest_syntax_error; if( p->nField>0 ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; p->type = CFTYPE_MANIFEST; } md5sum_init(); return p; manifest_syntax_error: /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ md5sum_init(); manifest_destroy(p); return 0; } /* ** Get a manifest given the rid for the control artifact. Return ** a pointer to the manifest on success or NULL if there is a failure. */ Manifest *manifest_get(int rid, int cfType){ Blob content; Manifest *p; p = manifest_cache_find(rid); if( p ){ if( cfType!=CFTYPE_ANY && cfType!=p->type ){ manifest_cache_insert(p); p = 0; } return p; } content_get(rid, &content); p = manifest_parse(&content, rid); if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){ manifest_destroy(p); p = 0; } return p; } /* ** Given a checkin name, load and parse the manifest for that checkin. ** Throw a fatal error if anything goes wrong. */ Manifest *manifest_get_by_name(const char *zName, int *pRid){ int rid; Manifest *p; rid = name_to_rid(zName); if( !is_a_version(rid) ){ fossil_fatal("no such checkin: %s", zName); } if( pRid ) *pRid = rid; p = manifest_get(rid, CFTYPE_MANIFEST); if( p==0 ){ fossil_fatal("cannot parse manifest for checkin: %s", zName); } return p; } /* ** COMMAND: test-parse-manifest ** ** Usage: %fossil test-parse-manifest FILENAME ?N? ** ** Parse the manifest and discarded. Use for testing only. */ void manifest_test_parse_cmd(void){ Manifest *p; Blob b; int i; int n = 1; sqlite3_open(":memory:", &g.db); if( g.argc!=3 && g.argc!=4 ){ usage("FILENAME"); } blob_read_from_file(&b, g.argv[2]); if( g.argc>3 ) n = atoi(g.argv[3]); for(i=0; i<n; i++){ Blob b2; blob_copy(&b2, &b); p = manifest_parse(&b2, 0); manifest_destroy(p); } } /* ** Fetch the baseline associated with the delta-manifest p. ** Return 0 on success. If unable to parse the baseline, ** throw an error. If the baseline is a manifest, throw an ** error if throwError is true, or record that p is an orphan ** and return 1 throwError is false. */ static int fetch_baseline(Manifest *p, int throwError){ if( p->zBaseline!=0 && p->pBaseline==0 ){ int rid = uuid_to_rid(p->zBaseline, 0); if( rid==0 && !throwError ){ rid = content_new(p->zBaseline); db_multi_exec( "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", rid, p->rid ); return 1; } p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST); if( p->pBaseline==0 ){ if( !throwError && db_exists("SELECT 1 FROM phantom WHERE rid=%d",rid) ){ db_multi_exec( "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", rid, p->rid ); return 1; } fossil_fatal("cannot access baseline manifest %S", p->zBaseline); } } return 0; } /* ** Rewind a manifest-file iterator back to the beginning of the manifest. */ void manifest_file_rewind(Manifest *p){ p->iFile = 0; fetch_baseline(p, 1); if( p->pBaseline ){ p->pBaseline->iFile = 0; } } /* ** Advance to the next manifest-file. ** ** Return NULL for end-of-records or if there is an error. If an error ** occurs and pErr!=0 then store 1 in *pErr. */ ManifestFile *manifest_file_next( Manifest *p, int *pErr ){ ManifestFile *pOut = 0; if( pErr ) *pErr = 0; if( p->pBaseline==0 ){ /* Manifest p is a baseline-manifest. Just scan down the list ** of files. */ if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; }else{ /* Manifest p is a delta-manifest. Scan the baseline but amend the ** file list in the baseline with changes described by p. */ Manifest *pB = p->pBaseline; int cmp; while(1){ if( pB->iFile>=pB->nFile ){ /* We have used all entries out of the baseline. Return the next ** entry from the delta. */ if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; break; }else if( p->iFile>=p->nFile ){ /* We have used all entries from the delta. Return the next ** entry from the baseline. */ if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++]; break; }else if( (cmp = strcmp(pB->aFile[pB->iFile].zName, p->aFile[p->iFile].zName)) < 0 ){ /* The next baseline entry comes before the next delta entry. ** So return the baseline entry. */ pOut = &pB->aFile[pB->iFile++]; break; }else if( cmp>0 ){ /* The next delta entry comes before the next baseline ** entry so return the delta entry */ pOut = &p->aFile[p->iFile++]; break; }else if( p->aFile[p->iFile].zUuid ){ /* The next delta entry is a replacement for the next baseline ** entry. Skip the baseline entry and return the delta entry */ pB->iFile++; pOut = &p->aFile[p->iFile++]; break; }else{ /* The next delta entry is a delete of the next baseline ** entry. Skip them both. Repeat the loop to find the next ** non-delete entry. */ pB->iFile++; p->iFile++; continue; } } } return pOut; } /* ** Translate a filename into a filename-id (fnid). Create a new fnid ** if no previously exists. */ static int filename_to_fnid(const char *zFilename){ |
︙ | ︙ | |||
687 688 689 690 691 692 693 | /* ** Add a single entry to the mlink table. Also add the filename to ** the filename table if it is not there already. */ static void add_one_mlink( int mid, /* The record ID of the manifest */ | | | | | | | | > > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > | > > > > > < > | > > > | > > > | | | | | | > > | | < > | < < | < < | < < | | > > > > | | | < | < | < < < < < < | | | > | > > > | | < < | | | | < < > | | > > | | < | < < < < < < < | < < | | 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 | /* ** Add a single entry to the mlink table. Also add the filename to ** the filename table if it is not there already. */ static void add_one_mlink( int mid, /* The record ID of the manifest */ const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */ const char *zToUuid, /* UUID for the mlink.fid. "" to delele */ const char *zFilename, /* Filename */ const char *zPrior /* Previous filename. NULL if unchanged */ ){ int fnid, pfnid, pid, fid; static Stmt s1; fnid = filename_to_fnid(zFilename); if( zPrior==0 ){ pfnid = 0; }else{ pfnid = filename_to_fnid(zPrior); } if( zFromUuid==0 || zFromUuid[0]==0 ){ pid = 0; }else{ pid = uuid_to_rid(zFromUuid, 1); } if( zToUuid==0 || zToUuid[0]==0 ){ fid = 0; }else{ fid = uuid_to_rid(zToUuid, 1); } db_static_prepare(&s1, "INSERT INTO mlink(mid,pid,fid,fnid,pfnid)" "VALUES(:m,:p,:f,:n,:pfn)" ); db_bind_int(&s1, ":m", mid); db_bind_int(&s1, ":p", pid); db_bind_int(&s1, ":f", fid); db_bind_int(&s1, ":n", fnid); db_bind_int(&s1, ":pfn", pfnid); db_exec(&s1); if( pid && fid ){ content_deltify(pid, fid, 0); } } /* ** Do a binary search to find a file in the p->aFile[] array. ** ** As an optimization, guess that the file we seek is at index p->iFile. ** That will usually be the case. If it is not found there, then do the ** actual binary search. ** ** Update p->iFile to be the index of the file that is found. */ static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){ int lwr, upr; int c; int i; lwr = 0; upr = p->nFile - 1; if( p->iFile>=lwr && p->iFile<upr ){ c = strcmp(p->aFile[p->iFile+1].zName, zName); if( c==0 ){ return &p->aFile[++p->iFile]; }else if( c>0 ){ upr = p->iFile; }else{ lwr = p->iFile+1; } } while( lwr<=upr ){ i = (lwr+upr)/2; c = strcmp(p->aFile[i].zName, zName); if( c<0 ){ lwr = i+1; }else if( c>0 ){ upr = i-1; }else{ p->iFile = i; return &p->aFile[i]; } } return 0; } /* ** Locate a file named zName in the aFile[] array of the given manifest. ** Return a pointer to the appropriate ManifestFile object. Return NULL ** if not found. ** ** This routine works even if p is a delta-manifest. The pointer ** returned might be to the baseline. ** ** We assume that filenames are in sorted order and use a binary search. */ ManifestFile *manifest_file_seek(Manifest *p, const char *zName){ ManifestFile *pFile; pFile = manifest_file_seek_base(p, zName); if( pFile && pFile->zUuid==0 ) return 0; if( pFile==0 && p->zBaseline ){ fetch_baseline(p, 1); pFile = manifest_file_seek_base(p->pBaseline, zName); } return pFile; } /* ** This strcmp() function handles NULL arguments. NULLs sort first. */ static int strcmp_null(const char *zOne, const char *zTwo){ if( zOne==0 ){ if( zTwo==0 ) return 0; return -1; }else if( zTwo==0 ){ return +1; }else{ return strcmp(zOne, zTwo); } } /* ** Add mlink table entries associated with manifest cid. The ** parent manifest is pid. ** ** A single mlink entry is added for every file that changed content ** and/or name going from pid to cid. ** ** Deleted files have mlink.fid=0. ** Added files have mlink.pid=0. ** Edited files have both mlink.pid!=0 and mlink.fid!=0 */ static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){ Blob otherContent; int otherRid; int i, rc; ManifestFile *pChildFile, *pParentFile; Manifest **ppOther; static Stmt eq; db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid"); db_bind_int(&eq, ":mid", cid); rc = db_step(&eq); db_reset(&eq); if( rc==SQLITE_ROW ) return; assert( pParent==0 || pChild==0 ); if( pParent==0 ){ ppOther = &pParent; otherRid = pid; }else{ ppOther = &pChild; otherRid = cid; } if( (*ppOther = manifest_cache_find(otherRid))==0 ){ content_get(otherRid, &otherContent); if( blob_size(&otherContent)==0 ) return; *ppOther = manifest_parse(&otherContent, otherRid); if( *ppOther==0 ) return; } if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ manifest_destroy(*ppOther); return; } if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ content_deltify(pid, cid, 0); }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ content_deltify(pParent->pBaseline->rid, cid, 0); } for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){ if( pChildFile->zPrior ){ pParentFile = manifest_file_seek(pParent, pChildFile->zPrior); if( pParentFile ){ add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, pChildFile->zName, pChildFile->zPrior); } }else{ pParentFile = manifest_file_seek(pParent, pChildFile->zName); if( pParentFile==0 ){ if( pChildFile->zUuid ){ add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); } }else if( strcmp_null(pChildFile->zUuid, pParentFile->zUuid)!=0 ){ add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, pChildFile->zName, 0); } } } if( pParent->zBaseline && pChild->zBaseline ){ for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ if( pParentFile->zUuid ) continue; pChildFile = manifest_file_seek(pChild, pParentFile->zName); if( pChildFile ){ add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); } } } manifest_cache_insert(*ppOther); } /* ** True if manifest_crosslink_begin() has been called but ** manifest_crosslink_end() is still pending. */ static int manifest_crosslink_busy = 0; |
︙ | ︙ | |||
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 | ** any key: ** ** * Manifest ** * Control ** * Wiki Page ** * Ticket Change ** * Cluster ** ** If the input is a control artifact, then make appropriate entries ** in the auxiliary tables of the database in order to crosslink the ** artifact. ** ** If global variable g.xlinkClusterOnly is true, then ignore all ** control artifacts other than clusters. ** ** Historical note: This routine original processed manifests only. ** Processing for other control artifacts was added later. The name ** of the routine, "manifest_crosslink", and the name of this source ** file, is a legacy of its original use. */ int manifest_crosslink(int rid, Blob *pContent){ int i; | > > | > > | | | > > > > | | | | | | | | | | > > > > > > > > > | > > > | > | > | | | > > > | > > | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | > | < | < < < < < < < | < < < | < < < < | < < | < | 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 | ** any key: ** ** * Manifest ** * Control ** * Wiki Page ** * Ticket Change ** * Cluster ** * Attachment ** * Event ** ** If the input is a control artifact, then make appropriate entries ** in the auxiliary tables of the database in order to crosslink the ** artifact. ** ** If global variable g.xlinkClusterOnly is true, then ignore all ** control artifacts other than clusters. ** ** Historical note: This routine original processed manifests only. ** Processing for other control artifacts was added later. The name ** of the routine, "manifest_crosslink", and the name of this source ** file, is a legacy of its original use. */ int manifest_crosslink(int rid, Blob *pContent){ int i; Manifest *p; Stmt q; int parentid = 0; if( (p = manifest_cache_find(rid))!=0 ){ blob_reset(pContent); }else if( (p = manifest_parse(pContent, rid))==0 ){ return 0; } if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ manifest_destroy(p); return 0; } if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ manifest_destroy(p); return 0; } db_begin_transaction(); if( p->type==CFTYPE_MANIFEST ){ if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ char *zCom; for(i=0; i<p->nParent; i++){ int pid = uuid_to_rid(p->azParent[i], 1); db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate); if( i==0 ){ add_mlink(pid, 0, rid, p); parentid = pid; } } db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); while( db_step(&q)==SQLITE_ROW ){ int cid = db_column_int(&q, 0); add_mlink(rid, p, cid, 0); } db_finalize(&q); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment," "bgcolor,euser,ecomment)" "VALUES('ci'," " coalesce(" " (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d)," " %.17g" " )," " %d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", TAG_DATE, rid, p->rDate, rid, p->zUser, p->zComment, TAG_BGCOLOR, rid, TAG_USER, rid, TAG_COMMENT, rid ); zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" " WHERE rowid=last_insert_rowid()"); wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE); free(zCom); /* If this is a delta-manifest, record the fact that this repository ** contains delta manifests, to free the "commit" logic to generate ** new delta manifests. */ if( p->zBaseline!=0 ){ static int once = 0; if( !once ){ db_set_int("seen-delta-manifest", 1, 0); once = 0; } } } } if( p->type==CFTYPE_CLUSTER ){ static Stmt del1; tag_insert("cluster", 1, 0, rid, p->rDate, rid); db_static_prepare(&del1, "DELETE FROM unclustered WHERE rid=:rid"); for(i=0; i<p->nCChild; i++){ int mid; mid = uuid_to_rid(p->azCChild[i], 1); if( mid>0 ){ db_bind_int(&del1, ":rid", mid); db_step(&del1); db_reset(&del1); } } } if( p->type==CFTYPE_CONTROL || p->type==CFTYPE_MANIFEST || p->type==CFTYPE_EVENT ){ for(i=0; i<p->nTag; i++){ int tid; int type; if( p->aTag[i].zUuid ){ tid = uuid_to_rid(p->aTag[i].zUuid, 1); }else{ tid = rid; } if( tid ){ switch( p->aTag[i].zName[0] ){ case '-': type = 0; break; /* Cancel prior occurances */ case '+': type = 1; break; /* Apply to target only */ case '*': type = 2; break; /* Propagate to descendants */ default: fossil_fatal("unknown tag type in manifest: %s", p->aTag); return 0; } tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, rid, p->rDate, tid); } } if( parentid ){ tag_propagate_all(parentid); } } if( p->type==CFTYPE_WIKI ){ char *zTag = mprintf("wiki-%s", p->zWikiTitle); int tagid = tag_findid(zTag, 1); int prior; char *zComment; int nWiki; char zLength[40]; while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; nWiki = strlen(p->zWiki); sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); tag_insert(zTag, 1, zLength, rid, p->rDate, rid); free(zTag); prior = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=%d AND mtime<%.17g" " ORDER BY mtime DESC", tagid, p->rDate ); if( prior ){ content_deltify(prior, rid, 0); } if( nWiki>0 ){ zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); }else{ zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); } db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment," " bgcolor,euser,ecomment)" "VALUES('w',%.17g,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", p->rDate, rid, p->zUser, zComment, TAG_BGCOLOR, rid, TAG_BGCOLOR, rid, TAG_USER, rid, TAG_COMMENT, rid ); free(zComment); } if( p->type==CFTYPE_EVENT ){ char *zTag = mprintf("event-%s", p->zEventId); int tagid = tag_findid(zTag, 1); int prior, subsequent; int nWiki; char zLength[40]; while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; nWiki = strlen(p->zWiki); sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); tag_insert(zTag, 1, zLength, rid, p->rDate, rid); free(zTag); prior = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=%d AND mtime<%.17g" " ORDER BY mtime DESC", tagid, p->rDate ); if( prior ){ content_deltify(prior, rid, 0); db_multi_exec( "DELETE FROM event" " WHERE type='e'" " AND tagid=%d" " AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)", tagid, tagid ); } subsequent = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=%d AND mtime>%.17g" " ORDER BY mtime", tagid, p->rDate ); if( subsequent ){ content_deltify(rid, subsequent, 0); }else{ db_multi_exec( "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" "VALUES('e',%.17g,%d,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", p->rEventDate, rid, tagid, p->zUser, p->zComment, TAG_BGCOLOR, rid ); } } if( p->type==CFTYPE_TICKET ){ char *zTag; assert( manifest_crosslink_busy==1 ); zTag = mprintf("tkt-%s", p->zTicketUuid); tag_insert(zTag, 1, 0, rid, p->rDate, rid); free(zTag); db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", p->zTicketUuid); } if( p->type==CFTYPE_ATTACHMENT ){ db_multi_exec( "INSERT INTO attachment(attachid, mtime, src, target," "filename, comment, user)" "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);", rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName, (p->zComment ? p->zComment : ""), p->zUser ); db_multi_exec( "UPDATE attachment SET isLatest = (mtime==" "(SELECT max(mtime) FROM attachment" " WHERE target=%Q AND filename=%Q))" " WHERE target=%Q AND filename=%Q", p->zAttachTarget, p->zAttachName, p->zAttachTarget, p->zAttachName ); if( strlen(p->zAttachTarget)!=UUID_SIZE || !validate16(p->zAttachTarget, UUID_SIZE) ){ char *zComment; if( p->zAttachSrc && p->zAttachSrc[0] ){ zComment = mprintf("Add attachment \"%h\" to wiki page [%h]", p->zAttachName, p->zAttachTarget); }else{ zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]", p->zAttachName, p->zAttachTarget); } db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('w',%.17g,%d,%Q,%Q)", p->rDate, rid, p->zUser, zComment ); free(zComment); }else{ char *zComment; if( p->zAttachSrc && p->zAttachSrc[0] ){ zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]", p->zAttachName, p->zAttachTarget); }else{ zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]", p->zAttachName, p->zAttachTarget); } db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('t',%.17g,%d,%Q,%Q)", p->rDate, rid, p->zUser, zComment ); free(zComment); } } db_end_transaction(0); if( p->type==CFTYPE_MANIFEST ){ manifest_cache_insert(p); }else{ manifest_destroy(p); } return 1; } |
Changes to src/md5.c.
︙ | ︙ | |||
39 40 41 42 43 44 45 46 | int isInit; uint32 buf[4]; uint32 bits[2]; unsigned char in[64]; }; typedef struct Context MD5Context; /* | > > > > | > > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | int isInit; uint32 buf[4]; uint32 bits[2]; unsigned char in[64]; }; typedef struct Context MD5Context; #if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) # define byteReverse(A,B) #else /* * Convert an array of integers to little-endian. * Note: this code is a no-op on little-endian machines. */ static void byteReverse (unsigned char *buf, unsigned longs){ uint32 t; do { t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | ((unsigned)buf[1]<<8 | buf[0]); *(uint32 *)buf = t; buf += 4; } while (--longs); } #endif /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) |
︙ | ︙ | |||
312 313 314 315 316 317 318 319 320 321 322 323 324 325 | /* ** Add the content of a blob to the incremental MD5 checksum. */ void md5sum_step_blob(Blob *p){ md5sum_step_text(blob_buffer(p), blob_size(p)); } /* ** Finish the incremental MD5 checksum. Store the result in blob pOut ** if pOut!=0. Also return a pointer to the result. ** ** This resets the incremental checksum preparing for the next round ** of computation. The return pointer points to a static buffer that | > > > > > > > > > > > > > > > > > > > > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | /* ** Add the content of a blob to the incremental MD5 checksum. */ void md5sum_step_blob(Blob *p){ md5sum_step_text(blob_buffer(p), blob_size(p)); } /* ** For trouble-shooting only: ** ** Report the current state of the incremental checksum. */ const char *md5sum_current_state(void){ unsigned int cksum = 0; unsigned int *pFirst, *pLast; static char zResult[12]; pFirst = (unsigned int*)&incrCtx; pLast = (unsigned int*)((&incrCtx)+1); while( pFirst<pLast ){ cksum += *pFirst; pFirst++; } sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum); return zResult; } /* ** Finish the incremental MD5 checksum. Store the result in blob pOut ** if pOut!=0. Also return a pointer to the result. ** ** This resets the incremental checksum preparing for the next round ** of computation. The return pointer points to a static buffer that |
︙ | ︙ |
Changes to src/merge3.c.
︙ | ︙ | |||
149 150 151 152 153 154 155 | int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ int *aC1; /* Changes from pPivot to pV1 */ int *aC2; /* Changes from pPivot to pV2 */ int i1, i2; /* Index into aC1[] and aC2[] */ int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ int limit1, limit2; /* Sizes of aC1[] and aC2[] */ int nConflict = 0; /* Number of merge conflicts seen so far */ | | | | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ int *aC1; /* Changes from pPivot to pV1 */ int *aC2; /* Changes from pPivot to pV2 */ int i1, i2; /* Index into aC1[] and aC2[] */ int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ int limit1, limit2; /* Sizes of aC1[] and aC2[] */ int nConflict = 0; /* Number of merge conflicts seen so far */ static const char zBegin[] = "<<<<<<< BEGIN MERGE CONFLICT\n"; static const char zMid[] = "============================\n"; static const char zEnd[] = ">>>>>>> END MERGE CONFLICT\n"; blob_zero(pOut); /* Merge results stored in pOut */ /* Compute the edits that occur from pPivot => pV1 (into aC1) ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is ** an array of integer triples. Within each triple, the first integer ** is the number of lines of text to copy directly from the pivot, ** the second integer is the number of lines of text to omit from the ** pivot, and the third integer is the number of lines of text that are ** inserted. The edit array ends with a triple of 0,0,0. */ aC1 = text_diff(pPivot, pV1, 0, 0, 1); aC2 = text_diff(pPivot, pV2, 0, 0, 1); if( aC1==0 || aC2==0 ){ free(aC1); free(aC2); return -1; } blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */ |
︙ | ︙ |
Changes to src/name.c.
︙ | ︙ | |||
109 110 111 112 113 114 115 | return rc; } /* ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. */ static int is_date(const char *z){ | | | | | | | | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | return rc; } /* ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. */ static int is_date(const char *z){ if( !fossil_isdigit(z[0]) ) return 0; if( !fossil_isdigit(z[1]) ) return 0; if( !fossil_isdigit(z[2]) ) return 0; if( !fossil_isdigit(z[3]) ) return 0; if( z[4]!='-') return 0; if( !fossil_isdigit(z[5]) ) return 0; if( !fossil_isdigit(z[6]) ) return 0; if( z[7]!='-') return 0; if( !fossil_isdigit(z[8]) ) return 0; if( !fossil_isdigit(z[9]) ) return 0; return 1; } /* ** Convert a symbolic tag name into the UUID of a check-in that contains ** that tag. If the tag appears on multiple check-ins, return the UUID ** of the most recent check-in with the tag. |
︙ | ︙ | |||
278 279 280 281 282 283 284 | int rid; Blob name; if( zName==0 || zName[0]==0 ) return 0; blob_init(&name, zName, -1); if( name_to_uuid(&name, -1) ){ blob_reset(&name); | | | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | int rid; Blob name; if( zName==0 || zName[0]==0 ) return 0; blob_init(&name, zName, -1); if( name_to_uuid(&name, -1) ){ blob_reset(&name); for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} if( zName[i]==0 ){ rid = atoi(zName); if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ return rid; } } fossil_error(1, "no such artifact: %s", zName); |
︙ | ︙ | |||
346 347 348 349 350 351 352 | Blob name; if( zName==0 || zName[0]==0 ) return 0; blob_init(&name, zName, -1); rc = name_to_uuid(&name, -1); if( rc==1 ){ blob_reset(&name); | | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | Blob name; if( zName==0 || zName[0]==0 ) return 0; blob_init(&name, zName, -1); rc = name_to_uuid(&name, -1); if( rc==1 ){ blob_reset(&name); for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} if( zName[i]==0 ){ rid = atoi(zName); if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ return rid; } } return 0; |
︙ | ︙ |
Added src/popen.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | /* ** Copyright (c) 2010 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains an implementation of a bi-directional popen(). */ #include "config.h" #include "popen.h" #ifdef _WIN32 #include <windows.h> #include <fcntl.h> /* ** Print a fatal error and quit. */ static void win32_fatal_error(const char *zMsg){ fossil_fatal("%s"); } #endif #ifdef _WIN32 /* ** On windows, create a child process and specify the stdin, stdout, ** and stderr channels for that process to use. ** ** Return the number of errors. */ static int win32_create_child_process( char *zCmd, /* The command that the child process will run */ HANDLE hIn, /* Standard input */ HANDLE hOut, /* Standard output */ HANDLE hErr, /* Standard error */ DWORD *pChildPid /* OUT: Child process handle */ ){ STARTUPINFO si; PROCESS_INFORMATION pi; BOOL rc; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE); si.hStdInput = hIn; SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE); si.hStdOutput = hOut; SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE); si.hStdError = hErr; rc = CreateProcess( NULL, /* Application Name */ zCmd, /* Command-line */ NULL, /* Process attributes */ NULL, /* Thread attributes */ TRUE, /* Inherit Handles */ 0, /* Create flags */ NULL, /* Environment */ NULL, /* Current directory */ &si, /* Startup Info */ &pi /* Process Info */ ); if( rc ){ CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); *pChildPid = pi.dwProcessId; }else{ win32_fatal_error("cannot create child process"); } return rc!=0; } #endif /* ** Create a child process running shell command "zCmd". *ppOut is ** a FILE that becomes the standard input of the child process. ** (The caller writes to *ppOut in order to send text to the child.) ** *ppIn is stdout from the child process. (The caller ** reads from *ppIn in order to receive input from the child.) ** Note that *ppIn is an unbuffered file descriptor, not a FILE. ** The process ID of the child is written into *pChildPid. ** ** Return the number of errors. */ int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){ #ifdef _WIN32 HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr; SECURITY_ATTRIBUTES saAttr; DWORD childPid = 0; int fd; saAttr.nLength = sizeof(saAttr); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; hStderr = GetStdHandle(STD_ERROR_HANDLE); if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){ win32_fatal_error("cannot create pipe for stdout"); } SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE); if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){ win32_fatal_error("cannot create pipe for stdin"); } SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE); win32_create_child_process((char*)zCmd, hStdinRd, hStdoutWr, hStderr,&childPid); *pChildPid = childPid; *pfdIn = _open_osfhandle((long)hStdoutRd, 0); fd = _open_osfhandle((long)hStdinWr, 0); *ppOut = _fdopen(fd, "w"); CloseHandle(hStdinRd); CloseHandle(hStdoutWr); return 0; #else int pin[2], pout[2]; *pfdIn = 0; *ppOut = 0; *pChildPid = 0; if( pipe(pin)<0 ){ return 1; } if( pipe(pout)<0 ){ close(pin[0]); close(pin[1]); return 1; } *pChildPid = fork(); if( *pChildPid<0 ){ close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); *pChildPid = 0; return 1; } if( *pChildPid==0 ){ /* This is the child process */ close(0); dup(pout[0]); close(pout[0]); close(pout[1]); close(1); dup(pin[1]); close(pin[0]); close(pin[1]); execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0); return 1; }else{ /* This is the parent process */ close(pin[1]); *pfdIn = pin[0]; close(pout[0]); *ppOut = fdopen(pout[1], "w"); return 0; } #endif } /* ** Close the connection to a child process previously created using ** popen2(). Kill off the child process, then close the pipes. */ void pclose2(int fdIn, FILE *pOut, int childPid){ #ifdef _WIN32 /* Not implemented, yet */ close(fdIn); fclose(pOut); #else close(fdIn); fclose(pOut); kill(childPid, SIGINT); #endif } |
Changes to src/pqueue.c.
︙ | ︙ | |||
60 61 62 63 64 65 66 | pqueue_init(p); } /* ** Change the size of the queue so that it contains N slots */ static void pqueue_resize(PQueue *p, int N){ | | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | pqueue_init(p); } /* ** Change the size of the queue so that it contains N slots */ static void pqueue_resize(PQueue *p, int N){ p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); p->sz = N; } /* ** Insert element e into the queue. */ void pqueue_insert(PQueue *p, int e, double v){ |
︙ | ︙ |
Changes to src/printf.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 | NULL pointers replaced by SQL NULL. %Q */ #define etPOINTER 15 /* The %p conversion */ #define etHTMLIZE 16 /* Make text safe for HTML */ #define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */ #define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */ #define etFOSSILIZE 19 /* The fossil header encoding format. */ #define etPATH 20 /* Path type */ | | | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | NULL pointers replaced by SQL NULL. %Q */ #define etPOINTER 15 /* The %p conversion */ #define etHTMLIZE 16 /* Make text safe for HTML */ #define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */ #define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */ #define etFOSSILIZE 19 /* The fossil header encoding format. */ #define etPATH 20 /* Path type */ #define etWIKISTR 21 /* Wiki text rendered from a char*: %w */ #define etWIKIBLOB 22 /* Wiki text rendered from a Blob*: %W */ #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */ /* ** An "etByte" is an 8-bit unsigned value. */ typedef unsigned char etByte; |
︙ | ︙ | |||
558 559 560 561 562 563 564 | break; case etPATH: { int i; int limit = flag_alternateform ? va_arg(ap,int) : -1; char *e = va_arg(ap,char*); if( e==0 ){e="";} length = StrNLen32(e, limit); | | | 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | break; case etPATH: { int i; int limit = flag_alternateform ? va_arg(ap,int) : -1; char *e = va_arg(ap,char*); if( e==0 ){e="";} length = StrNLen32(e, limit); zExtra = bufpt = fossil_malloc(length+1); for( i=0; i<length; i++ ){ if( e[i]=='\\' ){ bufpt[i]='/'; }else{ bufpt[i]=e[i]; } } |
︙ | ︙ | |||
603 604 605 606 607 608 609 | Blob *pBlob = va_arg(ap, Blob*); char *zOrig = blob_buffer(pBlob); int i, j, n, cnt; n = blob_size(pBlob); if( limit>=0 && limit<n ) n = limit; for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; } if( n+cnt+2 > etBUFSIZE ){ | | | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | Blob *pBlob = va_arg(ap, Blob*); char *zOrig = blob_buffer(pBlob); int i, j, n, cnt; n = blob_size(pBlob); if( limit>=0 && limit<n ) n = limit; for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; } if( n+cnt+2 > etBUFSIZE ){ bufpt = zExtra = fossil_malloc( n + cnt + 2 ); }else{ bufpt = buf; } bufpt[0] = '\''; for(i=0, j=1; i<n; i++, j++){ if( zOrig[i]=='\'' ){ bufpt[j++] = '\''; } bufpt[j] = zOrig[i]; |
︙ | ︙ | |||
632 633 634 635 636 637 638 | if( limit<0 ) limit = strlen(escarg); for(i=n=0; i<limit; i++){ if( escarg[i]=='\'' ) n++; } needQuote = !isnull && xtype==etSQLESCAPE2; n += i + 1 + needQuote*2; if( n>etBUFSIZE ){ | | < | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 | if( limit<0 ) limit = strlen(escarg); for(i=n=0; i<limit; i++){ if( escarg[i]=='\'' ) n++; } needQuote = !isnull && xtype==etSQLESCAPE2; n += i + 1 + needQuote*2; if( n>etBUFSIZE ){ bufpt = zExtra = fossil_malloc( n ); }else{ bufpt = buf; } j = 0; if( needQuote ) bufpt[j++] = '\''; for(i=0; i<limit; i++){ bufpt[j++] = ch = escarg[i]; |
︙ | ︙ |
Changes to src/rebuild.c.
︙ | ︙ | |||
70 71 72 73 74 75 76 | @ CREATE TABLE IF NOT EXISTS concealed( @ hash TEXT PRIMARY KEY, @ content TEXT @ ); ; /* | | > > > > > > > > > > > > > > > > > > > | | < > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | > > > > > > > > | > | | > | | | < < < < < < | > | | | | | | | > | | > > > > > > > | | | | | < > | | > | < | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | @ CREATE TABLE IF NOT EXISTS concealed( @ hash TEXT PRIMARY KEY, @ content TEXT @ ); ; /* ** Variables used to store state information about an on-going "rebuild" ** or "deconstruct". */ static int totalSize; /* Total number of artifacts to process */ static int processCnt; /* Number processed so far */ static int ttyOutput; /* Do progress output */ static Bag bagDone; /* Bag of records rebuilt */ static char *zFNameFormat; /* Format string for filenames on deconstruct */ static int prefixLength; /* Length of directory prefix for deconstruct */ /* ** Draw the percent-complete message. ** The input is actually the permill complete. */ static void percent_complete(int permill){ static int lastOutput = -1; if( permill>lastOutput ){ printf(" %d.%d%% complete...\r", permill/10, permill%10); fflush(stdout); lastOutput = permill; } } /* ** Called after each artifact is processed */ static void rebuild_step_done(rid){ /* assert( bag_find(&bagDone, rid)==0 ); */ bag_insert(&bagDone, rid); if( ttyOutput ){ processCnt++; if (!g.fQuiet && totalSize>0) { percent_complete((processCnt*1000)/totalSize); } } } /* ** Rebuild cross-referencing information for the artifact ** rid with content pBase and all of its descendants. This ** routine clears the content buffer before returning. ** ** If the zFNameFormat variable is set, then this routine is ** called to run "fossil deconstruct" instead of the usual ** "fossil rebuild". In that case, instead of rebuilding the ** cross-referencing information, write the file content out ** to the approriate directory. ** ** In both cases, this routine automatically recurses to process ** other artifacts that are deltas off of the current artifact. ** This is the most efficient way to extract all of the original ** artifact content from the Fossil repository. */ static void rebuild_step(int rid, int size, Blob *pBase){ static Stmt q1; Bag children; Blob copy; Blob *pUse; int nChild, i, cid; while( rid>0 ){ /* Fix up the "blob.size" field if needed. */ if( size!=blob_size(pBase) ){ db_multi_exec( "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid ); } /* Find all children of artifact rid */ db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid"); db_bind_int(&q1, ":rid", rid); bag_init(&children); while( db_step(&q1)==SQLITE_ROW ){ int cid = db_column_int(&q1, 0); if( !bag_find(&bagDone, cid) ){ bag_insert(&children, cid); } } nChild = bag_count(&children); db_reset(&q1); /* Crosslink the artifact */ if( nChild==0 ){ pUse = pBase; }else{ blob_copy(©, pBase); pUse = © } if( zFNameFormat==0 ){ /* We are doing "fossil rebuild" */ manifest_crosslink(rid, pUse); }else{ /* We are doing "fossil deconstruct" */ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); blob_write_to_file(pUse,zFile); free(zFile); free(zUuid); } blob_reset(pUse); rebuild_step_done(rid); /* Call all children recursively */ rid = 0; for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ static Stmt q2; int sz; db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid"); db_bind_int(&q2, ":rid", cid); if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ Blob delta, next; db_ephemeral_blob(&q2, 0, &delta); blob_uncompress(&delta, &delta); blob_delta_apply(pBase, &delta, &next); blob_reset(&delta); db_reset(&q2); if( i<nChild ){ rebuild_step(cid, sz, &next); }else{ /* Tail recursion */ rid = cid; size = sz; blob_reset(pBase); *pBase = next; } }else{ db_reset(&q2); blob_reset(pBase); } } bag_clear(&children); } } /* ** Check to see if the "sym-trunk" tag exists. If not, create it ** and attach it to the very first check-in. */ static void rebuild_tag_trunk(void){ int tagid = db_int(0, "SELECT 1 FROM tag WHERE tagname='sym-trunk'"); int rid; char *zUuid; if( tagid>0 ) return; rid = db_int(0, "SELECT pid FROM plink AS x WHERE NOT EXISTS(" " SELECT 1 FROM plink WHERE cid=x.pid)"); if( rid==0 ) return; /* Add the trunk tag to the root of the whole tree */ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid==0 ) return; tag_add_artifact("sym-", "trunk", zUuid, 0, 2, 0, 0); tag_add_artifact("", "branch", zUuid, "trunk", 2, 0, 0); } /* ** Core function to rebuild the infomration in the derived tables of a ** fossil repository from the blobs. This function is shared between ** 'rebuild_database' ('rebuild') and 'reconstruct_cmd' ** ('reconstruct'), both of which have to regenerate this information ** from scratch. ** ** If the randomize parameter is true, then the BLOBs are deliberately ** extracted in a random order. This feature is used to test the ** ability of fossil to accept records in any order and still ** construct a sane repository. */ int rebuild_db(int randomize, int doOut){ Stmt s; int errCnt = 0; char *zTable; int incrSize; bag_init(&bagDone); ttyOutput = doOut; processCnt = 0; if (!g.fQuiet) { percent_complete(0); } db_multi_exec(zSchemaUpdates); for(;;){ zTable = db_text(0, "SELECT name FROM sqlite_master /*scan*/" " WHERE type='table'" " AND name NOT IN ('blob','delta','rcvfrom','user'," |
︙ | ︙ | |||
237 238 239 240 241 242 243 244 245 246 247 248 249 250 | "DELETE FROM unclustered" " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" ); db_multi_exec( "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" ); totalSize = db_int(0, "SELECT count(*) FROM blob"); db_prepare(&s, "SELECT rid, size FROM blob /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" ); manifest_crosslink_begin(); while( db_step(&s)==SQLITE_ROW ){ | > > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | "DELETE FROM unclustered" " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" ); db_multi_exec( "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" ); totalSize = db_int(0, "SELECT count(*) FROM blob"); incrSize = totalSize/100; totalSize += incrSize*2; db_prepare(&s, "SELECT rid, size FROM blob /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" ); manifest_crosslink_begin(); while( db_step(&s)==SQLITE_ROW ){ |
︙ | ︙ | |||
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); rebuild_step_done(rid); } } db_finalize(&s); manifest_crosslink_end(); rebuild_tag_trunk(); if(!g.fQuiet && ttyOutput ){ printf("\n"); } return errCnt; } /* ** COMMAND: rebuild ** ** Usage: %fossil rebuild ?REPOSITORY? ** ** Reconstruct the named repository database from the core ** records. Run this command after updating the fossil ** executable in a way that changes the database schema. */ void rebuild_database(void){ int forceFlag; int randomizeFlag; int errCnt; forceFlag = find_option("force","f",0)!=0; randomizeFlag = find_option("randomize", 0, 0)!=0; if( g.argc==3 ){ db_open_repository(g.argv[2]); }else{ db_find_and_open_repository(1); if( g.argc!=2 ){ usage("?REPOSITORY-FILENAME?"); } db_close(); db_open_repository(g.zRepositoryName); } db_begin_transaction(); ttyOutput = 1; errCnt = rebuild_db(randomizeFlag, 1); if( errCnt && !forceFlag ){ printf("%d errors. Rolling back changes. Use --force to force a commit.\n", errCnt); db_end_transaction(1); }else{ db_end_transaction(0); } } /* ** COMMAND: test-detach ** | > > > > > > > > > > > > | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); rebuild_step_done(rid); } } db_finalize(&s); manifest_crosslink_end(); rebuild_tag_trunk(); if( !g.fQuiet && totalSize>0 ){ processCnt += incrSize; percent_complete((processCnt*1000)/totalSize); } create_cluster(); if( !g.fQuiet && totalSize>0 ){ processCnt += incrSize; percent_complete((processCnt*1000)/totalSize); } if(!g.fQuiet && ttyOutput ){ printf("\n"); } return errCnt; } /* ** COMMAND: rebuild ** ** Usage: %fossil rebuild ?REPOSITORY? ** ** Reconstruct the named repository database from the core ** records. Run this command after updating the fossil ** executable in a way that changes the database schema. */ void rebuild_database(void){ int forceFlag; int randomizeFlag; int errCnt; int omitVerify; omitVerify = find_option("noverify",0,0)!=0; forceFlag = find_option("force","f",0)!=0; randomizeFlag = find_option("randomize", 0, 0)!=0; if( g.argc==3 ){ db_open_repository(g.argv[2]); }else{ db_find_and_open_repository(1); if( g.argc!=2 ){ usage("?REPOSITORY-FILENAME?"); } db_close(); db_open_repository(g.zRepositoryName); } db_begin_transaction(); ttyOutput = 1; errCnt = rebuild_db(randomizeFlag, 1); if( errCnt && !forceFlag ){ printf("%d errors. Rolling back changes. Use --force to force a commit.\n", errCnt); db_end_transaction(1); }else{ if( omitVerify ) verify_cancel(); db_end_transaction(0); } } /* ** COMMAND: test-detach ** |
︙ | ︙ | |||
338 339 340 341 342 343 344 345 346 347 348 349 350 351 | "UPDATE config SET value=lower(hex(randomblob(20)))" " WHERE name='project-code';" "UPDATE config SET value='detached-' || value" " WHERE name='project-name' AND value NOT GLOB 'detached-*';" ); db_end_transaction(0); } /* ** COMMAND: scrub ** %fossil scrub [--verily] [--force] [REPOSITORY] ** ** The command removes sensitive information (such as passwords) from a ** repository so that the respository can be sent to an untrusted reader. | > > > > > > > > > > > > > > > > > > > > > > | 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | "UPDATE config SET value=lower(hex(randomblob(20)))" " WHERE name='project-code';" "UPDATE config SET value='detached-' || value" " WHERE name='project-name' AND value NOT GLOB 'detached-*';" ); db_end_transaction(0); } /* ** COMMAND: test-create-clusters ** ** Create clusters for all unclustered artifacts if the number of unclustered ** artifacts exceeds the current clustering threshold. */ void test_createcluster_cmd(void){ if( g.argc==3 ){ db_open_repository(g.argv[2]); }else{ db_find_and_open_repository(1); if( g.argc!=2 ){ usage("?REPOSITORY-FILENAME?"); } db_close(); db_open_repository(g.zRepositoryName); } db_begin_transaction(); create_cluster(); db_end_transaction(0); } /* ** COMMAND: scrub ** %fossil scrub [--verily] [--force] [REPOSITORY] ** ** The command removes sensitive information (such as passwords) from a ** repository so that the respository can be sent to an untrusted reader. |
︙ | ︙ | |||
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | db_end_transaction(0); db_multi_exec("VACUUM;"); }else{ rebuild_db(0, 1); db_end_transaction(0); } } /* ** COMMAND: reconstruct ** ** Usage: %fossil reconstruct FILENAME DIRECTORY ** ** This command studies the artifacts (files) in DIRECTORY and ** reconstructs the fossil record from them. It places the new | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > < < < > | < | < < < < | < < < < < < < | | < > > | < < | < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | db_end_transaction(0); db_multi_exec("VACUUM;"); }else{ rebuild_db(0, 1); db_end_transaction(0); } } /* ** Recursively read all files from the directory zPath and install ** every file read as a new artifact in the repository. */ void recon_read_dir(char *zPath){ DIR *d; struct dirent *pEntry; Blob aContent; /* content of the just read artifact */ static int nFileRead = 0; d = opendir(zPath); if( d ){ while( (pEntry=readdir(d))!=0 ){ Blob path; char *zSubpath; if( pEntry->d_name[0]=='.' ){ continue; } zSubpath = mprintf("%s/%s",zPath,pEntry->d_name); if( file_isdir(zSubpath)==1 ){ recon_read_dir(zSubpath); } blob_init(&path, 0, 0); blob_appendf(&path, "%s", zSubpath); if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){ fossil_panic("some unknown error occurred while reading \"%s\"", blob_str(&path)); } content_put(&aContent, 0, 0); blob_reset(&path); blob_reset(&aContent); free(zSubpath); printf("\r%d", ++nFileRead); fflush(stdout); } }else { fossil_panic("encountered error %d while trying to open \"%s\".", errno, g.argv[3]); } } /* ** COMMAND: reconstruct ** ** Usage: %fossil reconstruct FILENAME DIRECTORY ** ** This command studies the artifacts (files) in DIRECTORY and ** reconstructs the fossil record from them. It places the new ** fossil repository in FILENAME. Subdirectories are read, files ** with leading '.' in the filename are ignored. ** */ void reconstruct_cmd(void) { char *zPassword; if( g.argc!=4 ){ usage("FILENAME DIRECTORY"); } if( file_isdir(g.argv[3])!=1 ){ printf("\"%s\" is not a directory\n\n", g.argv[3]); usage("FILENAME DIRECTORY"); } db_create_repository(g.argv[2]); db_open_repository(g.argv[2]); db_open_config(0); db_begin_transaction(); db_initial_setup(0, 0, 1); printf("Reading files from directory \"%s\"...\n", g.argv[3]); recon_read_dir(g.argv[3]); printf("\nBuilding the Fossil repository...\n"); rebuild_db(0, 1); /* Skip the verify_before_commit() step on a reconstruct. Most artifacts ** will have been changed and verification therefore takes a really, really ** long time. */ verify_cancel(); db_end_transaction(0); printf("project-id: %s\n", db_get("project-code", 0)); printf("server-id: %s\n", db_get("server-code", 0)); zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); } /* ** COMMAND: deconstruct ** ** Usage %fossil deconstruct ?-R|--repository REPOSITORY? ?-L|--prefixlength N? DESTINATION ** ** This command exports all artifacts of o given repository and ** writes all artifacts to the file system. The DESTINATION directory ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where ** AABBBBBBBBB.. is the 40 character artifact ID, AA the first 2 characters. ** If -L|--prefixlength is given, the length (default 2) of the directory ** prefix can be set to 0,1,..,9 characters. */ void deconstruct_cmd(void){ const char *zDestDir; const char *zPrefixOpt; Stmt s; /* check number of arguments */ if( (g.argc != 3) && (g.argc != 5) && (g.argc != 7)){ usage ("?-R|--repository REPOSITORY? ?-L|--prefixlength N? DESTINATION"); } /* get and check argument destination directory */ zDestDir = g.argv[g.argc-1]; if( !*zDestDir || !file_isdir(zDestDir)) { fossil_panic("DESTINATION(%s) is not a directory!",zDestDir); } /* get and check prefix length argument and build format string */ zPrefixOpt=find_option("prefixlength","L",1); if( !zPrefixOpt ){ prefixLength = 2; }else{ if( zPrefixOpt[0]>='0' && zPrefixOpt[0]<='9' && !zPrefixOpt[1] ){ prefixLength = (int)(*zPrefixOpt-'0'); }else{ fossil_fatal("N(%s) is not a a valid prefix length!",zPrefixOpt); } } #ifndef _WIN32 if( access(zDestDir, W_OK) ){ fossil_fatal("DESTINATION(%s) is not writeable!",zDestDir); } #else /* write access on windows is not checked, errors will be ** dected on blob_write_to_file */ #endif if( prefixLength ){ zFNameFormat = mprintf("%s/%%.%ds/%%s",zDestDir,prefixLength); }else{ zFNameFormat = mprintf("%s/%%s",zDestDir); } /* open repository and open query for all artifacts */ db_find_and_open_repository(1); bag_init(&bagDone); ttyOutput = 1; processCnt = 0; if (!g.fQuiet) { printf("0 (0%%)...\r"); fflush(stdout); } totalSize = db_int(0, "SELECT count(*) FROM blob"); db_prepare(&s, "SELECT rid, size FROM blob /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" ); while( db_step(&s)==SQLITE_ROW ){ int rid = db_column_int(&s, 0); int size = db_column_int(&s, 1); if( size>=0 ){ Blob content; content_get(rid, &content); rebuild_step(rid, size, &content); } } db_finalize(&s); db_prepare(&s, "SELECT rid, size FROM blob" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" ); while( db_step(&s)==SQLITE_ROW ){ int rid = db_column_int(&s, 0); int size = db_column_int(&s, 1); if( size>=0 ){ if( !bag_find(&bagDone, rid) ){ Blob content; content_get(rid, &content); rebuild_step(rid, size, &content); } } } db_finalize(&s); if(!g.fQuiet && ttyOutput ){ printf("\n"); } /* free filename format string */ free(zFNameFormat); zFNameFormat = 0; } |
Changes to src/report.c.
︙ | ︙ | |||
86 87 88 89 90 91 92 | } /* ** Remove whitespace from both ends of a string. */ char *trim_string(const char *zOrig){ int i; | | | | | | | | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | } /* ** Remove whitespace from both ends of a string. */ char *trim_string(const char *zOrig){ int i; while( fossil_isspace(*zOrig) ){ zOrig++; } i = strlen(zOrig); while( i>0 && fossil_isspace(zOrig[i-1]) ){ i--; } return mprintf("%.*s", i, zOrig); } /* ** Extract a numeric (integer) value from a string. */ char *extract_integer(const char *zOrig){ if( zOrig == NULL || zOrig[0] == 0 ) return ""; while( *zOrig && !fossil_isdigit(*zOrig) ){ zOrig++; } if( *zOrig ){ /* we have a digit. atoi() will get as much of the number as it ** can. We'll run it through mprintf() to get a string. Not ** an efficient way to do it, but effective. */ return mprintf("%d", atoi(zOrig)); } return ""; } /* ** Remove blank lines from the beginning of a string and ** all whitespace from the end. Removes whitespace preceeding a NL, ** which also converts any CRNL sequence into a single NL. */ char *remove_blank_lines(const char *zOrig){ int i, j, n; char *z; for(i=j=0; fossil_isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; } n = strlen(&zOrig[j]); while( n>0 && fossil_isspace(zOrig[j+n-1]) ){ n--; } z = mprintf("%.*s", n, &zOrig[j]); for(i=j=0; z[i]; i++){ if( z[i+1]=='\n' && z[i]!='\n' && fossil_isspace(z[i]) ){ z[j] = z[i]; while(fossil_isspace(z[j]) && z[j] != '\n' ){ j--; } j++; continue; } z[j++] = z[i]; } z[j] = 0; return z; } /*********************************************************************/ /* ** This is the SQLite authorizer callback used to make sure that the ** SQL statements entered by users do not try to do anything untoward. ** If anything suspicious is tried, set *(char**)pError to an error ** message obtained from malloc. */ int report_query_authorizer( void *pError, int code, const char *zArg1, const char *zArg2, const char *zArg3, const char *zArg4 ){ |
︙ | ︙ | |||
210 211 212 213 214 215 216 | const char *zTail; sqlite3_stmt *pStmt; int rc; /* First make sure the SQL is a single query command by verifying that ** the first token is "SELECT" and that there are no unquoted semicolons. */ | | | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | const char *zTail; sqlite3_stmt *pStmt; int rc; /* First make sure the SQL is a single query command by verifying that ** the first token is "SELECT" and that there are no unquoted semicolons. */ for(i=0; fossil_isspace(zSql[i]); i++){} if( strncasecmp(&zSql[i],"select",6)!=0 ){ return mprintf("The SQL must be a SELECT statement"); } for(i=0; zSql[i]; i++){ if( zSql[i]==';' ){ int bad; int c = zSql[i+1]; |
︙ | ︙ | |||
275 276 277 278 279 280 281 | } zTitle = db_column_text(&q, 0); zSQL = db_column_text(&q, 1); zOwner = db_column_text(&q, 2); zClrKey = db_column_text(&q, 3); @ <table cellpadding=0 cellspacing=0 border=0> @ <tr><td valign="top" align="right">Title:</td><td width=15></td> | | | | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | } zTitle = db_column_text(&q, 0); zSQL = db_column_text(&q, 1); zOwner = db_column_text(&q, 2); zClrKey = db_column_text(&q, 3); @ <table cellpadding=0 cellspacing=0 border=0> @ <tr><td valign="top" align="right">Title:</td><td width=15></td> @ <td colspan="3">%h(zTitle)</td></tr> @ <tr><td valign="top" align="right">Owner:</td><td></td> @ <td colspan="3">%h(zOwner)</td></tr> @ <tr><td valign="top" align="right">SQL:</td><td></td> @ <td valign="top"><pre> @ %h(zSQL) @ </pre></td> @ <td width=15></td><td valign="top"> output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3"); @ </td> |
︙ | ︙ | |||
326 327 328 329 330 331 332 | return; }else if( rn>0 && P("del1") ){ zTitle = db_text(0, "SELECT title FROM reportfmt " "WHERE rn=%d", rn); if( zTitle==0 ) cgi_redirect("reportlist"); style_header("Are You Sure?"); | | | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | return; }else if( rn>0 && P("del1") ){ zTitle = db_text(0, "SELECT title FROM reportfmt " "WHERE rn=%d", rn); if( zTitle==0 ) cgi_redirect("reportlist"); style_header("Are You Sure?"); @ <form action="rptedit" method="post"> @ <p>You are about to delete all traces of the report @ <strong>%h(zTitle)</strong> from @ the database. This is an irreversible operation. All records @ related to this report will be removed and cannot be recovered.</p> @ @ <input type="hidden" name="rn" value="%d(rn)"> login_insert_csrf_secret(); |
︙ | ︙ | |||
391 392 393 394 395 396 397 | zTitle = mprintf("Copy Of %s", zTitle); zOwner = g.zLogin; } } if( zOwner==0 ) zOwner = g.zLogin; style_submenu_element("Cancel", "Cancel", "reportlist"); if( rn>0 ){ | | | | | | | | | | | | | | | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 | zTitle = mprintf("Copy Of %s", zTitle); zOwner = g.zLogin; } } if( zOwner==0 ) zOwner = g.zLogin; style_submenu_element("Cancel", "Cancel", "reportlist"); if( rn>0 ){ style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn); } style_header(rn>0 ? "Edit Report Format":"Create New Report Format"); if( zErr ){ @ <blockquote class="reportError">%h(zErr)</blockquote> } @ <form action="rptedit" method="post"><div> @ <input type="hidden" name="rn" value="%d(rn)" /> @ <p>Report Title:<br /> @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p> @ <p>Enter a complete SQL query statement against the "TICKET" table:<br /> @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea> @ </p> login_insert_csrf_secret(); if( g.okAdmin ){ @ <p>Report owner: @ <input type="text" name="w" size="20" value="%h(zOwner)" /> @ </p> } else { @ <input type="hidden" name="w" value="%h(zOwner)" /> } @ <p>Enter an optional color key in the following box. (If blank, no @ color key is displayed.) Each line contains the text for a single @ entry in the key. The first token of each line is the background @ color for that line.<br /> @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea> @ </p> if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){ @ <p>This report format is owned by %h(zOwner). You are not allowed @ to change it.</p> @ </form> report_format_hints(); style_footer(); return; } @ <input type="submit" value="Apply Changes" /> if( rn>0 ){ @ <input type="submit" value="Delete This Report" name="del1" /> } @ </div></form> report_format_hints(); style_footer(); } /* ** Output a bunch of text that provides information about report ** formats */ static void report_format_hints(void){ char *zSchema; zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'"); if( zSchema==0 ){ zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master" " WHERE name='ticket'"); } @ <hr /><h3>TICKET Schema</h3> @ <blockquote><pre> @ %h(zSchema) @ </pre></blockquote> @ <h3>Notes</h3> @ <ul> @ <li><p>The SQL must consist of a single SELECT statement</p></li> @ |
︙ | ︙ | |||
476 477 478 479 480 481 482 | @ @ <h3>Examples</h3> @ <p>In this example, the first column in the result set is named @ "bgcolor". The value of this column is not displayed. Instead, it @ selects the background color of each row based on the TICKET.STATUS @ field of the database. The color key at the right shows the various @ color codes.</p> | | | | | | | | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 | @ @ <h3>Examples</h3> @ <p>In this example, the first column in the result set is named @ "bgcolor". The value of this column is not displayed. Instead, it @ selects the background color of each row based on the TICKET.STATUS @ field of the database. The color key at the right shows the various @ color codes.</p> @ <table class="rpteditex"> @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">new or active</td></tr> @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">review</td></tr> @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">fixed</td></tr> @ <tr style="background-color:#bde5d6;"><td class="rpteditex">tested</td></tr> @ <tr style="background-color:#cacae5;"><td class="rpteditex">defer</td></tr> @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">closed</td></tr> @ </table> @ <blockquote><pre> @ SELECT @ CASE WHEN status IN ('new','active') THEN '#f2dcdc' @ WHEN status='review' THEN '#e8e8bd' @ WHEN status='fixed' THEN '#cfe8bd' @ WHEN status='tested' THEN '#bde5d6' |
︙ | ︙ | |||
508 509 510 511 512 513 514 | @ priority AS 'Pri', @ title AS 'Title' @ FROM ticket @ </pre></blockquote> @ <p>To base the background color on the TICKET.PRIORITY or @ TICKET.SEVERITY fields, substitute the following code for the @ first column of the query:</p> | | | | | | | | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | @ priority AS 'Pri', @ title AS 'Title' @ FROM ticket @ </pre></blockquote> @ <p>To base the background color on the TICKET.PRIORITY or @ TICKET.SEVERITY fields, substitute the following code for the @ first column of the query:</p> @ <table class="rpteditex"> @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">1</td></tr> @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">2</td></tr> @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">3</td></tr> @ <tr style="background-color:#cacae5;"><td class="rpteditex">4</td></tr> @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">5</td></tr> @ </table> @ <blockquote><pre> @ SELECT @ CASE priority WHEN 1 THEN '#f2dcdc' @ WHEN 2 THEN '#e8e8bd' @ WHEN 3 THEN '#cfe8bd' @ WHEN 4 THEN '#cacae5' |
︙ | ︙ | |||
581 582 583 584 585 586 587 | @ title AS 'Title', @ wiki(substr(description,0,80)) AS 'Description' @ FROM ticket @ </pre></blockquote> @ } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | @ title AS 'Title', @ wiki(substr(description,0,80)) AS 'Description' @ FROM ticket @ </pre></blockquote> @ } /* ** The state of the report generation. */ struct GenerateHTML { int rn; /* Report number */ int nCount; /* Row number */ int nCol; /* Number of columns */ |
︙ | ︙ | |||
721 722 723 724 725 726 727 | @ <tr><td colspan=%d(pState->nCol)><font size=1> </font></td></tr> } /* Output the data for this entry from the database */ zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; if( zBg==0 ) zBg = "white"; | | | | 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 | @ <tr><td colspan=%d(pState->nCol)><font size=1> </font></td></tr> } /* Output the data for this entry from the database */ zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; if( zBg==0 ) zBg = "white"; @ <tr style="background-color:%h(zBg)"> zTid = 0; zPage[0] = 0; for(i=0; i<nArg; i++){ char *zData; if( i==pState->iBg ) continue; zData = azArg[i]; if( zData==0 ) zData = ""; if( pState->iNewRow>=0 && i>=pState->iNewRow ){ if( zTid && g.okWrite ){ @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td> zTid = 0; } if( zData[0] ){ Blob content; @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)> blob_init(&content, zData, -1); wiki_convert(&content, 0, 0); blob_reset(&content); } }else if( azName[i][0]=='#' ){ zTid = zData; if( g.okHistory ){ |
︙ | ︙ | |||
770 771 772 773 774 775 776 | /* ** Output the text given in the argument. Convert tabs and newlines into ** spaces. */ static void output_no_tabs(const char *z){ while( z && z[0] ){ int i, j; | | | | 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | /* ** Output the text given in the argument. Convert tabs and newlines into ** spaces. */ static void output_no_tabs(const char *z){ while( z && z[0] ){ int i, j; for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} if( i>0 ){ cgi_printf("%.*s", i, z); } for(j=i; fossil_isspace(z[j]); j++){} if( j>i ){ cgi_printf("%*s", j-i, ""); } z += j; } } |
︙ | ︙ | |||
814 815 816 817 818 819 820 | /* ** Generate HTML that describes a color key. */ void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ int i, j, k; char *zSafeKey, *zToFree; | | | | | | | | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 | /* ** Generate HTML that describes a color key. */ void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ int i, j, k; char *zSafeKey, *zToFree; while( fossil_isspace(*zClrKey) ) zClrKey++; if( zClrKey[0]==0 ) return; @ <table %s(zTabArgs)> if( horiz ){ @ <tr> } zToFree = zSafeKey = mprintf("%h", zClrKey); while( zSafeKey[0] ){ while( fossil_isspace(*zSafeKey) ) zSafeKey++; for(i=0; zSafeKey[i] && !fossil_isspace(zSafeKey[i]); i++){} for(j=i; fossil_isspace(zSafeKey[j]); j++){} for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){} if( !horiz ){ cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n", i, zSafeKey, k-j, &zSafeKey[j]); }else{ cgi_printf("<td style=\"background-color: %.*s;\">%.*s</td>\n", i, zSafeKey, k-j, &zSafeKey[j]); } zSafeKey += k; } free(zToFree); if( horiz ){ @ </tr> |
︙ | ︙ | |||
905 906 907 908 909 910 911 | count = 0; if( !tabs ){ struct GenerateHTML sState; db_multi_exec("PRAGMA empty_result_callbacks=ON"); style_submenu_element("Raw", "Raw", | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 | count = 0; if( !tabs ){ struct GenerateHTML sState; db_multi_exec("PRAGMA empty_result_callbacks=ON"); style_submenu_element("Raw", "Raw", "rptview?tablist=1&%h", PD("QUERY_STRING","")); if( g.okAdmin || (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){ style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn); } if( g.okTktFmt ){ style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn); } if( g.okNewTkt ){ style_submenu_element("New Ticket", "Create a new ticket", "%s/tktnew", g.zTop); } style_header(zTitle); output_color_key(zClrKey, 1, "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\""); @ <table border="1" cellpadding="2" cellspacing="0" class="report"> sState.rn = rn; sState.nCount = 0; sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2); sqlite3_set_authorizer(g.db, 0, 0); @ </table> if( zErr1 ){ @ <p class="reportError">Error: %h(zErr1)</p> }else if( zErr2 ){ @ <p class="reportError">Error: %h(zErr2)</p> } style_footer(); }else{ sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2); sqlite3_set_authorizer(g.db, 0, 0); cgi_set_content_type("text/plain"); } } /* ** report number for full table ticket export */ static const char zFullTicketRptRn[] = "0"; /* ** report title for full table ticket export */ static const char zFullTicketRptTitle[] = "full ticket export"; /* ** show all reports, which can be used for ticket show. ** Output is written to stdout as tab delimited table */ void rpt_list_reports(void){ Stmt q; char const aRptOutFrmt[] = "%s\t%s\n"; printf("Available reports:\n"); printf(aRptOutFrmt,"report number","report title"); printf(aRptOutFrmt,zFullTicketRptRn,zFullTicketRptTitle); db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn"); while( db_step(&q)==SQLITE_ROW ){ const char *zRn = db_column_text(&q, 0); const char *zTitle = db_column_text(&q, 1); printf(aRptOutFrmt,zRn,zTitle); } db_finalize(&q); } /* ** user defined separator used by ticket show command */ static const char *zSep = 0; /* ** select the quoting algorithm for "ticket show" */ #if INTERFACE typedef enum eTktShowEnc { tktNoTab=0, tktFossilize=1 } tTktShowEncoding; #endif static tTktShowEncoding tktEncode = tktNoTab; /* ** Output the text given in the argument. Convert tabs and newlines into ** spaces. */ static void output_no_tabs_file(const char *z){ switch( tktEncode ){ case tktFossilize: { char *zFosZ; if( z && *z ){ zFosZ = fossilize(z,-1); printf("%s",zFosZ); free(zFosZ); } break; } default: while( z && z[0] ){ int i, j; for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} if( i>0 ){ printf("%.*s", i, z); } for(j=i; fossil_isspace(z[j]); j++){} if( j>i ){ printf("%*s", j-i, ""); } z += j; } break; } } /* ** Output a row as a tab-separated line of text. */ int output_separated_file( void *pUser, /* Pointer to row-count integer */ int nArg, /* Number of columns in this result row */ char **azArg, /* Text of data in all columns */ char **azName /* Names of the columns */ ){ int *pCount = (int*)pUser; int i; if( *pCount==0 ){ for(i=0; i<nArg; i++){ output_no_tabs_file(azName[i]); printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); } } ++*pCount; for(i=0; i<nArg; i++){ output_no_tabs_file(azArg[i]); printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); } return 0; } /* ** Generate a report. The rn query parameter is the report number. ** The output is written to stdout as flat file. The zFilter paramater ** is a full WHERE-condition. */ void rptshow( const char *zRep, const char *zSepIn, const char *zFilter, tTktShowEncoding enc ){ Stmt q; char *zSql; const char *zTitle; const char *zOwner; const char *zClrKey; char *zErr1 = 0; char *zErr2 = 0; int count = 0; int rn; if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){ zTitle = zFullTicketRptTitle; zSql = "SELECT * FROM ticket"; zOwner = g.zLogin; zClrKey = ""; }else{ rn = atoi(zRep); if( rn ){ db_prepare(&q, "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn); }else{ db_prepare(&q, "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep); } if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); rpt_list_reports(); fossil_fatal("unkown report format(%s)!",zRep); } zTitle = db_column_malloc(&q, 0); zSql = db_column_malloc(&q, 1); zOwner = db_column_malloc(&q, 2); zClrKey = db_column_malloc(&q, 3); db_finalize(&q); } if( zFilter ){ zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); } count = 0; tktEncode = enc; zSep = zSepIn; sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); sqlite3_exec(g.db, zSql, output_separated_file, &count, &zErr2); sqlite3_set_authorizer(g.db, 0, 0); if( zFilter ){ free(zSql); } } |
Changes to src/rss.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to create a RSS feed for the CGI interface. */ #include "config.h" | | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to create a RSS feed for the CGI interface. */ #include "config.h" #include <time.h> #include "rss.h" #include <assert.h> /* ** WEBPAGE: timeline.rss */ void page_timeline_rss(void){ Stmt q; int nLine=0; |
︙ | ︙ |
Changes to src/schema.c.
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 248 249 250 251 252 | @ @ -- A record of phantoms. A phantom is a record for which we know the @ -- UUID but we do not (yet) know the file content. @ -- @ CREATE TABLE phantom( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ @ -- Unclustered records. An unclustered record is a record (including @ -- a cluster records themselves) that is not mentioned by some other @ -- cluster. @ -- @ -- Phantoms are usually included in the unclustered table. A new cluster @ -- will never be created that contains a phantom. But another repository | > > > > > > > > > > | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | @ @ -- A record of phantoms. A phantom is a record for which we know the @ -- UUID but we do not (yet) know the file content. @ -- @ CREATE TABLE phantom( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ @ -- A record of orphaned delta-manifests. An orphan is a delta-manifest @ -- for which we have content, but its baseline-manifest is a phantom. @ -- We have to track all orphan maniftests so that when the baseline arrives, @ -- we know to process the orphaned deltas. @ CREATE TABLE orphan( @ rid INTEGER PRIMARY KEY, -- Delta manifest with a phantom baseline @ baseline INTEGER -- Phantom baseline of this orphan @ ); @ CREATE INDEX orphan_baseline ON orphan(baseline); @ @ -- Unclustered records. An unclustered record is a record (including @ -- a cluster records themselves) that is not mentioned by some other @ -- cluster. @ -- @ -- Phantoms are usually included in the unclustered table. A new cluster @ -- will never be created that contains a phantom. But another repository |
︙ | ︙ |
Changes to src/search.c.
︙ | ︙ | |||
40 41 42 43 44 45 46 | */ Search *search_init(const char *zPattern){ int nPattern = strlen(zPattern); Search *p; char *z; int i; | | < | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | */ Search *search_init(const char *zPattern){ int nPattern = strlen(zPattern); Search *p; char *z; int i; p = fossil_malloc( nPattern + sizeof(*p) + 1); z = (char*)&p[1]; strcpy(z, zPattern); memset(p, 0, sizeof(*p)); while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){ while( !fossil_isalnum(*z) && *z ){ z++; } if( *z==0 ) break; p->a[p->nTerm].z = z; for(i=1; fossil_isalnum(z[i]) || z[i]=='_'; i++){} p->a[p->nTerm].n = i; z += i; p->nTerm++; } return p; } |
︙ | ︙ |
Changes to src/setup.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** ** Implementation of the Setup page */ #include <assert.h> #include "config.h" #include "setup.h" /* ** Output a single entry for a menu generated using an HTML table. ** If zLink is not NULL or an empty string, then it is the page that ** the menu entry will hyperlink to. If zLink is NULL or "", then ** the menu entry has no hyperlink - it is disabled. */ | > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** ** Implementation of the Setup page */ #include <assert.h> #include "config.h" #include "setup.h" /* ** The table of web pages supported by this application is generated ** automatically by the "mkindex" program and written into a file ** named "page_index.h". We include that file here to get access ** to the table. */ #include "page_index.h" /* ** Output a single entry for a menu generated using an HTML table. ** If zLink is not NULL or an empty string, then it is the page that ** the menu entry will hyperlink to. If zLink is NULL or "", then ** the menu entry has no hyperlink - it is disabled. */ |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 68 | @ <table border="0" cellspacing="20"> setup_menu_entry("Users", "setup_ulist", "Grant privileges to individual users."); setup_menu_entry("Access", "setup_access", "Control access settings."); setup_menu_entry("Configuration", "setup_config", "Configure the WWW components of the repository"); setup_menu_entry("Timeline", "setup_timeline", "Timeline display preferences"); setup_menu_entry("Tickets", "tktsetup", "Configure the trouble-ticketing system for this repository"); setup_menu_entry("Skins", "setup_skin", "Select from a menu of prepackaged \"skins\" for the web interface"); setup_menu_entry("CSS", "setup_editcss", | > > | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | @ <table border="0" cellspacing="20"> setup_menu_entry("Users", "setup_ulist", "Grant privileges to individual users."); setup_menu_entry("Access", "setup_access", "Control access settings."); setup_menu_entry("Configuration", "setup_config", "Configure the WWW components of the repository"); setup_menu_entry("Settings", "setup_settings", "Web interface to the \"fossil settings\" command"); setup_menu_entry("Timeline", "setup_timeline", "Timeline display preferences"); setup_menu_entry("Tickets", "tktsetup", "Configure the trouble-ticketing system for this repository"); setup_menu_entry("Skins", "setup_skin", "Select from a menu of prepackaged \"skins\" for the web interface"); setup_menu_entry("CSS", "setup_editcss", |
︙ | ︙ | |||
97 98 99 100 101 102 103 | if( !g.okAdmin ){ login_needed(); return; } style_submenu_element("Add", "Add User", "setup_uedit"); style_header("User List"); | | | | < | | | | | | | | < | | | | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | if( !g.okAdmin ){ login_needed(); return; } style_submenu_element("Add", "Add User", "setup_uedit"); style_header("User List"); @ <table class="usetupLayoutTable"> @ <tr><td class="usetupColumnLayout"> @ <span class="note">Users:</span> @ <table class="usetupUserList"> @ <tr> @ <th class="usetupListUser" style="text-align: right;padding-right: 20px;">User ID</th> @ <th class="usetupListCap" style="text-align: center;padding-right: 15px;">Capabilities</th> @ <th class="usetupListCon" style="text-align: left;">Contact Info</th> @ </tr> db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login"); while( db_step(&s)==SQLITE_ROW ){ const char *zCap = db_column_text(&s, 2); if( strstr(zCap, "s") ) zCap = "s"; @ <tr> @ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;"> if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){ @ <a href="setup_uedit?id=%d(db_column_int(&s,0))"> } @ %h(db_column_text(&s,1)) if( g.okAdmin ){ @ </a> } @ </td> @ <td class="usetupListCap" style="text-align: center;padding-right: 15px;">%s(zCap)</td> @ <td class="usetupListCon" style="text-align: left;">%s(db_column_text(&s,3))</td> @ </tr> } @ </table> @ </td><td class="usetupColumnLayout"> @ <span class="note">Notes:</span> @ <ol> @ <li><p>The permission flags are as follows:</p> @ <table> @ <tr><td valign="top"><b>a</b></td> @ <td><i>Admin:</i> Create and delete users</td></tr> @ <tr><td valign="top"><b>b</b></td> @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr> |
︙ | ︙ | |||
179 180 181 182 183 184 185 | @ <tr><td valign="top"><b>v</b></td> @ <td><i>Developer:</i> Inherit privileges of @ user <tt>developer</tt></td></tr> @ <tr><td valign="top"><b>w</b></td> @ <td><i>Write-Tkt:</i> Edit tickets</td></tr> @ <tr><td valign="top"><b>z</b></td> @ <td><i>Zip download:</i> Download a baseline via the | | > | | > | | | | | | > | > | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | @ <tr><td valign="top"><b>v</b></td> @ <td><i>Developer:</i> Inherit privileges of @ user <tt>developer</tt></td></tr> @ <tr><td valign="top"><b>w</b></td> @ <td><i>Write-Tkt:</i> Edit tickets</td></tr> @ <tr><td valign="top"><b>z</b></td> @ <td><i>Zip download:</i> Download a baseline via the @ <tt>/zip</tt> URL even without @ check<span class="capability">o</span>ut @ and <span class="capability">h</span>istory permissions</td></tr> @ </table> @ </li> @ @ <li><p> @ Every user, logged in or not, inherits the privileges of @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Any human can login as <span class="usertype">anonymous</span> since the @ password is clearly displayed on the login page for them to type. The @ purpose of requiring anonymous to log in is to prevent access by spiders. @ Every logged-in user inherits the combined privileges of @ <span class="usertype">anonymous</span> and @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Users with privilege <span class="capability">v</span> inherit the combined @ privileges of <span class="usertype">developer</span>, @ <span class="usertype">anonymous</span>, and @ <span class="usertype">nobody</span>. @ </p></li> @ @ </ol> @ </td></tr></table> style_footer(); } |
︙ | ︙ | |||
321 322 323 324 325 326 327 | }else{ zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid); } if( uid>0 && db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid) ){ style_header("User Creation Error"); | | | | 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | }else{ zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid); } if( uid>0 && db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid) ){ style_header("User Creation Error"); @ <span class="loginError">Login "%h(zLogin)" is already used by @ a different user.</span> @ @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p> style_footer(); return; } login_verify_csrf_secret(); db_multi_exec( |
︙ | ︙ | |||
351 352 353 354 355 356 357 | oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam = oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = ""; if( uid ){ zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); | | | | | | | | | | | | | | | | | | | | | | | | > | > | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | | > | | | | | | > | | > | | | > | | | | | | > | | | | | > | > | | | > | > | | | > > | > > | > | | | | | > | > | | | > > | | | > | | | | | > | | | | | | | | > | | | | > | > | | > | < | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam = oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = ""; if( uid ){ zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); if( strchr(zCap, 'a') ) oaa = " checked=\"checked\""; if( strchr(zCap, 'b') ) oab = " checked=\"checked\""; if( strchr(zCap, 'c') ) oac = " checked=\"checked\""; if( strchr(zCap, 'd') ) oad = " checked=\"checked\""; if( strchr(zCap, 'e') ) oae = " checked=\"checked\""; if( strchr(zCap, 'f') ) oaf = " checked=\"checked\""; if( strchr(zCap, 'g') ) oag = " checked=\"checked\""; if( strchr(zCap, 'h') ) oah = " checked=\"checked\""; if( strchr(zCap, 'i') ) oai = " checked=\"checked\""; if( strchr(zCap, 'j') ) oaj = " checked=\"checked\""; if( strchr(zCap, 'k') ) oak = " checked=\"checked\""; if( strchr(zCap, 'm') ) oam = " checked=\"checked\""; if( strchr(zCap, 'n') ) oan = " checked=\"checked\""; if( strchr(zCap, 'o') ) oao = " checked=\"checked\""; if( strchr(zCap, 'p') ) oap = " checked=\"checked\""; if( strchr(zCap, 'r') ) oar = " checked=\"checked\""; if( strchr(zCap, 's') ) oas = " checked=\"checked\""; if( strchr(zCap, 't') ) oat = " checked=\"checked\""; if( strchr(zCap, 'u') ) oau = " checked=\"checked\""; if( strchr(zCap, 'v') ) oav = " checked=\"checked\""; if( strchr(zCap, 'w') ) oaw = " checked=\"checked\""; if( strchr(zCap, 'z') ) oaz = " checked=\"checked\""; } /* figure out inherited permissions */ memset(inherit, 0, sizeof(inherit)); if( strcmp(zLogin, "developer") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritDeveloper\">•</span>"; } free(z2); } if( strcmp(zLogin, "reader") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritReader\">•</span>"; } free(z2); } if( strcmp(zLogin, "anonymous") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritAnonymous\">•</span>"; } free(z2); } if( strcmp(zLogin, "nobody") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritNobody\">•</span>"; } free(z2); } /* Begin generating the page */ style_submenu_element("Cancel", "Cancel", "setup_ulist"); if( uid ){ style_header(mprintf("Edit User %h", zLogin)); }else{ style_header("Add A New User"); } @ <div class="ueditCapBox"> @ <form action="%s(g.zPath)" method="post"><div> login_insert_csrf_secret(); @ <table> @ <tr> @ <td class="usetupEditLabel">User ID:</td> if( uid ){ @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td> }else{ @ <td>(new user)<input type="hidden" name="id" value="0" /></td> } @ </tr> @ <tr> @ <td class="usetupEditLabel">Login:</td> @ <td><input type="text" name="login" value="%h(zLogin)" /></td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Contact Info:</td> @ <td><input type="text" name="info" size="40" value="%h(zInfo)" /></td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Capabilities:</td> @ <td> #define B(x) inherit[x] if( g.okSetup ){ @ <input type="checkbox" name="as"%s(oas) />%s(B('s'))Setup<br /> } @ <input type="checkbox" name="aa"%s(oaa) />%s(B('a'))Admin<br /> @ <input type="checkbox" name="ad"%s(oad) />%s(B('d'))Delete<br /> @ <input type="checkbox" name="ae"%s(oae) />%s(B('e'))Email<br /> @ <input type="checkbox" name="ap"%s(oap) />%s(B('p'))Password<br /> @ <input type="checkbox" name="ai"%s(oai) />%s(B('i'))Check-In<br /> @ <input type="checkbox" name="ao"%s(oao) />%s(B('o'))Check-Out<br /> @ <input type="checkbox" name="ah"%s(oah) />%s(B('h'))History<br /> @ <input type="checkbox" name="au"%s(oau) />%s(B('u'))Reader<br /> @ <input type="checkbox" name="av"%s(oav) />%s(B('v'))Developer<br /> @ <input type="checkbox" name="ag"%s(oag) />%s(B('g'))Clone<br /> @ <input type="checkbox" name="aj"%s(oaj) />%s(B('j'))Read Wiki<br /> @ <input type="checkbox" name="af"%s(oaf) />%s(B('f'))New Wiki<br /> @ <input type="checkbox" name="am"%s(oam) />%s(B('m'))Append Wiki<br /> @ <input type="checkbox" name="ak"%s(oak) />%s(B('k'))Write Wiki<br /> @ <input type="checkbox" name="ab"%s(oab) />%s(B('b'))Attachments<br /> @ <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br /> @ <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br /> @ <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br /> @ <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br /> @ <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br /> @ <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip @ </td> @ </tr> @ <tr> @ <td align="right">Password:</td> if( zPw[0] ){ /* Obscure the password for all users */ @ <td><input type="password" name="pw" value="**********" /></td> }else{ /* Show an empty password as an empty input field */ @ <td><input type="password" name="pw" value="" /></td> } @ </tr> if( !higherUser ){ @ <tr> @ <td> </td> @ <td><input type="submit" name="submit" value="Apply Changes" /></td> @ </tr> } @ </table> @ </div></form> @ </div> @ <h2>Privileges And Capabilities:</h2> @ <ul> if( higherUser ){ @ <li><p class=missingPriv"> @ User %h(zLogin) has Setup privileges and you only have Admin privileges @ so you are not permitted to make changes to %h(zLogin). @ </p></li> @ } @ <li><p> @ The <span class="capability">Setup</span> user can make arbitrary @ configuration changes. An <span class="usertype">Admin</span> user @ can add other users and change user privileges @ and reset user passwords. Both automatically get all other privileges @ listed below. Use these two settings with discretion. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritNobody"><big>•</big></span>" mark @ indicates the privileges of <span class="usertype">nobody</span> that @ are available to all users regardless of whether or not they are logged in. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritAnonymous"><big>•</big></span>" mark @ indicates the privileges of <span class="usertype">anonymous</span> that @ are inherited by all logged-in users. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritDeveloper"><big>•</big></span>" mark @ indicates the privileges of <span class="usertype">developer</span> that @ are inherited by all users with the @ <span class="capability">Developer</span> privilege. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritReader"><big>•</big></span>" mark @ indicates the privileges of <span class="usertype">reader</span> that @ are inherited by all users with the <span class="capability">Reader</span> @ privilege. @ </p></li> @ @ <li><p> @ The <span class="capability">Delete</span> privilege give the user the @ ability to erase wiki, tickets, and attachments that have been added @ by anonymous users. This capability is intended for deletion of spam. @ The delete capability is only in effect for 24 hours after the item @ is first posted. The <span class="usertype">Setup</span> user can @ delete anything at any time. @ </p></li> @ @ <li><p> @ The <span class="capability">History</span> privilege allows a user @ to see most hyperlinks. This is recommended ON for most logged-in users @ but OFF for user "nobody" to avoid problems with spiders trying to walk @ every historical version of every baseline and file. @ </p></li> @ @ <li><p> @ The <span class="capability">Zip</span> privilege allows a user to @ see the "download as ZIP" @ hyperlink and permits access to the <tt>/zip</tt> page. This allows @ users to download ZIP archives without granting other rights like @ <span class="capability">Read</span> or @ <span class="capability">History</span>. This privilege is recommended for @ user <span class="usertype">nobody</span> so that automatic package @ downloaders can obtain the sources without going through the login @ procedure. @ </p></li> @ @ <li><p> @ The <span class="capability">Check-in</span> privilege allows remote @ users to "push". The <span class="capability">Check-out</span> privilege @ allows remote users to "pull". The <span class="capability">Clone</span> @ privilege allows remote users to "clone". @ </p></li> @ @ <li><p> @ The <span class="capability">Read Wiki</span>, @ <span class="capability">New Wiki</span>, @ <span class="capability">Append Wiki</span>, and @ <b>Write Wiki</b> privileges control access to wiki pages. The @ <span class="capability">Read Ticket</span>, @ <span class="capability">New Ticket</span>, @ <span class="capability">Append Ticket</span>, and @ <span class="capability">Write Ticket</span> privileges control access @ to trouble tickets. @ The <span class="capability">Ticket Report</span> privilege allows @ the user to create or edit ticket report formats. @ </p></li> @ @ <li><p> @ Users with the <span class="capability">Password</span> privilege @ are allowed to change their own password. Recommended ON for most @ users but OFF for special users <span class="usertype">developer</span>, @ <span class="usertype">anonymous</span>, @ and <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ The <span class="capability">EMail</span> privilege allows the display of @ sensitive information such as the email address of users and contact @ information on tickets. Recommended OFF for @ <span class="usertype">anonymousy</span> and for @ <span class="usertype">nobody</span> but ON for @ <span class="usertype">developer</span>. @ </p></li> @ @ <li><p> @ The <span class="capability">Attachment</span> privilege is needed in @ order to add attachments to tickets or wiki. Write privilege on the @ ticket or wiki is also required. @ </p></li> @ @ <li><p> @ Login is prohibited if the password is an empty string. @ </p></li> @ </ul> @ @ <h2>Special Logins</h2> @ @ <ul> @ <li><p> @ No login is required for user <span class="usertype">nobody</span>. The @ capabilities of the <span class="usertype">nobody</span> user are @ inherited by all users, regardless of whether or not they are logged in. @ To disable universal access to the repository, make sure no user named @ <span class="usertype">nobody</span> exists or that the @ <span class="usertype">nobody</span> user has no capabilities @ enabled. The password for <span class="usertype">nobody</span> is ignore. @ To avoid problems with spiders overloading the server, it is recommended @ that the <span class="capability">h</span> (History) capability be turned @ off for the <span class="usertype">nobody</span> user. @ </p></li> @ @ <li><p> @ Login is required for user <span class="usertype">anonymous</span> but the @ password is displayed on the login screen beside the password entry box @ so anybody who can read should be able to login as anonymous. @ On the other hand, spiders and web-crawlers will typically not @ be able to login. Set the capabilities of the @ <span class="usertype">anonymous</span> @ user to things that you want any human to be able to do, but not any @ spider. Every other logged-in user inherits the privileges of @ <span class="usertype">anonymous</span>. @ </p></li> @ @ <li><p> @ The <span class="usertype">developer</span> user is intended as a template @ for trusted users with check-in privileges. When adding new trusted users, @ simply select the <span class="capability">developer</span> privilege to @ cause the new user to inherit all privileges of the @ <span class="usertype">developer</span> @ user. Similarly, the <span class="usertype">reader</span> user is a @ template for users who are allowed more access than @ <span class="usertype">anonymous</span>, @ but less than a <span class="usertype">developer</span>. @ </p></li> @ </ul> style_footer(); } /* ** Generate a checkbox for an attribute. */ |
︙ | ︙ | |||
649 650 651 652 653 654 655 | if( iQ!=iVal ){ login_verify_csrf_secret(); db_set(zVar, iQ ? "1" : "0", 0); iVal = iQ; } } if( iVal ){ | | > | | | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 | if( iQ!=iVal ){ login_verify_csrf_secret(); db_set(zVar, iQ ? "1" : "0", 0); iVal = iQ; } } if( iVal ){ @ <input type="checkbox" name="%s(zQParm)" checked="checked" /> @ <b>%s(zLabel)</b> }else{ @ <input type="checkbox" name="%s(zQParm)" /> <b>%s(zLabel)</b> } } /* ** Generate an entry box for an attribute. */ void entry_attribute( const char *zLabel, /* The text label on the entry box */ int width, /* Width of the entry box */ const char *zVar, /* The corresponding row in the VAR table */ const char *zQParm, /* The query parameter */ char *zDflt /* Default value if VAR table entry does not exist */ ){ const char *zVal = db_get(zVar, zDflt); const char *zQ = P(zQParm); if( zQ && strcmp(zQ,zVal)!=0 ){ login_verify_csrf_secret(); db_set(zVar, zQ, 0); zVal = zQ; } @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" /> @ <b>%s(zLabel)</b> } /* ** Generate a text box for an attribute. */ static void textarea_attribute( |
︙ | ︙ | |||
696 697 698 699 700 701 702 | if( zQ && strcmp(zQ,z)!=0 ){ login_verify_csrf_secret(); db_set(zVar, zQ, 0); z = zQ; } if( rows>0 && cols>0 ){ @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea> | > | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | | > > > > > | | | > | | | 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 | if( zQ && strcmp(zQ,z)!=0 ){ login_verify_csrf_secret(); db_set(zVar, zQ, 0); z = zQ; } if( rows>0 && cols>0 ){ @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea> if (zLabel && *zLabel) @ <span class="textareaLabel">%s(zLabel)</span> } } /* ** WEBPAGE: setup_access */ void setup_access(void){ login_check_credentials(); if( !g.okSetup ){ login_needed(); } style_header("Access Control Settings"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/setup_access" method="post"><div> login_insert_csrf_secret(); @ <hr /> onoff_attribute("Require password for local access", "localauth", "localauth", 0); @ <p>When enabled, the password sign-in is required for @ web access coming from 127.0.0.1. When disabled, web access @ from 127.0.0.1 is allows without any login - the user id is selected @ from the ~/.fossil database. Password login is always required @ for incoming web connections on internet addresses other than @ 127.0.0.1.</p> @ <hr /> onoff_attribute("Allow REMOTE_USER authentication", "remote_user_ok", "remote_user_ok", 0); @ <p>When enabled, if the REMOTE_USER environment variable is set to the @ login name of a valid user and no other login credentials are available, @ then the REMOTE_USER is accepted as an authenticated user. @ </p> @ <hr /> entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766"); @ <p>The number of hours for which a login is valid. This must be a @ positive number. The default is 8760 hours which is approximately equal @ to a year.</p> @ <hr /> entry_attribute("Download packet limit", 10, "max-download", "mxdwn", "5000000"); @ <p>Fossil tries to limit out-bound sync, clone, and pull packets @ to this many bytes, uncompressed. If the client requires more data @ than this, then the client will issue multiple HTTP requests. @ Values below 1 million are not recommended. 5 million is a @ reasonable number.</p> @ <hr /> onoff_attribute("Show javascript button to fill in CAPTCHA", "auto-captcha", "autocaptcha", 0); @ <p>When enabled, a button appears on the login screen for user @ "anonymous" that will automatically fill in the CAPTCHA password. @ This is less secure that forcing the user to do it manually, but is @ probably secure enough and it is certainly more convenient for @ anonymous users.</p> @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> db_end_transaction(0); style_footer(); } /* ** WEBPAGE: setup_timeline */ void setup_timeline(void){ login_check_credentials(); if( !g.okSetup ){ login_needed(); } style_header("Timeline Display Preferences"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/setup_timeline" method="post"><div> login_insert_csrf_secret(); @ <hr /> onoff_attribute("Allow block-markup in timeline", "timeline-block-markup", "tbm", 0); @ <p>In timeline displays, check-in comments can be displayed with or @ without block markup (paragraphs, tables, etc.)</p> @ <hr /> onoff_attribute("Use Universal Coordinated Time (UTC)", "timeline-utc", "utc", 1); @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or @ Zulu) instead of in local time.</p> @ <hr /> onoff_attribute("Show version differences by default", "show-version-diffs", "vdiff", 0); @ <p>On the version-information pages linked from the timeline can either @ show complete diffs of all file changes, or can just list the names of @ the files that have changed. Users can get to either page by @ clicking. This setting selects the default.</p> @ <hr /> entry_attribute("Max timeline comment length", 6, "timeline-max-comment", "tmc", "0"); @ <p>The maximum length of a comment to be displayed in a timeline. @ "0" there is no length limit.</p> @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> db_end_transaction(0); style_footer(); } /* ** WEBPAGE: setup_settings */ void setup_settings(void){ struct stControlSettings const *pSet; login_check_credentials(); if( !g.okSetup ){ login_needed(); } style_header("Settings"); db_begin_transaction(); @ <p>This page provides a simple interface to the "fossil setting" command. @ See the "fossil help setting" output below for further information on @ the meaning of each setting.</p><hr /> @ <form action="%s(g.zBaseURL)/setup_settings" method="post"><div> @ <table border="0"><tr><td valign="top"> login_insert_csrf_secret(); for(pSet=ctrlSettings; pSet->name!=0; pSet++){ if( pSet->width==0 ){ onoff_attribute(pSet->name, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, is_truth(pSet->def)); @ <br /> } } @ </td><td style="width: 30;"></td><td valign="top"> for(pSet=ctrlSettings; pSet->name!=0; pSet++){ if( pSet->width!=0 ){ entry_attribute(pSet->name, /*pSet->width*/ 40, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, (char*)pSet->def); @ <br /> } } @ </td></tr></table> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> @ <hr /><p> @ These settings work in the same way, as the <kbd>set</kbd> commandline:<br /> @ </p><pre>%s(zHelp_setting_cmd)</pre> db_end_transaction(0); style_footer(); } /* ** WEBPAGE: setup_config */ void setup_config(void){ login_check_credentials(); if( !g.okSetup ){ login_needed(); } style_header("WWW Configuration"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/setup_config" method="post"><div> login_insert_csrf_secret(); @ <hr /> entry_attribute("Project Name", 60, "project-name", "pn", ""); @ <p>Give your project a name so visitors know what this site is about. @ The project name will also be used as the RSS feed title.</p> @ <hr /> textarea_attribute("Project Description", 5, 60, "project-description", "pd", ""); @ <p>Describe your project. This will be used in page headers for search @ engines as well as a short RSS description.</p> @ <hr /> entry_attribute("Index Page", 60, "index-page", "idxpg", "/home"); @ <p>Enter the pathname of the page to display when the "Home" menu @ option is selected and when no pathname is @ specified in the URL. For example, if you visit the url:</p> @ @ <blockquote><p>%h(g.zBaseURL)</p></blockquote> @ @ <p>And you have specified an index page of "/home" the above will @ automatically redirect to:</p> @ @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote> @ @ <p>The default "/home" page displays a Wiki page with the same name @ as the Project Name specified above. Some sites prefer to redirect @ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p> @ @ <p>Note: To avoid a redirect loop or other problems, this entry must @ begin with "/" and it must specify a valid page. For example, @ "<b>/home</b>" will work but "<b>home</b>" will not, since it omits the @ leading "/".</p> @ <hr /> onoff_attribute("Use HTML as wiki markup language", "wiki-use-html", "wiki-use-html", 0); @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed @ but all other wiki formatting will be ignored. This option is helpful @ if you have chosen to use a rich HTML editor for wiki markup such as @ TinyMCE.</p> @ <p><strong>CAUTION:</strong> when @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki. @ No sanitization is done. This means that it is very possible for malicious @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> @ <p>This should <strong>only</strong> be enabled when wiki editing is limited @ to trusted users. It should <strong>not</strong> be used on a publically @ editable wiki.</p> @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> db_end_transaction(0); style_footer(); } /* ** WEBPAGE: setup_editcss */ |
︙ | ︙ | |||
890 891 892 893 894 895 896 | textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS); } if( P("submit")!=0 ){ db_end_transaction(0); cgi_redirect("setup_editcss"); } style_header("Edit CSS"); | | | | | | | | | | 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS); } if( P("submit")!=0 ){ db_end_transaction(0); cgi_redirect("setup_editcss"); } style_header("Edit CSS"); @ <form action="%s(g.zBaseURL)/setup_editcss" method="post"><div> login_insert_csrf_secret(); @ Edit the CSS below:<br /> textarea_attribute("", 40, 80, "css", "css", zDefaultCSS); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ </div></form> @ <p><span class="note">Note:</span> Press your browser Reload button after @ modifying the CSS in order to pull in the modified CSS file.</p> @ <hr /> @ The default CSS is shown below for reference. Other examples @ of CSS files can be seen on the <a href="setup_skin">skins page</a>. @ See also the <a href="setup_header">header</a> and @ <a href="setup_footer">footer</a> editing screens. @ <blockquote><pre> cgi_append_default_css(); @ </pre></blockquote> style_footer(); db_end_transaction(0); } /* ** WEBPAGE: setup_header |
︙ | ︙ | |||
928 929 930 931 932 933 934 | if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name='header'"); cgi_replace_parameter("header", zDefaultHeader); }else{ textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader); } style_header("Edit Page Header"); | | | | | | | 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 | if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name='header'"); cgi_replace_parameter("header", zDefaultHeader); }else{ textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader); } style_header("Edit Page Header"); @ <form action="%s(g.zBaseURL)/setup_header" method="post"><div> login_insert_csrf_secret(); @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to @ generate the beginning of every page through start of the main @ menu.</p> textarea_attribute("", 40, 80, "header", "header", zDefaultHeader); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ </div></form> @ <hr /> @ The default header is shown below for reference. Other examples @ of headers can be seen on the <a href="setup_skin">skins page</a>. @ See also the <a href="setup_editcss">CSS</a> and @ <a href="setup_footer">footer</a> editing screeens. @ <blockquote><pre> @ %h(zDefaultHeader) @ </pre></blockquote> |
︙ | ︙ | |||
966 967 968 969 970 971 972 | if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name='footer'"); cgi_replace_parameter("footer", zDefaultFooter); }else{ textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter); } style_header("Edit Page Footer"); | | | | | | | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 | if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name='footer'"); cgi_replace_parameter("footer", zDefaultFooter); }else{ textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter); } style_header("Edit Page Footer"); @ <form action="%s(g.zBaseURL)/setup_footer" method="post"><div> login_insert_csrf_secret(); @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to @ generate the end of every page.</p> textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ </div></form> @ <hr /> @ The default footer is shown below for reference. Other examples @ of footers can be seen on the <a href="setup_skin">skins page</a>. @ See also the <a href="setup_editcss">CSS</a> and @ <a href="setup_header">header</a> editing screens. @ <blockquote><pre> @ %h(zDefaultFooter) @ </pre></blockquote> |
︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 | ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo"); @ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks @ like this:</p> | | | | | | | | | | | | | 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 | ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo"); @ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks @ like this:</p> @ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" /></p></blockquote> @ @ <p>The logo is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>. @ The logo may or may not appear on each @ page depending on the <a href="setup_editcss">CSS</a> and @ <a href="setup_header">header setup</a>.</p> @ @ <form action="%s(g.zBaseURL)/setup_logo" method="post" @ enctype="multipart/form-data"><div> @ <p>To set a new logo image, select a file to use as the logo using @ the entry box below and then press the "Change Logo" button.</p> login_insert_csrf_secret(); @ Logo Image file: @ <input type="file" name="im" size="60" accept="image/*" /><br /> @ <input type="submit" name="set" value="Change Logo" /> @ <input type="submit" name="clr" value="Revert To Default" /> @ </div></form> @ @ <p><span class="note">Note:</span> Your browser has probably cached the @ logo image, so you will probably need to press the Reload button on your @ browser after changing the logo to provoke your browser to reload the new @ logo image. </p> style_footer(); db_end_transaction(0); } |
Changes to src/sha1.c.
1 | /* | | < < > < | < < < | < < | < < | | | > | | < | < < | < > > < < < < < < > > < < < > | < < < < < < < < < > > > | | < > < < | < < < < < < < < < | < < < | < < < | < < < < < < < | < < | | < < < < < < < < | < < < < < | < > > > > > > > > > < | > > | < > | < | < > | < | < < < < < < < < < < | > < | < | < < < < | < | < < < < | < < | | | < | | < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < | < < < < > > > > | | < < < | < > > | | > > > > > | < > | < > | < > > > > > < < > | | < | < < < | < < | < | < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < < | < < < < | < < < < | | | | | | | < | < < < < < < > | < | < < < < < | < | > > > > | | < < < < < < < < < < | < < < | | < < < < | | | > | | < | | | | < < < | < < < < < < < < < | < < < < < < < | < < < | | < < < | | > > | < < < < < | | | < < < | < < < | | < | | | < | < < < > | < < | < < < < | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | /* ** This implementation of SHA1. */ #include <sys/types.h> #include "config.h" #include "sha1.h" /* ** The SHA1 implementation below is adapted from: ** ** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ ** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ ** ** SHA-1 in C ** By Steve Reid <steve@edmweb.com> ** 100% Public Domain */ typedef struct SHA1Context SHA1Context; struct SHA1Context { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; }; /* * blk0() and blk() perform the initial expand. * I got the idea of expanding during the round function from SSLeay * * blk0le() for little-endian and blk0be() for big-endian. */ #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #define blk0le(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #define blk0be(i) block->l[i] #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 * * Rl0() for little-endian and Rb0() for big-endian. Endianness is * determined at run-time. */ #define Rl0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=rol(w,30); #define Rb0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); typedef union { unsigned char c[64]; unsigned int l[16]; } CHAR64LONG16; /* * Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) { unsigned int a, b, c, d, e; CHAR64LONG16 *block; static int one = 1; CHAR64LONG16 workspace; block = &workspace; (void)memcpy(block, buffer, 64); /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ if( 1 == *(unsigned char*)&one ){ Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); }else{ Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); } R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* * SHA1Init - Initialize new context */ static void SHA1Init(SHA1Context *context){ /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* * Run your data through this. */ static void SHA1Update( SHA1Context *context, const unsigned char *data, unsigned int len ){ unsigned int i, j; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1] += (len>>29)+1; j = (j >> 3) & 63; if ((j + len) > 63) { (void)memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) SHA1Transform(context->state, &data[i]); j = 0; } else { i = 0; } (void)memcpy(&context->buffer[j], &data[i], len - i); } /* * Add padding and return the message digest. */ static void SHA1Final(SHA1Context *context, unsigned char digest[20]){ unsigned int i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1Update(context, (const unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) SHA1Update(context, (const unsigned char *)"\0", 1); SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ if (digest) { for (i = 0; i < 20; i++) digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } } /* ** Convert a digest into base-16. digest should be declared as ** "unsigned char digest[20]" in the calling function. The SHA1 ** digest is stored in the first 20 bytes. zBuf should |
︙ | ︙ | |||
437 438 439 440 441 442 443 | static int incrInit = 0; /* ** Add more text to the incremental SHA1 checksum. */ void sha1sum_step_text(const char *zText, int nBytes){ if( !incrInit ){ | | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | static int incrInit = 0; /* ** Add more text to the incremental SHA1 checksum. */ void sha1sum_step_text(const char *zText, int nBytes){ if( !incrInit ){ SHA1Init(&incrCtx); incrInit = 1; } if( nBytes<=0 ){ if( nBytes==0 ) return; nBytes = strlen(zText); } SHA1Update(&incrCtx, (unsigned char*)zText, nBytes); } /* ** Add the content of a blob to the incremental SHA1 checksum. */ void sha1sum_step_blob(Blob *p){ sha1sum_step_text(blob_buffer(p), blob_size(p)); |
︙ | ︙ | |||
466 467 468 469 470 471 472 | ** of computation. The return pointer points to a static buffer that ** is overwritten by subsequent calls to this function. */ char *sha1sum_finish(Blob *pOut){ unsigned char zResult[20]; static char zOut[41]; sha1sum_step_text(0,0); | | | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | ** of computation. The return pointer points to a static buffer that ** is overwritten by subsequent calls to this function. */ char *sha1sum_finish(Blob *pOut){ unsigned char zResult[20]; static char zOut[41]; sha1sum_step_text(0,0); SHA1Final(&incrCtx, zResult); incrInit = 0; DigestToBase16(zResult, zOut); if( pOut ){ blob_zero(pOut); blob_append(pOut, zOut, 40); } return zOut; |
︙ | ︙ | |||
493 494 495 496 497 498 499 | unsigned char zResult[20]; char zBuf[10240]; in = fopen(zFilename,"rb"); if( in==0 ){ return 1; } | | | | | | | | | | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | unsigned char zResult[20]; char zBuf[10240]; in = fopen(zFilename,"rb"); if( in==0 ){ return 1; } SHA1Init(&ctx); for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n); } fclose(in); blob_zero(pCksum); blob_resize(pCksum, 40); SHA1Final(&ctx, zResult); DigestToBase16(zResult, blob_buffer(pCksum)); return 0; } /* ** Compute the SHA1 checksum of a blob in memory. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be either ** uninitialized or the same blob as pIn. ** ** Return the number of errors. */ int sha1sum_blob(const Blob *pIn, Blob *pCksum){ SHA1Context ctx; unsigned char zResult[20]; SHA1Init(&ctx); SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); if( pIn==pCksum ){ blob_reset(pCksum); }else{ blob_zero(pCksum); } blob_resize(pCksum, 40); SHA1Final(&ctx, zResult); DigestToBase16(zResult, blob_buffer(pCksum)); return 0; } /* ** Compute the SHA1 checksum of a zero-terminated string. The ** result is held in memory obtained from mprintf(). */ char *sha1sum(const char *zIn){ SHA1Context ctx; unsigned char zResult[20]; char zDigest[41]; SHA1Init(&ctx); SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); SHA1Final(&ctx, zResult); DigestToBase16(zResult, zDigest); return mprintf("%s", zDigest); } /* ** Convert a cleartext password for a specific user into a SHA1 hash. ** |
︙ | ︙ | |||
571 572 573 574 575 576 577 | */ char *sha1_shared_secret(const char *zPw, const char *zLogin){ static char *zProjectId = 0; SHA1Context ctx; unsigned char zResult[20]; char zDigest[41]; | | | | | | | | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | */ char *sha1_shared_secret(const char *zPw, const char *zLogin){ static char *zProjectId = 0; SHA1Context ctx; unsigned char zResult[20]; char zDigest[41]; SHA1Init(&ctx); if( zProjectId==0 ){ zProjectId = db_get("project-code", 0); /* On the first xfer request of a clone, the project-code is not yet ** known. Use the cleartext password, since that is all we have. */ if( zProjectId==0 ){ return mprintf("%s", zPw); } } SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId)); SHA1Update(&ctx, (unsigned char*)"/", 1); SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin)); SHA1Update(&ctx, (unsigned char*)"/", 1); SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw)); SHA1Final(&ctx, zResult); DigestToBase16(zResult, zDigest); return mprintf("%s", zDigest); } /* ** COMMAND: sha1sum ** %fossil sha1sum FILE... |
︙ | ︙ |
Added src/shell.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 | /* ** 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 contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. */ #if defined(_WIN32) || defined(WIN32) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include "sqlite3.h" #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) # include <pwd.h> # endif # include <unistd.h> # include <sys/types.h> #endif #ifdef __OS2__ # include <unistd.h> #endif #if defined(HAVE_READLINE) && HAVE_READLINE==1 # include <readline/readline.h> # include <readline/history.h> #else # define readline(p) local_getline(p,stdin) # define add_history(X) # define read_history(X) # define write_history(X) # define stifle_history(X) #endif #if defined(_WIN32) || defined(WIN32) # include <io.h> #define isatty(h) _isatty(h) #define access(f,m) _access((f),(m)) #else /* Make sure isatty() has a prototype. */ extern int isatty(); #endif #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() * thus we always assume that we have a console. That can be * overridden with the -batch command line option. */ #define isatty(x) 1 #endif #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL) #include <sys/time.h> #include <sys/resource.h> /* 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 */ static void beginTimer(void){ if( enableTimer ){ getrusage(RUSAGE_SELF, &sBegin); } } /* Return the difference of two time_structs in seconds */ static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + (double)(pEnd->tv_sec - pStart->tv_sec); } /* ** Print the timing results. */ static void endTimer(void){ if( enableTimer ){ struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); printf("CPU Time: user %f sys %f\n", timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); } } #define BEGIN_TIMER beginTimer() #define END_TIMER endTimer() #define HAS_TIMER 1 #elif (defined(_WIN32) || defined(WIN32)) #include <windows.h> /* Saved resource information for the beginning of an operation */ static HANDLE hProcess; static FILETIME ftKernelBegin; 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). */ static int hasTimer(void){ if( getProcessTimesAddr ){ return 1; } else { /* GetProcessTimes() isn't supported in WIN95 and some other Windows versions. ** See if the version we are running on has it, and if it does, save off ** a pointer to it and the current process handle. */ hProcess = GetCurrentProcess(); if( hProcess ){ HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); if( NULL != hinstLib ){ getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); if( NULL != getProcessTimesAddr ){ return 1; } FreeLibrary(hinstLib); } } } return 0; } /* ** Begin timing an operation */ static void beginTimer(void){ if( enableTimer && getProcessTimesAddr ){ FILETIME ftCreation, ftExit; getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin); } } /* Return the difference of two FILETIME structs in seconds */ static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ sqlite_int64 i64Start = *((sqlite_int64 *) pStart); sqlite_int64 i64End = *((sqlite_int64 *) pEnd); return (double) ((i64End - i64Start) / 10000000.0); } /* ** Print the timing results. */ static void endTimer(void){ if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd); printf("CPU Time: user %f sys %f\n", timeDiff(&ftUserBegin, &ftUserEnd), timeDiff(&ftKernelBegin, &ftKernelEnd)); } } #define BEGIN_TIMER beginTimer() #define END_TIMER endTimer() #define HAS_TIMER hasTimer() #else #define BEGIN_TIMER #define END_TIMER #define HAS_TIMER 0 #endif /* ** Used to prevent warnings about unused parameters */ #define UNUSED_PARAMETER(x) (void)(x) /* ** If the following flag is set, then command execution stops ** at an error if we are not interactive. */ static int bail_on_error = 0; /* ** Threat stdin as an interactive input if the following variable ** is true. Otherwise, assume stdin is connected to a file or pipe. */ static int stdin_is_interactive = 1; /* ** The following is the open SQLite database. We make a pointer ** to this database a static variable so that it can be accessed ** by the SIGINT handler to interrupt database processing. */ static sqlite3 *db = 0; /* ** True if an interrupt (Control-C) has been received. */ static volatile int seenInterrupt = 0; /* ** This is the name of our program. It is set in main(), used ** in a number of other places, mostly for error messages. */ static char *Argv0; /* ** Prompt strings. Initialized in main. Settable with ** .prompt main continue */ static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ /* ** Write I/O traces to the following stream. */ #ifdef SQLITE_ENABLE_IOTRACE static FILE *iotrace = 0; #endif /* ** This routine works like printf in that its first argument is a ** format string and subsequent arguments are values to be substituted ** in place of % fields. The result of formatting this string ** is written to iotrace. */ #ifdef SQLITE_ENABLE_IOTRACE static void iotracePrintf(const char *zFormat, ...){ va_list ap; char *z; if( iotrace==0 ) return; va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); va_end(ap); fprintf(iotrace, "%s", z); sqlite3_free(z); } #endif /* ** Determines if a string is a number of not. */ static int isNumber(const char *z, int *realnum){ if( *z=='-' || *z=='+' ) z++; if( !isdigit(*z) ){ return 0; } z++; if( realnum ) *realnum = 0; while( isdigit(*z) ){ z++; } if( *z=='.' ){ z++; if( !isdigit(*z) ) return 0; while( isdigit(*z) ){ z++; } if( realnum ) *realnum = 1; } if( *z=='e' || *z=='E' ){ z++; if( *z=='+' || *z=='-' ) z++; if( !isdigit(*z) ) return 0; while( isdigit(*z) ){ z++; } if( realnum ) *realnum = 1; } return *z==0; } /* ** A global char* and an SQL function to access its current value ** from within an SQL statement. This program used to use the ** sqlite_exec_printf() API to substitue a string into an SQL statement. ** The correct way to do this with sqlite3 is to use the bind API, but ** since the shell is built around the callback paradigm it would be a lot ** of work. Instead just use this hack, which is quite harmless. */ static const char *zShellStatic = 0; static void shellstaticFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ assert( 0==argc ); assert( zShellStatic ); UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC); } /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. ** ** The interface is like "readline" but no command-line editing ** is done. */ static char *local_getline(char *zPrompt, FILE *in){ char *zLine; int nLine; int n; int eol; if( zPrompt && *zPrompt ){ printf("%s",zPrompt); fflush(stdout); } nLine = 100; zLine = malloc( nLine ); if( zLine==0 ) return 0; n = 0; eol = 0; while( !eol ){ if( n+100>nLine ){ nLine = nLine*2 + 100; zLine = realloc(zLine, nLine); if( zLine==0 ) return 0; } if( fgets(&zLine[n], nLine - n, in)==0 ){ if( n==0 ){ free(zLine); return 0; } zLine[n] = 0; eol = 1; break; } while( zLine[n] ){ n++; } if( n>0 && zLine[n-1]=='\n' ){ n--; if( n>0 && zLine[n-1]=='\r' ) n--; zLine[n] = 0; eol = 1; } } zLine = realloc( zLine, n+1 ); return zLine; } /* ** Retrieve a single line of input text. ** ** zPrior is a string of prior text retrieved. If not the empty ** string, then issue a continuation prompt. */ static char *one_input_line(const char *zPrior, FILE *in){ char *zPrompt; char *zResult; if( in!=0 ){ return local_getline(0, in); } if( zPrior && zPrior[0] ){ zPrompt = continuePrompt; }else{ zPrompt = mainPrompt; } zResult = readline(zPrompt); #if defined(HAVE_READLINE) && HAVE_READLINE==1 if( zResult && *zResult ) add_history(zResult); #endif return zResult; } struct previous_mode_data { int valid; /* Is there legit data in here? */ int mode; int showHeader; int colWidth[100]; }; /* ** An pointer to an instance of this structure is passed from ** the main program to the callback. This is used to communicate ** state and mode information. */ struct callback_data { sqlite3 *db; /* The database */ int echoOn; /* True to echo input commands */ int statsOn; /* True to display memory stats before each finalize */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ int mode; /* An output mode setting */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int showHeader; /* True to show column names in List or Column mode */ char *zDestTable; /* Name of destination table when MODE_Insert */ char separator[20]; /* Separator character for MODE_List */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ char nullvalue[20]; /* The text to print when a NULL comes back from ** the database */ struct previous_mode_data explainPrev; /* Holds the mode information just before ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ }; /* ** These are the allowed modes. */ #define MODE_Line 0 /* One column per line. Blank line between records */ #define MODE_Column 1 /* One record per line in neat columns */ #define MODE_List 2 /* One record per line with a separator */ #define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ #define MODE_Html 4 /* Generate an XHTML table */ #define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 7 /* Quote strings, numbers are plain */ #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ static const char *modeDescr[] = { "line", "column", "list", "semi", "html", "insert", "tcl", "csv", "explain", }; /* ** Number of elements in an array */ #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. */ static int strlen30(const char *z){ const char *z2 = z; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } /* ** A callback for the sqlite3_log() interface. */ static void shellLog(void *pArg, int iErrCode, const char *zMsg){ struct callback_data *p = (struct callback_data*)pArg; if( p->pLog==0 ) return; fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); fflush(p->pLog); } /* ** Output the given string as a hex-encoded blob (eg. X'1234' ) */ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ int i; char *zBlob = (char *)pBlob; fprintf(out,"X'"); for(i=0; i<nBlob; i++){ fprintf(out,"%02x",zBlob[i]); } fprintf(out,"'"); } /* ** Output the given string as a quoted string using SQL quoting conventions. */ static void output_quoted_string(FILE *out, const char *z){ int i; int nSingle = 0; for(i=0; z[i]; i++){ if( z[i]=='\'' ) nSingle++; } if( nSingle==0 ){ fprintf(out,"'%s'",z); }else{ fprintf(out,"'"); while( *z ){ for(i=0; z[i] && z[i]!='\''; i++){} if( i==0 ){ fprintf(out,"''"); z++; }else if( z[i]=='\'' ){ fprintf(out,"%.*s''",i,z); z += i+1; }else{ fprintf(out,"%s",z); break; } } fprintf(out,"'"); } } /* ** Output the given string as a quoted according to C or TCL quoting rules. */ static void output_c_string(FILE *out, const char *z){ unsigned int c; fputc('"', out); while( (c = *(z++))!=0 ){ if( c=='\\' ){ fputc(c, out); fputc(c, out); }else if( c=='\t' ){ fputc('\\', out); fputc('t', out); }else if( c=='\n' ){ fputc('\\', out); fputc('n', out); }else if( c=='\r' ){ fputc('\\', out); fputc('r', out); }else if( !isprint(c) ){ fprintf(out, "\\%03o", c&0xff); }else{ fputc(c, out); } } fputc('"', out); } /* ** Output the given string with characters that are special to ** HTML escaped. */ static void output_html_string(FILE *out, const char *z){ int i; while( *z ){ for(i=0; z[i] && z[i]!='<' && z[i]!='&' && z[i]!='>' && z[i]!='\"' && z[i]!='\''; i++){} if( i>0 ){ fprintf(out,"%.*s",i,z); } if( z[i]=='<' ){ fprintf(out,"<"); }else if( z[i]=='&' ){ fprintf(out,"&"); }else if( z[i]=='>' ){ fprintf(out,">"); }else if( z[i]=='\"' ){ fprintf(out,"""); }else if( z[i]=='\'' ){ fprintf(out,"'"); }else{ break; } z += i + 1; } } /* ** If a field contains any character identified by a 1 in the following ** array, then the string must be quoted for CSV. */ static const char needCsvQuote[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; /* ** Output a single term of CSV. Actually, p->separator is used for ** the separator, which may or may not be a comma. p->nullvalue is ** the null value. Strings are quoted using ANSI-C rules. Numbers ** appear outside of quotes. */ static void output_csv(struct callback_data *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ fprintf(out,"%s",p->nullvalue); }else{ int i; int nSep = strlen30(p->separator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] || (z[i]==p->separator[0] && (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){ i = 0; break; } } if( i==0 ){ putc('"', out); for(i=0; z[i]; i++){ if( z[i]=='"' ) putc('"', out); putc(z[i], out); } putc('"', out); }else{ fprintf(out, "%s", z); } } if( bSep ){ fprintf(p->out, "%s", p->separator); } } #ifdef SIGINT /* ** This routine runs when the user presses Ctrl-C */ static void interrupt_handler(int NotUsed){ UNUSED_PARAMETER(NotUsed); seenInterrupt = 1; if( db ) sqlite3_interrupt(db); } #endif /* ** This is the callback routine that the shell ** invokes for each row of a query result. */ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int *aiType){ int i; struct callback_data *p = (struct callback_data*)pArg; switch( p->mode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; for(i=0; i<nArg; i++){ int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } if( p->cnt++>0 ) fprintf(p->out,"\n"); for(i=0; i<nArg; i++){ fprintf(p->out,"%*s = %s\n", w, azCol[i], azArg[i] ? azArg[i] : p->nullvalue); } break; } case MODE_Explain: case MODE_Column: { if( p->cnt++==0 ){ for(i=0; i<nArg; i++){ int w, n; if( i<ArraySize(p->colWidth) ){ w = p->colWidth[i]; }else{ w = 0; } if( w<=0 ){ w = strlen30(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); if( w<n ) w = n; } if( i<ArraySize(p->actualWidth) ){ p->actualWidth[i] = w; } if( p->showHeader ){ fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); } } if( p->showHeader ){ for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; }else{ w = 10; } fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" "----------------------------------------------------------", i==nArg-1 ? "\n": " "); } } } if( azArg==0 ) break; for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; }else{ w = 10; } if( p->mode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ w = strlen30(azArg[i]); } fprintf(p->out,"%-*.*s%s",w,w, azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); } break; } case MODE_Semi: case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); } } if( azArg==0 ) break; for(i=0; i<nArg; i++){ char *z = azArg[i]; if( z==0 ) z = p->nullvalue; fprintf(p->out, "%s", z); if( i<nArg-1 ){ fprintf(p->out, "%s", p->separator); }else if( p->mode==MODE_Semi ){ fprintf(p->out, ";\n"); }else{ fprintf(p->out, "\n"); } } break; } case MODE_Html: { if( p->cnt++==0 && p->showHeader ){ fprintf(p->out,"<TR>"); for(i=0; i<nArg; i++){ fprintf(p->out,"<TH>"); output_html_string(p->out, azCol[i]); fprintf(p->out,"</TH>\n"); } fprintf(p->out,"</TR>\n"); } if( azArg==0 ) break; fprintf(p->out,"<TR>"); for(i=0; i<nArg; i++){ fprintf(p->out,"<TD>"); output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); fprintf(p->out,"</TD>\n"); } fprintf(p->out,"</TR>\n"); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_c_string(p->out,azCol[i] ? azCol[i] : ""); fprintf(p->out, "%s", p->separator); } fprintf(p->out,"\n"); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); fprintf(p->out, "%s", p->separator); } fprintf(p->out,"\n"); break; } case MODE_Csv: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); } fprintf(p->out,"\n"); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ output_csv(p, azArg[i], i<nArg-1); } fprintf(p->out,"\n"); break; } case MODE_Insert: { p->cnt++; if( azArg==0 ) break; fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable); for(i=0; i<nArg; i++){ char *zSep = i>0 ? ",": ""; if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ fprintf(p->out,"%sNULL",zSep); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( zSep[0] ) fprintf(p->out,"%s",zSep); output_quoted_string(p->out, azArg[i]); }else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){ fprintf(p->out,"%s%s",zSep, azArg[i]); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); if( zSep[0] ) fprintf(p->out,"%s",zSep); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ fprintf(p->out,"%s%s",zSep, azArg[i]); }else{ if( zSep[0] ) fprintf(p->out,"%s",zSep); output_quoted_string(p->out, azArg[i]); } } fprintf(p->out,");\n"); break; } } return 0; } /* ** This is the callback routine that the SQLite library ** invokes for each row of a query result. */ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ /* since we don't have type info, call the shell_callback with a NULL value */ return shell_callback(pArg, nArg, azArg, azCol, NULL); } /* ** Set the destination table field of the callback_data structure to ** the name of the table given. Escape any quote characters in the ** table name. */ static void set_table_name(struct callback_data *p, const char *zName){ int i, n; int needQuote; char *z; if( p->zDestTable ){ free(p->zDestTable); p->zDestTable = 0; } if( zName==0 ) return; needQuote = !isalpha((unsigned char)*zName) && *zName!='_'; for(i=n=0; zName[i]; i++, n++){ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ){ needQuote = 1; if( zName[i]=='\'' ) n++; } } if( needQuote ) n += 2; z = p->zDestTable = malloc( n+1 ); if( z==0 ){ fprintf(stderr,"Error: out of memory\n"); exit(1); } n = 0; if( needQuote ) z[n++] = '\''; for(i=0; zName[i]; i++){ z[n++] = zName[i]; if( zName[i]=='\'' ) z[n++] = '\''; } if( needQuote ) z[n++] = '\''; z[n] = 0; } /* zIn is either a pointer to a NULL-terminated string in memory obtained ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ static char *appendText(char *zIn, char const *zAppend, char quote){ int len; int i; int nAppend = strlen30(zAppend); int nIn = (zIn?strlen30(zIn):0); len = nAppend+nIn+1; if( quote ){ len += 2; for(i=0; i<nAppend; i++){ if( zAppend[i]==quote ) len++; } } zIn = (char *)realloc(zIn, len); if( !zIn ){ return 0; } if( quote ){ char *zCsr = &zIn[nIn]; *zCsr++ = quote; for(i=0; i<nAppend; i++){ *zCsr++ = zAppend[i]; if( zAppend[i]==quote ) *zCsr++ = quote; } *zCsr++ = quote; *zCsr++ = '\0'; assert( (zCsr-zIn)==len ); }else{ memcpy(&zIn[nIn], zAppend, nAppend); zIn[len-1] = '\0'; } return zIn; } /* ** Execute a query statement that has a single result column. Print ** that result column on a line by itself with a semicolon terminator. ** ** This is used, for example, to show the schema of the database by ** querying the SQLITE_MASTER table. */ static int run_table_dump_query( FILE *out, /* Send output here */ sqlite3 *db, /* Database to query */ const char *zSelect, /* SELECT statement to extract content */ const char *zFirstRow /* Print before first row, if not NULL */ ){ sqlite3_stmt *pSelect; int rc; rc = sqlite3_prepare(db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ return rc; } rc = sqlite3_step(pSelect); while( rc==SQLITE_ROW ){ if( zFirstRow ){ fprintf(out, "%s", zFirstRow); zFirstRow = 0; } fprintf(out, "%s;\n", sqlite3_column_text(pSelect, 0)); rc = sqlite3_step(pSelect); } return sqlite3_finalize(pSelect); } /* ** Allocate space and save off current error string. */ static char *save_err_msg( sqlite3 *db /* Database to query */ ){ int nErrMsg = 1+strlen30(sqlite3_errmsg(db)); char *zErrMsg = sqlite3_malloc(nErrMsg); if( zErrMsg ){ memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg); } return zErrMsg; } /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ struct callback_data *pArg, /* Pointer to struct callback_data */ int bReset /* True to reset the stats */ ){ int iCur; int iHiwtr; if( pArg && pArg->out ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Allocations: %d (max %d)\n", iCur, iHiwtr); /* ** Not currently used by the CLI. ** iHiwtr = iCur = -1; ** sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset); ** fprintf(pArg->out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr); */ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); /* ** Not currently used by the CLI. ** iHiwtr = iCur = -1; ** sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset); ** fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", iCur, iHiwtr); */ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Largest Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", iHiwtr); #ifdef YYTRACKMAXSTACKDEPTH iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", iCur, iHiwtr); #endif } if( pArg && pArg->out && db ){ iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); } if( pArg && pArg->out && db && pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); fprintf(pArg->out, "Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); } return 0; } /* ** Execute a statement or set of statements. Print ** any result rows/columns depending on the current mode ** set via the supplied callback. ** ** This is very similar to SQLite's built-in sqlite3_exec() ** function except it takes a slightly different callback ** and callback data argument. */ static int shell_exec( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */ /* (not the same as sqlite3_exec) */ struct callback_data *pArg, /* Pointer to struct callback_data */ char **pzErrMsg /* Error msg written here */ ){ sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ const char *zLeftover; /* Tail of unprocessed SQL */ if( pzErrMsg ){ *pzErrMsg = NULL; } while( zSql[0] && (SQLITE_OK == rc) ){ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); if( SQLITE_OK != rc ){ if( pzErrMsg ){ *pzErrMsg = save_err_msg(db); } }else{ if( !pStmt ){ /* this happens for a comment or white-space */ zSql = zLeftover; while( isspace(zSql[0]) ) zSql++; continue; } /* save off the prepared statment handle and reset row count */ if( pArg ){ pArg->pStmt = pStmt; pArg->cnt = 0; } /* echo the sql statement if echo on */ if( pArg && pArg->echoOn ){ const char *zStmtSql = sqlite3_sql(pStmt); fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); } /* perform the first step. this will tell us if we ** have a result set or not and how wide it is. */ rc = sqlite3_step(pStmt); /* if we have a result set... */ if( SQLITE_ROW == rc ){ /* if we have a callback... */ if( xCallback ){ /* allocate space for col name ptr, value ptr, and type */ int nCol = sqlite3_column_count(pStmt); void *pData = sqlite3_malloc(3*nCol*sizeof(const char*) + 1); if( !pData ){ rc = SQLITE_NOMEM; }else{ char **azCols = (char **)pData; /* Names of result columns */ char **azVals = &azCols[nCol]; /* Results */ int *aiTypes = (int *)&azVals[nCol]; /* Result types */ int i; assert(sizeof(int) <= sizeof(char *)); /* save off ptrs to column names */ for(i=0; i<nCol; i++){ azCols[i] = (char *)sqlite3_column_name(pStmt, i); } do{ /* extract the data and data types */ for(i=0; i<nCol; i++){ azVals[i] = (char *)sqlite3_column_text(pStmt, i); aiTypes[i] = sqlite3_column_type(pStmt, i); if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ rc = SQLITE_NOMEM; break; /* from for */ } } /* end for */ /* if data and types extracted successfully... */ if( SQLITE_ROW == rc ){ /* call the supplied callback with the result row data */ if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){ rc = SQLITE_ABORT; }else{ rc = sqlite3_step(pStmt); } } } while( SQLITE_ROW == rc ); sqlite3_free(pData); } }else{ do{ rc = sqlite3_step(pStmt); } while( rc == SQLITE_ROW ); } } /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ display_stats(db, pArg, 0); } /* Finalize the statement just executed. If this fails, save a ** copy of the error message. Otherwise, set zSql to point to the ** next statement to execute. */ rc = sqlite3_finalize(pStmt); if( rc==SQLITE_OK ){ zSql = zLeftover; while( isspace(zSql[0]) ) zSql++; }else if( pzErrMsg ){ *pzErrMsg = save_err_msg(db); } /* clear saved stmt handle */ if( pArg ){ pArg->pStmt = NULL; } } } /* end while */ return rc; } /* ** This is a different callback routine used for dumping the database. ** Each row received by this callback consists of a table name, ** the table type ("index" or "table") and SQL to create the table. ** This routine should print text sufficient to recreate the table. */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ int rc; const char *zTable; const char *zType; const char *zSql; const char *zPrepStmt = 0; struct callback_data *p = (struct callback_data *)pArg; UNUSED_PARAMETER(azCol); if( nArg!=3 ) return 1; zTable = azArg[0]; zType = azArg[1]; zSql = azArg[2]; if( strcmp(zTable, "sqlite_sequence")==0 ){ zPrepStmt = "DELETE FROM sqlite_sequence;\n"; }else if( strcmp(zTable, "sqlite_stat1")==0 ){ fprintf(p->out, "ANALYZE sqlite_master;\n"); }else if( strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ fprintf(p->out, "PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = sqlite3_mprintf( "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); fprintf(p->out, "%s\n", zIns); sqlite3_free(zIns); return 0; }else{ fprintf(p->out, "%s;\n", zSql); } if( strcmp(zType, "table")==0 ){ sqlite3_stmt *pTableInfo = 0; char *zSelect = 0; char *zTableInfo = 0; char *zTmp = 0; int nRow = 0; zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0); zTableInfo = appendText(zTableInfo, zTable, '"'); zTableInfo = appendText(zTableInfo, ");", 0); rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0); free(zTableInfo); if( rc!=SQLITE_OK || !pTableInfo ){ return 1; } zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0); zTmp = appendText(zTmp, zTable, '"'); if( zTmp ){ zSelect = appendText(zSelect, zTmp, '\''); } zSelect = appendText(zSelect, " || ' VALUES(' || ", 0); rc = sqlite3_step(pTableInfo); while( rc==SQLITE_ROW ){ const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1); zSelect = appendText(zSelect, "quote(", 0); zSelect = appendText(zSelect, zText, '"'); rc = sqlite3_step(pTableInfo); if( rc==SQLITE_ROW ){ zSelect = appendText(zSelect, ") || ',' || ", 0); }else{ zSelect = appendText(zSelect, ") ", 0); } nRow++; } rc = sqlite3_finalize(pTableInfo); if( rc!=SQLITE_OK || nRow==0 ){ free(zSelect); return 1; } zSelect = appendText(zSelect, "|| ')' FROM ", 0); zSelect = appendText(zSelect, zTable, '"'); rc = run_table_dump_query(p->out, p->db, zSelect, zPrepStmt); if( rc==SQLITE_CORRUPT ){ zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0); rc = run_table_dump_query(p->out, p->db, zSelect, 0); } if( zSelect ) free(zSelect); } return 0; } /* ** Run zQuery. Use dump_callback() as the callback routine so that ** the contents of the query are output as SQL statements. ** ** If we get a SQLITE_CORRUPT error, rerun the query after appending ** "ORDER BY rowid DESC" to the end. */ static int run_schema_dump_query( struct callback_data *p, const char *zQuery, char **pzErrMsg ){ int rc; rc = sqlite3_exec(p->db, zQuery, dump_callback, p, pzErrMsg); if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); if( pzErrMsg ) sqlite3_free(*pzErrMsg); zQ2 = malloc( len+100 ); if( zQ2==0 ) return rc; sqlite3_snprintf(sizeof(zQ2), zQ2, "%s ORDER BY rowid DESC", zQuery); rc = sqlite3_exec(p->db, zQ2, dump_callback, p, pzErrMsg); free(zQ2); } return rc; } /* ** Text of a help message */ static char zHelp[] = ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail ON|OFF Stop after hitting an error. Default OFF\n" ".databases List names and files of attached databases\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" ".echo ON|OFF Turn command echo on or off\n" ".exit Exit this program\n" ".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n" " With no args, it turns EXPLAIN on.\n" ".header(s) ON|OFF Turn display of headers on or off\n" ".help Show this message\n" ".import FILE TABLE Import data from FILE into TABLE\n" ".indices ?TABLE? Show names of all indices\n" " If TABLE specified, only show indices for tables\n" " matching LIKE pattern TABLE.\n" #ifdef SQLITE_ENABLE_IOTRACE ".iotrace FILE Enable I/O diagnostic logging to FILE\n" #endif #ifndef SQLITE_OMIT_LOAD_EXTENSION ".load FILE ?ENTRY? Load an extension library\n" #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML <table> code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" " list Values delimited by .separator string\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Print STRING in place of NULL values\n" ".output FILENAME Send output to FILENAME\n" ".output stdout Send output to the screen\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" " LIKE pattern TABLE.\n" ".separator STRING Change separator used by output mode and .import\n" ".show Show the current values for various settings\n" ".stats ON|OFF Turn stats on or off\n" ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" ".timeout MS Try opening locked tables for MS milliseconds\n" ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" ; static char zTimerHelp[] = ".timer ON|OFF Turn the CPU timer measurement on or off\n" ; /* Forward reference */ static int process_input(struct callback_data *p, FILE *in); /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ static void open_db(struct callback_data *p){ if( p->db==0 ){ sqlite3_open(p->zDbFilename, &p->db); db = p->db; if( db && sqlite3_errcode(db)==SQLITE_OK ){ sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0, shellstaticFunc, 0, 0); } if( db==0 || SQLITE_OK!=sqlite3_errcode(db) ){ fprintf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite3_errmsg(db)); exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif } } /* ** Do C-language style dequoting. ** ** \t -> tab ** \n -> newline ** \r -> carriage return ** \NNN -> ascii character NNN in octal ** \\ -> backslash */ static void resolve_backslashes(char *z){ int i, j; char c; for(i=j=0; (c = z[i])!=0; i++, j++){ if( c=='\\' ){ c = z[++i]; if( c=='n' ){ c = '\n'; }else if( c=='t' ){ c = '\t'; }else if( c=='r' ){ c = '\r'; }else if( c>='0' && c<='7' ){ c -= '0'; if( z[i+1]>='0' && z[i+1]<='7' ){ i++; c = (c<<3) + z[i] - '0'; if( z[i+1]>='0' && z[i+1]<='7' ){ i++; c = (c<<3) + z[i] - '0'; } } } } z[j] = c; } z[j] = 0; } /* ** Interpret zArg as a boolean value. Return either 0 or 1. */ static int booleanValue(char *zArg){ int val = atoi(zArg); int j; for(j=0; zArg[j]; j++){ zArg[j] = (char)tolower(zArg[j]); } if( strcmp(zArg,"on")==0 ){ val = 1; }else if( strcmp(zArg,"yes")==0 ){ val = 1; } return val; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ static int do_meta_command(char *zLine, struct callback_data *p){ int i = 1; int nArg = 0; int n, c; int rc = 0; char *azArg[50]; /* Parse the input line into tokens. */ while( zLine[i] && nArg<ArraySize(azArg) ){ while( isspace((unsigned char)zLine[i]) ){ i++; } if( zLine[i]==0 ) break; if( zLine[i]=='\'' || zLine[i]=='"' ){ int delim = zLine[i++]; azArg[nArg++] = &zLine[i]; while( zLine[i] && zLine[i]!=delim ){ i++; } if( zLine[i]==delim ){ zLine[i++] = 0; } if( delim=='"' ) resolve_backslashes(azArg[nArg-1]); }else{ azArg[nArg++] = &zLine[i]; while( zLine[i] && !isspace((unsigned char)zLine[i]) ){ i++; } if( zLine[i] ) zLine[i++] = 0; resolve_backslashes(azArg[nArg-1]); } } /* Process the input line. */ if( nArg==0 ) return 0; /* no tokens, no error */ n = strlen30(azArg[0]); c = azArg[0][0]; if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 && nArg<4){ const char *zDestFile; const char *zDb; sqlite3 *pDest; sqlite3_backup *pBackup; if( nArg==2 ){ zDestFile = azArg[1]; zDb = "main"; }else{ zDestFile = azArg[2]; zDb = azArg[1]; } rc = sqlite3_open(zDestFile, &pDest); if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile); sqlite3_close(pDest); return 1; } open_db(p); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); sqlite3_close(pDest); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else{ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } sqlite3_close(pDest); }else if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){ bail_on_error = booleanValue(azArg[1]); }else if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); memcpy(&data, p, sizeof(data)); data.showHeader = 1; data.mode = MODE_Column; data.colWidth[0] = 3; data.colWidth[1] = 15; data.colWidth[2] = 58; data.cnt = 0; sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg); if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){ char *zErrMsg = 0; open_db(p); /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ fprintf(p->out, "PRAGMA foreign_keys=OFF;\n"); fprintf(p->out, "BEGIN TRANSACTION;\n"); p->writableSchema = 0; sqlite3_exec(p->db, "PRAGMA writable_schema=ON", 0, 0, 0); if( nArg==1 ){ run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'", 0 ); run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE name=='sqlite_sequence'", 0 ); run_table_dump_query(p->out, p->db, "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 ); }else{ int i; for(i=1; i<nArg; i++){ zShellStatic = azArg[i]; run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE tbl_name LIKE shellstatic() AND type=='table'" " AND sql NOT NULL", 0); run_table_dump_query(p->out, p->db, "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL" " AND type IN ('index','trigger','view')" " AND tbl_name LIKE shellstatic()", 0 ); zShellStatic = 0; } } if( p->writableSchema ){ fprintf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF", 0, 0, 0); if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); }else{ fprintf(p->out, "COMMIT;\n"); } }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){ p->echoOn = booleanValue(azArg[1]); }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 && nArg==1 ){ rc = 2; }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 && nArg<3 ){ int val = nArg>=2 ? booleanValue(azArg[1]) : 1; if(val == 1) { if(!p->explainPrev.valid) { p->explainPrev.valid = 1; p->explainPrev.mode = p->mode; p->explainPrev.showHeader = p->showHeader; memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth)); } /* We could put this code under the !p->explainValid ** condition so that it does not execute if we are already in ** explain mode. However, always executing it allows us an easy ** was to reset to explain mode in case the user previously ** did an .explain followed by a .width, .mode or .header ** command. */ p->mode = MODE_Explain; p->showHeader = 1; memset(p->colWidth,0,ArraySize(p->colWidth)); p->colWidth[0] = 4; /* addr */ p->colWidth[1] = 13; /* opcode */ p->colWidth[2] = 4; /* P1 */ p->colWidth[3] = 4; /* P2 */ p->colWidth[4] = 4; /* P3 */ p->colWidth[5] = 13; /* P4 */ p->colWidth[6] = 2; /* P5 */ p->colWidth[7] = 13; /* Comment */ }else if (p->explainPrev.valid) { p->explainPrev.valid = 0; p->mode = p->explainPrev.mode; p->showHeader = p->explainPrev.showHeader; memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth)); } }else if( c=='h' && (strncmp(azArg[0], "header", n)==0 || strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){ p->showHeader = booleanValue(azArg[1]); }else if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ fprintf(stderr,"%s",zHelp); if( HAS_TIMER ){ fprintf(stderr,"%s",zTimerHelp); } }else if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){ char *zTable = azArg[2]; /* Insert data into this table */ char *zFile = azArg[1]; /* The file from which to extract data */ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int nSep; /* Number of bytes in p->separator[] */ char *zSql; /* An SQL statement */ char *zLine; /* A single line of input from the file */ char **azCol; /* zLine[] broken up into columns */ char *zCommit; /* How to commit changes */ FILE *in; /* The input file */ int lineno = 0; /* Line number of input file */ open_db(p); nSep = strlen30(p->separator); if( nSep==0 ){ fprintf(stderr, "Error: non-null separator required for import\n"); return 1; } zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); return 1; } nByte = strlen30(zSql); rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ if (pStmt) sqlite3_finalize(pStmt); fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); return 1; } nCol = sqlite3_column_count(pStmt); sqlite3_finalize(pStmt); pStmt = 0; if( nCol==0 ) return 0; /* no columns, no error */ zSql = malloc( nByte + 20 + nCol*2 ); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); return 1; } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable); j = strlen30(zSql); for(i=1; i<nCol; i++){ zSql[j++] = ','; zSql[j++] = '?'; } zSql[j++] = ')'; zSql[j] = 0; rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); free(zSql); if( rc ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); if (pStmt) sqlite3_finalize(pStmt); return 1; } in = fopen(zFile, "rb"); if( in==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); sqlite3_finalize(pStmt); return 1; } azCol = malloc( sizeof(azCol[0])*(nCol+1) ); if( azCol==0 ){ fprintf(stderr, "Error: out of memory\n"); fclose(in); sqlite3_finalize(pStmt); return 1; } sqlite3_exec(p->db, "BEGIN", 0, 0, 0); zCommit = "COMMIT"; while( (zLine = local_getline(0, in))!=0 ){ char *z; i = 0; lineno++; azCol[0] = zLine; for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){ if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){ *z = 0; i++; if( i<nCol ){ azCol[i] = &z[nSep]; z += nSep-1; } } } /* end for */ *z = 0; if( i+1!=nCol ){ fprintf(stderr, "Error: %s line %d: expected %d columns of data but found %d\n", zFile, lineno, nCol, i+1); zCommit = "ROLLBACK"; free(zLine); rc = 1; break; /* from while */ } for(i=0; i<nCol; i++){ sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); } sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); free(zLine); if( rc!=SQLITE_OK ){ fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); zCommit = "ROLLBACK"; rc = 1; break; /* from while */ } } /* end while */ free(azCol); fclose(in); sqlite3_finalize(pStmt); sqlite3_exec(p->db, zCommit, 0, 0, 0); }else if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.mode = MODE_List; if( nArg==1 ){ rc = sqlite3_exec(p->db, "SELECT name FROM sqlite_master " "WHERE type='index' AND name NOT LIKE 'sqlite_%' " "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type='index' " "ORDER BY 1", callback, &data, &zErrMsg ); }else{ zShellStatic = azArg[1]; rc = sqlite3_exec(p->db, "SELECT name FROM sqlite_master " "WHERE type='index' AND tbl_name LIKE shellstatic() " "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type='index' AND tbl_name LIKE shellstatic() " "ORDER BY 1", callback, &data, &zErrMsg ); zShellStatic = 0; } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n"); rc = 1; } }else #ifdef SQLITE_ENABLE_IOTRACE if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){ extern void (*sqlite3IoTrace)(const char*, ...); if( iotrace && iotrace!=stdout ) fclose(iotrace); iotrace = 0; if( nArg<2 ){ sqlite3IoTrace = 0; }else if( strcmp(azArg[1], "-")==0 ){ sqlite3IoTrace = iotracePrintf; iotrace = stdout; }else{ iotrace = fopen(azArg[1], "w"); if( iotrace==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); sqlite3IoTrace = 0; rc = 1; }else{ sqlite3IoTrace = iotracePrintf; } } }else #endif #ifndef SQLITE_OMIT_LOAD_EXTENSION if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){ const char *zFile, *zProc; char *zErrMsg = 0; zFile = azArg[1]; zProc = nArg>=3 ? azArg[2] : 0; open_db(p); rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=1 ){ const char *zFile = azArg[1]; if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){ fclose(p->pLog); p->pLog = 0; } if( strcmp(zFile,"stdout")==0 ){ p->pLog = stdout; }else if( strcmp(zFile, "stderr")==0 ){ p->pLog = stderr; }else if( strcmp(zFile, "off")==0 ){ p->pLog = 0; }else{ p->pLog = fopen(zFile, "w"); if( p->pLog==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); } } }else if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){ int n2 = strlen30(azArg[1]); if( (n2==4 && strncmp(azArg[1],"line",n2)==0) || (n2==5 && strncmp(azArg[1],"lines",n2)==0) ){ p->mode = MODE_Line; }else if( (n2==6 && strncmp(azArg[1],"column",n2)==0) || (n2==7 && strncmp(azArg[1],"columns",n2)==0) ){ p->mode = MODE_Column; }else if( n2==4 && strncmp(azArg[1],"list",n2)==0 ){ p->mode = MODE_List; }else if( n2==4 && strncmp(azArg[1],"html",n2)==0 ){ p->mode = MODE_Html; }else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; }else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->separator), p->separator, ","); }else if( n2==4 && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); }else if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, "table"); }else { fprintf(stderr,"Error: mode should be one of: " "column csv html insert line list tabs tcl\n"); rc = 1; } }else if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==3 ){ int n2 = strlen30(azArg[1]); if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, azArg[2]); }else { fprintf(stderr, "Error: invalid arguments: " " \"%s\". Enter \".help\" for help\n", azArg[2]); rc = 1; } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) { sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); }else if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){ if( p->out!=stdout ){ fclose(p->out); } if( strcmp(azArg[1],"stdout")==0 ){ p->out = stdout; sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout"); }else{ p->out = fopen(azArg[1], "wb"); if( p->out==0 ){ fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]); p->out = stdout; rc = 1; } else { sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]); } } }else if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){ if( nArg >= 2) { strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); } if( nArg >= 3) { strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){ rc = 2; }else if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){ FILE *alt = fopen(azArg[1], "rb"); if( alt==0 ){ fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p, alt); fclose(alt); } }else if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){ const char *zSrcFile; const char *zDb; sqlite3 *pSrc; sqlite3_backup *pBackup; int nTimeout = 0; if( nArg==2 ){ zSrcFile = azArg[1]; zDb = "main"; }else{ zSrcFile = azArg[2]; zDb = azArg[1]; } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); sqlite3_close(pSrc); return 1; } open_db(p); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_close(pSrc); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK || rc==SQLITE_BUSY ){ if( rc==SQLITE_BUSY ){ if( nTimeout++ >= 3 ) break; sqlite3_sleep(100); } } sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ fprintf(stderr, "Error: source database is busy\n"); rc = 1; }else{ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } sqlite3_close(pSrc); }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.mode = MODE_Semi; if( nArg>1 ){ int i; for(i=0; azArg[1][i]; i++) azArg[1][i] = (char)tolower(azArg[1][i]); if( strcmp(azArg[1],"sqlite_master")==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = "CREATE TABLE sqlite_master (\n" " type text,\n" " name text,\n" " tbl_name text,\n" " rootpage integer,\n" " sql text\n" ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n" " type text,\n" " name text,\n" " tbl_name text,\n" " rootpage integer,\n" " sql text\n" ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else{ zShellStatic = azArg[1]; rc = sqlite3_exec(p->db, "SELECT sql FROM " " (SELECT sql sql, type type, tbl_name tbl_name, name name" " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " "WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL " "ORDER BY substr(type,2,1), name", callback, &data, &zErrMsg); zShellStatic = 0; } }else{ rc = sqlite3_exec(p->db, "SELECT sql FROM " " (SELECT sql sql, type type, tbl_name tbl_name, name name" " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" "ORDER BY substr(type,2,1), name", callback, &data, &zErrMsg ); } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ fprintf(stderr,"Error: querying schema information\n"); rc = 1; }else{ rc = 0; } }else if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){ sqlite3_snprintf(sizeof(p->separator), p->separator, "%.*s", (int)sizeof(p->separator)-1, azArg[1]); }else if( c=='s' && strncmp(azArg[0], "show", n)==0 && nArg==1 ){ int i; fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off"); fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); fprintf(p->out,"%9.9s: ", "nullvalue"); output_c_string(p->out, p->nullvalue); fprintf(p->out, "\n"); fprintf(p->out,"%9.9s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); fprintf(p->out,"%9.9s: ", "separator"); output_c_string(p->out, p->separator); fprintf(p->out, "\n"); fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); fprintf(p->out,"%9.9s: ","width"); for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { fprintf(p->out,"%d ",p->colWidth[i]); } fprintf(p->out,"\n"); }else if( c=='s' && strncmp(azArg[0], "stats", n)==0 && nArg>1 && nArg<3 ){ p->statsOn = booleanValue(azArg[1]); }else if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){ char **azResult; int nRow; char *zErrMsg; open_db(p); if( nArg==1 ){ rc = sqlite3_get_table(p->db, "SELECT name FROM sqlite_master " "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' " "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type IN ('table','view') " "ORDER BY 1", &azResult, &nRow, 0, &zErrMsg ); }else{ zShellStatic = azArg[1]; rc = sqlite3_get_table(p->db, "SELECT name FROM sqlite_master " "WHERE type IN ('table','view') AND name LIKE shellstatic() " "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type IN ('table','view') AND name LIKE shellstatic() " "ORDER BY 1", &azResult, &nRow, 0, &zErrMsg ); zShellStatic = 0; } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n"); rc = 1; }else{ int len, maxlen = 0; int i, j; int nPrintCol, nPrintRow; for(i=1; i<=nRow; i++){ if( azResult[i]==0 ) continue; len = strlen30(azResult[i]); if( len>maxlen ) maxlen = len; } nPrintCol = 80/(maxlen+2); if( nPrintCol<1 ) nPrintCol = 1; nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; for(i=0; i<nPrintRow; i++){ for(j=i+1; j<=nRow; j+=nPrintRow){ char *zSp = j<=nPrintRow ? "" : " "; printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); } printf("\n"); } } sqlite3_free_table(azResult); }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 ){ enableTimer = booleanValue(azArg[1]); }else if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){ int j; assert( nArg<=ArraySize(azArg) ); for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ p->colWidth[j-1] = atoi(azArg[j]); } }else { fprintf(stderr, "Error: unknown command or invalid arguments: " " \"%s\". Enter \".help\" for help\n", azArg[0]); rc = 1; } return rc; } /* ** Return TRUE if a semicolon occurs anywhere in the first N characters ** of string z[]. */ static int _contains_semicolon(const char *z, int N){ int i; for(i=0; i<N; i++){ if( z[i]==';' ) return 1; } return 0; } /* ** Test to see if a line consists entirely of whitespace. */ static int _all_whitespace(const char *z){ for(; *z; z++){ if( isspace(*(unsigned char*)z) ) continue; if( *z=='/' && z[1]=='*' ){ z += 2; while( *z && (*z!='*' || z[1]!='/') ){ z++; } if( *z==0 ) return 0; z++; continue; } if( *z=='-' && z[1]=='-' ){ z += 2; while( *z && *z!='\n' ){ z++; } if( *z==0 ) return 1; continue; } return 0; } return 1; } /* ** Return TRUE if the line typed in is an SQL command terminator other ** than a semi-colon. The SQL Server style "go" command is understood ** as is the Oracle "/". */ static int _is_command_terminator(const char *zLine){ while( isspace(*(unsigned char*)zLine) ){ zLine++; }; if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){ return 1; /* Oracle */ } if( tolower(zLine[0])=='g' && tolower(zLine[1])=='o' && _all_whitespace(&zLine[2]) ){ return 1; /* SQL Server */ } return 0; } /* ** Return true if zSql is a complete SQL statement. Return false if it ** ends in the middle of a string literal or C-style comment. */ static int _is_complete(char *zSql, int nSql){ int rc; if( zSql==0 ) return 1; zSql[nSql] = ';'; zSql[nSql+1] = 0; rc = sqlite3_complete(zSql); zSql[nSql] = 0; return rc; } /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ static int process_input(struct callback_data *p, FILE *in){ char *zLine = 0; char *zSql = 0; int nSql = 0; int nSqlPrior = 0; char *zErrMsg; int rc; int errCnt = 0; int lineno = 0; int startline = 0; while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){ fflush(p->out); free(zLine); zLine = one_input_line(zSql, in); if( zLine==0 ){ break; /* We have reached EOF */ } if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } lineno++; if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue; if( zLine && zLine[0]=='.' && nSql==0 ){ if( p->echoOn ) printf("%s\n", zLine); rc = do_meta_command(zLine, p); if( rc==2 ){ /* exit requested */ break; }else if( rc ){ errCnt++; } continue; } if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){ memcpy(zLine,";",2); } nSqlPrior = nSql; if( zSql==0 ){ int i; for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){} if( zLine[i]!=0 ){ nSql = strlen30(zLine); zSql = malloc( nSql+3 ); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); exit(1); } memcpy(zSql, zLine, nSql+1); startline = lineno; } }else{ int len = strlen30(zLine); zSql = realloc( zSql, nSql + len + 4 ); if( zSql==0 ){ fprintf(stderr,"Error: out of memory\n"); exit(1); } zSql[nSql++] = '\n'; memcpy(&zSql[nSql], zLine, len+1); nSql += len; } if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) && sqlite3_complete(zSql) ){ p->cnt = 0; open_db(p); BEGIN_TIMER; rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; if( in!=0 || !stdin_is_interactive ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error: near line %d:", startline); }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:"); } if( zErrMsg!=0 ){ fprintf(stderr, "%s %s\n", zPrefix, zErrMsg); sqlite3_free(zErrMsg); zErrMsg = 0; }else{ fprintf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } errCnt++; } free(zSql); zSql = 0; nSql = 0; } } if( zSql ){ if( !_all_whitespace(zSql) ) fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); free(zSql); } free(zLine); return errCnt; } /* ** Return a pathname which is the user's home directory. A ** 0 return indicates an error of some kind. Space to hold the ** resulting string is obtained from malloc(). The calling ** function should free the result. */ static char *find_home_dir(void){ char *home_dir = NULL; #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL) struct passwd *pwent; uid_t uid = getuid(); if( (pwent=getpwuid(uid)) != NULL) { home_dir = pwent->pw_dir; } #endif #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv() */ home_dir = strdup("/"); #else #if defined(_WIN32) || defined(WIN32) || defined(__OS2__) if (!home_dir) { home_dir = getenv("USERPROFILE"); } #endif if (!home_dir) { home_dir = getenv("HOME"); } #if defined(_WIN32) || defined(WIN32) || defined(__OS2__) if (!home_dir) { char *zDrive, *zPath; int n; zDrive = getenv("HOMEDRIVE"); zPath = getenv("HOMEPATH"); if( zDrive && zPath ){ n = strlen30(zDrive) + strlen30(zPath) + 1; home_dir = malloc( n ); if( home_dir==0 ) return 0; sqlite3_snprintf(n, home_dir, "%s%s", zDrive, zPath); return home_dir; } home_dir = "c:\\"; } #endif #endif /* !_WIN32_WCE */ if( home_dir ){ int n = strlen30(home_dir) + 1; char *z = malloc( n ); if( z ) memcpy(z, home_dir, n); home_dir = z; } return home_dir; } /* ** Read input from the file given by sqliterc_override. Or if that ** parameter is NULL, take input from ~/.sqliterc ** ** Returns the number of errors. */ static int process_sqliterc( struct callback_data *p, /* Configuration data */ const char *sqliterc_override /* Name of config file. NULL to use default */ ){ char *home_dir = NULL; const char *sqliterc = sqliterc_override; char *zBuf = 0; FILE *in = NULL; int nBuf; int rc = 0; if (sqliterc == NULL) { home_dir = find_home_dir(); if( home_dir==0 ){ #if !defined(__RTP__) && !defined(_WRS_KERNEL) fprintf(stderr,"%s: Error: cannot locate your home directory\n", Argv0); #endif return 1; } nBuf = strlen30(home_dir) + 16; zBuf = malloc( nBuf ); if( zBuf==0 ){ fprintf(stderr,"%s: Error: out of memory\n",Argv0); return 1; } sqlite3_snprintf(nBuf, zBuf,"%s/.sqliterc",home_dir); free(home_dir); sqliterc = (const char*)zBuf; } in = fopen(sqliterc,"rb"); if( in ){ if( stdin_is_interactive ){ fprintf(stderr,"-- Loading resources from %s\n",sqliterc); } rc = process_input(p,in); fclose(in); } free(zBuf); return rc; } /* ** Show available command line options */ static const char zOptions[] = " -help show this message\n" " -init filename read/process named file\n" " -echo print commands before execution\n" " -[no]header turn headers on or off\n" " -bail stop after hitting an error\n" " -interactive force interactive I/O\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" " -csv set output mode to 'csv'\n" " -html set output mode to HTML\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -separator 'x' set output field separator (|)\n" " -stats print memory stats before each finalize\n" " -nullvalue 'text' set text string for NULL values\n" " -version show SQLite version\n" ; static void usage(int showDetail){ fprintf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n" "FILENAME is the name of an SQLite database. A new database is created\n" "if the file does not previously exist.\n", Argv0); if( showDetail ){ fprintf(stderr, "OPTIONS include:\n%s", zOptions); }else{ fprintf(stderr, "Use the -help option for additional information\n"); } exit(1); } /* ** Initialize the state information in data */ static void main_init(struct callback_data *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; memcpy(data->separator,"|", 2); data->showHeader = 0; sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); } int main(int argc, char **argv){ char *zErrMsg = 0; struct callback_data data; const char *zInitFile = 0; char *zFirstCmd = 0; int i; int rc = 0; Argv0 = argv[0]; main_init(&data); stdin_is_interactive = isatty(0); /* Make sure we have a valid signal handler early, before anything ** else is done. */ #ifdef SIGINT signal(SIGINT, interrupt_handler); #endif /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** and the first command to execute. */ for(i=1; i<argc-1; i++){ char *z; if( argv[i][0]!='-' ) break; z = argv[i]; if( z[0]=='-' && z[1]=='-' ) z++; if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){ i++; }else if( strcmp(argv[i],"-init")==0 ){ i++; zInitFile = argv[i]; /* Need to check for batch mode here to so we can avoid printing ** informational messages (like from process_sqliterc) before ** we do the actual processing of arguments later in a second pass. */ }else if( strcmp(argv[i],"-batch")==0 ){ stdin_is_interactive = 0; } } if( i<argc ){ #if defined(SQLITE_OS_OS2) && SQLITE_OS_OS2 data.zDbFilename = (const char *)convertCpPathToUtf8( argv[i++] ); #else data.zDbFilename = argv[i++]; #endif }else{ #ifndef SQLITE_OMIT_MEMORYDB data.zDbFilename = ":memory:"; #else data.zDbFilename = 0; #endif /***** Begin Fossil Patch *****/ { extern void fossil_open(sqlite3**, const char **); fossil_open(&data.db, &data.zDbFilename); } /***** End Fossil Patch *****/ } if( i<argc ){ zFirstCmd = argv[i++]; } if( i<argc ){ fprintf(stderr,"%s: Error: too many options: \"%s\"\n", Argv0, argv[i]); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } data.out = stdout; #ifdef SQLITE_OMIT_MEMORYDB if( data.zDbFilename==0 ){ fprintf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; } #endif /* Go ahead and open the database file if it already exists. If the ** file does not exist, delay opening it. This prevents empty database ** files from being created if a user mistypes the database name argument ** to the sqlite command-line tool. */ if( access(data.zDbFilename, 0)==0 ){ open_db(&data); } /* Process the initialization file if there is one. If no -init option ** is given on the command line, look for a file named ~/.sqliterc and ** try to process it. */ rc = process_sqliterc(&data,zInitFile); if( rc>0 ){ return rc; } /* Make a second pass through the command-line argument and set ** options. This second pass is delayed until after the initialization ** file is processed so that the command-line arguments will override ** settings in the initialization file. */ for(i=1; i<argc && argv[i][0]=='-'; i++){ char *z = argv[i]; if( z[1]=='-' ){ z++; } if( strcmp(z,"-init")==0 ){ i++; }else if( strcmp(z,"-html")==0 ){ data.mode = MODE_Html; }else if( strcmp(z,"-list")==0 ){ data.mode = MODE_List; }else if( strcmp(z,"-line")==0 ){ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.separator,",",2); }else if( strcmp(z,"-separator")==0 ){ i++; if(i>=argc){ fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } sqlite3_snprintf(sizeof(data.separator), data.separator, "%.*s",(int)sizeof(data.separator)-1,argv[i]); }else if( strcmp(z,"-nullvalue")==0 ){ i++; if(i>=argc){ fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, "%.*s",(int)sizeof(data.nullvalue)-1,argv[i]); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ data.echoOn = 1; }else if( strcmp(z,"-stats")==0 ){ data.statsOn = 1; }else if( strcmp(z,"-bail")==0 ){ bail_on_error = 1; }else if( strcmp(z,"-version")==0 ){ printf("%s\n", sqlite3_libversion()); return 0; }else if( strcmp(z,"-interactive")==0 ){ stdin_is_interactive = 1; }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ usage(1); }else{ fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } } if( zFirstCmd ){ /* Run just the command that follows the database name */ if( zFirstCmd[0]=='.' ){ rc = do_meta_command(zFirstCmd, &data); }else{ open_db(&data); rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg); if( zErrMsg!=0 ){ fprintf(stderr,"Error: %s\n", zErrMsg); return rc!=0 ? rc : 1; }else if( rc!=0 ){ fprintf(stderr,"Error: unable to process SQL \"%s\"\n", zFirstCmd); return rc; } } }else{ /* Run commands received from standard input */ if( stdin_is_interactive ){ char *zHome; char *zHistory = 0; int nHistory; printf( "SQLite version %s\n" "Enter \".help\" for instructions\n" "Enter SQL statements terminated with a \";\"\n", sqlite3_libversion() ); zHome = find_home_dir(); if( zHome ){ nHistory = strlen30(zHome) + 20; if( (zHistory = malloc(nHistory))!=0 ){ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); } } #if defined(HAVE_READLINE) && HAVE_READLINE==1 if( zHistory ) read_history(zHistory); #endif rc = process_input(&data, 0); if( zHistory ){ stifle_history(100); write_history(zHistory); free(zHistory); } free(zHome); }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); if( data.db ){ if( sqlite3_close(data.db)!=SQLITE_OK ){ fprintf(stderr,"Error: cannot close database \"%s\"\n", sqlite3_errmsg(db)); rc++; } } return rc; } |
Changes to src/shun.c.
︙ | ︙ | |||
67 68 69 70 71 72 73 | } } style_header("Shunned Artifacts"); if( zUuid && P("sub") ){ login_verify_csrf_secret(); db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid); if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){ | | | | | | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | } } style_header("Shunned Artifacts"); if( zUuid && P("sub") ){ login_verify_csrf_secret(); db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid); if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){ @ <p class="noMoreShun">Artifact @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> is no @ longer being shunned.</p> }else{ @ <p class="noMoreShun">Artifact %s(zUuid) will no longer @ be shunned. But it does not exist in the repository. It @ may be necessary to rebuild the repository using the @ <b>fossil rebuild</b> command-line before the artifact content @ can pulled in from other respositories.</p> } } if( zUuid && P("add") ){ login_verify_csrf_secret(); db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid); @ <p class="shunned">Artifact @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> has been @ shunned. It will no longer be pushed. @ It will be removed from the repository the next time the respository @ is rebuilt using the <b>fossil rebuild</b> command-line</p> } @ <p>A shunned artifact will not be pushed nor accepted in a pull and the @ artifact content will be purged from the repository the next time the @ repository is rebuilt. A list of shunned artifacts can be seen at the @ bottom of this page.</p> @ @ <a name="addshun"></a> |
︙ | ︙ | |||
110 111 112 113 114 115 116 | @ from the repository. Inappropriate content includes such things as @ spam added to Wiki, files that violate copyright or patent agreements, @ or artifacts that by design or accident interfere with the processing @ of the repository. Do not shun artifacts merely to remove them from @ sight - set the "hidden" tag on such artifacts instead.</p> @ @ <blockquote> | | | | | | | | | | | | | | | | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | @ from the repository. Inappropriate content includes such things as @ spam added to Wiki, files that violate copyright or patent agreements, @ or artifacts that by design or accident interfere with the processing @ of the repository. Do not shun artifacts merely to remove them from @ sight - set the "hidden" tag on such artifacts instead.</p> @ @ <blockquote> @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50" /> @ <input type="submit" name="add" value="Shun" /> @ </div></form> @ </blockquote> @ @ <p>Enter the UUID of a previous shunned artifact to cause it to be @ accepted again in the repository. The artifact content is not @ restored because the content is unknown. The only change is that @ the formerly shunned artifact will be accepted on subsequent sync @ operations.</p> @ @ <blockquote> @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <input type="text" name="uuid" size="50" /> @ <input type="submit" name="sub" value="Accept" /> @ </div></form> @ </blockquote> @ @ <p>Press the Rebuild button below to rebuild the respository. The @ content of newly shunned artifacts is not purged until the repository @ is rebuilt. On larger repositories, the rebuild may take minute or @ two, so be patient after pressing the button.</p> @ @ <blockquote> @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <input type="submit" name="rebuild" value="Rebuild" /> @ </div></form> @ </blockquote> @ @ <hr /><p>Shunned Artifacts:</p> @ <blockquote><p> db_prepare(&q, "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)" " FROM shun ORDER BY uuid"); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); int stillExists = db_column_int(&q, 1); cnt++; if( stillExists ){ @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br /> }else{ @ <b>%s(zUuid)</b><br /> } } if( cnt==0 ){ @ <i>no artifacts are shunned on this server</i> } db_finalize(&q); @ </p></blockquote> style_footer(); } /* ** Remove from the BLOB table all artifacts that are in the SHUN table. */ void shun_artifacts(void){ |
︙ | ︙ | |||
227 228 229 230 231 232 233 | @ finding and fixing attempts to inject illicit content into the @ repository.</p> @ @ <p>Click on the "rcvid" to show a list of specific artifacts received @ by a transaction. After identifying illicit artifacts, remove them @ using the "Shun" feature.</p> @ | | | | > | | | | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | @ finding and fixing attempts to inject illicit content into the @ repository.</p> @ @ <p>Click on the "rcvid" to show a list of specific artifacts received @ by a transaction. After identifying illicit artifacts, remove them @ using the "Shun" feature.</p> @ @ <table cellpadding="0" cellspacing="0" border="0"> @ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th> @ <th style="padding-right: 15px;text-align: left;">Date</th> @ <th style="padding-right: 15px;text-align: left;">User</th> @ <th style="text-align: left;">IP Address</th></tr> cnt = 0; while( db_step(&q)==SQLITE_ROW ){ int rcvid = db_column_int(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zIpAddr = db_column_text(&q, 3); if( cnt==30 ){ style_submenu_element("Older", "Older", "rcvfromlist?ofst=%d", ofst+30); }else{ cnt++; @ <tr> @ <td style="padding-right: 15px;text-align: right;"><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td> @ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td> @ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td> @ <td style="text-align: left;">%s(zIpAddr)</td> @ </tr> } } db_finalize(&q); @ </table> style_footer(); } |
︙ | ︙ | |||
275 276 277 278 279 280 281 | style_header("Content Source %d", rcvid); db_prepare(&q, "SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr" " FROM rcvfrom LEFT JOIN user USING(uid)" " WHERE rcvid=%d", rcvid ); | | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | style_header("Content Source %d", rcvid); db_prepare(&q, "SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr" " FROM rcvfrom LEFT JOIN user USING(uid)" " WHERE rcvid=%d", rcvid ); @ <table cellspacing="15" cellpadding="0" border="0"> @ <tr><td valign="top" align="right"><b>rcvid:</b></td> @ <td valign="top">%d(rcvid)</td></tr> if( db_step(&q)==SQLITE_ROW ){ const char *zUser = db_column_text(&q, 0); const char *zDate = db_column_text(&q, 1); const char *zIpAddr = db_column_text(&q, 2); @ <tr><td valign="top" align="right"><b>User:</b></td> |
︙ | ︙ | |||
300 301 302 303 304 305 306 | @ <tr><td valign="top" align="right"><b>Artifacts:</b></td> @ <td valign="top"> while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zUuid = db_column_text(&q, 1); int size = db_column_int(&q, 2); @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a> | | > > | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | @ <tr><td valign="top" align="right"><b>Artifacts:</b></td> @ <td valign="top"> while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zUuid = db_column_text(&q, 1); int size = db_column_int(&q, 2); @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a> @ (rid: %d(rid), size: %d(size))<br /> } @ </td></tr> @ </table> db_finalize(&q); style_footer(); } |
Changes to src/skins.c.
︙ | ︙ | |||
163 164 165 166 167 168 169 | @ media="screen"> @ </head> @ <body> @ <div class="header"> @ <div class="logo"> @ <img src="$baseurl/logo" alt="logo"> @ </div> | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | @ media="screen"> @ </head> @ <body> @ <div class="header"> @ <div class="logo"> @ <img src="$baseurl/logo" alt="logo"> @ </div> @ <div class="title"><small>$<project_name></small><br />$<title></div> @ <div class="status"><nobr><th1> @ if {[info exists login]} { @ puts "Logged in as $login" @ } else { @ puts "Not logged in" @ } @ </th1></nobr></div> |
︙ | ︙ | |||
598 599 600 601 602 603 604 | @ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css" @ media="screen"> @ </head> @ <body> @ <div class="header"> @ <div class="logo"> @ <!-- <img src="$baseurl/logo" alt="logo"> --> | | | 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 | @ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css" @ media="screen"> @ </head> @ <body> @ <div class="header"> @ <div class="logo"> @ <!-- <img src="$baseurl/logo" alt="logo"> --> @ <br /><nobr>$<project_name></nobr> @ </div> @ <div class="title">$<title></div> @ <div class="status"><nobr><th1> @ if {[info exists login]} { @ puts "Logged in as $login" @ } else { @ puts "Not logged in" |
︙ | ︙ | |||
654 655 656 657 658 659 660 661 | ; /* ** An array of available built-in skins. */ static struct BuiltinSkin { const char *zName; const char *zValue; } aBuiltinSkin[] = { | > | | | | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | ; /* ** An array of available built-in skins. */ static struct BuiltinSkin { const char *zName; const char *zValue; const char *zAuthor; } aBuiltinSkin[] = { { "Default", 0 /* Filled in at runtime */, 0}, { "Plain Gray, No Logo", zBuiltinSkin1, "Unknown"}, { "Khaki, No Logo", zBuiltinSkin2, "Unknown"}, { "Black & White, Menu on Left", zBuiltinSkin3, "Unknown"}, }; /* ** For a skin named zSkinName, compute the name of the CONFIG table ** entry where that skin is stored and return it. ** ** Return NULL if zSkinName is NULL or an empty string. |
︙ | ︙ | |||
729 730 731 732 733 734 735 | login_needed(); } db_begin_transaction(); /* Process requests to delete a user-defined skin */ if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ style_header("Confirm Custom Skin Delete"); | | | | | | | 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 | login_needed(); } db_begin_transaction(); /* Process requests to delete a user-defined skin */ if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ style_header("Confirm Custom Skin Delete"); @ <form action="%s(g.zBaseURL)/setup_skin" method="post"><div> @ <p>Deletion of a custom skin is a permanent action that cannot @ be undone. Please confirm that this is what you want to do:</p> @ <input type="hidden" name="sn" value="%h(P("sn"))" /> @ <input type="submit" name="del2" value="Confirm - Delete The Skin" /> @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" /> login_insert_csrf_secret(); @ </div></form> style_footer(); return; } if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } |
︙ | ︙ | |||
810 811 812 813 814 815 816 | @ <h2>Available Skins:</h2> @ <ol> for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ z = aBuiltinSkin[i].zName; if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ @ <li><p>%h(z). <b>Currently In Use</b></p> }else{ | | | | | | | 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | @ <h2>Available Skins:</h2> @ <ol> for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ z = aBuiltinSkin[i].zName; if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ @ <li><p>%h(z). <b>Currently In Use</b></p> }else{ @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"><div> @ %h(z). @ <input type="hidden" name="sn" value="%h(z)" /> @ <input type="submit" name="load" value="Use This Skin" /> @ </div></form></li> } } db_prepare(&q, "SELECT substr(name, 6), value FROM config" " WHERE name GLOB 'skin:*'" " ORDER BY name" ); while( db_step(&q)==SQLITE_ROW ){ const char *zN = db_column_text(&q, 0); const char *zV = db_column_text(&q, 1); if( strcmp(zV, zCurrent)==0 ){ @ <li><p>%h(zN). <b>Currently In Use</b></p> }else{ @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"> @ %h(zN). @ <input type="hidden" name="sn" value="%h(zN)"> @ <input type="submit" name="load" value="Use This Skin"> @ <input type="submit" name="del1" value="Delete This Skin"> @ </form></li> } } db_finalize(&q); @ </ol> style_footer(); db_end_transaction(0); } |
Changes to src/sqlite3.c.
1 2 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | | 1 2 3 4 5 6 7 8 9 10 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.7.3. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a one translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% are more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other |
︙ | ︙ | |||
211 212 213 214 215 216 217 | /* ** The maximum value of a ?nnn wildcard that the parser will accept. */ #ifndef SQLITE_MAX_VARIABLE_NUMBER # define SQLITE_MAX_VARIABLE_NUMBER 999 #endif | | | < > > | | | | | < | | > | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | /* ** The maximum value of a ?nnn wildcard that the parser will accept. */ #ifndef SQLITE_MAX_VARIABLE_NUMBER # define SQLITE_MAX_VARIABLE_NUMBER 999 #endif /* Maximum page size. The upper bound on this value is 65536. This a limit ** imposed by the use of 16-bit offsets within each page. ** ** Earlier versions of SQLite allowed the user to change this value at ** compile time. This is no longer permitted, on the grounds that it creates ** a library that is technically incompatible with an SQLite library ** compiled with a different limit. If a process operating on a database ** with a page-size of 65536 bytes crashes, then an instance of SQLite ** compiled with the default page-size limit will not be able to rollback ** the aborted transaction. This could lead to database corruption. */ #ifdef SQLITE_MAX_PAGE_SIZE # undef SQLITE_MAX_PAGE_SIZE #endif #define SQLITE_MAX_PAGE_SIZE 65536 /* ** The default size of a database page. */ #ifndef SQLITE_DEFAULT_PAGE_SIZE # define SQLITE_DEFAULT_PAGE_SIZE 1024 |
︙ | ︙ | |||
349 350 351 352 353 354 355 | # define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif /* | | > > > > > > | | | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | # define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never ** threadsafe. 1 means the library is serialized which is the highest ** level of threadsafety. 2 means the libary is multithreaded - multiple ** threads can use SQLite as long as no two threads try to use the same ** database connection at the same time. ** ** Older versions of SQLite used an optional THREADSAFE macro. ** We support that for legacy. */ #if !defined(SQLITE_THREADSAFE) #if defined(THREADSAFE) # define SQLITE_THREADSAFE THREADSAFE #else # define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ #endif #endif /* ** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. ** It determines whether or not the features related to ** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can |
︙ | ︙ | |||
629 630 631 632 633 634 635 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management | | | | | | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 #define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
︙ | ︙ | |||
686 687 688 689 690 691 692 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** | | | | 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** ** ^The sqlite3_compileoption_get() function allows iterating ** over the list of options that were defined at compile time by ** returning the N-th compile time option string. ^If N is out of range, ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ ** prefix is omitted from any strings returned by ** sqlite3_compileoption_get(). ** ** ^Support for the diagnostic functions sqlite3_compileoption_used() ** and sqlite3_compileoption_get() may be omitted by specifying the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); |
︙ | ︙ | |||
800 801 802 803 804 805 806 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is | | | 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is ** successfully destroyed and all associated resources are deallocated. ** ** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with ** the [sqlite3] object prior to attempting to close the object. ^If ** sqlite3_close() is called on a [database connection] that still has ** outstanding [prepared statements] or [BLOB handles], then it returns ** SQLITE_BUSY. |
︙ | ︙ | |||
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only | > > > > > > > > > | 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. ** ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified ** by the user. The fourth argument to [sqlite3_file_control()] should ** point to an integer (type int) containing the new chunk-size to use ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and ** improve performance on some systems. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
︙ | ︙ | |||
1280 1281 1282 1283 1284 1285 1286 | ** or modify this field while holding a particular static mutex. ** The application should never modify anything within the sqlite3_vfs ** object once the object has been registered. ** ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** | | > > > > | | | | > | | | > | | | | | | | 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 | ** or modify this field while holding a particular static mutex. ** The application should never modify anything within the sqlite3_vfs ** object once the object has been registered. ** ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** ** ^SQLite guarantees that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained ** from xFullPathname() with an optional suffix added. ** ^If a suffix is added to the zFilename parameter, it will ** consist of a single "-" character followed by no more than ** 10 alphanumeric and/or "-" characters. ** ^SQLite further guarantees that ** the string will be valid and unchanged until xClose() is ** called. Because of the previous sentence, ** the [sqlite3_file] can safely store a pointer to the ** filename if it needs to remember the filename for some reason. ** If the zFilename parameter to xOpen is a NULL pointer then xOpen ** must invent its own temporary name for the file. ^Whenever the ** xFilename parameter is NULL it will also be the case that the ** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. ** ** The flags argument to xOpen() includes all bits set in ** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] ** or [sqlite3_open16()] is used, then flags includes at least ** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. ** If xOpen() opens a file read-only then it sets *pOutFlags to ** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. ** ** ^(SQLite will also add one of the following flags to the xOpen() ** call, depending on the object being opened: ** ** <ul> ** <li> [SQLITE_OPEN_MAIN_DB] ** <li> [SQLITE_OPEN_MAIN_JOURNAL] ** <li> [SQLITE_OPEN_TEMP_DB] ** <li> [SQLITE_OPEN_TEMP_JOURNAL] ** <li> [SQLITE_OPEN_TRANSIENT_DB] ** <li> [SQLITE_OPEN_SUBJOURNAL] ** <li> [SQLITE_OPEN_MASTER_JOURNAL] ** <li> [SQLITE_OPEN_WAL] ** </ul>)^ ** ** The file I/O implementation can use the object type flags to ** change the way it deals with files. For example, an application ** that does not care about crash recovery or rollback might make ** the open of a journal file a no-op. Writes to this journal would ** also be no-ops, and any attempt to read the journal would return ** SQLITE_IOERR. Or the implementation might recognize that a database ** file will be doing page-aligned sector reads and writes in a random ** order and set up its I/O subsystem accordingly. ** ** SQLite might also add one of the following flags to the xOpen method: ** ** <ul> ** <li> [SQLITE_OPEN_DELETEONCLOSE] ** <li> [SQLITE_OPEN_EXCLUSIVE] ** </ul> ** ** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be ** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] ** will be set for TEMP databases and their journals, transient ** databases, and subjournals. ** ** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction ** with the [SQLITE_OPEN_CREATE] flag, which are both directly ** analogous to the O_EXCL and O_CREAT flags of the POSIX open() ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the ** SQLITE_OPEN_CREATE, is used to indicate that file should always ** be created, and that it is an error if it already exists. ** It is <i>not</i> used to indicate the file should be opened ** for exclusive access. ** ** ^At least szOsFile bytes of memory are allocated by SQLite ** to hold the [sqlite3_file] structure passed as the third ** argument to xOpen. The xOpen method does not have to ** allocate the structure; it should just fill it in. Note that ** the xOpen method must set the sqlite3_file.pMethods to either ** a valid [sqlite3_io_methods] object or to NULL. xOpen must do ** this even if the open fails. SQLite expects that the sqlite3_file.pMethods ** element will be valid after xOpen returns regardless of the success ** or failure of the xOpen call. ** ** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] ** to test whether a file is at least readable. The file can be a ** directory. ** ** ^SQLite will always allocate at least mxPathname+1 bytes for the ** output buffer xFullPathname. The exact size of the output buffer ** is also passed as a parameter to both methods. If the output buffer ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is ** handled as a fatal error by SQLite, vfs implementations should endeavor ** to prevent this by setting mxPathname to a sufficiently large value. ** ** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() ** interfaces are not strictly a part of the filesystem, but they are ** included in the VFS structure for completeness. ** The xRandomness() function attempts to return nBytes bytes ** of good-quality randomness into zOut. The return value is ** the actual number of bytes of randomness obtained. ** The xSleep() method causes the calling thread to sleep for at ** least the number of microseconds given. ^The xCurrentTime() ** method returns a Julian Day Number for the current date and time as ** a floating point value. ** ^The xCurrentTimeInt64() method returns, as an integer, the Julian ** Day Number multipled by 86400000 (the number of milliseconds in ** a 24-hour day). ** ^SQLite will use the xCurrentTimeInt64() method to get the current ** date and time if that method is available (if iVersion is 2 or ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. */ |
︙ | ︙ | |||
1771 1772 1773 1774 1775 1776 1777 | ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the ** following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] | | | < | | | | | < | | 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 | ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the ** following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] ** <li> [sqlite3_status()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory ** allocation statistics are disabled by default. ** </dd> ** ** <dt>SQLITE_CONFIG_SCRATCH</dt> ** <dd> ^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 ** 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. ** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. ** ^SQLite will use no more than two scratch buffers per thread. So ** N should be set to twice the expected maximum number of threads. ** ^SQLite will never require a scratch buffer that is more than 6 ** times the database page size. ^If SQLite needs needs additional ** scratch memory beyond what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.</dd> ** ** <dt>SQLITE_CONFIG_PAGECACHE</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implemenation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. ** There are three arguments to this option: A pointer to 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each ** page header. ^The page header size is 20 to 40 bytes depending on ** the host architecture. ^It is harmless, apart from the wasted memory, ** to make sz a little too large. The first ** argument should point to an allocation of at least sz*N bytes of memory. ** ^SQLite will use the memory provided by the first argument to satisfy its ** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space. ** The pointer in the first argument must ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.</dd> ** ** <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. |
︙ | ︙ | |||
1943 1944 1945 1946 1947 1948 1949 | ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally | | > > > | > > > | 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 | ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally ** rounded down to the next smaller multiple of 8. ^(The lookaside memory ** configuration for a database connection can only be changed when that ** connection is not currently using lookaside memory, or in other words ** when the "current value" returned by ** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^</dd> ** ** </dl> */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ /* |
︙ | ︙ | |||
2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** Definition: A <b>result table</b> is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These | > > > | 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. ** ** Definition: A <b>result table</b> is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These |
︙ | ︙ | |||
2269 2270 2271 2272 2273 2274 2275 | ** in NULL pointers. All other values are in their UTF-8 zero-terminated ** string representation as returned by [sqlite3_column_text()]. ** ** A result table might consist of one or more memory allocations. ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** | | | 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 | ** in NULL pointers. All other values are in their UTF-8 zero-terminated ** string representation as returned by [sqlite3_column_text()]. ** ** A result table might consist of one or more memory allocations. ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** ** ^(As an example of the result table format, suppose a query result ** is as follows: ** ** <blockquote><pre> ** Name | Age ** ----------------------- ** Alice | 43 ** Bob | 28 |
︙ | ︙ | |||
2293 2294 2295 2296 2297 2298 2299 | ** azResult[1] = "Age"; ** azResult[2] = "Alice"; ** azResult[3] = "43"; ** azResult[4] = "Bob"; ** azResult[5] = "28"; ** azResult[6] = "Cindy"; ** azResult[7] = "21"; | | | | | | 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 | ** azResult[1] = "Age"; ** azResult[2] = "Alice"; ** azResult[3] = "43"; ** azResult[4] = "Bob"; ** azResult[5] = "28"; ** azResult[6] = "Cindy"; ** azResult[7] = "21"; ** </pre></blockquote>)^ ** ** ^The sqlite3_get_table() function evaluates one or more ** semicolon-separated SQL statements in the zero-terminated UTF-8 ** string of its 2nd parameter and returns a result table to the ** pointer given in its 3rd parameter. ** ** After the application has finished with the result from sqlite3_get_table(), ** it must pass the result table pointer to sqlite3_free_table() in order to ** release the memory that was malloced. Because of the way the ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling ** function must not try to call [sqlite3_free()] directly. Only ** [sqlite3_free_table()] is able to release the memory properly and safely. ** ** The sqlite3_get_table() interface is implemented as a wrapper around ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ SQLITE_API int sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ int *pnRow, /* Number of result rows written here */ int *pnColumn, /* Number of result columns written here */ |
︙ | ︙ | |||
2465 2466 2467 2468 2469 2470 2471 | ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc() and the prior allocation is freed. ** ^If sqlite3_realloc() returns NULL, then the prior allocation ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() | | > > | 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 | ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc() and the prior allocation is freed. ** ^If sqlite3_realloc() returns NULL, then the prior allocation ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() ** is always aligned to at least an 8 byte boundary, or to a ** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time ** option is used. ** ** In SQLite version 3.5.0 and 3.5.1, it was possible to define ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in ** implementation of these routines to be omitted. That capability ** is no longer provided. Only built-in memory allocators can be used. ** ** The Windows OS interface layer calls |
︙ | ︙ | |||
2723 2724 2725 2726 2727 2728 2729 | SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** | | | | | > > > > > > > > > > > | | 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 | SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to ** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for ** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** ** ^The parameter P is passed through as the only parameter to the ** callback function X. ^The parameter N is the number of ** [virtual machine instructions] that are evaluated between successive ** invocations of the callback X. ** ** ^Only a single progress handler may be defined at one time per ** [database connection]; setting a new progress handler cancels the ** old one. ^Setting parameter X to NULL disables the progress handler. ** ^The progress handler is also disabled by setting N to a value less ** than 1. ** ** ^If the progress callback returns non-zero, the operation is ** interrupted. This feature can be used to implement a ** "Cancel" button on a GUI progress dialog box. ** ** The progress handler callback must not do anything that will modify ** the database connection that invoked the progress handler. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** */ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); |
︙ | ︙ | |||
2792 2793 2794 2795 2796 2797 2798 | ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined ** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], | | | 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 | ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined ** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], ** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_PRIVATECACHE] flags, ** then the behavior is undefined. ** ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection ** opens in the multi-thread [threading mode] as long as the single-thread ** mode has not been set at compile-time or start-time. ^If the ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens ** in the serialized [threading mode] unless single-thread was |
︙ | ︙ | |||
2917 2918 2919 2920 2921 2922 2923 | ** CAPI3REF: Run-time Limits ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the | | | | | > > > > > | 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 | ** CAPI3REF: Run-time Limits ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the ** new limit for that construct.)^ ** ** ^If the new limit is a negative number, the limit is unchanged. ** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a ** [limits | hard upper bound] ** set at compile-time by a C preprocessor macro called ** [limits | SQLITE_MAX_<i>NAME</i>]. ** (The "_LIMIT_" in the name is changed to "_MAX_".))^ ** ^Attempts to increase a limit above its hard upper bound are ** silently truncated to the hard upper bound. ** ** ^Regardless of whether or not the limit was changed, the ** [sqlite3_limit()] interface returns the prior value of the limit. ** ^Hence, to find the current value of a limit without changing it, ** simply invoke this interface with the third parameter set to -1. ** ** Run-time limits are intended for use in applications that manage ** both their own internal database and also databases that are controlled ** by untrusted external sources. An example application might be a ** web browser that has its own databases for storing history and ** separate databases controlled by JavaScript applications downloaded ** off the Internet. The internal databases can be given the ** large, default limits. Databases managed by external sources can |
︙ | ︙ | |||
2956 2957 2958 2959 2960 2961 2962 | ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> ** ^(<dt>SQLITE_LIMIT_LENGTH</dt> | | | > > | < | 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 | ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> ** ^(<dt>SQLITE_LIMIT_LENGTH</dt> ** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^ ** ** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt> ** <dd>The maximum length of an SQL statement, in bytes.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_COLUMN</dt> ** <dd>The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index ** or in an ORDER BY or GROUP BY clause.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt> ** <dd>The maximum depth of the parse tree on any expression.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> ** <dd>The maximum number of instructions in a virtual machine program ** used to implement an SQL statement. This limit is not currently ** enforced, though that might be added in some future release of ** SQLite.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> ** <dd>The maximum number of arguments on a function.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_ATTACHED</dt> ** <dd>The maximum number of [ATTACH | attached databases].)^</dd> ** ** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt> ** <dd>The maximum length of the pattern argument to the [LIKE] or ** [GLOB] operators.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt> ** <dd>The maximum index number of any [parameter] in an SQL statement.)^ ** ** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> ** <dd>The maximum depth of recursion for triggers.</dd>)^ ** </dl> */ #define SQLITE_LIMIT_LENGTH 0 #define SQLITE_LIMIT_SQL_LENGTH 1 |
︙ | ︙ | |||
3060 3061 3062 3063 3064 3065 3066 | ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** ** <ol> ** <li> ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL | | < < < < < | | | | | > > > > > | 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 | ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** ** <ol> ** <li> ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL ** statement and try to run it again. ** </li> ** ** <li> ** ^When an error occurs, [sqlite3_step()] will return one of the detailed ** [error codes] or [extended error codes]. ^The legacy behavior was that ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code ** and the application would have to make a second call to [sqlite3_reset()] ** in order to find the underlying cause of the problem. With the "v2" prepare ** interfaces, the underlying reason for the error is returned immediately. ** </li> ** ** <li> ** ^If the specific value bound to [parameter | host parameter] in the ** WHERE clause might influence the choice of query plan for a statement, ** then the statement will be automatically recompiled, as if there had been ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled. ** the ** </li> ** </ol> */ SQLITE_API int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ |
︙ | ︙ | |||
3149 3150 3151 3152 3153 3154 3155 | ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications | | | 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 | ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications ** still make the distinction between protected and unprotected ** sqlite3_value objects even when not strictly required. ** ** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. ** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used with |
︙ | ︙ | |||
3195 3196 3197 3198 3199 3200 3201 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, | | | 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, ** and VVV represents an alphanumeric identifier.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** ** ^The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from ** [sqlite3_prepare_v2()] or its variants. ** |
︙ | ︙ | |||
3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 | /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column | > > | 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 | /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). ** ** See also: [sqlite3_data_count()] */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column |
︙ | ︙ | |||
3534 3535 3536 3537 3538 3539 3540 | ** by sqlite3_step(). The use of the "v2" interface is recommended. */ SQLITE_API int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** | | | > > > > > > | 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 | ** by sqlite3_step(). The use of the "v2" interface is recommended. */ SQLITE_API int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. ** ^If prepared statement P does not have results ready to return ** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of ** interfaces) then sqlite3_data_count(P) returns 0. ** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. ** ** See also: [sqlite3_column_count()] */ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes ** KEYWORDS: SQLITE_TEXT ** |
︙ | ︙ | |||
3615 3616 3617 3618 3619 3620 3621 | ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. | > > > > > > > > > > > > | | > | < < < < < | 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 | ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. ** ^If the result is NULL, then sqlite3_column_bytes() returns zero. ** ** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts ** the string to UTF-16 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes16() uses ** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns ** the number of bytes in that string. ** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. ** ** ^The values returned by [sqlite3_column_bytes()] and ** [sqlite3_column_bytes16()] do not include the zero terminators at the end ** of the string. ^For clarity: the values returned by ** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of ** bytes in the string, not the number of characters. ** ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), ** even empty strings, are always zero terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** ** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by ** [sqlite3_column_value()] is used in any other way, including calls ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], |
︙ | ︙ | |||
3671 3672 3673 3674 3675 3676 3677 | ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are ** used in the table for brevity and because they are familiar to most ** C programmers. ** | | | | | | | | 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 | ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are ** used in the table for brevity and because they are familiar to most ** C programmers. ** ** Note that when type conversions occur, pointers returned by prior ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or ** sqlite3_column_text16() may be invalidated. ** Type conversions and pointer invalidations might occur ** in the following cases: ** ** <ul> ** <li> The initial content is a BLOB and sqlite3_column_text() or ** sqlite3_column_text16() is called. A zero-terminator might ** need to be added to the string.</li> ** <li> The initial content is UTF-8 text and sqlite3_column_bytes16() or ** sqlite3_column_text16() is called. The content must be converted ** to UTF-16.</li> ** <li> The initial content is UTF-16 text and sqlite3_column_bytes() or ** sqlite3_column_text() is called. The content must be converted ** to UTF-8.</li> ** </ul> ** ** ^Conversions between UTF-16be and UTF-16le are always done in place and do ** not invalidate a prior pointer, though of course the content of the buffer ** that the prior pointer references will have been modified. Other kinds ** of conversion are done in place when it is possible, but sometimes they ** are not possible and in those cases prior pointers are invalidated. ** ** The safest and easiest to remember policy is to invoke these routines ** in one of the following ways: ** ** <ul> ** <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li> ** <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li> ** <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li> ** </ul> ** ** In other words, you should call sqlite3_column_text(), ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result ** into the desired format, then invoke sqlite3_column_bytes() or ** sqlite3_column_bytes16() to find the size of the result. Do not mix calls ** to sqlite3_column_text() or sqlite3_column_blob() with calls to ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() |
︙ | ︙ | |||
3740 3741 3742 3743 3744 3745 3746 | SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. | > | | > | | | > > > | > | < < < > > > > > > | 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 | SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors or ** or if the statement is never been evaluated, then sqlite3_finalize() returns ** SQLITE_OK. ^If the most recent evaluation of statement S failed, then ** sqlite3_finalize(S) returns the appropriate [error code] or ** [extended error code]. ** ** ^The sqlite3_finalize(S) routine can be called at any point during ** the life cycle of [prepared statement] S: ** before statement S is ever evaluated, after ** one or more calls to [sqlite3_reset()], or after any call ** to [sqlite3_step()] regardless of whether or not the statement has ** completed execution. ** ** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. ** ** The application must finalize every [prepared statement] in order to avoid ** resource leaks. It is a grievous error for the application to try to use ** a prepared statement after it has been finalized. Any use of a prepared ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object ** ** The sqlite3_reset() function is called to reset a [prepared statement] |
︙ | ︙ | |||
3786 3787 3788 3789 3790 3791 3792 | /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** | | | > | < | > | | | > | | | | | | | | | > > > > > > > > < < < < < | 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 | /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** ** ^These functions (collectively known as "function creation routines") ** 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) ** and the presence or absence of a destructor callback for ** the application data pointer. ** ** ^The first parameter is the [database connection] to which the SQL ** function is to be added. ^If an application uses more than one database ** connection then application-defined SQL functions must be added ** to each database connection separately. ** ** ^The second parameter is the name of the SQL function to be created or ** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 ** representation, exclusive of the zero-terminator. ^Note that the name ** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. ** ^Any attempt to create a function with a longer name ** will result in [SQLITE_MISUSE] being returned. ** ** ^The third parameter (nArg) ** is the number of arguments that the SQL function or ** aggregate takes. ^If this parameter is -1, then the SQL function or ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is ** undefined. ** ** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. Every SQL function implementation must be able to work ** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be ** more efficient with one encoding than another. ^An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. ** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** If there is only a single implementation which does not care what text ** encoding is used, then the fourth argument should be [SQLITE_ANY]. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** ^The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** 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 ** callbacks. ** ** ^If the tenth parameter to sqlite3_create_function_v2() is not NULL, ** then it is invoked when the function is deleted, either by being ** overloaded or when the database connection closes. ** ^When the destructure callback of the tenth parameter is invoked, it ** is passed a single argument which is a copy of the pointer which was ** the fifth parameter to sqlite3_create_function_v2(). ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. ^SQLite will use ** the implementation that most closely matches the way in which the ** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with ** a negative nArg. ^A function where the preferred text encoding ** matches the database encoding is a better ** match than a function where the encoding is different. ** ^A function where the encoding difference is between UTF16le and UTF16be ** is a closer match than a function where the encoding difference is ** between UTF8 and UTF16. ** ** ^Built-in functions may be overloaded by new application-defined functions. ** ** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ SQLITE_API int sqlite3_create_function( |
︙ | ︙ | |||
3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 | const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. | > > > > > > > > > > > | 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 | const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); SQLITE_API int sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. |
︙ | ︙ | |||
3974 3975 3976 3977 3978 3979 3980 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** | | | 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. ** ** ^The first time the sqlite3_aggregate_context(C,N) routine is called ** for a particular aggregate function, SQLite ** allocates N of memory, zeroes out that memory, and returns a pointer ** to the new memory. ^On second and subsequent calls to ** sqlite3_aggregate_context() for the same aggregate function instance, |
︙ | ︙ | |||
4226 4227 4228 4229 4230 4231 4232 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** | | | | | | > | > > | < < > | < | < > > > > | > > > > > | > > > | < | < | < < | | > | | > > > > > > > > > > > > > > > > > | | < < | | | | | | | 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. ** ** ^The name of the collation is a UTF-8 string ** for sqlite3_create_collation() and sqlite3_create_collation_v2() ** and a UTF-16 string in native byte order for sqlite3_create_collation16(). ** ^Collation names that compare equal according to [sqlite3_strnicmp()] are ** considered to be the same name. ** ** ^(The third argument (eTextRep) must be one of the constants: ** <ul> ** <li> [SQLITE_UTF8], ** <li> [SQLITE_UTF16LE], ** <li> [SQLITE_UTF16BE], ** <li> [SQLITE_UTF16], or ** <li> [SQLITE_UTF16_ALIGNED]. ** </ul>)^ ** ^The eTextRep argument determines the encoding of strings passed ** to the collating function callback, xCallback. ** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep ** force strings to be UTF16 with native byte order. ** ^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 ** through as the first argument to the collating function callback. ** ** ^The fifth argument, xCallback, is a pointer to the collating function. ** ^Multiple collating functions can be registered using the same name but ** with different eTextRep parameters and SQLite will use whichever ** function requires the least amount of data transformation. ** ^If the xCallback argument is NULL then the collating function is ** deleted. ^When all collating functions having the same name are deleted, ** that collation is no longer usable. ** ** ^The collating function callback is invoked with a copy of the pArg ** application data pointer and with two strings in the encoding specified ** 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 ** 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. ** The collating function must obey the following properties for all ** strings A, B, and C: ** ** <ol> ** <li> If A==B then B==A. ** <li> If A==B and B==C then A==C. ** <li> If A<B THEN B>A. ** <li> If A<B and B<C then A<C. ** </ol> ** ** If a collating function fails any of the above constraints and that ** collating function is registered and used, then the behavior of SQLite ** is undefined. ** ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** with the addition that the xDestroy callback is invoked on pArg when ** the collating function is deleted. ** ^Collating functions are deleted when they are overridden by later ** calls to the collation creation functions or when the ** [database connection] is closed using [sqlite3_close()]. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ SQLITE_API int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); SQLITE_API int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); SQLITE_API int sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); /* ** CAPI3REF: Collation Needed Callbacks ** ** ^To avoid having to register all collation sequences before a database |
︙ | ︙ | |||
4377 4378 4379 4380 4381 4382 4383 | const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** | | | | | > > > | 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 | const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** ** The sqlite3_sleep() function causes the current thread to suspend execution ** for at least a number of milliseconds specified in its parameter. ** ** If the operating system does not support sleep requests with ** millisecond time resolution, then the time will be rounded up to ** the nearest second. The number of milliseconds of sleep actually ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ SQLITE_API int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is |
︙ | ︙ | |||
4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 | ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. */ SQLITE_API int sqlite3_release_memory(int); /* ** CAPI3REF: Impose A Limit On Heap Size ** | > > | | > > > > | > > > > | < > > > | < > | > > | > > > > > > | | > > > > | | > > | > > > > > > | > > > | | < < | < < > | > | 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 | ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. ** ^The sqlite3_release_memory() routine is a no-op returning zero ** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. */ SQLITE_API int sqlite3_release_memory(int); /* ** CAPI3REF: Impose A Limit On Heap Size ** ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the ** soft limit on the amount of heap memory that may be allocated by SQLite. ** ^SQLite strives to keep heap memory utilization below the soft heap ** limit by reducing the number of pages held in the page cache ** as heap memory usages approaches the limit. ** ^The soft heap limit is "soft" because even though SQLite strives to stay ** below the limit, it will exceed the limit rather than generate ** an [SQLITE_NOMEM] error. In other words, the soft heap limit ** is advisory only. ** ** ^The return value from sqlite3_soft_heap_limit64() is the size of ** the soft heap limit prior to the call. ^If the argument N is negative ** then no change is made to the soft heap limit. Hence, the current ** size of the soft heap limit can be determined by invoking ** sqlite3_soft_heap_limit64() with a negative argument. ** ** ^If the argument N is zero then the soft heap limit is disabled. ** ** ^(The soft heap limit is not enforced in the current implementation ** if one or more of following conditions are true: ** ** <ul> ** <li> The soft heap limit is set to zero. ** <li> 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. ** <li> An alternative page cache implementation is specifed using ** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). ** <li> The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. ** </ul>)^ ** ** Beginning with SQLite version 3.7.3, the soft heap limit is enforced ** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] ** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], ** the soft heap limit is enforced on every memory allocation. Without ** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced ** when memory is allocated by the page cache. Testing suggests that because ** the page cache is the predominate memory user in SQLite, most ** applications will achieve adequate soft heap limit enforcement without ** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. ** ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface ** DEPRECATED ** ** This is a deprecated version of the [sqlite3_soft_heap_limit64()] ** interface. This routine is provided for historical compatibility ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table ** ** ^This routine returns metadata about a specific column of a specific ** database table accessible using the [database connection] handle ** passed as the first function argument. |
︙ | ︙ | |||
4765 4766 4767 4768 4769 4770 4771 | ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* | | | < | > > > > > | > > > | > | > > > | < > > > | | > | < | > > > | < | < < | 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 | ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions ** ** ^This interface causes the xEntryPoint() function to be invoked for ** each new [database connection] that is created. The idea here is that ** xEntryPoint() is the entry point for a statically linked SQLite extension ** that is to be automatically loaded into all new database connections. ** ** ^(Even though the function prototype shows that xEntryPoint() takes ** no arguments and returns void, SQLite invokes xEntryPoint() with three ** arguments and expects and integer result as if the signature of the ** entry point where as follows: ** ** <blockquote><pre> ** int xEntryPoint( ** sqlite3 *db, ** const char **pzErrMsg, ** const struct sqlite3_api_routines *pThunk ** ); ** </pre></blockquote>)^ ** ** If the xEntryPoint routine encounters an error, it should make *pzErrMsg ** point to an appropriate error message (obtained from [sqlite3_mprintf()]) ** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg ** is NULL before calling the xEntryPoint(). ^SQLite will invoke ** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any ** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], ** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. ** ** ^Calling sqlite3_auto_extension(X) with an entry point X that is already ** on the list of automatic extensions is a harmless no-op. ^No entry point ** will be called more than once for each database connection that is opened. ** ** See also: [sqlite3_reset_auto_extension()]. */ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading ** ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ SQLITE_API void sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. |
︙ | ︙ | |||
5431 5432 5433 5434 5435 5436 5437 | ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. | | | 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 | ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. ** ^The xMutexInit routine is called by SQLite exactly once for each ** effective call to [sqlite3_initialize()]. ** ** ^The xMutexEnd method defined by this structure is invoked as ** part of system shutdown by the sqlite3_shutdown() function. The ** implementation of this method is expected to release all outstanding ** resources obtained by the mutex methods implementation, especially ** those obtained by the xMutexInit method. ^The xMutexEnd() |
︙ | ︙ | |||
5464 5465 5466 5467 5468 5469 5470 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to | | | 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to ** invoke xMutexInit() multiple times within the same process and without ** intervening calls to xMutexEnd(). Second and subsequent calls to ** xMutexInit() must be no-ops. ** ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory ** allocation for a static mutex. ^However xMutexAlloc() may use SQLite ** memory allocation for a fast or recursive mutex. |
︙ | ︙ | |||
5628 5629 5630 5631 5632 5633 5634 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 | > | | | | 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_SCRATCHMALLOC 18 #define SQLITE_TESTCTRL_LAST 18 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after ** *pHighwater is written. ^(Some parameters do not record the highest ** value. For those parameters ** nothing is written into *pHighwater and the resetFlag is ignored.)^ ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** ** ^The sqlite3_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** This routine is threadsafe but is not atomic. This routine can be ** called while other threads are running the same or different SQLite ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time ** and it is possible that another thread might change the parameter |
︙ | ︙ | |||
5697 5698 5699 5700 5701 5702 5703 | ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache | | | 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 | ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** where too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> |
︙ | ︙ | |||
5720 5721 5722 5723 5724 5725 5726 | ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory | | | 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 | ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory ** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values ** returned include overflows because the requested allocation was too ** larger (that is, because the requested allocation was larger than the ** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer ** slots were available. ** </dd>)^ ** |
︙ | ︙ | |||
5760 5761 5762 5763 5764 5765 5766 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that | | > > > | 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that ** determines the parameter to interrogate. The set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections |
︙ | ︙ | |||
5895 5896 5897 5898 5899 5900 5901 | /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an | | > | | | | > > > > | > | > > > | | > | | | | | | > > | | | | | > | | | | | | | | | > | | < < | | < | > | | | | | | | | | 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 | /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an ** instance of the sqlite3_pcache_methods structure.)^ ** In many applications, most of the heap memory allocated by ** SQLite is used for the page cache. ** By implementing a ** custom page cache using this API, an application can better control ** the amount of memory consumed by SQLite, the way in which ** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** ** The alternative page cache mechanism is an ** extreme measure that is only needed by the most demanding applications. ** The built-in page cache is recommended for most uses. ** ** ^(The contents of the sqlite3_pcache_methods structure are copied to an ** internal buffer by SQLite within the call to [sqlite3_config]. Hence ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** ** ^(The xInit() method is called once for each effective ** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() ** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ ** The intent of the xInit() method is to set up global data structures ** required by the custom page cache implementation. ** ^(If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache.)^ ** ** ^The xShutdown() method is called by [sqlite3_shutdown()]. ** It can be used to clean up ** any outstanding resources before process shutdown, if required. ** ^The xShutdown() method may be NULL. ** ** ^SQLite automatically serializes calls to the xInit method, ** so the xInit method need not be threadsafe. ^The ** xShutdown method is only called from [sqlite3_shutdown()] so it does ** not need to be threadsafe either. All other methods must be threadsafe ** in multithreaded applications. ** ** ^SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). ** ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must ** be allocated by the cache. ^szPage will not be a power of two. ^szPage ** will the page size of the database file that is to be cached plus an ** increment (here called "R") of about 100 or 200. SQLite will use the ** extra R bytes on each page to store metadata about the underlying ** database page on disk. The value of R depends ** on the SQLite version, the target platform, and how SQLite was compiled. ** ^R is constant for a particular build of SQLite. ^The second argument to ** xCreate(), bPurgeable, is true if the cache being created will ** be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation ** does not have to do anything special based with the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to ** false will always have the "discard" flag set to true. ** ^Hence, a cache created with bPurgeable false will ** never contain any unpinned pages. ** ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** ** The xPagecount() method must return the number of pages currently ** stored in the cache, both pinned and unpinned. ** ** The xFetch() method locates a page in the cache and returns a pointer to ** the page, or a NULL pointer. ** A "page", in this context, means a buffer of szPage bytes aligned at an ** 8-byte boundary. The page to be fetched is determined by the key. ^The ** mimimum key value is 1. After it has been retrieved using xFetch, the page ** is considered to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the ** behavior of the cache implementation should use the value of the createFlag ** parameter to help it determined what action to take: ** ** <table border=1 width=85% align=center> ** <tr><th> createFlag <th> Behaviour when page is not already in cache ** <tr><td> 0 <td> Do not allocate a new page. Return NULL. ** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so. ** Otherwise return NULL. ** <tr><td> 2 <td> Make every effort to allocate a new page. Only return ** NULL if allocating a new page is effectively impossible. ** </table> ** ** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite ** will only use a createFlag of 2 after a prior call with a createFlag of 1 ** failed.)^ In between the to xFetch() calls, SQLite may ** attempt to unpin one or more cache pages by spilling the content of ** pinned pages to disk and synching the operating system disk cache. ** ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. If the third parameter, discard, is non-zero, ** then the page must be evicted from the cache. ** ^If the discard parameter is ** zero, then the page may be discarded or retained at the discretion of ** page cache implementation. ^The page cache implementation ** may choose to evict unpinned pages at any time. ** ** The cache must not perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch(). ** ** The xRekey() method is used to change the key value associated with the ** page passed as the second argument. If the cache ** previously contains an entry associated with newKey, it must be ** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** ** When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal ** to the value of the iLimit parameter passed to xTruncate(). If any ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] ** handle invalid, and will not use it with any other sqlite3_pcache_methods |
︙ | ︙ | |||
6182 6183 6184 6185 6186 6187 6188 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed | | | 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed ** up and the total number of pages in the source database file. ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces ** retrieve these two values, respectively. ** ** ^The values returned by these functions are only updated by ** sqlite3_backup_step(). ^If the source database is modified during a backup ** operation, then the values are not updated to account for any extra ** pages that need to be updated or the size of the source database file |
︙ | ︙ | |||
6278 6279 6280 6281 6282 6283 6284 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing | | | 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing ** unlock-notify callback is canceled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** |
︙ | ︙ | |||
6360 6361 6362 6363 6364 6365 6366 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a | | | 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a ** case-independent fashion, using the same definition of case independence ** that SQLite uses internally when comparing identifiers. */ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: Error Logging Interface ** |
︙ | ︙ | |||
6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 | #endif #if 0 } /* End of the 'extern "C"' block */ #endif #endif /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include hash.h in the middle of sqliteInt.h ******************/ /************** Begin file hash.h ********************************************/ /* ** 2001 September 22 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 | #endif #if 0 } /* End of the 'extern "C"' block */ #endif #endif /* ** 2010 August 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. ** ************************************************************************* */ #ifndef _SQLITE3RTREE_H_ #define _SQLITE3RTREE_H_ #if 0 extern "C" { #endif typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; /* ** Register a geometry callback named zGeom that can be used as part of an ** R-Tree geometry query as follows: ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...) */ SQLITE_API int sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes), void *pContext ); /* ** A pointer to a structure of the following type is passed as the first ** argument to callbacks registered using rtree_geometry_callback(). */ struct sqlite3_rtree_geometry { void *pContext; /* Copy of pContext passed to s_r_g_c() */ int nParam; /* Size of array aParam[] */ double *aParam; /* Parameters passed to SQL geom function */ void *pUser; /* Callback implementation user data */ void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ }; #if 0 } /* end of the 'extern "C"' block */ #endif #endif /* ifndef _SQLITE3RTREE_H_ */ /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include hash.h in the middle of sqliteInt.h ******************/ /************** Begin file hash.h ********************************************/ /* ** 2001 September 22 |
︙ | ︙ | |||
7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 | typedef struct Column Column; typedef struct Db Db; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprSpan ExprSpan; typedef struct FKey FKey; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; | > | 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 | typedef struct Column Column; typedef struct Db Db; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprSpan ExprSpan; typedef struct FKey FKey; typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; |
︙ | ︙ | |||
7160 7161 7162 7163 7164 7165 7166 | /* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the ** following values. ** ** NOTE: These values must match the corresponding PAGER_ values in ** pager.h. */ | | | | | < | 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 | /* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the ** following values. ** ** NOTE: These values must match the corresponding PAGER_ values in ** pager.h. */ #define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ #define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ #define BTREE_MEMORY 4 /* This is an in-memory DB */ #define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */ #define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */ SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int); SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); |
︙ | ︙ | |||
7201 7202 7203 7204 7205 7206 7207 | SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); /* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR | | > > > > > > > | < | 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 | SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); /* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR ** of the flags shown below. ** ** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set. ** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data ** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With ** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored ** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL ** indices.) */ #define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ #define BTREE_BLOBKEY 2 /* Table has keys only - no data */ SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); |
︙ | ︙ | |||
7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 | /* ** Allowed values for the flags parameter to sqlite3PagerOpen(). ** ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 | > | 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 | /* ** Allowed values for the flags parameter to sqlite3PagerOpen(). ** ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ #define PAGER_MEMORY 0x0004 /* In-memory database */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 |
︙ | ︙ | |||
7866 7867 7868 7869 7870 7871 7872 | void(*)(DbPage*) ); SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); | | | 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 | void(*)(DbPage*) ); SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int); SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); |
︙ | ︙ | |||
7893 7894 7895 7896 7897 7898 7899 | SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); /* Functions used to manage pager transactions and savepoints. */ | | | 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 | SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); /* Functions used to manage pager transactions and savepoints. */ SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); |
︙ | ︙ | |||
8460 8461 8462 8463 8464 8465 8466 | ** If this is a no-op implementation, implement everything as macros. */ #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) | | | | 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 | ** If this is a no-op implementation, implement everything as macros. */ #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) #define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) #define sqlite3MutexInit() SQLITE_OK #define sqlite3MutexEnd() #endif /* defined(SQLITE_MUTEX_OMIT) */ /************** End of mutex.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ |
︙ | ︙ | |||
8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 | void *pUserData; /* User data parameter */ FuncDef *pNext; /* Next function with same name */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ char *zName; /* SQL name of the function. */ FuncDef *pHash; /* Next with a different name but the same hash */ }; /* ** Possible values for FuncDef.flags */ #define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ | > > > > > > > > > > > > > > > > > > > > > | 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 | void *pUserData; /* User data parameter */ FuncDef *pNext; /* Next function with same name */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ char *zName; /* SQL name of the function. */ FuncDef *pHash; /* Next with a different name but the same hash */ FuncDestructor *pDestructor; /* Reference counted destructor function */ }; /* ** This structure encapsulates a user-function destructor callback (as ** configured using create_function_v2()) and a reference counter. When ** create_function_v2() is called to create a function with a destructor, ** a single object of this type is allocated. FuncDestructor.nRef is set to ** the number of FuncDef objects created (either 1 or 3, depending on whether ** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor ** member of each of the new FuncDef objects is set to point to the allocated ** FuncDestructor. ** ** Thereafter, when one of the FuncDef objects is deleted, the reference ** count on this object is decremented. When it reaches 0, the destructor ** is invoked and the FuncDestructor structure freed. */ struct FuncDestructor { int nRef; void (*xDestroy)(void *); void *pUserData; }; /* ** Possible values for FuncDef.flags */ #define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ |
︙ | ︙ | |||
8823 8824 8825 8826 8827 8828 8829 | ** function likeFunc. Argument pArg is cast to a (void *) and made ** available as the function user-data (sqlite3_user_data()). The ** FuncDef.flags variable is set to the value passed as the flags ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ | | | | | | 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 | ** function likeFunc. Argument pArg is cast to a (void *) and made ** available as the function user-data (sqlite3_user_data()). The ** FuncDef.flags variable is set to the value passed as the flags ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ pArg, 0, xFunc, 0, 0, #zName, 0, 0} #define LIKEFUNC(zName, nArg, arg, flags) \ {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \ SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} /* ** All current savepoints are stored in a linked list starting at ** sqlite3.pSavepoint. The first element in the list is the most recently ** opened savepoint. Savepoints are added to the list by the vdbe ** OP_Savepoint instruction. */ |
︙ | ︙ | |||
9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 | struct Table { char *zName; /* Name of the table or view */ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Root BTree node for this table (see note above) */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK | > | 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 | struct Table { char *zName; /* Name of the table or view */ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Root BTree node for this table (see note above) */ unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK |
︙ | ︙ | |||
9181 9182 9183 9184 9185 9186 9187 | /* ** An instance of the following structure is passed as the first ** argument to sqlite3VdbeKeyCompare and is used to control the ** comparison of the two index keys. */ struct KeyInfo { sqlite3 *db; /* The database connection */ | | | | 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 | /* ** An instance of the following structure is passed as the first ** argument to sqlite3VdbeKeyCompare and is used to control the ** comparison of the two index keys. */ struct KeyInfo { sqlite3 *db; /* The database connection */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nField; /* Number of entries in aColl[] */ u8 *aSortOrder; /* Sort order for each column. May be NULL */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; /* ** An instance of the following structure holds information about a ** single index record that has already been parsed out into individual ** values. |
︙ | ︙ | |||
10305 10306 10307 10308 10309 10310 10311 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) #endif /* ** Internal function prototypes */ SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); | < | 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) #endif /* ** Internal function prototypes */ SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); SQLITE_PRIVATE int sqlite3Strlen30(const char*); #define sqlite3StrNICmp sqlite3_strnicmp SQLITE_PRIVATE int sqlite3MallocInit(void); SQLITE_PRIVATE void sqlite3MallocEnd(void); SQLITE_PRIVATE void *sqlite3Malloc(int); SQLITE_PRIVATE void *sqlite3MallocZero(int); |
︙ | ︙ | |||
10329 10330 10331 10332 10333 10334 10335 | SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*); SQLITE_PRIVATE void *sqlite3ScratchMalloc(int); SQLITE_PRIVATE void sqlite3ScratchFree(void*); SQLITE_PRIVATE void *sqlite3PageMalloc(int); SQLITE_PRIVATE void sqlite3PageFree(void*); SQLITE_PRIVATE void sqlite3MemSetDefault(void); SQLITE_PRIVATE void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); | | | 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 10602 10603 10604 10605 | SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*); SQLITE_PRIVATE void *sqlite3ScratchMalloc(int); SQLITE_PRIVATE void sqlite3ScratchFree(void*); SQLITE_PRIVATE void *sqlite3PageMalloc(int); SQLITE_PRIVATE void sqlite3PageFree(void*); SQLITE_PRIVATE void sqlite3MemSetDefault(void); SQLITE_PRIVATE void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); SQLITE_PRIVATE int sqlite3HeapNearlyFull(void); /* ** On systems with ample stack space and that support alloca(), make ** use of alloca() to obtain space for large automatic objects. By default, ** obtain space from malloc(). ** ** The alloca() routine never returns NULL. This will cause code paths |
︙ | ︙ | |||
10500 10501 10502 10503 10504 10505 10506 | SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCachePush(Parse*); SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*, int); SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int); SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*); SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int); | < | 10762 10763 10764 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 | SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCachePush(Parse*); SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*, int); SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int); SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*); SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int); SQLITE_PRIVATE int sqlite3ExprCode(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse*, Expr*, int); SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse*, Expr*); SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int); SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse*, Expr*, int, int); |
︙ | ︙ | |||
10620 10621 10622 10623 10624 10625 10626 | # define sqlite3AuthRead(a,b,c,d) # define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*); | < < | < | 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 | # define sqlite3AuthRead(a,b,c,d) # define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*); SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE int sqlite3Utf8Read(const u8*, const u8**); /* ** Routines to read and write variable-length integers. These used to ** be defined locally, but now we use the varint routines in the util.c |
︙ | ︙ | |||
10676 10677 10678 10679 10680 10681 10682 | SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); | | | 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 | SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); SQLITE_PRIVATE const char *sqlite3ErrStr(int); SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); |
︙ | ︙ | |||
10747 10748 10749 10750 10751 10752 10753 | SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int); SQLITE_PRIVATE void sqlite3SchemaFree(void *); SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *); SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *); SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), | | > > | 11005 11006 11007 11008 11009 11010 11011 11012 11013 11014 11015 11016 11017 11018 11019 11020 11021 | SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int); SQLITE_PRIVATE void sqlite3SchemaFree(void *); SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *); SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *); SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*), FuncDestructor *pDestructor ); SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int); SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*); |
︙ | ︙ | |||
11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 | Bool rowidIsValid; /* True if lastRowid is valid */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ Btree *pBt; /* Separate file holding temporary table */ int pseudoTableReg; /* Register holding pseudotable content. */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int nField; /* Number of fields in the header */ i64 seqCount; /* Sequence counter */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ | > | 11927 11928 11929 11930 11931 11932 11933 11934 11935 11936 11937 11938 11939 11940 11941 | Bool rowidIsValid; /* True if lastRowid is valid */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ Btree *pBt; /* Separate file holding temporary table */ int pseudoTableReg; /* Register holding pseudotable content. */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int nField; /* Number of fields in the header */ i64 seqCount; /* Sequence counter */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ |
︙ | ︙ | |||
11761 11762 11763 11764 11765 11766 11767 11768 11769 11770 11771 11772 11773 11774 | double r; /* Real value */ sqlite3 *db; /* The associated database connection */ char *z; /* String or BLOB value */ int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */ }; /* One or more of the following flags are set to indicate the validOK ** representations of the value stored in the Mem struct. ** | > > > > | 12022 12023 12024 12025 12026 12027 12028 12029 12030 12031 12032 12033 12034 12035 12036 12037 12038 12039 | double r; /* Real value */ sqlite3 *db; /* The associated database connection */ char *z; /* String or BLOB value */ int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ #ifdef SQLITE_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ #endif void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */ }; /* One or more of the following flags are set to indicate the validOK ** representations of the value stored in the Mem struct. ** |
︙ | ︙ | |||
11787 11788 11789 11790 11791 11792 11793 11794 11795 11796 11797 11798 11799 11800 11801 11802 11803 11804 11805 11806 | #define MEM_Null 0x0001 /* Value is NULL */ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ #define MEM_TypeMask 0x00ff /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String rep is nul terminated */ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ #define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ | > < < > > > > > > > > | 12052 12053 12054 12055 12056 12057 12058 12059 12060 12061 12062 12063 12064 12065 12066 12067 12068 12069 12070 12071 12072 12073 12074 12075 12076 12077 12078 12079 12080 12081 12082 12083 12084 12085 12086 12087 12088 12089 12090 12091 12092 12093 12094 12095 12096 12097 12098 | #define MEM_Null 0x0001 /* Value is NULL */ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ #define MEM_Invalid 0x0080 /* Value is undefined */ #define MEM_TypeMask 0x00ff /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String rep is nul terminated */ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ #define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ #ifdef SQLITE_OMIT_INCRBLOB #undef MEM_Zero #define MEM_Zero 0x0000 #endif /* ** Clear any existing type flags from a Mem and replace them with f */ #define MemSetTypeFlag(p, f) \ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) /* ** Return true if a memory cell is not marked as invalid. This macro ** is for use inside assert() statements only. */ #ifdef SQLITE_DEBUG #define memIsValid(M) ((M)->flags & MEM_Invalid)==0 #endif /* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains ** additional information about auxiliary information bound to arguments ** of the function. This is used to implement the sqlite3_get_auxdata() ** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data ** that can be associated with a constant argument to a function. This ** allows functions such as "regexp" to compile their constant regular |
︙ | ︙ | |||
12000 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 | SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); SQLITE_PRIVATE const char *sqlite3OpcodeName(int); SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); #ifndef SQLITE_OMIT_FOREIGN_KEY SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 #endif | > > > > | 12272 12273 12274 12275 12276 12277 12278 12279 12280 12281 12282 12283 12284 12285 12286 12287 12288 12289 | SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); SQLITE_PRIVATE const char *sqlite3OpcodeName(int); SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 #endif |
︙ | ︙ | |||
12357 12358 12359 12360 12361 12362 12363 | cnt++; }while( nextC ); end_getDigits: va_end(ap); return cnt; } | < < < < < < | 12633 12634 12635 12636 12637 12638 12639 12640 12641 12642 12643 12644 12645 12646 | cnt++; }while( nextC ); end_getDigits: va_end(ap); return cnt; } /* ** Parse a timezone extension on the end of a date-time. ** The extension is of the form: ** ** (+/-)HH:MM ** ** Or the "zulu" notation: |
︙ | ︙ | |||
12564 12565 12566 12567 12568 12569 12570 | ** as there is a year and date. */ static int parseDateOrTime( sqlite3_context *context, const char *zDate, DateTime *p ){ | | | < < | 12834 12835 12836 12837 12838 12839 12840 12841 12842 12843 12844 12845 12846 12847 12848 12849 12850 12851 12852 12853 12854 12855 12856 | ** as there is a year and date. */ static int parseDateOrTime( sqlite3_context *context, const char *zDate, DateTime *p ){ double r; if( parseYyyyMmDd(zDate,p)==0 ){ return 0; }else if( parseHhMmSs(zDate, p)==0 ){ return 0; }else if( sqlite3StrICmp(zDate,"now")==0){ setDateTimeToCurrent(context, p); return 0; }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){ p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); p->validJD = 1; return 0; } return 1; } |
︙ | ︙ | |||
12795 12796 12797 12798 12799 12800 12801 | /* ** weekday N ** ** Move the date to the same time on the next occurrence of ** weekday N where 0==Sunday, 1==Monday, and so forth. If the ** date is already on the appropriate weekday, this is a no-op. */ | | > | | 13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 | /* ** weekday N ** ** Move the date to the same time on the next occurrence of ** weekday N where 0==Sunday, 1==Monday, and so forth. If the ** date is already on the appropriate weekday, this is a no-op. */ if( strncmp(z, "weekday ", 8)==0 && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8) && (n=(int)r)==r && n>=0 && r<7 ){ sqlite3_int64 Z; computeYMD_HMS(p); p->validTZ = 0; p->validJD = 0; computeJD(p); Z = ((p->iJD + 129600000)/86400000) % 7; if( Z>n ) Z -= 7; |
︙ | ︙ | |||
12851 12852 12853 12854 12855 12856 12857 | case '4': case '5': case '6': case '7': case '8': case '9': { double rRounder; | > > | | > | 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 | case '4': case '5': case '6': case '7': case '8': case '9': { double rRounder; for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){ rc = 1; break; } if( z[n]==':' ){ /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the ** specified number of hours, minutes, seconds, and fractional seconds ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be ** omitted. */ const char *z2 = z; |
︙ | ︙ | |||
13506 13507 13508 13509 13510 13511 13512 13513 13514 13515 13516 13517 13518 13519 | return pVfs->xRandomness(pVfs, nByte, zBufOut); } SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){ return pVfs->xSleep(pVfs, nMicro); } SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ int rc; if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); }else{ double r; rc = pVfs->xCurrentTime(pVfs, &r); *pTimeOut = (sqlite3_int64)(r*86400000.0); } | > > > > > > | 13778 13779 13780 13781 13782 13783 13784 13785 13786 13787 13788 13789 13790 13791 13792 13793 13794 13795 13796 13797 | return pVfs->xRandomness(pVfs, nByte, zBufOut); } SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){ return pVfs->xSleep(pVfs, nMicro); } SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ int rc; /* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64() ** method to get the current date and time if that method is available ** (if iVersion is 2 or greater and the function pointer is not NULL) and ** will fall back to xCurrentTime() if xCurrentTimeInt64() is ** unavailable. */ if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); }else{ double r; rc = pVfs->xCurrentTime(pVfs, &r); *pTimeOut = (sqlite3_int64)(r*86400000.0); } |
︙ | ︙ | |||
13889 13890 13891 13892 13893 13894 13895 | ** redirected to xMalloc. Similarly, we know that nByte>0 becauses ** cases where nByte<=0 will have been intercepted by higher-level ** routines and redirected to xFree. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 && nByte>0 ); | | | 14167 14168 14169 14170 14171 14172 14173 14174 14175 14176 14177 14178 14179 14180 14181 | ** redirected to xMalloc. Similarly, we know that nByte>0 becauses ** cases where nByte<=0 will have been intercepted by higher-level ** routines and redirected to xFree. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 && nByte>0 ); assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */ p--; p = realloc(p, nByte+8 ); if( p ){ p[0] = nByte; p++; }else{ testcase( sqlite3GlobalConfig.xLog!=0 ); |
︙ | ︙ | |||
14295 14296 14297 14298 14299 14300 14301 14302 14303 14304 14305 14306 14307 14308 | ** much more likely to break and we are much more liking to find ** the error. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ struct MemBlockHdr *pOldHdr; void *pNew; assert( mem.disallow==0 ); pOldHdr = sqlite3MemsysGetHeader(pPrior); pNew = sqlite3MemMalloc(nByte); if( pNew ){ memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize); if( nByte>pOldHdr->iSize ){ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize); } | > | 14573 14574 14575 14576 14577 14578 14579 14580 14581 14582 14583 14584 14585 14586 14587 | ** much more likely to break and we are much more liking to find ** the error. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ struct MemBlockHdr *pOldHdr; void *pNew; assert( mem.disallow==0 ); assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */ pOldHdr = sqlite3MemsysGetHeader(pPrior); pNew = sqlite3MemMalloc(nByte); if( pNew ){ memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize); if( nByte>pOldHdr->iSize ){ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize); } |
︙ | ︙ | |||
14329 14330 14331 14332 14333 14334 14335 | sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); } /* ** Set the "type" of an allocation. */ SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){ | | | | 14608 14609 14610 14611 14612 14613 14614 14615 14616 14617 14618 14619 14620 14621 14622 14623 14624 14625 14626 14627 14628 14629 14630 14631 14632 14633 14634 14635 14636 14637 14638 14639 14640 14641 | sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); } /* ** Set the "type" of an allocation. */ SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){ if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); pHdr->eType = eType; } } /* ** Return TRUE if the mask of type in eType matches the type of the ** allocation p. Also return true if p==NULL. ** ** This routine is designed for use within an assert() statement, to ** verify the type of an allocation. For example: ** ** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); */ SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){ int rc = 1; if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ if( (pHdr->eType&eType)==0 ){ rc = 0; } } |
︙ | ︙ | |||
14370 14371 14372 14373 14374 14375 14376 | ** This routine is designed for use within an assert() statement, to ** verify the type of an allocation. For example: ** ** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); */ SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){ int rc = 1; | | | 14649 14650 14651 14652 14653 14654 14655 14656 14657 14658 14659 14660 14661 14662 14663 | ** This routine is designed for use within an assert() statement, to ** verify the type of an allocation. For example: ** ** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); */ SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){ int rc = 1; if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ if( (pHdr->eType&eType)!=0 ){ rc = 0; } } |
︙ | ︙ | |||
15564 15565 15566 15567 15568 15569 15570 | ** (an allocation larger than 0x40000000) was requested and this ** routine should return 0 without freeing pPrior. */ static void *memsys5Realloc(void *pPrior, int nBytes){ int nOld; void *p; assert( pPrior!=0 ); | | | 15843 15844 15845 15846 15847 15848 15849 15850 15851 15852 15853 15854 15855 15856 15857 | ** (an allocation larger than 0x40000000) was requested and this ** routine should return 0 without freeing pPrior. */ static void *memsys5Realloc(void *pPrior, int nBytes){ int nOld; void *p; assert( pPrior!=0 ); assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */ assert( nBytes>=0 ); if( nBytes==0 ){ return 0; } nOld = memsys5Size(pPrior); if( nBytes<=nOld ){ return pPrior; |
︙ | ︙ | |||
16622 16623 16624 16625 16626 16627 16628 16629 16630 16631 16632 16633 16634 16635 | } } #else /* Use the built-in recursive mutexes if they are available. */ pthread_mutex_lock(&p->mutex); #if SQLITE_MUTEX_NREF p->owner = pthread_self(); p->nRef++; #endif #endif #ifdef SQLITE_DEBUG if( p->trace ){ | > | 16901 16902 16903 16904 16905 16906 16907 16908 16909 16910 16911 16912 16913 16914 16915 | } } #else /* Use the built-in recursive mutexes if they are available. */ pthread_mutex_lock(&p->mutex); #if SQLITE_MUTEX_NREF assert( p->nRef>0 || p->owner==0 ); p->owner = pthread_self(); p->nRef++; #endif #endif #ifdef SQLITE_DEBUG if( p->trace ){ |
︙ | ︙ | |||
16694 16695 16696 16697 16698 16699 16700 16701 16702 16703 16704 16705 16706 16707 | ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void pthreadMutexLeave(sqlite3_mutex *p){ assert( pthreadMutexHeld(p) ); #if SQLITE_MUTEX_NREF p->nRef--; #endif assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX if( p->nRef==0 ){ pthread_mutex_unlock(&p->mutex); } | > | 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 | ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void pthreadMutexLeave(sqlite3_mutex *p){ assert( pthreadMutexHeld(p) ); #if SQLITE_MUTEX_NREF p->nRef--; if( p->nRef==0 ) p->owner = 0; #endif assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX if( p->nRef==0 ){ pthread_mutex_unlock(&p->mutex); } |
︙ | ︙ | |||
16958 16959 16960 16961 16962 16963 16964 | /* ** This routine deallocates a previously ** allocated mutex. SQLite is careful to deallocate every ** mutex that it allocates. */ static void winMutexFree(sqlite3_mutex *p){ assert( p ); | | | 17239 17240 17241 17242 17243 17244 17245 17246 17247 17248 17249 17250 17251 17252 17253 | /* ** This routine deallocates a previously ** allocated mutex. SQLite is careful to deallocate every ** mutex that it allocates. */ static void winMutexFree(sqlite3_mutex *p){ assert( p ); assert( p->nRef==0 && p->owner==0 ); assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); DeleteCriticalSection(&p->mutex); sqlite3_free(p); } /* ** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt |
︙ | ︙ | |||
16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 | static void winMutexEnter(sqlite3_mutex *p){ #ifdef SQLITE_DEBUG DWORD tid = GetCurrentThreadId(); assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); #endif EnterCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG p->owner = tid; p->nRef++; if( p->trace ){ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif } | > | 17263 17264 17265 17266 17267 17268 17269 17270 17271 17272 17273 17274 17275 17276 17277 | static void winMutexEnter(sqlite3_mutex *p){ #ifdef SQLITE_DEBUG DWORD tid = GetCurrentThreadId(); assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); #endif EnterCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG assert( p->nRef>0 || p->owner==0 ); p->owner = tid; p->nRef++; if( p->trace ){ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif } |
︙ | ︙ | |||
17035 17036 17037 17038 17039 17040 17041 17042 17043 17044 17045 17046 17047 17048 | */ static void winMutexLeave(sqlite3_mutex *p){ #ifndef NDEBUG DWORD tid = GetCurrentThreadId(); assert( p->nRef>0 ); assert( p->owner==tid ); p->nRef--; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #endif LeaveCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG if( p->trace ){ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } | > | 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326 17327 17328 17329 17330 17331 | */ static void winMutexLeave(sqlite3_mutex *p){ #ifndef NDEBUG DWORD tid = GetCurrentThreadId(); assert( p->nRef>0 ); assert( p->owner==tid ); p->nRef--; if( p->nRef==0 ) p->owner = 0; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #endif LeaveCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG if( p->trace ){ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } |
︙ | ︙ | |||
17084 17085 17086 17087 17088 17089 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 17108 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** Memory allocation functions used throughout sqlite. */ /* ** This routine runs when the memory allocator sees that the ** total memory allocation is about to exceed the soft heap ** limit. */ static void softHeapLimitEnforcer( void *NotUsed, sqlite3_int64 NotUsed2, int allocSize ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); sqlite3_release_memory(allocSize); } /* ** Set the soft heap-size limit for the library. Passing a zero or ** negative value indicates no limit. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < < < < > > > > | | | < | < < < < < < < < < < < < | < < < < | < < < < < < < | < | < < < < < < < < < | < < < < < < < < < < | | > > | | | | | > > > > > > > > < < < < | | < | < < < < < | | > > > > > > > > > | 17367 17368 17369 17370 17371 17372 17373 17374 17375 17376 17377 17378 17379 17380 17381 17382 17383 17384 17385 17386 17387 17388 17389 17390 17391 17392 17393 17394 17395 17396 17397 17398 17399 17400 17401 17402 17403 17404 17405 17406 17407 17408 17409 17410 17411 17412 17413 17414 17415 17416 17417 17418 17419 17420 17421 17422 17423 17424 17425 17426 17427 17428 17429 17430 17431 17432 17433 17434 17435 17436 17437 17438 17439 17440 17441 17442 17443 17444 17445 17446 17447 17448 17449 17450 17451 17452 17453 17454 17455 17456 17457 17458 17459 17460 17461 17462 17463 17464 17465 17466 17467 17468 17469 17470 17471 17472 17473 17474 17475 17476 17477 17478 17479 17480 17481 17482 17483 17484 17485 17486 17487 17488 17489 17490 17491 17492 17493 17494 17495 17496 17497 17498 17499 17500 17501 17502 17503 17504 17505 17506 17507 17508 17509 17510 17511 17512 17513 17514 17515 17516 17517 17518 17519 17520 17521 17522 17523 17524 17525 17526 17527 17528 17529 17530 17531 17532 17533 17534 17535 17536 17537 17538 17539 17540 17541 17542 17543 17544 17545 17546 17547 17548 17549 17550 17551 17552 17553 17554 17555 17556 17557 17558 17559 17560 17561 17562 17563 17564 17565 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** Memory allocation functions used throughout sqlite. */ /* ** Attempt to release up to n bytes of non-essential memory currently ** held by SQLite. An example of non-essential memory is memory used to ** cache database pages that are not currently in use. */ SQLITE_API int sqlite3_release_memory(int n){ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT return sqlite3PcacheReleaseMemory(n); #else /* IMPLEMENTATION-OF: R-34391-24921 The sqlite3_release_memory() routine ** is a no-op returning zero if SQLite is not compiled with ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ UNUSED_PARAMETER(n); return 0; #endif } /* ** An instance of the following object records the location of ** each unused scratch buffer. */ typedef struct ScratchFreeslot { struct ScratchFreeslot *pNext; /* Next unused scratch buffer */ } ScratchFreeslot; /* ** State information local to the memory allocation subsystem. */ static SQLITE_WSD struct Mem0Global { sqlite3_mutex *mutex; /* Mutex to serialize access */ /* ** The alarm callback and its arguments. The mem0.mutex lock will ** be held while the callback is running. Recursive calls into ** the memory subsystem are allowed, but no new callbacks will be ** issued. */ sqlite3_int64 alarmThreshold; void (*alarmCallback)(void*, sqlite3_int64,int); void *alarmArg; /* ** Pointers to the end of sqlite3GlobalConfig.pScratch memory ** (so that a range test can be used to determine if an allocation ** being freed came from pScratch) and a pointer to the list of ** unused scratch allocations. */ void *pScratchEnd; ScratchFreeslot *pScratchFree; u32 nScratchFree; /* ** True if heap is nearly "full" where "full" is defined by the ** sqlite3_soft_heap_limit() setting. */ int nearlyFull; } mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 }; #define mem0 GLOBAL(struct Mem0Global, mem0) /* ** This routine runs when the memory allocator sees that the ** total memory allocation is about to exceed the soft heap ** limit. */ static void softHeapLimitEnforcer( void *NotUsed, sqlite3_int64 NotUsed2, int allocSize ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); sqlite3_release_memory(allocSize); } /* ** Change the alarm callback */ static int sqlite3MemoryAlarm( void(*xCallback)(void *pArg, sqlite3_int64 used,int N), void *pArg, sqlite3_int64 iThreshold ){ int nUsed; sqlite3_mutex_enter(mem0.mutex); mem0.alarmCallback = xCallback; mem0.alarmArg = pArg; mem0.alarmThreshold = iThreshold; nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed); sqlite3_mutex_leave(mem0.mutex); return SQLITE_OK; } #ifndef SQLITE_OMIT_DEPRECATED /* ** Deprecated external interface. Internal/core SQLite code ** should call sqlite3MemoryAlarm. */ SQLITE_API int sqlite3_memory_alarm( void(*xCallback)(void *pArg, sqlite3_int64 used,int N), void *pArg, sqlite3_int64 iThreshold ){ return sqlite3MemoryAlarm(xCallback, pArg, iThreshold); } #endif /* ** Set the soft heap-size limit for the library. Passing a zero or ** negative value indicates no limit. */ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ sqlite3_int64 priorLimit; sqlite3_int64 excess; #ifndef SQLITE_OMIT_AUTOINIT sqlite3_initialize(); #endif sqlite3_mutex_enter(mem0.mutex); priorLimit = mem0.alarmThreshold; sqlite3_mutex_leave(mem0.mutex); if( n<0 ) return priorLimit; if( n>0 ){ sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n); }else{ sqlite3MemoryAlarm(0, 0, 0); } excess = sqlite3_memory_used() - n; if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); return priorLimit; } SQLITE_API void sqlite3_soft_heap_limit(int n){ if( n<0 ) n = 0; sqlite3_soft_heap_limit64(n); } /* ** Initialize the memory allocation subsystem. */ SQLITE_PRIVATE int sqlite3MallocInit(void){ if( sqlite3GlobalConfig.m.xMalloc==0 ){ sqlite3MemSetDefault(); } memset(&mem0, 0, sizeof(mem0)); if( sqlite3GlobalConfig.bCoreMutex ){ mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); } if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100 && sqlite3GlobalConfig.nScratch>0 ){ int i, n, sz; ScratchFreeslot *pSlot; sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch); sqlite3GlobalConfig.szScratch = sz; pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch; n = sqlite3GlobalConfig.nScratch; mem0.pScratchFree = pSlot; mem0.nScratchFree = n; for(i=0; i<n-1; i++){ pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot); pSlot = pSlot->pNext; } pSlot->pNext = 0; mem0.pScratchEnd = (void*)&pSlot[1]; }else{ mem0.pScratchEnd = 0; sqlite3GlobalConfig.pScratch = 0; sqlite3GlobalConfig.szScratch = 0; sqlite3GlobalConfig.nScratch = 0; } if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 || sqlite3GlobalConfig.nPage<1 ){ sqlite3GlobalConfig.pPage = 0; sqlite3GlobalConfig.szPage = 0; sqlite3GlobalConfig.nPage = 0; } return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); } /* ** Return true if the heap is currently under memory pressure - in other ** words if the amount of heap used is close to the limit set by ** sqlite3_soft_heap_limit(). */ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){ return mem0.nearlyFull; } /* ** Deinitialize the memory allocation subsystem. */ SQLITE_PRIVATE void sqlite3MallocEnd(void){ if( sqlite3GlobalConfig.m.xShutdown ){ sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData); |
︙ | ︙ | |||
17247 17248 17249 17250 17251 17252 17253 | int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag); res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */ return res; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 17587 17588 17589 17590 17591 17592 17593 17594 17595 17596 17597 17598 17599 17600 | int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag); res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */ return res; } /* ** Trigger the alarm */ static void sqlite3MallocAlarm(int nByte){ void (*xCallback)(void*,sqlite3_int64,int); sqlite3_int64 nowUsed; void *pArg; |
︙ | ︙ | |||
17309 17310 17311 17312 17313 17314 17315 17316 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326 17327 17328 17329 17330 17331 17332 17333 17334 17335 17336 17337 17338 | void *p; assert( sqlite3_mutex_held(mem0.mutex) ); nFull = sqlite3GlobalConfig.m.xRoundup(n); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed+nFull >= mem0.alarmThreshold ){ sqlite3MallocAlarm(nFull); } } p = sqlite3GlobalConfig.m.xMalloc(nFull); if( p==0 && mem0.alarmCallback ){ sqlite3MallocAlarm(nFull); p = sqlite3GlobalConfig.m.xMalloc(nFull); } if( p ){ nFull = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; return nFull; } /* ** Allocate memory. This routine is like sqlite3_malloc() except that it ** assumes the memory subsystem has already been initialized. */ SQLITE_PRIVATE void *sqlite3Malloc(int n){ void *p; | > > > > > > | > > | 17619 17620 17621 17622 17623 17624 17625 17626 17627 17628 17629 17630 17631 17632 17633 17634 17635 17636 17637 17638 17639 17640 17641 17642 17643 17644 17645 17646 17647 17648 17649 17650 17651 17652 17653 17654 17655 17656 17657 17658 17659 17660 17661 17662 17663 17664 17665 17666 17667 17668 17669 17670 17671 17672 17673 17674 17675 17676 17677 | void *p; assert( sqlite3_mutex_held(mem0.mutex) ); nFull = sqlite3GlobalConfig.m.xRoundup(n); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed+nFull >= mem0.alarmThreshold ){ mem0.nearlyFull = 1; sqlite3MallocAlarm(nFull); }else{ mem0.nearlyFull = 0; } } p = sqlite3GlobalConfig.m.xMalloc(nFull); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( p==0 && mem0.alarmCallback ){ sqlite3MallocAlarm(nFull); p = sqlite3GlobalConfig.m.xMalloc(nFull); } #endif if( p ){ nFull = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; return nFull; } /* ** Allocate memory. This routine is like sqlite3_malloc() except that it ** assumes the memory subsystem has already been initialized. */ SQLITE_PRIVATE void *sqlite3Malloc(int n){ void *p; if( n<=0 /* IMP: R-65312-04917 */ || n>=0x7fffff00 ){ /* A memory allocation of a number of bytes which is near the maximum ** signed integer value might cause an integer overflow inside of the ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving ** 255 bytes of overhead. SQLite itself will never use anything near ** this amount. The only way to reach the limit is with sqlite3_malloc() */ p = 0; }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); mallocWithAlarm(n, &p); sqlite3_mutex_leave(mem0.mutex); }else{ p = sqlite3GlobalConfig.m.xMalloc(n); } assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */ return p; } /* ** This version of the memory allocation is for use by the application. ** First make sure the memory subsystem is initialized, then do the ** allocation. |
︙ | ︙ | |||
17383 17384 17385 17386 17387 17388 17389 17390 17391 | ** routine is intended to get memory to old large transient data ** structures that would not normally fit on the stack of an ** embedded processor. */ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){ void *p; assert( n>0 ); #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) | > > > > > > > > > > > > > > > > > > > > > > > | | < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < | > > > > > > > > > | > > > > > > | > > > > > < < < < < < < < < < < < < < < < < < < < | 17701 17702 17703 17704 17705 17706 17707 17708 17709 17710 17711 17712 17713 17714 17715 17716 17717 17718 17719 17720 17721 17722 17723 17724 17725 17726 17727 17728 17729 17730 17731 17732 17733 17734 17735 17736 17737 17738 17739 17740 17741 17742 17743 17744 17745 17746 17747 17748 17749 17750 17751 17752 17753 17754 17755 17756 17757 17758 17759 17760 17761 17762 17763 17764 17765 17766 17767 17768 17769 17770 17771 17772 17773 17774 17775 17776 17777 17778 17779 17780 17781 17782 17783 17784 17785 17786 17787 17788 | ** routine is intended to get memory to old large transient data ** structures that would not normally fit on the stack of an ** embedded processor. */ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){ void *p; assert( n>0 ); sqlite3_mutex_enter(mem0.mutex); if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){ p = mem0.pScratchFree; mem0.pScratchFree = mem0.pScratchFree->pNext; mem0.nScratchFree--; sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1); sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); sqlite3_mutex_leave(mem0.mutex); }else{ if( sqlite3GlobalConfig.bMemstat ){ sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); n = mallocWithAlarm(n, &p); if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3_mutex_leave(mem0.mutex); p = sqlite3GlobalConfig.m.xMalloc(n); } sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH); } assert( sqlite3_mutex_notheld(mem0.mutex) ); #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) /* Verify that no more than two scratch allocations per thread ** are outstanding at one time. (This is only checked in the ** single-threaded case since checking in the multi-threaded case ** would be much more complicated.) */ assert( scratchAllocOut<=1 ); if( p ) scratchAllocOut++; #endif return p; } SQLITE_PRIVATE void sqlite3ScratchFree(void *p){ if( p ){ #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) /* Verify that no more than two scratch allocation per thread ** is outstanding at one time. (This is only checked in the ** single-threaded case since checking in the multi-threaded case ** would be much more complicated.) */ assert( scratchAllocOut>=1 && scratchAllocOut<=2 ); scratchAllocOut--; #endif if( p>=sqlite3GlobalConfig.pScratch && p<mem0.pScratchEnd ){ /* Release memory from the SQLITE_CONFIG_SCRATCH allocation */ ScratchFreeslot *pSlot; pSlot = (ScratchFreeslot*)p; sqlite3_mutex_enter(mem0.mutex); pSlot->pNext = mem0.pScratchFree; mem0.pScratchFree = pSlot; mem0.nScratchFree++; assert( mem0.nScratchFree<=sqlite3GlobalConfig.nScratch ); sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); sqlite3_mutex_leave(mem0.mutex); }else{ /* Release memory back to the heap */ assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) ); assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); if( sqlite3GlobalConfig.bMemstat ){ int iSize = sqlite3MallocSize(p); sqlite3_mutex_enter(mem0.mutex); sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3GlobalConfig.m.xFree(p); } } } } /* ** TRUE if p is a lookaside memory allocation from db */ |
︙ | ︙ | |||
17511 17512 17513 17514 17515 17516 17517 | } } /* ** Free memory previously obtained from sqlite3Malloc(). */ SQLITE_API void sqlite3_free(void *p){ | | | 17815 17816 17817 17818 17819 17820 17821 17822 17823 17824 17825 17826 17827 17828 17829 | } } /* ** Free memory previously obtained from sqlite3Malloc(). */ SQLITE_API void sqlite3_free(void *p){ if( p==0 ) return; /* IMP: R-49053-54554 */ assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p)); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); sqlite3GlobalConfig.m.xFree(p); |
︙ | ︙ | |||
17558 17559 17560 17561 17562 17563 17564 | /* ** Change the size of an existing memory allocation */ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){ int nOld, nNew; void *pNew; if( pOld==0 ){ | | | > > > | 17862 17863 17864 17865 17866 17867 17868 17869 17870 17871 17872 17873 17874 17875 17876 17877 17878 17879 17880 17881 17882 17883 17884 17885 17886 17887 17888 17889 | /* ** Change the size of an existing memory allocation */ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){ int nOld, nNew; void *pNew; if( pOld==0 ){ return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */ } if( nBytes<=0 ){ sqlite3_free(pOld); /* IMP: R-31593-10574 */ return 0; } if( nBytes>=0x7fffff00 ){ /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ return 0; } nOld = sqlite3MallocSize(pOld); /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second ** argument to xRealloc is always a value returned by a prior call to ** xRoundup. */ nNew = sqlite3GlobalConfig.m.xRoundup(nBytes); if( nOld==nNew ){ pNew = pOld; }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= |
︙ | ︙ | |||
17594 17595 17596 17597 17598 17599 17600 17601 17602 17603 17604 17605 17606 17607 | nNew = sqlite3MallocSize(pNew); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } return pNew; } /* ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ | > | 17901 17902 17903 17904 17905 17906 17907 17908 17909 17910 17911 17912 17913 17914 17915 | nNew = sqlite3MallocSize(pNew); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */ return pNew; } /* ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ |
︙ | ︙ | |||
19757 19758 19759 19760 19761 19762 19763 19764 19765 19766 19767 19768 19769 19770 19771 19772 19773 19774 19775 19776 19777 19778 19779 19780 | /* Convenient short-hand */ #define UpperToLower sqlite3UpperToLower /* ** Some systems have stricmp(). Others have strcasecmp(). Because ** there is no consistency, we will define our own. */ SQLITE_PRIVATE int sqlite3StrICmp(const char *zLeft, const char *zRight){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; } /* | > > > > > > < | > | > > | | < < | > > > > > | > > > > | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | > > > > > | > > | | > | | | > | > | | | | > | > > | | | | > > > > > > > | 20065 20066 20067 20068 20069 20070 20071 20072 20073 20074 20075 20076 20077 20078 20079 20080 20081 20082 20083 20084 20085 20086 20087 20088 20089 20090 20091 20092 20093 20094 20095 20096 20097 20098 20099 20100 20101 20102 20103 20104 20105 20106 20107 20108 20109 20110 20111 20112 20113 20114 20115 20116 20117 20118 20119 20120 20121 20122 20123 20124 20125 20126 20127 20128 20129 20130 20131 20132 20133 20134 20135 20136 20137 20138 20139 20140 20141 20142 20143 20144 20145 20146 20147 20148 20149 20150 20151 20152 20153 20154 20155 20156 20157 20158 20159 20160 20161 20162 20163 20164 20165 20166 20167 20168 20169 20170 20171 20172 20173 20174 20175 20176 20177 20178 20179 20180 20181 20182 20183 20184 20185 20186 20187 20188 20189 20190 20191 20192 20193 20194 20195 20196 20197 20198 20199 20200 20201 20202 20203 20204 20205 20206 | /* Convenient short-hand */ #define UpperToLower sqlite3UpperToLower /* ** Some systems have stricmp(). Others have strcasecmp(). Because ** there is no consistency, we will define our own. ** ** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows ** applications and extensions to compare the contents of two buffers ** containing UTF-8 strings in a case-independent fashion, using the same ** definition of case independence that SQLite uses internally when ** comparing identifiers. */ SQLITE_PRIVATE int sqlite3StrICmp(const char *zLeft, const char *zRight){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; } /* ** The string z[] is an text representation of a real number. ** Convert this string to a double and write it into *pResult. ** ** The string z[] is length bytes in length (bytes, not characters) and ** uses the encoding enc. The string is not necessarily zero-terminated. ** ** Return TRUE if the result is a valid real number (or integer) and FALSE ** if the string is empty or contains extraneous text. Valid numbers ** are in one of these formats: ** ** [+-]digits[E[+-]digits] ** [+-]digits.[digits][E[+-]digits] ** [+-].digits[E[+-]digits] ** ** Leading and trailing whitespace is ignored for the purpose of determining ** validity. ** ** If some prefix of the input string is a valid number, this routine ** returns FALSE but it still converts the prefix and writes the result ** into *pResult. */ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ #ifndef SQLITE_OMIT_FLOATING_POINT int incr = (enc==SQLITE_UTF8?1:2); const char *zEnd = z + length; /* sign * significand * (10 ^ (esign * exponent)) */ int sign = 1; /* sign of significand */ i64 s = 0; /* significand */ int d = 0; /* adjust exponent for shifting decimal point */ int esign = 1; /* sign of exponent */ int e = 0; /* exponent */ int eValid = 1; /* True exponent is either not used or is well-formed */ double result; int nDigits = 0; *pResult = 0.0; /* Default return value, in case of an error */ if( enc==SQLITE_UTF16BE ) z++; /* skip leading spaces */ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; if( z>=zEnd ) return 0; /* get sign of significand */ if( *z=='-' ){ sign = -1; z+=incr; }else if( *z=='+' ){ z+=incr; } /* skip leading zeroes */ while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++; /* copy max significant digits to significand */ while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ s = s*10 + (*z - '0'); z+=incr, nDigits++; } /* skip non-significant significand digits ** (increase exponent by d to shift decimal left) */ while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++; if( z>=zEnd ) goto do_atof_calc; /* if decimal point is present */ if( *z=='.' ){ z+=incr; /* copy digits from after decimal to significand ** (decrease exponent by d to shift decimal right) */ while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ s = s*10 + (*z - '0'); z+=incr, nDigits++, d--; } /* skip non-significant digits */ while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++; } if( z>=zEnd ) goto do_atof_calc; /* if exponent is present */ if( *z=='e' || *z=='E' ){ z+=incr; eValid = 0; if( z>=zEnd ) goto do_atof_calc; /* get sign of exponent */ if( *z=='-' ){ esign = -1; z+=incr; }else if( *z=='+' ){ z+=incr; } /* copy digits to exponent */ while( z<zEnd && sqlite3Isdigit(*z) ){ e = e*10 + (*z - '0'); z+=incr; eValid = 1; } } /* skip trailing spaces */ if( nDigits && eValid ){ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; } do_atof_calc: /* adjust exponent by d, and update sign */ e = (e*esign) + d; if( e<0 ) { esign = -1; e *= -1; } else { esign = 1; |
︙ | ︙ | |||
19947 19948 19949 19950 19951 19952 19953 | result = (double)s; } } /* store the result */ *pResult = result; | | | | > | | > | > | > > > | | | > > > > | | < < > > | | > | | > > > | > | < | < < > | | | | | | | | | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < | 20251 20252 20253 20254 20255 20256 20257 20258 20259 20260 20261 20262 20263 20264 20265 20266 20267 20268 20269 20270 20271 20272 20273 20274 20275 20276 20277 20278 20279 20280 20281 20282 20283 20284 20285 20286 20287 20288 20289 20290 20291 20292 20293 20294 20295 20296 20297 20298 20299 20300 20301 20302 20303 20304 20305 20306 20307 20308 20309 20310 20311 20312 20313 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 20327 20328 20329 20330 20331 20332 20333 20334 20335 20336 20337 20338 20339 20340 20341 20342 20343 20344 20345 20346 20347 20348 20349 20350 20351 20352 20353 20354 20355 20356 20357 20358 | result = (double)s; } } /* store the result */ *pResult = result; /* return true if number and no extra non-whitespace chracters after */ return z>=zEnd && nDigits>0 && eValid; #else return !sqlite3Atoi64(z, pResult, length, enc); #endif /* SQLITE_OMIT_FLOATING_POINT */ } /* ** Compare the 19-character string zNum against the text representation ** value 2^63: 9223372036854775808. Return negative, zero, or positive ** if zNum is less than, equal to, or greater than the string. ** Note that zNum must contain exactly 19 characters. ** ** Unlike memcmp() this routine is guaranteed to return the difference ** in the values of the last digit if the only difference is in the ** last digit. So, for example, ** ** compare2pow63("9223372036854775800", 1) ** ** will return -8. */ static int compare2pow63(const char *zNum, int incr){ int c = 0; int i; /* 012345678901234567 */ const char *pow63 = "922337203685477580"; for(i=0; c==0 && i<18; i++){ c = (zNum[i*incr]-pow63[i])*10; } if( c==0 ){ c = zNum[18*incr] - '8'; testcase( c==(-1) ); testcase( c==0 ); testcase( c==(+1) ); } return c; } /* ** Convert zNum to a 64-bit signed integer and write ** the value of the integer into *pNum. ** If zNum is exactly 9223372036854665808, return 2. ** This is a special case as the context will determine ** if it is too big (used as a negative). ** If zNum is not an integer or is an integer that ** is too large to be expressed with 64 bits, ** then return 1. Otherwise return 0. ** ** length is the number of bytes in the string (bytes, not characters). ** The string is not necessarily zero-terminated. The encoding is ** given by enc. */ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ int incr = (enc==SQLITE_UTF8?1:2); i64 v = 0; int neg = 0; /* assume positive */ int i; int c = 0; const char *zStart; const char *zEnd = zNum + length; if( enc==SQLITE_UTF16BE ) zNum++; while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr; if( zNum>=zEnd ) goto do_atoi_calc; if( *zNum=='-' ){ neg = 1; zNum+=incr; }else if( *zNum=='+' ){ zNum+=incr; } do_atoi_calc: zStart = zNum; while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ v = v*10 + c - '0'; } *pNum = neg ? -v : v; testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){ /* zNum is empty or contains non-numeric text or is longer ** than 19 digits (thus guaranteeing that it is too large) */ return 1; }else if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ 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 c<neg ? 0 : 1; } } /* ** If zNum represents an integer that will fit in 32-bits, then set ** *pValue to that integer and return true. Otherwise return false. ** |
︙ | ︙ | |||
22607 22608 22609 22610 22611 22612 22613 22614 22615 22616 22617 22618 22619 22620 | unsigned char eFileLock; /* The type of lock held on this fd */ 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 */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS | > | 22883 22884 22885 22886 22887 22888 22889 22890 22891 22892 22893 22894 22895 22896 22897 | unsigned char eFileLock; /* The type of lock held on this fd */ 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 */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS |
︙ | ︙ | |||
25365 25366 25367 25368 25369 25370 25371 25372 25373 25374 25375 25376 25377 25378 25379 25380 25381 25382 25383 25384 25385 25386 25387 | while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ amt -= wrote; offset += wrote; pBuf = &((char*)pBuf)[wrote]; } SimulateIOError(( wrote=(-1), amt=1 )); SimulateDiskfullError(( wrote=0, amt=1 )); if( amt>0 ){ if( wrote<0 ){ /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ pFile->lastErrno = 0; /* not a system error */ return SQLITE_FULL; } } return SQLITE_OK; } #ifdef SQLITE_TEST /* ** Count the number of fullsyncs and normal syncs. This is used to test ** that syncs and fullsyncs are occurring at the right times. | > > | 25642 25643 25644 25645 25646 25647 25648 25649 25650 25651 25652 25653 25654 25655 25656 25657 25658 25659 25660 25661 25662 25663 25664 25665 25666 | while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ amt -= wrote; offset += wrote; pBuf = &((char*)pBuf)[wrote]; } SimulateIOError(( wrote=(-1), amt=1 )); SimulateDiskfullError(( wrote=0, amt=1 )); if( amt>0 ){ if( wrote<0 ){ /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ pFile->lastErrno = 0; /* not a system error */ return SQLITE_FULL; } } return SQLITE_OK; } #ifdef SQLITE_TEST /* ** Count the number of fullsyncs and normal syncs. This is used to test ** that syncs and fullsyncs are occurring at the right times. |
︙ | ︙ | |||
25575 25576 25577 25578 25579 25580 25581 25582 | return rc; } /* ** Truncate an open file to a specified size */ static int unixTruncate(sqlite3_file *id, i64 nByte){ int rc; | > | > > > > > > > > > > | | | | | 25854 25855 25856 25857 25858 25859 25860 25861 25862 25863 25864 25865 25866 25867 25868 25869 25870 25871 25872 25873 25874 25875 25876 25877 25878 25879 25880 25881 25882 25883 25884 25885 25886 25887 25888 25889 25890 25891 25892 25893 25894 25895 25896 | return rc; } /* ** Truncate an open file to a specified size */ static int unixTruncate(sqlite3_file *id, i64 nByte){ unixFile *pFile = (unixFile *)id; int rc; assert( pFile ); 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 = ftruncate(pFile->h, (off_t)nByte); if( rc ){ pFile->lastErrno = errno; return SQLITE_IOERR_TRUNCATE; }else{ #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to ** doing a hot-journal rollback or a write to some file other than a ** normal database file) and we truncate the file to zero length, ** that effectively updates the change counter. This might happen ** when restoring a database using the backup API from a zero-length ** source. */ if( pFile->inNormalWrite && nByte==0 ){ pFile->transCntrChng = 1; } #endif return SQLITE_OK; } } |
︙ | ︙ | |||
25635 25636 25637 25638 25639 25640 25641 25642 25643 25644 25645 25646 25647 25648 25649 25650 25651 25652 25653 25654 25655 | /* ** Handler for proxy-locking file-control verbs. Defined below in the ** proxying locking division. */ static int proxyFileControl(sqlite3_file*,int,void*); #endif /* ** Information and control of an open file handle. */ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((unixFile*)id)->eFileLock; return SQLITE_OK; } case SQLITE_LAST_ERRNO: { *(int*)pArg = ((unixFile*)id)->lastErrno; return SQLITE_OK; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | < < > > > | 25925 25926 25927 25928 25929 25930 25931 25932 25933 25934 25935 25936 25937 25938 25939 25940 25941 25942 25943 25944 25945 25946 25947 25948 25949 25950 25951 25952 25953 25954 25955 25956 25957 25958 25959 25960 25961 25962 25963 25964 25965 25966 25967 25968 25969 25970 25971 25972 25973 25974 25975 25976 25977 25978 25979 25980 25981 25982 25983 25984 25985 25986 25987 25988 25989 25990 25991 25992 25993 25994 25995 25996 25997 25998 25999 26000 26001 26002 26003 26004 26005 26006 | /* ** Handler for proxy-locking file-control verbs. Defined below in the ** proxying locking division. */ static int proxyFileControl(sqlite3_file*,int,void*); #endif /* ** This function is called to handle the SQLITE_FCNTL_SIZE_HINT ** file-control operation. ** ** If the user has configured a chunk-size for this file, it could be ** that the file needs to be extended at this point. Otherwise, the ** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix. */ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ if( pFile->szChunk ){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ if( fstat(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; } #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 ** the last byte in each block within the extended region. This ** is the same technique used by glibc to implement posix_fallocate() ** on systems that do not have a real fallocate() system call. */ 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) ){ pFile->lastErrno = errno; return SQLITE_IOERR_TRUNCATE; } iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; do { nWrite = seekAndWrite(pFile, iWrite, "", 1); iWrite += nBlk; } while( nWrite==1 && iWrite<nSize ); if( nWrite!=1 ) return SQLITE_IOERR_WRITE; #endif } } return SQLITE_OK; } /* ** Information and control of an open file handle. */ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((unixFile*)id)->eFileLock; return SQLITE_OK; } case SQLITE_LAST_ERRNO: { *(int*)pArg = ((unixFile*)id)->lastErrno; return SQLITE_OK; } case SQLITE_FCNTL_CHUNK_SIZE: { ((unixFile*)id)->szChunk = *(int *)pArg; return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { return fcntlSizeHint((unixFile *)id, *(i64 *)pArg); } #ifndef NDEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and ** it hence it is OK for the transaction change counter to be ** unchanged. */ |
︙ | ︙ | |||
26100 26101 26102 26103 26104 26105 26106 | if( !apNew ){ rc = SQLITE_IOERR_NOMEM; goto shmpage_out; } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, | | | 26437 26438 26439 26440 26441 26442 26443 26444 26445 26446 26447 26448 26449 26450 26451 | if( !apNew ){ rc = SQLITE_IOERR_NOMEM; goto shmpage_out; } 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; } pShmNode->apRegion[pShmNode->nRegion] = pMem; pShmNode->nRegion++; |
︙ | ︙ | |||
26617 26618 26619 26620 26621 26622 26623 26624 26625 26626 26627 26628 | assert( pNew->pInode==NULL ); /* Parameter isDelete is only used on vxworks. Express this explicitly ** here to prevent compiler warnings about unused parameters. */ UNUSED_PARAMETER(isDelete); OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; pNew->fileFlags = 0; | > > > > > > > > > > > < | 26954 26955 26956 26957 26958 26959 26960 26961 26962 26963 26964 26965 26966 26967 26968 26969 26970 26971 26972 26973 26974 26975 26976 26977 26978 26979 26980 26981 26982 26983 | assert( pNew->pInode==NULL ); /* Parameter isDelete is only used on vxworks. Express this explicitly ** here to prevent compiler warnings about unused parameters. */ UNUSED_PARAMETER(isDelete); /* Usually the path zFilename should not be a relative pathname. The ** exception is when opening the proxy "conch" file in builds that ** include the special Apple locking styles. */ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE assert( zFilename==0 || zFilename[0]=='/' || pVfs->pAppData==(void*)&autolockIoFinder ); #else assert( zFilename==0 || zFilename[0]=='/' ); #endif OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; pNew->fileFlags = 0; pNew->zPath = zFilename; #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ noLock = 1; rc = SQLITE_NOMEM; |
︙ | ︙ | |||
27923 27924 27925 27926 27927 27928 27929 27930 27931 27932 27933 27934 27935 27936 27937 27938 27939 27940 27941 27942 27943 | ** bytes of writable memory. */ static int proxyGetHostID(unsigned char *pHostID, int *pError){ struct timespec timeout = {1, 0}; /* 1 sec timeout */ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); if( gethostuuid(pHostID, &timeout) ){ int err = errno; if( pError ){ *pError = err; } return SQLITE_IOERR; } #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ if( sqlite3_hostid_num != 0){ pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF)); } #endif | > > > | 28270 28271 28272 28273 28274 28275 28276 28277 28278 28279 28280 28281 28282 28283 28284 28285 28286 28287 28288 28289 28290 28291 28292 28293 | ** bytes of writable memory. */ static int proxyGetHostID(unsigned char *pHostID, int *pError){ struct timespec timeout = {1, 0}; /* 1 sec timeout */ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); #if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\ && __MAC_OS_X_VERSION_MIN_REQUIRED<1050 if( gethostuuid(pHostID, &timeout) ){ int err = errno; if( pError ){ *pError = err; } return SQLITE_IOERR; } #endif #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ if( sqlite3_hostid_num != 0){ pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF)); } #endif |
︙ | ︙ | |||
28520 28521 28522 28523 28524 28525 28526 | pCtx->pOldMethod = pFile->pMethod; pFile->pMethod = &proxyIoMethods; }else{ if( pCtx->conchFile ){ pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); sqlite3_free(pCtx->conchFile); } | | | 28870 28871 28872 28873 28874 28875 28876 28877 28878 28879 28880 28881 28882 28883 28884 | pCtx->pOldMethod = pFile->pMethod; pFile->pMethod = &proxyIoMethods; }else{ if( pCtx->conchFile ){ pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); sqlite3_free(pCtx->conchFile); } sqlite3DbFree(0, pCtx->lockProxyPath); sqlite3_free(pCtx->conchFilePath); sqlite3_free(pCtx); } OSTRACE(("TRANSPROXY %d %s\n", pFile->h, (rc==SQLITE_OK ? "ok" : "failed"))); return rc; } |
︙ | ︙ | |||
28711 28712 28713 28714 28715 28716 28717 | rc = proxyReleaseConch(pFile); if( rc ) return rc; } rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile); if( rc ) return rc; sqlite3_free(conchFile); } | | | | 29061 29062 29063 29064 29065 29066 29067 29068 29069 29070 29071 29072 29073 29074 29075 29076 29077 | rc = proxyReleaseConch(pFile); if( rc ) return rc; } rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile); if( rc ) return rc; sqlite3_free(conchFile); } sqlite3DbFree(0, pCtx->lockProxyPath); sqlite3_free(pCtx->conchFilePath); sqlite3DbFree(0, pCtx->dbPath); /* restore the original locking context and pMethod then close it */ pFile->lockingContext = pCtx->oldLockingContext; pFile->pMethod = pCtx->pOldMethod; sqlite3_free(pCtx); return pFile->pMethod->xClose(id); } return SQLITE_OK; |
︙ | ︙ | |||
29159 29160 29161 29162 29163 29164 29165 29166 29167 29168 29169 29170 29171 29172 | HANDLE h; /* Handle for accessing the file */ unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD sectorSize; /* Sector size of the device file is on */ winShm *pShm; /* Instance of shared memory on this file */ const char *zPath; /* Full pathname of this file */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif | > | 29509 29510 29511 29512 29513 29514 29515 29516 29517 29518 29519 29520 29521 29522 29523 | HANDLE h; /* Handle for accessing the file */ unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD sectorSize; /* Sector size of the device file is on */ winShm *pShm; /* Instance of shared memory on this file */ const char *zPath; /* Full pathname of this file */ int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif |
︙ | ︙ | |||
29669 29670 29671 29672 29673 29674 29675 29676 29677 29678 29679 29680 29681 29682 | *****************************************************************************/ #endif /* SQLITE_OS_WINCE */ /***************************************************************************** ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. ******************************************************************************/ /* ** Close a file. ** ** It is reported that an attempt to close a handle might sometimes ** fail. This is a very unreasonable result, but windows is notorious ** for being unreasonable so I do not doubt that it might happen. If | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 30020 30021 30022 30023 30024 30025 30026 30027 30028 30029 30030 30031 30032 30033 30034 30035 30036 30037 30038 30039 30040 30041 30042 30043 30044 30045 30046 30047 30048 30049 30050 30051 30052 30053 30054 30055 30056 30057 30058 30059 30060 30061 30062 30063 30064 30065 30066 30067 30068 30069 | *****************************************************************************/ #endif /* SQLITE_OS_WINCE */ /***************************************************************************** ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. ******************************************************************************/ /* ** Some microsoft compilers lack this definition. */ #ifndef INVALID_SET_FILE_POINTER # define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif /* ** Move the current position of the file handle passed as the first ** argument to offset iOffset within the file. If successful, return 0. ** Otherwise, set pFile->lastErrno and return non-zero. */ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ LONG upperBits; /* Most sig. 32 bits of new offset */ LONG lowerBits; /* Least sig. 32 bits of new offset */ DWORD dwRet; /* Value returned by SetFilePointer() */ upperBits = (LONG)((iOffset>>32) & 0x7fffffff); lowerBits = (LONG)(iOffset & 0xffffffff); /* API oddity: If successful, SetFilePointer() returns a dword ** containing the lower 32-bits of the new file-offset. Or, if it fails, ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine ** whether an error has actually occured, it is also necessary to call ** GetLastError(). */ dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ pFile->lastErrno = GetLastError(); return 1; } return 0; } /* ** Close a file. ** ** It is reported that an attempt to close a handle might sometimes ** fail. This is a very unreasonable result, but windows is notorious ** for being unreasonable so I do not doubt that it might happen. If |
︙ | ︙ | |||
29712 29713 29714 29715 29716 29717 29718 | } #endif OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); OpenCounter(-1); return rc ? SQLITE_OK : SQLITE_IOERR; } | < < < < < < < < < < | < | | < | | | < < | > > | | | | < < | | < < | > > < < < < < < < | > > > > > | < < < | | < | < | | | > > > > > < < > < < < | < | | > | > > > > > > > > | > | < | > | | 30099 30100 30101 30102 30103 30104 30105 30106 30107 30108 30109 30110 30111 30112 30113 30114 30115 30116 30117 30118 30119 30120 30121 30122 30123 30124 30125 30126 30127 30128 30129 30130 30131 30132 30133 30134 30135 30136 30137 30138 30139 30140 30141 30142 30143 30144 30145 30146 30147 30148 30149 30150 30151 30152 30153 30154 30155 30156 30157 30158 30159 30160 30161 30162 30163 30164 30165 30166 30167 30168 30169 30170 30171 30172 30173 30174 30175 30176 30177 30178 30179 30180 30181 30182 30183 30184 30185 30186 30187 30188 30189 30190 30191 30192 30193 30194 30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 30209 30210 30211 30212 30213 30214 30215 30216 30217 30218 30219 30220 30221 | } #endif OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); OpenCounter(-1); return rc ? SQLITE_OK : SQLITE_IOERR; } /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes ** wrong. */ static int winRead( sqlite3_file *id, /* File to read from */ void *pBuf, /* Write content into this buffer */ int amt, /* Number of bytes to read */ sqlite3_int64 offset /* Begin reading at this offset */ ){ winFile *pFile = (winFile*)id; /* file handle */ DWORD nRead; /* Number of bytes actually read from file */ assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_READ); OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); if( seekWinFile(pFile, offset) ){ return SQLITE_FULL; } if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ pFile->lastErrno = GetLastError(); return SQLITE_IOERR_READ; } if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[nRead], 0, amt-nRead); return SQLITE_IOERR_SHORT_READ; } return SQLITE_OK; } /* ** Write data from a buffer into a file. Return SQLITE_OK on success ** or some other error code on failure. */ static int winWrite( sqlite3_file *id, /* File to write into */ const void *pBuf, /* The bytes to be written */ int amt, /* Number of bytes to write */ sqlite3_int64 offset /* Offset into the file to begin writing at */ ){ int rc; /* True if error has occured, else false */ winFile *pFile = (winFile*)id; /* File handle */ assert( amt>0 ); assert( pFile ); SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype)); rc = seekWinFile(pFile, offset); if( rc==0 ){ u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ int nRem = amt; /* Number of bytes yet to be written */ DWORD nWrite; /* Bytes written by each WriteFile() call */ while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){ aRem += nWrite; nRem -= nWrite; } if( nRem>0 ){ pFile->lastErrno = GetLastError(); rc = 1; } } if( rc ){ if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ return SQLITE_FULL; } return SQLITE_IOERR_WRITE; } return SQLITE_OK; } /* ** Truncate an open file to a specified size */ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ winFile *pFile = (winFile*)id; /* File handle object */ int rc = SQLITE_OK; /* Return code for this function */ assert( pFile ); 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; } /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ if( seekWinFile(pFile, nByte) ){ rc = SQLITE_IOERR_TRUNCATE; }else if( 0==SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError(); rc = SQLITE_IOERR_TRUNCATE; } OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); return rc; } #ifdef SQLITE_TEST /* ** Count the number of fullsyncs and normal syncs. This is used to test ** that syncs and fullsyncs are occuring at the right times. |
︙ | ︙ | |||
30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 | case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((winFile*)id)->locktype; return SQLITE_OK; } case SQLITE_LAST_ERRNO: { *(int*)pArg = (int)((winFile*)id)->lastErrno; return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { sqlite3_int64 sz = *(sqlite3_int64*)pArg; SimulateIOErrorBenign(1); winTruncate(id, sz); SimulateIOErrorBenign(0); return SQLITE_OK; | > > > > | 30571 30572 30573 30574 30575 30576 30577 30578 30579 30580 30581 30582 30583 30584 30585 30586 30587 30588 | case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((winFile*)id)->locktype; return SQLITE_OK; } case SQLITE_LAST_ERRNO: { *(int*)pArg = (int)((winFile*)id)->lastErrno; return SQLITE_OK; } case SQLITE_FCNTL_CHUNK_SIZE: { ((winFile*)id)->szChunk = *(int *)pArg; return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { sqlite3_int64 sz = *(sqlite3_int64*)pArg; SimulateIOErrorBenign(1); winTruncate(id, sz); SimulateIOErrorBenign(0); return SQLITE_OK; |
︙ | ︙ | |||
30232 30233 30234 30235 30236 30237 30238 30239 30240 30241 30242 30243 30244 30245 | static int winDeviceCharacteristics(sqlite3_file *id){ UNUSED_PARAMETER(id); return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } #ifndef SQLITE_OMIT_WAL /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the winLockInfo objects used by ** this file, all of which may be shared by multiple threads. ** ** Function winShmMutexHeld() is used to assert() that the global mutex ** is held when required. This function is only used as part of assert() | > > > > > > > > | 30612 30613 30614 30615 30616 30617 30618 30619 30620 30621 30622 30623 30624 30625 30626 30627 30628 30629 30630 30631 30632 30633 | static int winDeviceCharacteristics(sqlite3_file *id){ UNUSED_PARAMETER(id); return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } #ifndef SQLITE_OMIT_WAL /* ** Windows will only let you create file view mappings ** on allocation size granularity boundaries. ** During sqlite3_os_init() we do a GetSystemInfo() ** to get the granularity size. */ SYSTEM_INFO winSysInfo; /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the winLockInfo objects used by ** this file, all of which may be shared by multiple threads. ** ** Function winShmMutexHeld() is used to assert() that the global mutex ** is held when required. This function is only used as part of assert() |
︙ | ︙ | |||
30400 30401 30402 30403 30404 30405 30406 30407 30408 30409 30410 30411 30412 30413 | ** ** This is not a VFS shared-memory method; it is a utility function called ** by VFS shared-memory methods. */ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ winShmNode **pp; winShmNode *p; assert( winShmMutexHeld() ); pp = &winShmNodeList; while( (p = *pp)!=0 ){ if( p->nRef==0 ){ int i; if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ | > | > > > | > > > | 30788 30789 30790 30791 30792 30793 30794 30795 30796 30797 30798 30799 30800 30801 30802 30803 30804 30805 30806 30807 30808 30809 30810 30811 30812 30813 30814 30815 30816 30817 | ** ** This is not a VFS shared-memory method; it is a utility function called ** by VFS shared-memory methods. */ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ winShmNode **pp; winShmNode *p; BOOL bRc; assert( winShmMutexHeld() ); pp = &winShmNodeList; while( (p = *pp)!=0 ){ if( p->nRef==0 ){ int i; if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ bRc = UnmapViewOfFile(p->aRegion[i].pMap); OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", (int)GetCurrentProcessId(), i, bRc ? "ok" : "failed")); bRc = CloseHandle(p->aRegion[i].hMap); OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n", (int)GetCurrentProcessId(), i, bRc ? "ok" : "failed")); } if( p->hFile.h != INVALID_HANDLE_VALUE ){ SimulateIOErrorBenign(1); winClose((sqlite3_file *)&p->hFile); SimulateIOErrorBenign(0); } if( deleteFlag ){ |
︙ | ︙ | |||
30485 30486 30487 30488 30489 30490 30491 30492 30493 30494 | winShmNodeList = pShmNode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, /* Name of the file (UTF-8) */ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ | > | | 30880 30881 30882 30883 30884 30885 30886 30887 30888 30889 30890 30891 30892 30893 30894 30895 30896 30897 30898 | winShmNodeList = pShmNode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, /* Name of the file (UTF-8) */ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */ 0); if( SQLITE_OK!=rc ){ rc = SQLITE_CANTOPEN_BKPT; goto shm_open_err; } /* Check to see if another process is holding the dead-man switch. |
︙ | ︙ | |||
30796 30797 30798 30799 30800 30801 30802 30803 30804 | while( pShmNode->nRegion<=iRegion ){ HANDLE hMap; /* file-mapping handle */ void *pMap = 0; /* Mapped memory region */ hMap = CreateFileMapping(pShmNode->hFile.h, NULL, PAGE_READWRITE, 0, nByte, NULL ); if( hMap ){ pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, | > > > > > | > > > > > | | 31192 31193 31194 31195 31196 31197 31198 31199 31200 31201 31202 31203 31204 31205 31206 31207 31208 31209 31210 31211 31212 31213 31214 31215 31216 31217 31218 31219 31220 31221 31222 31223 31224 31225 31226 31227 31228 31229 31230 31231 31232 31233 31234 31235 31236 31237 | while( pShmNode->nRegion<=iRegion ){ HANDLE hMap; /* file-mapping handle */ void *pMap = 0; /* Mapped memory region */ hMap = CreateFileMapping(pShmNode->hFile.h, NULL, PAGE_READWRITE, 0, nByte, NULL ); OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n", (int)GetCurrentProcessId(), pShmNode->nRegion, nByte, hMap ? "ok" : "failed")); if( hMap ){ int iOffset = pShmNode->nRegion*szRegion; int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, iOffset - iOffsetShift, szRegion + iOffsetShift ); OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n", (int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion, pMap ? "ok" : "failed")); } if( !pMap ){ pShmNode->lastErrno = GetLastError(); rc = SQLITE_IOERR; if( hMap ) CloseHandle(hMap); goto shmpage_out; } pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; pShmNode->nRegion++; } } shmpage_out: if( pShmNode->nRegion>iRegion ){ int iOffset = iRegion*szRegion; int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; char *p = (char *)pShmNode->aRegion[iRegion].pMap; *pp = (void *)&p[iOffsetShift]; }else{ *pp = 0; } sqlite3_mutex_leave(pShmNode->mutex); return rc; } |
︙ | ︙ | |||
31044 31045 31046 31047 31048 31049 31050 | DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = 0; #if SQLITE_OS_WINCE int isTemp = 0; #endif winFile *pFile = (winFile*)id; | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > < | | > | > > | 31450 31451 31452 31453 31454 31455 31456 31457 31458 31459 31460 31461 31462 31463 31464 31465 31466 31467 31468 31469 31470 31471 31472 31473 31474 31475 31476 31477 31478 31479 31480 31481 31482 31483 31484 31485 31486 31487 31488 31489 31490 31491 31492 31493 31494 31495 31496 31497 31498 31499 31500 31501 31502 31503 31504 31505 31506 31507 31508 31509 31510 31511 31512 31513 31514 31515 31516 31517 31518 31519 31520 31521 31522 31523 31524 31525 31526 31527 31528 31529 31530 31531 31532 31533 31534 31535 31536 31537 31538 31539 31540 31541 31542 31543 31544 31545 31546 31547 31548 31549 31550 31551 31552 31553 31554 31555 31556 31557 31558 31559 31560 31561 31562 31563 31564 31565 31566 31567 31568 31569 31570 31571 31572 31573 31574 31575 31576 31577 31578 31579 31580 31581 31582 31583 | DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = 0; #if SQLITE_OS_WINCE int isTemp = 0; #endif winFile *pFile = (winFile*)id; void *zConverted; /* Filename in OS encoding */ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE int eType = flags&0xFFFFFF00; /* Type of file to open */ #endif int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); #ifndef NDEBUG int isReadonly = (flags & SQLITE_OPEN_READONLY); #endif int isReadWrite = (flags & SQLITE_OPEN_READWRITE); #ifndef NDEBUG int isOpenJournal = (isCreate && ( eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_WAL )); #endif /* 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 ); assert( id!=0 ); UNUSED_PARAMETER(pVfs); pFile->h = INVALID_HANDLE_VALUE; /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zUtf8Name ){ assert(isDelete && !isOpenJournal); rc = getTempname(MAX_PATH+1, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zUtf8Name = zTmpname; } /* Convert the filename to the system encoding. */ zConverted = convertUtf8Filename(zUtf8Name); if( zConverted==0 ){ return SQLITE_NOMEM; } if( isReadWrite ){ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; }else{ dwDesiredAccess = GENERIC_READ; } /* 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. */ dwCreationDisposition = CREATE_NEW; }else if( isCreate ){ /* Open existing file, or create if it doesn't exist */ dwCreationDisposition = OPEN_ALWAYS; }else{ /* Opens a file, only if it exists. */ dwCreationDisposition = OPEN_EXISTING; } dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if( isDelete ){ #if SQLITE_OS_WINCE dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; isTemp = 1; #else dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE; #endif }else{ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; } /* Reports from the internet are that performance is always ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ #if SQLITE_OS_WINCE dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; #endif if( isNT() ){ h = CreateFileW((WCHAR*)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, |
︙ | ︙ | |||
31134 31135 31136 31137 31138 31139 31140 31141 31142 31143 31144 31145 31146 | NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); #endif } OSTRACE(("OPEN %d %s 0x%lx %s\n", h, zName, dwDesiredAccess, h==INVALID_HANDLE_VALUE ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); free(zConverted); | > > | | > | > > < | > | | 31595 31596 31597 31598 31599 31600 31601 31602 31603 31604 31605 31606 31607 31608 31609 31610 31611 31612 31613 31614 31615 31616 31617 31618 31619 31620 31621 31622 31623 31624 31625 31626 31627 31628 31629 31630 31631 31632 31633 31634 31635 31636 31637 31638 31639 31640 31641 31642 31643 31644 31645 31646 31647 31648 31649 31650 31651 31652 31653 31654 31655 31656 31657 31658 31659 | NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); #endif } OSTRACE(("OPEN %d %s 0x%lx %s\n", h, zName, dwDesiredAccess, h==INVALID_HANDLE_VALUE ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); free(zConverted); if( isReadWrite ){ return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags); }else{ return SQLITE_CANTOPEN_BKPT; } } if( pOutFlags ){ if( isReadWrite ){ *pOutFlags = SQLITE_OPEN_READWRITE; }else{ *pOutFlags = SQLITE_OPEN_READONLY; } } memset(pFile, 0, sizeof(*pFile)); pFile->pMethod = &winIoMethod; pFile->h = h; pFile->lastErrno = NO_ERROR; pFile->pVfs = pVfs; pFile->pShm = 0; pFile->zPath = zName; pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); #if SQLITE_OS_WINCE if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB && !winceCreateLock(zName, pFile) ){ CloseHandle(h); free(zConverted); return SQLITE_CANTOPEN_BKPT; } if( isTemp ){ pFile->zDeleteOnClose = zConverted; }else #endif { free(zConverted); } OpenCounter(+1); return rc; } /* ** Delete the named file. ** ** Note that windows does not allow a file to be deleted if some other ** process has it open. Sometimes a virus scanner or indexing program |
︙ | ︙ | |||
31697 31698 31699 31700 31701 31702 31703 31704 31705 31706 31707 31708 31709 31710 | winDlClose, /* xDlClose */ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ }; sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } SQLITE_API int sqlite3_os_end(void){ return SQLITE_OK; } | > > > > > > > | 32163 32164 32165 32166 32167 32168 32169 32170 32171 32172 32173 32174 32175 32176 32177 32178 32179 32180 32181 32182 32183 | winDlClose, /* xDlClose */ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ }; #ifndef SQLITE_OMIT_WAL /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); GetSystemInfo(&winSysInfo); assert(winSysInfo.dwAllocationGranularity > 0); #endif sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } SQLITE_API int sqlite3_os_end(void){ return SQLITE_OK; } |
︙ | ︙ | |||
31747 31748 31749 31750 31751 31752 31753 | ** sometimes grow into tens of thousands or larger. The size of the ** Bitvec object is the number of pages in the database file at the ** start of a transaction, and is thus usually less than a few thousand, ** but can be as large as 2 billion for a really big database. */ /* Size of the Bitvec structure in bytes. */ | | | 32220 32221 32222 32223 32224 32225 32226 32227 32228 32229 32230 32231 32232 32233 32234 | ** sometimes grow into tens of thousands or larger. The size of the ** Bitvec object is the number of pages in the database file at the ** start of a transaction, and is thus usually less than a few thousand, ** but can be as large as 2 billion for a really big database. */ /* Size of the Bitvec structure in bytes. */ #define BITVEC_SZ 512 /* Round the union size down to the nearest pointer boundary, since that's how ** it will be aligned within the Bitvec struct. */ #define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*)) /* Type of the array "element" for the bitmap representation. ** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE. |
︙ | ︙ | |||
32262 32263 32264 32265 32266 32267 32268 32269 32270 32271 32272 32273 32274 32275 32276 32277 32278 32279 32280 32281 | /*************************************************** General Interfaces ****** ** ** Initialize and shutdown the page cache subsystem. Neither of these ** functions are threadsafe. */ SQLITE_PRIVATE int sqlite3PcacheInitialize(void){ if( sqlite3GlobalConfig.pcache.xInit==0 ){ sqlite3PCacheSetDefault(); } return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg); } SQLITE_PRIVATE void sqlite3PcacheShutdown(void){ if( sqlite3GlobalConfig.pcache.xShutdown ){ sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg); } } /* ** Return the size in bytes of a PCache object. */ | > > > > | 32735 32736 32737 32738 32739 32740 32741 32742 32743 32744 32745 32746 32747 32748 32749 32750 32751 32752 32753 32754 32755 32756 32757 32758 | /*************************************************** General Interfaces ****** ** ** Initialize and shutdown the page cache subsystem. Neither of these ** functions are threadsafe. */ SQLITE_PRIVATE int sqlite3PcacheInitialize(void){ if( sqlite3GlobalConfig.pcache.xInit==0 ){ /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache. */ sqlite3PCacheSetDefault(); } return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg); } SQLITE_PRIVATE void sqlite3PcacheShutdown(void){ if( sqlite3GlobalConfig.pcache.xShutdown ){ /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg); } } /* ** Return the size in bytes of a PCache object. */ |
︙ | ︙ | |||
32728 32729 32730 32731 32732 32733 32734 | */ typedef struct PCache1 PCache1; typedef struct PgHdr1 PgHdr1; typedef struct PgFreeslot PgFreeslot; | > > > > > | | | 33205 33206 33207 33208 33209 33210 33211 33212 33213 33214 33215 33216 33217 33218 33219 33220 33221 33222 33223 33224 33225 | */ typedef struct PCache1 PCache1; typedef struct PgHdr1 PgHdr1; typedef struct PgFreeslot PgFreeslot; /* Each page cache is an instance of the following object. Every ** open database file (including each in-memory database and each ** temporary or transient database) has a single page cache which ** is an instance of this object. ** ** Pointers to structures of this type are cast and returned as ** opaque sqlite3_pcache* handles. */ struct PCache1 { /* Cache configuration parameters. Page size (szPage) and the purgeable ** flag (bPurgeable) are set when the cache is created. nMax may be ** modified at any time by a call to the pcache1CacheSize() method. ** The global mutex must be held when accessing nMax. */ |
︙ | ︙ | |||
32789 32790 32791 32792 32793 32794 32795 32796 32797 32798 32799 32800 32801 32802 | int nMaxPage; /* Sum of nMaxPage for purgeable caches */ int nMinPage; /* Sum of nMinPage for purgeable caches */ int nCurrentPage; /* Number of purgeable pages allocated */ PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */ int szSlot; /* Size of each free slot */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */ PgFreeslot *pFree; /* Free page blocks */ int isInit; /* True if initialized */ } pcache1_g; /* ** All code in this file should access the global structure above via the | > > > | 33271 33272 33273 33274 33275 33276 33277 33278 33279 33280 33281 33282 33283 33284 33285 33286 33287 | int nMaxPage; /* Sum of nMaxPage for purgeable caches */ int nMinPage; /* Sum of nMinPage for purgeable caches */ int nCurrentPage; /* Number of purgeable pages allocated */ PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */ int szSlot; /* Size of each free slot */ int nSlot; /* The number of pcache slots */ int nFreeSlot; /* Number of unused pcache slots */ int nReserve; /* Try to keep nFreeSlot above this */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */ PgFreeslot *pFree; /* Free page blocks */ int isInit; /* True if initialized */ } pcache1_g; /* ** All code in this file should access the global structure above via the |
︙ | ︙ | |||
32836 32837 32838 32839 32840 32841 32842 32843 32844 32845 32846 32847 32848 32849 | ** enough to contain 'n' buffers of 'sz' bytes each. */ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ if( pcache1.isInit ){ PgFreeslot *p; sz = ROUNDDOWN8(sz); pcache1.szSlot = sz; pcache1.pStart = pBuf; pcache1.pFree = 0; while( n-- ){ p = (PgFreeslot*)pBuf; p->pNext = pcache1.pFree; pcache1.pFree = p; pBuf = (void*)&((char*)pBuf)[sz]; | > > | 33321 33322 33323 33324 33325 33326 33327 33328 33329 33330 33331 33332 33333 33334 33335 33336 | ** enough to contain 'n' buffers of 'sz' bytes each. */ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ if( pcache1.isInit ){ PgFreeslot *p; sz = ROUNDDOWN8(sz); pcache1.szSlot = sz; pcache1.nSlot = pcache1.nFreeSlot = n; pcache1.nReserve = n>90 ? 10 : (n/10 + 1); pcache1.pStart = pBuf; pcache1.pFree = 0; while( n-- ){ p = (PgFreeslot*)pBuf; p->pNext = pcache1.pFree; pcache1.pFree = p; pBuf = (void*)&((char*)pBuf)[sz]; |
︙ | ︙ | |||
32862 32863 32864 32865 32866 32867 32868 32869 32870 32871 32872 32873 32874 32875 | void *p; assert( sqlite3_mutex_held(pcache1.mutex) ); sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); if( nByte<=pcache1.szSlot && pcache1.pFree ){ assert( pcache1.isInit ); p = (PgHdr1 *)pcache1.pFree; pcache1.pFree = pcache1.pFree->pNext; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); }else{ /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the ** global pcache mutex and unlock the pager-cache object pCache. This is ** so that if the attempt to allocate a new buffer causes the the ** configured soft-heap-limit to be breached, it will be possible to | > > | 33349 33350 33351 33352 33353 33354 33355 33356 33357 33358 33359 33360 33361 33362 33363 33364 | void *p; assert( sqlite3_mutex_held(pcache1.mutex) ); sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); if( nByte<=pcache1.szSlot && pcache1.pFree ){ assert( pcache1.isInit ); p = (PgHdr1 *)pcache1.pFree; pcache1.pFree = pcache1.pFree->pNext; pcache1.nFreeSlot--; assert( pcache1.nFreeSlot>=0 ); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); }else{ /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the ** global pcache mutex and unlock the pager-cache object pCache. This is ** so that if the attempt to allocate a new buffer causes the the ** configured soft-heap-limit to be breached, it will be possible to |
︙ | ︙ | |||
32895 32896 32897 32898 32899 32900 32901 32902 32903 32904 32905 32906 32907 32908 32909 32910 32911 32912 32913 32914 32915 32916 32917 32918 | if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; }else{ int iSize; assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); } } /* ** Allocate a new page object initially associated with cache pCache. */ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ int nByte = sizeof(PgHdr1) + pCache->szPage; void *pPg = pcache1Alloc(nByte); PgHdr1 *p; | > > > > > > > > > > > > > > > > > > > > > | 33384 33385 33386 33387 33388 33389 33390 33391 33392 33393 33394 33395 33396 33397 33398 33399 33400 33401 33402 33403 33404 33405 33406 33407 33408 33409 33410 33411 33412 33413 33414 33415 33416 33417 33418 33419 33420 33421 33422 33423 33424 33425 33426 33427 33428 | if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; pcache1.nFreeSlot++; assert( pcache1.nFreeSlot<=pcache1.nSlot ); }else{ int iSize; assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); } } #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* ** Return the size of a pcache allocation */ static int pcache1MemSize(void *p){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( p>=pcache1.pStart && p<pcache1.pEnd ){ return pcache1.szSlot; }else{ int iSize; assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); iSize = sqlite3MallocSize(p); sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); return iSize; } } #endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ /* ** Allocate a new page object initially associated with cache pCache. */ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ int nByte = sizeof(PgHdr1) + pCache->szPage; void *pPg = pcache1Alloc(nByte); PgHdr1 *p; |
︙ | ︙ | |||
32961 32962 32963 32964 32965 32966 32967 32968 32969 32970 32971 32972 32973 32974 | */ SQLITE_PRIVATE void sqlite3PageFree(void *p){ pcache1EnterMutex(); pcache1Free(p); pcache1LeaveMutex(); } /******************************************************************************/ /******** General Implementation Functions ************************************/ /* ** This function is used to resize the hash table used by the cache passed ** as the first argument. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > | 33471 33472 33473 33474 33475 33476 33477 33478 33479 33480 33481 33482 33483 33484 33485 33486 33487 33488 33489 33490 33491 33492 33493 33494 33495 33496 33497 33498 33499 33500 33501 33502 33503 33504 33505 33506 33507 33508 33509 33510 | */ SQLITE_PRIVATE void sqlite3PageFree(void *p){ pcache1EnterMutex(); pcache1Free(p); pcache1LeaveMutex(); } /* ** Return true if it desirable to avoid allocating a new page cache ** entry. ** ** If memory was allocated specifically to the page cache using ** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then ** it is desirable to avoid allocating a new page cache entry because ** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient ** for all page cache needs and we should not need to spill the ** allocation onto the heap. ** ** Or, the heap is used for all page cache memory put the heap is ** under memory pressure, then again it is desirable to avoid ** allocating a new page cache entry in order to avoid stressing ** the heap even further. */ static int pcache1UnderMemoryPressure(PCache1 *pCache){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){ return pcache1.nFreeSlot<pcache1.nReserve; }else{ return sqlite3HeapNearlyFull(); } } /******************************************************************************/ /******** General Implementation Functions ************************************/ /* ** This function is used to resize the hash table used by the cache passed ** as the first argument. ** |
︙ | ︙ | |||
33202 33203 33204 33205 33206 33207 33208 | ** ** 1. Regardless of the value of createFlag, the cache is searched for a ** copy of the requested page. If one is found, it is returned. ** ** 2. If createFlag==0 and the page is not already in the cache, NULL is ** returned. ** | | | > > | > > > | 33738 33739 33740 33741 33742 33743 33744 33745 33746 33747 33748 33749 33750 33751 33752 33753 33754 33755 33756 33757 33758 33759 33760 33761 33762 33763 33764 33765 33766 33767 33768 33769 33770 33771 33772 33773 33774 | ** ** 1. Regardless of the value of createFlag, the cache is searched for a ** copy of the requested page. If one is found, it is returned. ** ** 2. If createFlag==0 and the page is not already in the cache, NULL is ** returned. ** ** 3. If createFlag is 1, and the page is not already in the cache, then ** return NULL (do not allocate a new page) if any of the following ** conditions are true: ** ** (a) the number of pages pinned by the cache is greater than ** PCache1.nMax, or ** ** (b) the number of pages pinned by the cache is greater than ** the sum of nMax for all purgeable caches, less the sum of ** nMin for all other purgeable caches, or ** ** 4. If none of the first three conditions apply and the cache is marked ** as purgeable, and if one of the following is true: ** ** (a) The number of pages allocated for the cache is already ** PCache1.nMax, or ** ** (b) The number of pages allocated for all purgeable caches is ** already equal to or greater than the sum of nMax for all ** purgeable caches, ** ** (c) The system is under memory pressure and wants to avoid ** unnecessary pages cache entry allocations ** ** then attempt to recycle a page from the LRU list. If it is the right ** size, return the recycled buffer. Otherwise, free the buffer and ** proceed to step 5. ** ** 5. Otherwise, allocate and return a new page buffer. */ |
︙ | ︙ | |||
33252 33253 33254 33255 33256 33257 33258 33259 33260 33261 33262 33263 33264 33265 33266 33267 33268 | } /* Step 3 of header comment. */ nPinned = pCache->nPage - pCache->nRecyclable; if( createFlag==1 && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) || nPinned>=(pCache->nMax * 9 / 10) )){ goto fetch_out; } if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){ goto fetch_out; } /* Step 4. Try to recycle a page buffer if appropriate. */ if( pCache->bPurgeable && pcache1.pLruTail && ( | > | > > | 33793 33794 33795 33796 33797 33798 33799 33800 33801 33802 33803 33804 33805 33806 33807 33808 33809 33810 33811 33812 33813 33814 33815 33816 33817 33818 33819 33820 | } /* Step 3 of header comment. */ nPinned = pCache->nPage - pCache->nRecyclable; if( createFlag==1 && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) || nPinned>=(pCache->nMax * 9 / 10) || pcache1UnderMemoryPressure(pCache) )){ goto fetch_out; } if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){ goto fetch_out; } /* Step 4. Try to recycle a page buffer if appropriate. */ if( pCache->bPurgeable && pcache1.pLruTail && ( (pCache->nPage+1>=pCache->nMax) || pcache1.nCurrentPage>=pcache1.nMaxPage || pcache1UnderMemoryPressure(pCache) )){ pPage = pcache1.pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); if( pPage->pCache->szPage!=pCache->szPage ){ pcache1FreePage(pPage); pPage = 0; |
︙ | ︙ | |||
33405 33406 33407 33408 33409 33410 33411 33412 33413 33414 33415 33416 33417 33418 | /* ** Implementation of the sqlite3_pcache.xDestroy method. ** ** Destroy a cache allocated using pcache1Create(). */ static void pcache1Destroy(sqlite3_pcache *p){ PCache1 *pCache = (PCache1 *)p; pcache1EnterMutex(); pcache1TruncateUnsafe(pCache, 0); pcache1.nMaxPage -= pCache->nMax; pcache1.nMinPage -= pCache->nMin; pcache1EnforceMaxPage(); pcache1LeaveMutex(); sqlite3_free(pCache->apHash); | > | 33949 33950 33951 33952 33953 33954 33955 33956 33957 33958 33959 33960 33961 33962 33963 | /* ** Implementation of the sqlite3_pcache.xDestroy method. ** ** Destroy a cache allocated using pcache1Create(). */ static void pcache1Destroy(sqlite3_pcache *p){ PCache1 *pCache = (PCache1 *)p; assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); pcache1EnterMutex(); pcache1TruncateUnsafe(pCache, 0); pcache1.nMaxPage -= pCache->nMax; pcache1.nMinPage -= pCache->nMin; pcache1EnforceMaxPage(); pcache1LeaveMutex(); sqlite3_free(pCache->apHash); |
︙ | ︙ | |||
33452 33453 33454 33455 33456 33457 33458 | ** of bytes of memory released. */ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; if( pcache1.pStart==0 ){ PgHdr1 *p; pcache1EnterMutex(); | | | | 33997 33998 33999 34000 34001 34002 34003 34004 34005 34006 34007 34008 34009 34010 34011 34012 | ** of bytes of memory released. */ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; if( pcache1.pStart==0 ){ PgHdr1 *p; pcache1EnterMutex(); while( (nReq<0 || nFree<nReq) && ((p=pcache1.pLruTail)!=0) ){ nFree += pcache1MemSize(PGHDR1_TO_PAGE(p)); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); } pcache1LeaveMutex(); } return nFree; |
︙ | ︙ | |||
33962 33963 33964 33965 33966 33967 33968 | #ifdef SQLITE_OMIT_WAL # 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 | | | 34507 34508 34509 34510 34511 34512 34513 34514 34515 34516 34517 34518 34519 34520 34521 | #ifdef SQLITE_OMIT_WAL # 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 sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 |
︙ | ︙ | |||
33998 33999 34000 34001 34002 34003 34004 | */ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *); SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut); | | < | | 34543 34544 34545 34546 34547 34548 34549 34550 34551 34552 34553 34554 34555 34556 34557 34558 | */ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *); SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut); /* If the WAL is not empty, return the size of the database. */ SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal); /* Obtain or release the WRITER lock. */ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal); SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal); /* Undo any frames written (but not committed) to the log */ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx); |
︙ | ︙ | |||
34046 34047 34048 34049 34050 34051 34052 | #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ /************** End of wal.h *************************************************/ /************** Continuing where we left off in pager.c **********************/ | | | > > > > | 34590 34591 34592 34593 34594 34595 34596 34597 34598 34599 34600 34601 34602 34603 34604 34605 34606 34607 34608 34609 | #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ /************** End of wal.h *************************************************/ /************** Continuing where we left off in pager.c **********************/ /******************* NOTES ON THE DESIGN OF THE PAGER ************************ ** ** This comment block describes invariants that hold when using a rollback ** journal. These invariants do not apply for journal_mode=WAL, ** journal_mode=MEMORY, or journal_mode=OFF. ** ** Within this comment block, 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 |
︙ | ︙ | |||
34081 34082 34083 34084 34085 34086 34087 | ** ** (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 | | | 34629 34630 34631 34632 34633 34634 34635 34636 34637 34638 34639 34640 34641 34642 34643 | ** ** (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 on 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. |
︙ | ︙ | |||
34112 34113 34114 34115 34116 34117 34118 | ** 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 | | > | > | 34660 34661 34662 34663 34664 34665 34666 34667 34668 34669 34670 34671 34672 34673 34674 34675 34676 34677 34678 34679 34680 34681 34682 34683 34684 34685 34686 34687 34688 34689 | ** 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, thus signaling other connections on the same ** database to flush their caches. ** ** (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 is held on the database file when writing to ** the database file. ** ** (13) A SHARED lock is held on the database file while reading any ** content out of the database file. ** ******************************************************************************/ /* ** Macros for troubleshooting. Normally turned off */ #if 0 int sqlite3PagerTrace=1; /* True to enable tracing */ #define sqlite3DebugPrintf printf |
︙ | ︙ | |||
34150 34151 34152 34153 34154 34155 34156 | ** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file ** struct as its argument. */ #define PAGERID(p) ((int)(p->fd)) #define FILEHANDLEID(fd) ((int)fd) /* | > | | < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > | > > | | > > > > > > > > > > > > > > > > > | > > > | > > > | > > > | > > | > > > > > > > > > > | > > > > | > > > > > > | > > > > | > | > > | > > > > > > > > | | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | < > > > > > > | > | < | < | > > > > | > > > > > > > > > > > > > > | > > > > > | > > > > > | > > > > > > > > > > > | > > > > > > > | | < < < < | 34700 34701 34702 34703 34704 34705 34706 34707 34708 34709 34710 34711 34712 34713 34714 34715 34716 34717 34718 34719 34720 34721 34722 34723 34724 34725 34726 34727 34728 34729 34730 34731 34732 34733 34734 34735 34736 34737 34738 34739 34740 34741 34742 34743 34744 34745 34746 34747 34748 34749 34750 34751 34752 34753 34754 34755 34756 34757 34758 34759 34760 34761 34762 34763 34764 34765 34766 34767 34768 34769 34770 34771 34772 34773 34774 34775 34776 34777 34778 34779 34780 34781 34782 34783 34784 34785 34786 34787 34788 34789 34790 34791 34792 34793 34794 34795 34796 34797 34798 34799 34800 34801 34802 34803 34804 34805 34806 34807 34808 34809 34810 34811 34812 34813 34814 34815 34816 34817 34818 34819 34820 34821 34822 34823 34824 34825 34826 34827 34828 34829 34830 34831 34832 34833 34834 34835 34836 34837 34838 34839 34840 34841 34842 34843 34844 34845 34846 34847 34848 34849 34850 34851 34852 34853 34854 34855 34856 34857 34858 34859 34860 34861 34862 34863 34864 34865 34866 34867 34868 34869 34870 34871 34872 34873 34874 34875 34876 34877 34878 34879 34880 34881 34882 34883 34884 34885 34886 34887 34888 34889 34890 34891 34892 34893 34894 34895 34896 34897 34898 34899 34900 34901 34902 34903 34904 34905 34906 34907 34908 34909 34910 34911 34912 34913 34914 34915 34916 34917 34918 34919 34920 34921 34922 34923 34924 34925 34926 34927 34928 34929 34930 34931 34932 34933 34934 34935 34936 34937 34938 34939 34940 34941 34942 34943 34944 34945 34946 34947 34948 34949 34950 34951 34952 34953 34954 34955 34956 34957 34958 34959 34960 34961 34962 34963 34964 34965 34966 34967 34968 34969 34970 34971 34972 34973 34974 34975 34976 34977 34978 34979 34980 34981 34982 34983 34984 34985 34986 | ** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file ** struct as its argument. */ #define PAGERID(p) ((int)(p->fd)) #define FILEHANDLEID(fd) ((int)fd) /* ** The Pager.eState variable stores the current 'state' of a pager. A ** pager may be in any one of the seven states shown in the following ** state diagram. ** ** OPEN <------+------+ ** | | | ** V | | ** +---------> READER-------+ | ** | | | ** | V | ** |<-------WRITER_LOCKED------> ERROR ** | | ^ ** | V | ** |<------WRITER_CACHEMOD-------->| ** | | | ** | V | ** |<-------WRITER_DBMOD---------->| ** | | | ** | V | ** +<------WRITER_FINISHED-------->+ ** ** ** List of state transitions and the C [function] that performs each: ** ** OPEN -> READER [sqlite3PagerSharedLock] ** READER -> OPEN [pager_unlock] ** ** READER -> WRITER_LOCKED [sqlite3PagerBegin] ** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal] ** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal] ** WRITER_DBMOD -> WRITER_FINISHED [sqlite3PagerCommitPhaseOne] ** WRITER_*** -> READER [pager_end_transaction] ** ** WRITER_*** -> ERROR [pager_error] ** ERROR -> OPEN [pager_unlock] ** ** ** OPEN: ** ** The pager starts up in this state. Nothing is guaranteed in this ** state - the file may or may not be locked and the database size is ** unknown. The database may not be read or written. ** ** * No read or write transaction is active. ** * Any lock, or no lock at all, may be held on the database file. ** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted. ** ** READER: ** ** In this state all the requirements for reading the database in ** rollback (non-WAL) mode are met. Unless the pager is (or recently ** was) in exclusive-locking mode, a user-level read transaction is ** open. The database size is known in this state. ** ** A connection running with locking_mode=normal enters this state when ** it opens a read-transaction on the database and returns to state ** OPEN after the read-transaction is completed. However a connection ** running in locking_mode=exclusive (including temp databases) remains in ** this state even after the read-transaction is closed. The only way ** a locking_mode=exclusive connection can transition from READER to OPEN ** is via the ERROR state (see below). ** ** * A read transaction may be active (but a write-transaction cannot). ** * A SHARED or greater lock is held on the database file. ** * The dbSize variable may be trusted (even if a user-level read ** transaction is not active). The dbOrigSize and dbFileSize variables ** may not be trusted at this point. ** * If the database is a WAL database, then the WAL connection is open. ** * Even if a read-transaction is not open, it is guaranteed that ** there is no hot-journal in the file-system. ** ** WRITER_LOCKED: ** ** The pager moves to this state from READER when a write-transaction ** is first opened on the database. In WRITER_LOCKED state, all locks ** required to start a write-transaction are held, but no actual ** modifications to the cache or database have taken place. ** ** In rollback mode, a RESERVED or (if the transaction was opened with ** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when ** moving to this state, but the journal file is not written to or opened ** to in this state. If the transaction is committed or rolled back while ** in WRITER_LOCKED state, all that is required is to unlock the database ** file. ** ** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file. ** If the connection is running with locking_mode=exclusive, an attempt ** is made to obtain an EXCLUSIVE lock on the database file. ** ** * A write transaction is active. ** * If the connection is open in rollback-mode, a RESERVED or greater ** lock is held on the database file. ** * If the connection is open in WAL-mode, a WAL write transaction ** is open (i.e. sqlite3WalBeginWriteTransaction() has been successfully ** called). ** * The dbSize, dbOrigSize and dbFileSize variables are all valid. ** * The contents of the pager cache have not been modified. ** * The journal file may or may not be open. ** * Nothing (not even the first header) has been written to the journal. ** ** WRITER_CACHEMOD: ** ** A pager moves from WRITER_LOCKED state to this state when a page is ** first modified by the upper layer. In rollback mode the journal file ** is opened (if it is not already open) and a header written to the ** start of it. The database file on disk has not been modified. ** ** * A write transaction is active. ** * A RESERVED or greater lock is held on the database file. ** * The journal file is open and the first header has been written ** to it, but the header has not been synced to disk. ** * The contents of the page cache have been modified. ** ** WRITER_DBMOD: ** ** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state ** when it modifies the contents of the database file. WAL connections ** never enter this state (since they do not modify the database file, ** just the log file). ** ** * A write transaction is active. ** * An EXCLUSIVE or greater lock is held on the database file. ** * The journal file is open and the first header has been written ** and synced to disk. ** * The contents of the page cache have been modified (and possibly ** written to disk). ** ** WRITER_FINISHED: ** ** It is not possible for a WAL connection to enter this state. ** ** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD ** state after the entire transaction has been successfully written into the ** database file. In this state the transaction may be committed simply ** by finalizing the journal file. Once in WRITER_FINISHED state, it is ** not possible to modify the database further. At this point, the upper ** layer must either commit or rollback the transaction. ** ** * A write transaction is active. ** * An EXCLUSIVE or greater lock is held on the database file. ** * All writing and syncing of journal and database data has finished. ** If no error occured, all that remains is to finalize the journal to ** commit the transaction. If an error did occur, the caller will need ** to rollback the transaction. ** ** ERROR: ** ** The ERROR state is entered when an IO or disk-full error (including ** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it ** difficult to be sure that the in-memory pager state (cache contents, ** db size etc.) are consistent with the contents of the file-system. ** ** Temporary pager files may enter the ERROR state, but in-memory pagers ** cannot. ** ** For example, if an IO error occurs while performing a rollback, ** the contents of the page-cache may be left in an inconsistent state. ** At this point it would be dangerous to change back to READER state ** (as usually happens after a rollback). Any subsequent readers might ** report database corruption (due to the inconsistent cache), and if ** they upgrade to writers, they may inadvertently corrupt the database ** file. To avoid this hazard, the pager switches into the ERROR state ** instead of READER following such an error. ** ** Once it has entered the ERROR state, any attempt to use the pager ** to read or write data returns an error. Eventually, once all ** outstanding transactions have been abandoned, the pager is able to ** transition back to OPEN state, discarding the contents of the ** page-cache and any other in-memory state at the same time. Everything ** is reloaded from disk (and, if necessary, hot-journal rollback peformed) ** when a read-transaction is next opened on the pager (transitioning ** the pager into READER state). At that point the system has recovered ** from the error. ** ** Specifically, the pager jumps into the ERROR state if: ** ** 1. An error occurs while attempting a rollback. This happens in ** function sqlite3PagerRollback(). ** ** 2. An error occurs while attempting to finalize a journal file ** following a commit in function sqlite3PagerCommitPhaseTwo(). ** ** 3. An error occurs while attempting to write to the journal or ** database file in function pagerStress() in order to free up ** memory. ** ** In other cases, the error is returned to the b-tree layer. The b-tree ** layer then attempts a rollback operation. If the error condition ** persists, the pager enters the ERROR state via condition (1) above. ** ** Condition (3) is necessary because it can be triggered by a read-only ** statement executed within a transaction. In this case, if the error ** code were simply returned to the user, the b-tree layer would not ** automatically attempt a rollback, as it assumes that an error in a ** read-only statement cannot leave the pager in an internally inconsistent ** state. ** ** * The Pager.errCode variable is set to something other than SQLITE_OK. ** * There are one or more outstanding references to pages (after the ** last reference is dropped the pager should move back to OPEN state). ** * The pager is not an in-memory pager. ** ** ** Notes: ** ** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the ** connection is open in WAL mode. A WAL connection is always in one ** of the first four states. ** ** * Normally, a connection open in exclusive mode is never in PAGER_OPEN ** state. There are two exceptions: immediately after exclusive-mode has ** been turned on (and before any read or write transactions are ** executed), and when the pager is leaving the "error state". ** ** * See also: assert_pager_state(). */ #define PAGER_OPEN 0 #define PAGER_READER 1 #define PAGER_WRITER_LOCKED 2 #define PAGER_WRITER_CACHEMOD 3 #define PAGER_WRITER_DBMOD 4 #define PAGER_WRITER_FINISHED 5 #define PAGER_ERROR 6 /* ** The Pager.eLock variable is almost always set to one of the ** following locking-states, according to the lock currently held on ** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. ** This variable is kept up to date as locks are taken and released by ** the pagerLockDb() and pagerUnlockDb() wrappers. ** ** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY ** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not ** the operation was successful. In these circumstances pagerLockDb() and ** pagerUnlockDb() take a conservative approach - eLock is always updated ** when unlocking the file, and only updated when locking the file if the ** VFS call is successful. This way, the Pager.eLock variable may be set ** to a less exclusive (lower) value than the lock that is actually held ** at the system level, but it is never set to a more exclusive value. ** ** This is usually safe. If an xUnlock fails or appears to fail, there may ** be a few redundant xLock() calls or a lock may be held for longer than ** required, but nothing really goes wrong. ** ** The exception is when the database file is unlocked as the pager moves ** from ERROR to OPEN state. At this point there may be a hot-journal file ** in the file-system that needs to be rolled back (as part of a OPEN->SHARED ** transition, by the same pager or any other). If the call to xUnlock() ** fails at this point and the pager is left holding an EXCLUSIVE lock, this ** can confuse the call to xCheckReservedLock() call made later as part ** of hot-journal detection. ** ** xCheckReservedLock() is defined as returning true "if there is a RESERVED ** lock held by this process or any others". So xCheckReservedLock may ** return true because the caller itself is holding an EXCLUSIVE lock (but ** doesn't know it because of a previous error in xUnlock). If this happens ** a hot-journal may be mistaken for a journal being created by an active ** transaction in another process, causing SQLite to read from the database ** without rolling it back. ** ** To work around this, if a call to xUnlock() fails when unlocking the ** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It ** is only changed back to a real locking state after a successful call ** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition ** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK ** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE ** lock on the database file before attempting to roll it back. See function ** PagerSharedLock() for more detail. ** ** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in ** PAGER_OPEN state. */ #define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) /* ** A macro used for invoking the codec if there is one */ #ifdef SQLITE_HAS_CODEC # define CODEC1(P,D,N,X,E) \ if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; } |
︙ | ︙ | |||
34251 34252 34253 34254 34255 34256 34257 | Pgno iSubRec; /* Index of first record in sub-journal */ #ifndef SQLITE_OMIT_WAL u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ #endif }; /* | | > | < | < < | < < | > > > | | | > | < < < < < < < > > | < < < < < < < < < < < < < < < < < < | | | | | | < < < < | | > > > > | | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < | | | | | < < | | < | < < > | | > > > > < | 35022 35023 35024 35025 35026 35027 35028 35029 35030 35031 35032 35033 35034 35035 35036 35037 35038 35039 35040 35041 35042 35043 35044 35045 35046 35047 35048 35049 35050 35051 35052 35053 35054 35055 35056 35057 35058 35059 35060 35061 35062 35063 35064 35065 35066 35067 35068 35069 35070 35071 35072 35073 35074 35075 35076 35077 35078 35079 35080 35081 35082 35083 35084 35085 35086 35087 35088 35089 35090 35091 35092 35093 35094 35095 35096 35097 35098 35099 35100 35101 35102 35103 35104 35105 35106 35107 35108 35109 35110 35111 35112 35113 35114 35115 35116 35117 35118 35119 35120 35121 35122 35123 35124 35125 35126 35127 35128 35129 35130 35131 35132 35133 35134 35135 35136 35137 35138 35139 35140 35141 35142 35143 35144 35145 35146 35147 35148 35149 35150 35151 35152 35153 35154 35155 35156 35157 35158 35159 35160 35161 35162 35163 35164 35165 35166 35167 35168 35169 35170 35171 35172 35173 35174 35175 35176 35177 35178 35179 35180 35181 35182 35183 35184 35185 35186 35187 35188 35189 35190 35191 35192 35193 35194 35195 35196 35197 35198 35199 35200 35201 35202 35203 35204 35205 35206 35207 35208 35209 35210 35211 35212 35213 35214 35215 35216 35217 35218 35219 35220 35221 35222 35223 35224 35225 35226 35227 35228 35229 35230 35231 35232 35233 35234 35235 35236 35237 35238 35239 35240 35241 35242 35243 35244 35245 35246 35247 35248 35249 35250 35251 35252 35253 35254 35255 35256 35257 35258 35259 35260 35261 35262 35263 | Pgno iSubRec; /* Index of first record in sub-journal */ #ifndef SQLITE_OMIT_WAL u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ #endif }; /* ** A open page cache is an instance of struct Pager. A description of ** some of the more important member variables follows: ** ** eState ** ** The current 'state' of the pager object. See the comment and state ** diagram above for a description of the pager state. ** ** eLock ** ** For a real on-disk database, the current lock held on the database file - ** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. ** ** For a temporary or in-memory database (neither of which require any ** locks), this variable is always set to EXCLUSIVE_LOCK. Since such ** databases always have Pager.exclusiveMode==1, this tricks the pager ** logic into thinking that it already has all the locks it will ever ** need (and no reason to release them). ** ** In some (obscure) circumstances, this variable may also be set to ** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for ** details. ** ** changeCountDone ** ** This boolean variable is used to make sure that the change-counter ** (the 4-byte header field at byte offset 24 of the database file) is ** not updated more often than necessary. ** ** It is set to true when the change-counter field is updated, which ** can only happen if an exclusive lock is held on the database file. ** It is cleared (set to false) whenever an exclusive lock is ** relinquished on the database file. Each time a transaction is committed, ** The changeCountDone flag is inspected. If it is true, the work of ** updating the change-counter is omitted for the current transaction. ** ** This mechanism means that when running in exclusive mode, a connection ** need only update the change-counter once, for the first transaction ** committed. ** ** setMaster ** ** When PagerCommitPhaseOne() is called to commit a transaction, it may ** (or may not) specify a master-journal name to be written into the ** journal file before it is synced to disk. ** ** Whether or not a journal file contains a master-journal pointer affects ** the way in which the journal file is finalized after the transaction is ** committed or rolled back when running in "journal_mode=PERSIST" mode. ** If a journal file does not contain a master-journal pointer, it is ** finalized by overwriting the first journal header with zeroes. If ** it does contain a master-journal pointer the journal file is finalized ** by truncating it to zero bytes, just as if the connection were ** running in "journal_mode=truncate" mode. ** ** Journal files that contain master journal pointers cannot be finalized ** simply by overwriting the first journal-header with zeroes, as the ** master journal pointer could interfere with hot-journal rollback of any ** subsequently interrupted transaction that reuses the journal file. ** ** The flag is cleared as soon as the journal file is finalized (either ** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the ** journal file from being successfully finalized, the setMaster flag ** is cleared anyway (and the pager will move to ERROR state). ** ** doNotSpill, doNotSyncSpill ** ** These two boolean variables control the behaviour of cache-spills ** (calls made by the pcache module to the pagerStress() routine to ** write cached data to the file-system in order to free up memory). ** ** When doNotSpill is non-zero, writing to the database from pagerStress() ** is disabled altogether. This is done in a very obscure case that ** comes up during savepoint rollback that requires the pcache module ** to allocate a new page to prevent the journal file from being written ** while it is being traversed by code in pager_playback(). ** ** If doNotSyncSpill is non-zero, writing to the database from pagerStress() ** is permitted, but syncing the journal file is not. This flag is set ** by sqlite3PagerWrite() when the file-system sector-size is larger than ** the database page-size in order to prevent a journal sync from happening ** in between the journalling of two pages on the same sector. ** ** subjInMemory ** ** This is a boolean variable. If true, then any required sub-journal ** is opened as an in-memory journal file. If false, then in-memory ** sub-journals are only used for in-memory pager files. ** ** This variable is updated by the upper layer each time a new ** write-transaction is opened. ** ** dbSize, dbOrigSize, dbFileSize ** ** Variable dbSize is set to the number of pages in the database file. ** It is valid in PAGER_READER and higher states (all states except for ** OPEN and ERROR). ** ** dbSize is set based on the size of the database file, which may be ** larger than the size of the database (the value stored at offset ** 28 of the database header by the btree). If the size of the file ** is not an integer multiple of the page-size, the value stored in ** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2). ** Except, any file that is greater than 0 bytes in size is considered ** to have at least one page. (i.e. a 1KB file with 2K page-size leads ** to dbSize==1). ** ** During a write-transaction, if pages with page-numbers greater than ** dbSize are modified in the cache, dbSize is updated accordingly. ** Similarly, if the database is truncated using PagerTruncateImage(), ** dbSize is updated. ** ** Variables dbOrigSize and dbFileSize are valid in states ** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize ** variable at the start of the transaction. It is used during rollback, ** and to determine whether or not pages need to be journalled before ** being modified. ** ** Throughout a write-transaction, dbFileSize contains the size of ** the file on disk in pages. It is set to a copy of dbSize when the ** write-transaction is first opened, and updated when VFS calls are made ** to write or truncate the database file on disk. ** ** The only reason the dbFileSize variable is required is to suppress ** unnecessary calls to xTruncate() after committing a transaction. If, ** when a transaction is committed, the dbFileSize variable indicates ** that the database file is larger than the database image (Pager.dbSize), ** pager_truncate() is called. The pager_truncate() call uses xFilesize() ** to measure the database file on disk, and then truncates it if required. ** dbFileSize is not used when rolling back a transaction. In this case ** pager_truncate() is called unconditionally (which means there may be ** a call to xFilesize() that is not strictly required). In either case, ** pager_truncate() may cause the file to become smaller or larger. ** ** dbHintSize ** ** The dbHintSize variable is used to limit the number of calls made to ** the VFS xFileControl(FCNTL_SIZE_HINT) method. ** ** dbHintSize is set to a copy of the dbSize variable when a ** write-transaction is opened (at the same time as dbFileSize and ** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called, ** dbHintSize is increased to the number of pages that correspond to the ** size-hint passed to the method call. See pager_write_pagelist() for ** details. ** ** errCode ** ** The Pager.errCode variable is only ever used in PAGER_ERROR state. It ** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode ** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX ** sub-codes. */ struct Pager { sqlite3_vfs *pVfs; /* OS functions to use for IO */ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ u8 useJournal; /* Use a rollback journal on this file */ u8 noReadlock; /* Do not bother to obtain readlocks */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */ u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ /************************************************************************** ** The following block contains those class members that change during ** routine opertion. Class members not in this block are either fixed ** when the pager is first created or else only change when there is a ** significant mode change (such as changing the page_size, locking_mode, ** or the journal_mode). From another view, these class members describe ** the "state" of the pager, while other class members describe the ** "configuration" of the pager. */ u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */ u8 eLock; /* Current lock held on database file */ u8 changeCountDone; /* Set after incrementing the change-counter */ u8 setMaster; /* True if a m-j name has been written to jrnl */ u8 doNotSpill; /* Do not spill the cache when non-zero */ u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */ u8 subjInMemory; /* True to use in-memory sub-journals */ Pgno dbSize; /* Number of pages in the database */ Pgno dbOrigSize; /* dbSize before the current transaction */ Pgno dbFileSize; /* Number of pages in the database file */ Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ int errCode; /* One of several kinds of errors */ int nRec; /* Pages journalled since last j-header written */ u32 cksumInit; /* Quasi-random value added to every checksum */ u32 nSubRec; /* Number of records written to sub-journal */ Bitvec *pInJournal; /* One bit for each page in the database file */ sqlite3_file *fd; /* File descriptor for database */ sqlite3_file *jfd; /* File descriptor for main journal */ sqlite3_file *sjfd; /* File descriptor for sub-journal */ i64 journalOff; /* Current write offset in the journal file */ i64 journalHdr; /* Byte offset to previous journal header */ sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ PagerSavepoint *aSavepoint; /* Array of active savepoints */ int nSavepoint; /* Number of elements in aSavepoint[] */ char dbFileVers[16]; /* Changes whenever database file changes */ /* ** End of the routinely-changing class members ***************************************************************************/ u16 nExtra; /* Add this many bytes to each in-memory page */ i16 nReserve; /* Number of unused bytes at end of each page */ u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ u32 sectorSize; /* Assumed sector size during rollback */ int pageSize; /* Number of bytes in a page */ Pgno mxPgno; /* Maximum allowed size of the database */ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ #ifdef SQLITE_TEST int nHit, nMiss; /* Cache hits and missing */ int nRead, nWrite; /* Database pages read/written */ #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ #ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ void (*xCodecFree)(void*); /* Destructor for the codec */ void *pCodec; /* First argument to xCodec... methods */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ #ifndef SQLITE_OMIT_WAL Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ char *zWal; /* File name for write-ahead log */ #endif }; /* |
︙ | ︙ | |||
34515 34516 34517 34518 34519 34520 34521 34522 34523 34524 34525 34526 34527 | #endif /* ** The maximum legal page number is (2^31 - 1). */ #define PAGER_MAX_PGNO 2147483647 #ifndef NDEBUG /* ** Usage: ** ** assert( assert_pager_state(pPager) ); */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 35328 35329 35330 35331 35332 35333 35334 35335 35336 35337 35338 35339 35340 35341 35342 35343 35344 35345 35346 35347 35348 35349 35350 35351 35352 35353 35354 35355 35356 35357 35358 35359 35360 35361 35362 35363 35364 35365 35366 35367 35368 35369 35370 35371 35372 35373 35374 35375 35376 35377 35378 35379 35380 35381 35382 35383 35384 35385 35386 35387 35388 35389 35390 35391 35392 35393 35394 35395 35396 35397 35398 35399 35400 35401 35402 35403 35404 35405 35406 35407 35408 35409 35410 35411 35412 35413 35414 35415 35416 35417 35418 35419 35420 35421 35422 35423 35424 35425 35426 35427 35428 35429 35430 35431 35432 35433 35434 35435 35436 35437 35438 35439 35440 35441 35442 35443 35444 35445 35446 35447 35448 35449 35450 35451 35452 35453 35454 35455 35456 35457 35458 35459 35460 35461 35462 35463 35464 35465 35466 35467 35468 35469 35470 35471 35472 35473 35474 35475 35476 35477 35478 35479 35480 35481 35482 35483 35484 35485 35486 35487 35488 35489 35490 35491 35492 35493 35494 35495 35496 35497 35498 35499 35500 35501 35502 35503 35504 35505 35506 35507 35508 35509 35510 35511 35512 35513 35514 35515 35516 35517 35518 35519 35520 35521 35522 35523 35524 35525 35526 35527 35528 35529 35530 35531 35532 35533 35534 35535 35536 35537 35538 35539 35540 35541 35542 35543 35544 35545 35546 35547 35548 35549 35550 35551 35552 35553 35554 35555 35556 | #endif /* ** The maximum legal page number is (2^31 - 1). */ #define PAGER_MAX_PGNO 2147483647 /* ** The argument to this macro is a file descriptor (type sqlite3_file*). ** Return 0 if it is not open, or non-zero (but not 1) if it is. ** ** This is so that expressions can be written as: ** ** if( isOpen(pPager->jfd) ){ ... ** ** instead of ** ** if( pPager->jfd->pMethods ){ ... */ #define isOpen(pFd) ((pFd)->pMethods) /* ** Return true if this pager uses a write-ahead log instead of the usual ** rollback journal. Otherwise false. */ #ifndef SQLITE_OMIT_WAL static int pagerUseWal(Pager *pPager){ return (pPager->pWal!=0); } #else # define pagerUseWal(x) 0 # define pagerRollbackWal(x) 0 # define pagerWalFrames(v,w,x,y,z) 0 # define pagerOpenWalIfPresent(z) SQLITE_OK # define pagerBeginReadTransaction(z) SQLITE_OK #endif #ifndef NDEBUG /* ** Usage: ** ** assert( assert_pager_state(pPager) ); ** ** This function runs many asserts to try to find inconsistencies in ** the internal state of the Pager object. */ static int assert_pager_state(Pager *p){ Pager *pPager = p; /* State must be valid. */ assert( p->eState==PAGER_OPEN || p->eState==PAGER_READER || p->eState==PAGER_WRITER_LOCKED || p->eState==PAGER_WRITER_CACHEMOD || p->eState==PAGER_WRITER_DBMOD || p->eState==PAGER_WRITER_FINISHED || p->eState==PAGER_ERROR ); /* Regardless of the current state, a temp-file connection always behaves ** as if it has an exclusive lock on the database file. It never updates ** the change-counter field, so the changeCountDone flag is always set. */ assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK ); assert( p->tempFile==0 || pPager->changeCountDone ); /* If the useJournal flag is clear, the journal-mode must be "OFF". ** And if the journal-mode is "OFF", the journal file must not be open. */ assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); /* Check that MEMDB implies noSync. And an in-memory journal. Since ** this means an in-memory pager performs no IO at all, it cannot encounter ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing ** a journal file. (although the in-memory journal implementation may ** return SQLITE_IOERR_NOMEM while the journal file is being written). It ** is therefore not possible for an in-memory pager to enter the ERROR ** state. */ if( MEMDB ){ assert( p->noSync ); assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_MEMORY ); assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); assert( pagerUseWal(p)==0 ); } /* If changeCountDone is set, a RESERVED lock or greater must be held ** on the file. */ assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK ); assert( p->eLock!=PENDING_LOCK ); switch( p->eState ){ case PAGER_OPEN: assert( !MEMDB ); assert( pPager->errCode==SQLITE_OK ); assert( sqlite3PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile ); break; case PAGER_READER: assert( pPager->errCode==SQLITE_OK ); assert( p->eLock!=UNKNOWN_LOCK ); assert( p->eLock>=SHARED_LOCK || p->noReadlock ); break; case PAGER_WRITER_LOCKED: assert( p->eLock!=UNKNOWN_LOCK ); assert( pPager->errCode==SQLITE_OK ); if( !pagerUseWal(pPager) ){ assert( p->eLock>=RESERVED_LOCK ); } assert( pPager->dbSize==pPager->dbOrigSize ); assert( pPager->dbOrigSize==pPager->dbFileSize ); assert( pPager->dbOrigSize==pPager->dbHintSize ); assert( pPager->setMaster==0 ); break; case PAGER_WRITER_CACHEMOD: assert( p->eLock!=UNKNOWN_LOCK ); assert( pPager->errCode==SQLITE_OK ); if( !pagerUseWal(pPager) ){ /* It is possible that if journal_mode=wal here that neither the ** journal file nor the WAL file are open. This happens during ** a rollback transaction that switches from journal_mode=off ** to journal_mode=wal. */ assert( p->eLock>=RESERVED_LOCK ); assert( isOpen(p->jfd) || p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_WAL ); } assert( pPager->dbOrigSize==pPager->dbFileSize ); assert( pPager->dbOrigSize==pPager->dbHintSize ); break; case PAGER_WRITER_DBMOD: assert( p->eLock==EXCLUSIVE_LOCK ); assert( pPager->errCode==SQLITE_OK ); assert( !pagerUseWal(pPager) ); assert( p->eLock>=EXCLUSIVE_LOCK ); assert( isOpen(p->jfd) || p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_WAL ); assert( pPager->dbOrigSize<=pPager->dbHintSize ); break; case PAGER_WRITER_FINISHED: assert( p->eLock==EXCLUSIVE_LOCK ); assert( pPager->errCode==SQLITE_OK ); assert( !pagerUseWal(pPager) ); assert( isOpen(p->jfd) || p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_WAL ); break; case PAGER_ERROR: /* There must be at least one outstanding reference to the pager if ** in ERROR state. Otherwise the pager should have already dropped ** back to OPEN state. */ assert( pPager->errCode!=SQLITE_OK ); assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); break; } return 1; } /* ** Return a pointer to a human readable string in a static buffer ** containing the state of the Pager object passed as an argument. This ** is intended to be used within debuggers. For example, as an alternative ** to "print *pPager" in gdb: ** ** (gdb) printf "%s", print_pager_state(pPager) */ static char *print_pager_state(Pager *p){ static char zRet[1024]; sqlite3_snprintf(1024, zRet, "Filename: %s\n" "State: %s errCode=%d\n" "Lock: %s\n" "Locking mode: locking_mode=%s\n" "Journal mode: journal_mode=%s\n" "Backing store: tempFile=%d memDb=%d useJournal=%d\n" "Journal: journalOff=%lld journalHdr=%lld\n" "Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n" , p->zFilename , p->eState==PAGER_OPEN ? "OPEN" : p->eState==PAGER_READER ? "READER" : p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" : p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" : p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" : p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" : p->eState==PAGER_ERROR ? "ERROR" : "?error?" , (int)p->errCode , p->eLock==NO_LOCK ? "NO_LOCK" : p->eLock==RESERVED_LOCK ? "RESERVED" : p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" : p->eLock==SHARED_LOCK ? "SHARED" : p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?" , p->exclusiveMode ? "exclusive" : "normal" , p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" : p->journalMode==PAGER_JOURNALMODE_OFF ? "off" : p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" : p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" : p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" : p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?" , (int)p->tempFile, (int)p->memDb, (int)p->useJournal , p->journalOff, p->journalHdr , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize ); return zRet; } #endif /* ** Return true if it is necessary to write page *pPg into the sub-journal. ** A page needs to be written into the sub-journal if there exists one ** or more open savepoints for which: ** |
︙ | ︙ | |||
34582 34583 34584 34585 34586 34587 34588 34589 34590 34591 34592 34593 34594 34595 34596 34597 34598 34599 34600 | return rc; } /* ** Write a 32-bit integer into a string buffer in big-endian byte order. */ #define put32bits(A,B) sqlite3Put4byte((u8*)A,B) /* ** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK ** on success or an error code is something goes wrong. */ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){ char ac[4]; put32bits(ac, val); return sqlite3OsWrite(fd, ac, 4, offset); } /* | > | | | < | < > | < < > > | > > > > > > > > | > > > > > < > > > > > > > > | < | | > > > > > > > > | | 35594 35595 35596 35597 35598 35599 35600 35601 35602 35603 35604 35605 35606 35607 35608 35609 35610 35611 35612 35613 35614 35615 35616 35617 35618 35619 35620 35621 35622 35623 35624 35625 35626 35627 35628 35629 35630 35631 35632 35633 35634 35635 35636 35637 35638 35639 35640 35641 35642 35643 35644 35645 35646 35647 35648 35649 35650 35651 35652 35653 35654 35655 35656 35657 35658 35659 35660 35661 35662 35663 35664 35665 35666 35667 | return rc; } /* ** Write a 32-bit integer into a string buffer in big-endian byte order. */ #define put32bits(A,B) sqlite3Put4byte((u8*)A,B) /* ** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK ** on success or an error code is something goes wrong. */ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){ char ac[4]; put32bits(ac, val); return sqlite3OsWrite(fd, ac, 4, offset); } /* ** Unlock the database file to level eLock, which must be either NO_LOCK ** or SHARED_LOCK. Regardless of whether or not the call to xUnlock() ** succeeds, set the Pager.eLock variable to match the (attempted) new lock. ** ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it. See the comment above the #define of ** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; assert( !pPager->exclusiveMode ); assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); if( isOpen(pPager->fd) ){ assert( pPager->eLock>=eLock ); rc = sqlite3OsUnlock(pPager->fd, eLock); if( pPager->eLock!=UNKNOWN_LOCK ){ pPager->eLock = (u8)eLock; } IOTRACE(("UNLOCK %p %d\n", pPager, eLock)) } return rc; } /* ** Lock the database file to level eLock, which must be either SHARED_LOCK, ** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the ** Pager.eLock variable to the new locking state. ** ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. ** See the comment above the #define of UNKNOWN_LOCK for an explanation ** of this. */ static int pagerLockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK ); if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){ rc = sqlite3OsLock(pPager->fd, eLock); if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){ pPager->eLock = (u8)eLock; IOTRACE(("LOCK %p %d\n", pPager, eLock)) } } return rc; } /* ** This function determines whether or not the atomic-write optimization ** can be used with this pager. The optimization can be used if: ** ** (a) the value returned by OsDeviceCharacteristics() indicates that |
︙ | ︙ | |||
34690 34691 34692 34693 34694 34695 34696 | ** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES ** is defined, and NDEBUG is not defined, an assert() statement checks ** that the page is either dirty or still matches the calculated page-hash. */ #define CHECK_PAGE(x) checkPage(x) static void checkPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; | | | > | 35729 35730 35731 35732 35733 35734 35735 35736 35737 35738 35739 35740 35741 35742 35743 35744 35745 35746 35747 35748 35749 35750 | ** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES ** is defined, and NDEBUG is not defined, an assert() statement checks ** that the page is either dirty or still matches the calculated page-hash. */ #define CHECK_PAGE(x) checkPage(x) static void checkPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; assert( pPager->eState!=PAGER_ERROR ); assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) ); } #else #define pager_datahash(X,Y) 0 #define pager_pagehash(X) 0 #define pager_set_pagehash(X) #define CHECK_PAGE(x) #endif /* SQLITE_CHECK_PAGES */ /* ** When this is called the journal file for pager pPager must be open. ** This function attempts to read a master journal file name from the ** end of the file and, if successful, copies it into memory supplied |
︙ | ︙ | |||
34863 34864 34865 34866 34867 34868 34869 | ** - 4 bytes: Database page size. ** ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. */ static int writeJournalHdr(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */ | | | 35903 35904 35905 35906 35907 35908 35909 35910 35911 35912 35913 35914 35915 35916 35917 | ** - 4 bytes: Database page size. ** ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. */ static int writeJournalHdr(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */ u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */ u32 nWrite; /* Bytes of header sector written */ int ii; /* Loop counter */ assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ if( nHeader>JOURNAL_HDR_SZ(pPager) ){ nHeader = JOURNAL_HDR_SZ(pPager); |
︙ | ︙ | |||
34906 34907 34908 34909 34910 34911 34912 | ** * When the pager is in no-sync mode. Corruption can follow a ** power failure in this case anyway. ** ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees ** that garbage data is never appended to the journal file. */ assert( isOpen(pPager->fd) || pPager->noSync ); | | | 35946 35947 35948 35949 35950 35951 35952 35953 35954 35955 35956 35957 35958 35959 35960 | ** * When the pager is in no-sync mode. Corruption can follow a ** power failure in this case anyway. ** ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees ** that garbage data is never appended to the journal file. */ assert( isOpen(pPager->fd) || pPager->noSync ); if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY) || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) ){ memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); }else{ memset(zHeader, 0, sizeof(aJournalMagic)+4); } |
︙ | ︙ | |||
35030 35031 35032 35033 35034 35035 35036 | ){ return rc; } if( pPager->journalOff==0 ){ u32 iPageSize; /* Page-size field of journal header */ u32 iSectorSize; /* Sector-size field of journal header */ | < > > > > > > > > | 36070 36071 36072 36073 36074 36075 36076 36077 36078 36079 36080 36081 36082 36083 36084 36085 36086 36087 36088 36089 36090 36091 36092 36093 36094 36095 36096 36097 36098 | ){ return rc; } if( pPager->journalOff==0 ){ u32 iPageSize; /* Page-size field of journal header */ u32 iSectorSize; /* Sector-size field of journal header */ /* Read the page-size and sector-size journal header fields. */ if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize)) || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize)) ){ return rc; } /* Versions of SQLite prior to 3.5.8 set the page-size field of the ** journal header to zero. In this case, assume that the Pager.pageSize ** variable is already set to the correct page size. */ if( iPageSize==0 ){ iPageSize = pPager->pageSize; } /* Check that the values read from the page-size and sector-size fields ** are within range. To be 'in range', both values need to be a power ** of two greater than or equal to 512 or 32, and not greater than their ** respective compile time maximum limits. */ if( iPageSize<512 || iSectorSize<32 |
︙ | ︙ | |||
35060 35061 35062 35063 35064 35065 35066 | return SQLITE_DONE; } /* Update the page-size to match the value read from the journal. ** Use a testcase() macro to make sure that malloc failure within ** PagerSetPagesize() is tested. */ | < | < | 36107 36108 36109 36110 36111 36112 36113 36114 36115 36116 36117 36118 36119 36120 36121 36122 | return SQLITE_DONE; } /* Update the page-size to match the value read from the journal. ** Use a testcase() macro to make sure that malloc failure within ** PagerSetPagesize() is tested. */ rc = sqlite3PagerSetPagesize(pPager, &iPageSize, -1); testcase( rc!=SQLITE_OK ); /* Update the assumed sector-size to match the value used by ** the process that created this journal. If this journal was ** created by a process other than this one, then this routine ** is being called from within pager_playback(). The local value ** of Pager.sectorSize is restored at the end of that routine. */ |
︙ | ︙ | |||
35106 35107 35108 35109 35110 35111 35112 35113 35114 35115 35116 35117 35118 35119 | int rc; /* Return code */ int nMaster; /* Length of string zMaster */ i64 iHdrOff; /* Offset of header in journal file */ i64 jrnlSize; /* Size of journal file on disk */ u32 cksum = 0; /* Checksum of string zMaster */ assert( pPager->setMaster==0 ); if( !zMaster || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_OFF ){ return SQLITE_OK; } pPager->setMaster = 1; | > > | 36151 36152 36153 36154 36155 36156 36157 36158 36159 36160 36161 36162 36163 36164 36165 36166 | int rc; /* Return code */ int nMaster; /* Length of string zMaster */ i64 iHdrOff; /* Offset of header in journal file */ i64 jrnlSize; /* Size of journal file on disk */ u32 cksum = 0; /* Checksum of string zMaster */ assert( pPager->setMaster==0 ); assert( !pagerUseWal(pPager) ); if( !zMaster || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_OFF ){ return SQLITE_OK; } pPager->setMaster = 1; |
︙ | ︙ | |||
35142 35143 35144 35145 35146 35147 35148 | || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster))) || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum))) || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8))) ){ return rc; } pPager->journalOff += (nMaster+20); | < | 36189 36190 36191 36192 36193 36194 36195 36196 36197 36198 36199 36200 36201 36202 | || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster))) || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum))) || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8))) ){ return rc; } pPager->journalOff += (nMaster+20); /* If the pager is in peristent-journal mode, then the physical ** journal-file may extend past the end of the master-journal name ** and 8 bytes of magic data just written to the file. This is ** dangerous because the code to rollback a hot-journal file ** will not be able to find the master-journal name to determine ** whether or not the journal is hot. |
︙ | ︙ | |||
35178 35179 35180 35181 35182 35183 35184 | ** fail, since no attempt to allocate dynamic memory will be made. */ (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p); return p; } /* | | < < < < | | < < | 36224 36225 36226 36227 36228 36229 36230 36231 36232 36233 36234 36235 36236 36237 36238 36239 36240 36241 36242 | ** fail, since no attempt to allocate dynamic memory will be made. */ (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p); return p; } /* ** Discard the entire contents of the in-memory page-cache. */ static void pager_reset(Pager *pPager){ sqlite3BackupRestart(pPager->pBackup); sqlite3PcacheClear(pPager->pPCache); } /* ** Free all structures in the Pager.aSavepoint[] array and set both ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal ** if it is open and the pager is not in exclusive mode. */ |
︙ | ︙ | |||
35231 35232 35233 35234 35235 35236 35237 | assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); } } return rc; } /* | | | < < < < < < < < < < < < | | | > > | | > | < | | > > > > > > > > > > > > > > > | | | | < | < < < < | < | < < < < < | | < | < > | | < < < < < < | < | > > > > > > > > > > > | > > > > > > | > | | | < | < > > | < | > < < < < < < < < < < < < < < < < < < < < < < < < < < > | | | 36271 36272 36273 36274 36275 36276 36277 36278 36279 36280 36281 36282 36283 36284 36285 36286 36287 36288 36289 36290 36291 36292 36293 36294 36295 36296 36297 36298 36299 36300 36301 36302 36303 36304 36305 36306 36307 36308 36309 36310 36311 36312 36313 36314 36315 36316 36317 36318 36319 36320 36321 36322 36323 36324 36325 36326 36327 36328 36329 36330 36331 36332 36333 36334 36335 36336 36337 36338 36339 36340 36341 36342 36343 36344 36345 36346 36347 36348 36349 36350 36351 36352 36353 36354 36355 36356 36357 36358 36359 36360 36361 36362 36363 36364 36365 36366 36367 36368 36369 36370 36371 36372 36373 36374 36375 36376 36377 36378 36379 36380 36381 36382 36383 36384 36385 36386 36387 36388 36389 36390 36391 36392 36393 36394 36395 36396 36397 36398 36399 36400 36401 36402 36403 36404 36405 36406 36407 36408 36409 36410 36411 36412 36413 36414 36415 36416 36417 | assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); } } return rc; } /* ** This function is a no-op if the pager is in exclusive mode and not ** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN ** state. ** ** If the pager is not in exclusive-access mode, the database file is ** completely unlocked. If the file is unlocked and the file-system does ** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is ** closed (if it is open). ** ** If the pager is in ERROR state when this function is called, the ** contents of the pager cache are discarded before switching back to ** the OPEN state. Regardless of whether the pager is in exclusive-mode ** or not, any journal file left in the file-system will be treated ** as a hot-journal and rolled back the next time a read-transaction ** is opened (by this or by any other connection). */ static void pager_unlock(Pager *pPager){ assert( pPager->eState==PAGER_READER || pPager->eState==PAGER_OPEN || pPager->eState==PAGER_ERROR ); sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; releaseAllSavepoints(pPager); if( pagerUseWal(pPager) ){ assert( !isOpen(pPager->jfd) ); sqlite3WalEndReadTransaction(pPager->pWal); pPager->eState = PAGER_OPEN; }else if( !pPager->exclusiveMode ){ int rc; /* Error code returned by pagerUnlockDb() */ int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; /* If the operating system support deletion of open files, then ** close the journal file when dropping the database lock. Otherwise ** another connection with journal_mode=delete might delete the file ** out from under us. */ assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 ); assert( (PAGER_JOURNALMODE_OFF & 5)!=1 ); assert( (PAGER_JOURNALMODE_WAL & 5)!=1 ); assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 ); assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN) || 1!=(pPager->journalMode & 5) ){ sqlite3OsClose(pPager->jfd); } /* If the pager is in the ERROR state and the call to unlock the database ** file fails, set the current lock to UNKNOWN_LOCK. See the comment ** above the #define for UNKNOWN_LOCK for an explanation of why this ** is necessary. */ rc = pagerUnlockDb(pPager, NO_LOCK); if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){ pPager->eLock = UNKNOWN_LOCK; } /* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here ** without clearing the error code. This is intentional - the error ** code is cleared and the cache reset in the block below. */ assert( pPager->errCode || pPager->eState!=PAGER_ERROR ); pPager->changeCountDone = 0; pPager->eState = PAGER_OPEN; } /* If Pager.errCode is set, the contents of the pager cache cannot be ** trusted. Now that there are no outstanding references to the pager, ** it can safely move back to PAGER_OPEN state. This happens in both ** normal and exclusive-locking mode. */ if( pPager->errCode ){ assert( !MEMDB ); pager_reset(pPager); pPager->changeCountDone = pPager->tempFile; pPager->eState = PAGER_OPEN; pPager->errCode = SQLITE_OK; } pPager->journalOff = 0; pPager->journalHdr = 0; pPager->setMaster = 0; } /* ** This function is called whenever an IOERR or FULL error that requires ** the pager to transition into the ERROR state may ahve occurred. ** The first argument is a pointer to the pager structure, the second ** the error-code about to be returned by a pager API function. The ** value returned is a copy of the second argument to this function. ** ** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the ** IOERR sub-codes, the pager enters the ERROR state and the error code ** is stored in Pager.errCode. While the pager remains in the ERROR state, ** all major API calls on the Pager will immediately return Pager.errCode. ** ** The ERROR state indicates that the contents of the pager-cache ** cannot be trusted. This state can be cleared by completely discarding ** the contents of the pager-cache. If a transaction was active when ** the persistent error occurred, then the rollback journal may need ** to be replayed to restore the contents of the database file (as if ** it were a hot-journal). */ static int pager_error(Pager *pPager, int rc){ int rc2 = rc & 0xff; assert( rc==SQLITE_OK || !MEMDB ); assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK || (pPager->errCode & 0xff)==SQLITE_IOERR ); if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ pPager->errCode = rc; pPager->eState = PAGER_ERROR; } return rc; } /* ** This routine ends a transaction. A transaction is usually ended by ** either a COMMIT or a ROLLBACK operation. This routine may be called ** after rollback of a hot-journal, or if an error occurs while opening ** the journal file or writing the very first journal-header of a ** database transaction. ** ** This routine is never called in PAGER_ERROR state. If it is called ** in PAGER_NONE or PAGER_SHARED state and the lock held is less ** exclusive than a RESERVED lock, it is a no-op. ** ** Otherwise, any active savepoints are released. ** ** If the journal file is open, then it is "finalized". Once a journal ** file has been finalized it is not possible to use it to roll back a ** transaction. Nor will it be considered to be a hot-journal by this ** or any other database connection. Exactly how a journal is finalized |
︙ | ︙ | |||
35415 35416 35417 35418 35419 35420 35421 | ** The journal file is closed and deleted using sqlite3OsDelete(). ** ** If the pager is running in exclusive mode, this method of finalizing ** the journal file is never used. Instead, if the journalMode is ** DELETE and the pager is in exclusive mode, the method described under ** journalMode==PERSIST is used instead. ** | | | < < < | < > > > > > > > > > > > > > > | > < > < < < | > | > > > > > > > | > < < < | | | | < > | | > > | < < < > < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36434 36435 36436 36437 36438 36439 36440 36441 36442 36443 36444 36445 36446 36447 36448 36449 36450 36451 36452 36453 36454 36455 36456 36457 36458 36459 36460 36461 36462 36463 36464 36465 36466 36467 36468 36469 36470 36471 36472 36473 36474 36475 36476 36477 36478 36479 36480 36481 36482 36483 36484 36485 36486 36487 36488 36489 36490 36491 36492 36493 36494 36495 36496 36497 36498 36499 36500 36501 36502 36503 36504 36505 36506 36507 36508 36509 36510 36511 36512 36513 36514 36515 36516 36517 36518 36519 36520 36521 36522 36523 36524 36525 36526 36527 36528 36529 36530 36531 36532 36533 36534 36535 36536 36537 36538 36539 36540 36541 36542 36543 36544 36545 36546 36547 36548 36549 36550 36551 36552 36553 36554 36555 36556 36557 36558 36559 36560 36561 36562 36563 36564 36565 36566 36567 36568 36569 36570 36571 36572 36573 36574 36575 36576 36577 36578 36579 36580 36581 36582 36583 36584 36585 36586 36587 36588 36589 | ** The journal file is closed and deleted using sqlite3OsDelete(). ** ** If the pager is running in exclusive mode, this method of finalizing ** the journal file is never used. Instead, if the journalMode is ** DELETE and the pager is in exclusive mode, the method described under ** journalMode==PERSIST is used instead. ** ** After the journal is finalized, the pager moves to PAGER_READER state. ** If running in non-exclusive rollback mode, the lock on the file is ** downgraded to a SHARED_LOCK. ** ** SQLITE_OK is returned if no error occurs. If an error occurs during ** any of the IO operations to finalize the journal file or unlock the ** database then the IO error code is returned to the user. If the ** operation to finalize the journal file fails, then the code still ** tries to unlock the database file if not in exclusive mode. If the ** unlock operation fails as well, then the first error code related ** to the first error encountered (the journal finalization one) is ** returned. */ static int pager_end_transaction(Pager *pPager, int hasMaster){ int rc = SQLITE_OK; /* Error code from journal finalization operation */ int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ /* Do nothing if the pager does not have an open write transaction ** or at least a RESERVED lock. This function may be called when there ** is no write-transaction active but a RESERVED or greater lock is ** held under two circumstances: ** ** 1. After a successful hot-journal rollback, it is called with ** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK. ** ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE ** lock switches back to locking_mode=normal and then executes a ** read-transaction, this function is called with eState==PAGER_READER ** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed. */ assert( assert_pager_state(pPager) ); assert( pPager->eState!=PAGER_ERROR ); if( pPager->eState<PAGER_WRITER_LOCKED && pPager->eLock<RESERVED_LOCK ){ return SQLITE_OK; } releaseAllSavepoints(pPager); assert( isOpen(pPager->jfd) || pPager->pInJournal==0 ); if( isOpen(pPager->jfd) ){ assert( !pagerUseWal(pPager) ); /* Finalize the journal file. */ if( sqlite3IsMemJournal(pPager->jfd) ){ assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); sqlite3OsClose(pPager->jfd); }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ if( pPager->journalOff==0 ){ rc = SQLITE_OK; }else{ rc = sqlite3OsTruncate(pPager->jfd, 0); } pPager->journalOff = 0; }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) ){ rc = zeroJournalHdr(pPager, hasMaster); pPager->journalOff = 0; }else{ /* This branch may be executed with Pager.journalMode==MEMORY if ** a hot-journal was just rolled back. In this case the journal ** file should be closed and deleted. If this connection writes to ** the database file, it will do so using an in-memory journal. */ assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_WAL ); sqlite3OsClose(pPager->jfd); if( !pPager->tempFile ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } } } #ifdef SQLITE_CHECK_PAGES sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){ PgHdr *p = pager_lookup(pPager, 1); if( p ){ p->pageHash = 0; sqlite3PagerUnref(p); } } #endif sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; pPager->nRec = 0; sqlite3PcacheCleanAll(pPager->pPCache); sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); if( pagerUseWal(pPager) ){ /* Drop the WAL write-lock, if any. Also, if the connection was in ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE ** lock held on the database file. */ rc2 = sqlite3WalEndWriteTransaction(pPager->pWal); assert( rc2==SQLITE_OK ); } if( !pPager->exclusiveMode && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) ){ rc2 = pagerUnlockDb(pPager, SHARED_LOCK); pPager->changeCountDone = 0; } pPager->eState = PAGER_READER; pPager->setMaster = 0; return (rc==SQLITE_OK?rc2:rc); } /* ** Execute a rollback if a transaction is active and unlock the ** database file. ** ** If the pager has already entered the ERROR state, do not attempt ** the rollback at this time. Instead, pager_unlock() is called. The ** call to pager_unlock() will discard all in-memory pages, unlock ** the database file and move the pager back to OPEN state. If this ** means that there is a hot-journal left in the file-system, the next ** connection to obtain a shared lock on the pager (which may be this one) ** will roll it back. ** ** If the pager has not already entered the ERROR state, but an IO or ** malloc error occurs during a rollback, then this will itself cause ** the pager to enter the ERROR state. Which will be cleared by the ** call to pager_unlock(), as described above. */ static void pagerUnlockAndRollback(Pager *pPager){ if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){ assert( assert_pager_state(pPager) ); if( pPager->eState>=PAGER_WRITER_LOCKED ){ sqlite3BeginBenignMalloc(); sqlite3PagerRollback(pPager); sqlite3EndBenignMalloc(); }else if( !pPager->exclusiveMode ){ assert( pPager->eState==PAGER_READER ); pager_end_transaction(pPager, 0); } } pager_unlock(pPager); } /* ** Parameter aData must point to a buffer of pPager->pageSize bytes ** of data. Compute and return a checksum based ont the contents of the ** page of data and the current value of pPager->cksumInit. ** ** This is not a real checksum. It is really just the sum of the |
︙ | ︙ | |||
35572 35573 35574 35575 35576 35577 35578 | /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. ** The page begins at offset *pOffset into the file. The *pOffset ** value is increased to the start of the next page in the journal. ** | < | | | 36627 36628 36629 36630 36631 36632 36633 36634 36635 36636 36637 36638 36639 36640 36641 36642 | /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. ** The page begins at offset *pOffset into the file. The *pOffset ** value is increased to the start of the next page in the journal. ** ** The main rollback journal uses checksums - the statement journal does ** not. ** ** If the page number of the page record read from the (sub-)journal file ** is greater than the current value of Pager.dbSize, then playback is ** skipped and SQLITE_OK is returned. ** ** If pDone is not NULL, then it is a record of pages that have already ** been played back. If the page at *pOffset has already been played back |
︙ | ︙ | |||
35627 35628 35629 35630 35631 35632 35633 35634 35635 35636 35637 35638 35639 35640 | assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ aData = pPager->pTmpSpace; assert( aData ); /* Temp storage must have already been allocated */ assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); /* Read the page number and page data from the journal or sub-journal ** file. Return an error code to the caller if an IO error occurs. */ jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; rc = read32bits(jfd, *pOffset, &pgno); if( rc!=SQLITE_OK ) return rc; | > > > > > > > > > > > | 36681 36682 36683 36684 36685 36686 36687 36688 36689 36690 36691 36692 36693 36694 36695 36696 36697 36698 36699 36700 36701 36702 36703 36704 36705 | assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ aData = pPager->pTmpSpace; assert( aData ); /* Temp storage must have already been allocated */ assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction ** or savepoint rollback done at the request of the caller) or this is ** a hot-journal rollback. If it is a hot-journal rollback, the pager ** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback ** only reads from the main journal, not the sub-journal. */ assert( pPager->eState>=PAGER_WRITER_CACHEMOD || (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK) ); assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl ); /* Read the page number and page data from the journal or sub-journal ** file. Return an error code to the caller if an IO error occurs. */ jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; rc = read32bits(jfd, *pOffset, &pgno); if( rc!=SQLITE_OK ) return rc; |
︙ | ︙ | |||
35664 35665 35666 35667 35668 35669 35670 | /* If this page has already been played by before during the current ** rollback, then don't bother to play it back again. */ if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){ return rc; } | < | | | > > > | 36729 36730 36731 36732 36733 36734 36735 36736 36737 36738 36739 36740 36741 36742 36743 36744 36745 36746 36747 36748 36749 36750 36751 36752 36753 36754 36755 36756 36757 36758 36759 36760 36761 36762 36763 36764 36765 36766 | /* If this page has already been played by before during the current ** rollback, then don't bother to play it back again. */ if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){ return rc; } /* When playing back page 1, restore the nReserve setting */ if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){ pPager->nReserve = ((u8*)aData)[20]; pagerReportSize(pPager); } /* If the pager is in CACHEMOD state, then there must be a copy of this ** page in the pager cache. In this case just update the pager cache, ** not the database file. The page is left marked dirty in this case. ** ** An exception to the above rule: If the database is in no-sync mode ** and a page is moved during an incremental vacuum then the page may ** not be in the pager cache. Later: if a malloc() or IO error occurs ** during a Movepage() call, then the page may not be in the cache ** either. So the condition described in the above paragraph is not ** assert()able. ** ** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the ** pager cache if it exists and the main file. The page is then marked ** not dirty. Since this code is only executed in PAGER_OPEN state for ** a hot-journal rollback, it is guaranteed that the page-cache is empty ** if the pager is in OPEN state. ** ** Ticket #1171: The statement journal might contain page content that is ** different from the page content at the start of the transaction. ** This occurs when a page is changed prior to the start of a statement ** then changed again within the statement. When rolling back such a ** statement we must not write to the original database unless we know ** for certain that original page contents are synced into the main rollback |
︙ | ︙ | |||
35711 35712 35713 35714 35715 35716 35717 35718 35719 35720 35721 35722 35723 35724 35725 35726 | */ if( pagerUseWal(pPager) ){ pPg = 0; }else{ pPg = pager_lookup(pPager, pgno); } assert( pPg || !MEMDB ); PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), (isMainJrnl?"main-journal":"sub-journal") )); if( isMainJrnl ){ isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); }else{ isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC)); } | > < | > | 36778 36779 36780 36781 36782 36783 36784 36785 36786 36787 36788 36789 36790 36791 36792 36793 36794 36795 36796 36797 36798 36799 36800 36801 36802 36803 | */ if( pagerUseWal(pPager) ){ pPg = 0; }else{ pPg = pager_lookup(pPager, pgno); } assert( pPg || !MEMDB ); assert( pPager->eState!=PAGER_OPEN || pPg==0 ); PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), (isMainJrnl?"main-journal":"sub-journal") )); if( isMainJrnl ){ isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); }else{ isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC)); } if( isOpen(pPager->fd) && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) && isSynced ){ i64 ofst = (pgno-1)*(i64)pPager->pageSize; testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); assert( !pagerUseWal(pPager) ); rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ |
︙ | ︙ | |||
35797 35798 35799 35800 35801 35802 35803 | ** be written out into the database file before its journal file ** segment is synced. If a crash occurs during or following this, ** database corruption may ensue. */ assert( !pagerUseWal(pPager) ); sqlite3PcacheMakeClean(pPg); } | < | | | 36865 36866 36867 36868 36869 36870 36871 36872 36873 36874 36875 36876 36877 36878 36879 36880 | ** be written out into the database file before its journal file ** segment is synced. If a crash occurs during or following this, ** database corruption may ensue. */ assert( !pagerUseWal(pPager) ); sqlite3PcacheMakeClean(pPg); } pager_set_pagehash(pPg); /* If this was page 1, then restore the value of Pager.dbFileVers. ** Do this before any decoding. */ if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } /* Decode the page just read from disk */ |
︙ | ︙ | |||
35951 35952 35953 35954 35955 35956 35957 | /* ** This function is used to change the actual size of the database ** file in the file-system. This only happens when committing a transaction, ** or rolling back a transaction (including rolling back a hot-journal). ** | | | | | > > > | > > > | 37018 37019 37020 37021 37022 37023 37024 37025 37026 37027 37028 37029 37030 37031 37032 37033 37034 37035 37036 37037 37038 37039 37040 37041 37042 37043 37044 37045 37046 37047 37048 37049 37050 37051 37052 37053 37054 37055 37056 | /* ** This function is used to change the actual size of the database ** file in the file-system. This only happens when committing a transaction, ** or rolling back a transaction (including rolling back a hot-journal). ** ** If the main database file is not open, or the pager is not in either ** DBMOD or OPEN state, this function is a no-op. Otherwise, the size ** of the file is changed to nPage pages (nPage*pPager->pageSize bytes). ** If the file on disk is currently larger than nPage pages, then use the VFS ** xTruncate() method to truncate it. ** ** Or, it might might be the case that the file on disk is smaller than ** nPage pages. Some operating system implementations can get confused if ** you try to truncate a file to some size that is larger than it ** currently is, so detect this case and write a single zero byte to ** the end of the new file instead. ** ** If successful, return SQLITE_OK. If an IO error occurs while modifying ** the database file, return the error code to the caller. */ static int pager_truncate(Pager *pPager, Pgno nPage){ int rc = SQLITE_OK; assert( pPager->eState!=PAGER_ERROR ); assert( pPager->eState!=PAGER_READER ); if( isOpen(pPager->fd) && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ i64 currentSize, newSize; assert( pPager->eLock==EXCLUSIVE_LOCK ); /* TODO: Is it safe to use Pager.dbFileSize here? */ rc = sqlite3OsFileSize(pPager->fd, ¤tSize); newSize = pPager->pageSize*(i64)nPage; if( rc==SQLITE_OK && currentSize!=newSize ){ if( currentSize>newSize ){ rc = sqlite3OsTruncate(pPager->fd, newSize); }else{ |
︙ | ︙ | |||
36093 36094 36095 36096 36097 36098 36099 | int needPagerReset; /* True to reset page prior to first page rollback */ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ assert( isOpen(pPager->jfd) ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); | | | 37166 37167 37168 37169 37170 37171 37172 37173 37174 37175 37176 37177 37178 37179 37180 | int needPagerReset; /* True to reset page prior to first page rollback */ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ assert( isOpen(pPager->jfd) ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); if( rc!=SQLITE_OK ){ goto end_playback; } /* Read the master journal name from the journal, if it is present. ** If a master journal file name is specified, but the file is not ** present on disk, then the journal is not hot and does not need to be ** played back. |
︙ | ︙ | |||
36127 36128 36129 36130 36131 36132 36133 | /* This loop terminates either when a readJournalHdr() or ** pager_playback_one_page() call returns SQLITE_DONE or an IO error ** occurs. */ while( 1 ){ /* Read the next journal header from the journal file. If there are ** not enough bytes left in the journal file for a complete header, or | | | 37200 37201 37202 37203 37204 37205 37206 37207 37208 37209 37210 37211 37212 37213 37214 | /* This loop terminates either when a readJournalHdr() or ** pager_playback_one_page() call returns SQLITE_DONE or an IO error ** occurs. */ while( 1 ){ /* Read the next journal header from the journal file. If there are ** not enough bytes left in the journal file for a complete header, or ** it is corrupted, then a process must have failed while writing it. ** This indicates nothing more needs to be rolled back. */ rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; } |
︙ | ︙ | |||
36241 36242 36243 36244 36245 36246 36247 | pPager->changeCountDone = pPager->tempFile; if( rc==SQLITE_OK ){ zMaster = pPager->pTmpSpace; rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); testcase( rc!=SQLITE_OK ); } | | < < | > | 37314 37315 37316 37317 37318 37319 37320 37321 37322 37323 37324 37325 37326 37327 37328 37329 37330 | pPager->changeCountDone = pPager->tempFile; if( rc==SQLITE_OK ){ zMaster = pPager->pTmpSpace; rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK && !pPager->noSync && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags); } if( rc==SQLITE_OK ){ rc = pager_end_transaction(pPager, zMaster[0]!='\0'); testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK && zMaster[0] && res ){ |
︙ | ︙ | |||
36286 36287 36288 36289 36290 36291 36292 | static int readDbPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ Pgno pgno = pPg->pgno; /* Page number to read */ int rc = SQLITE_OK; /* Return code */ int isInWal = 0; /* True if page is in log file */ int pgsz = pPager->pageSize; /* Number of bytes to read */ | | | 37358 37359 37360 37361 37362 37363 37364 37365 37366 37367 37368 37369 37370 37371 37372 | static int readDbPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ Pgno pgno = pPg->pgno; /* Page number to read */ int rc = SQLITE_OK; /* Return code */ int isInWal = 0; /* True if page is in log file */ int pgsz = pPager->pageSize; /* Number of bytes to read */ assert( pPager->eState>=PAGER_READER && !MEMDB ); assert( isOpen(pPager->fd) ); if( NEVER(!isOpen(pPager->fd)) ){ assert( pPager->tempFile ); memset(pPg->pData, 0, pPager->pageSize); return SQLITE_OK; } |
︙ | ︙ | |||
36433 36434 36435 36436 36437 36438 36439 36440 36441 36442 36443 36444 36445 36446 36447 36448 36449 36450 36451 36452 36453 36454 36455 36456 36457 36458 36459 36460 36461 36462 36463 36464 | ); if( rc==SQLITE_OK && pPager->pBackup ){ PgHdr *p; for(p=pList; p; p=p->pDirty){ sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); } } return rc; } /* ** Begin a read transaction on the WAL. ** ** This routine used to be called "pagerOpenSnapshot()" because it essentially ** makes a snapshot of the database at the current point in time and preserves ** that snapshot for use by the reader in spite of concurrently changes by ** other writers or checkpointers. */ static int pagerBeginReadTransaction(Pager *pPager){ int rc; /* Return code */ int changed = 0; /* True if cache must be reset */ assert( pagerUseWal(pPager) ); /* sqlite3WalEndReadTransaction() was not called for the previous ** transaction in locking_mode=EXCLUSIVE. So call it now. If we ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ sqlite3WalEndReadTransaction(pPager->pWal); rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); | > > > > > > > > > | < < | < | < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < > > > | | | | < < < | 37505 37506 37507 37508 37509 37510 37511 37512 37513 37514 37515 37516 37517 37518 37519 37520 37521 37522 37523 37524 37525 37526 37527 37528 37529 37530 37531 37532 37533 37534 37535 37536 37537 37538 37539 37540 37541 37542 37543 37544 37545 37546 37547 37548 37549 37550 37551 37552 37553 37554 37555 37556 37557 37558 37559 37560 37561 37562 37563 37564 37565 37566 37567 37568 37569 37570 37571 37572 37573 37574 37575 37576 37577 37578 37579 37580 37581 37582 37583 37584 37585 37586 37587 37588 37589 37590 37591 37592 37593 37594 37595 37596 37597 37598 37599 37600 37601 37602 37603 37604 37605 37606 37607 37608 37609 37610 37611 37612 37613 37614 37615 37616 37617 37618 37619 37620 37621 37622 37623 37624 37625 37626 37627 37628 37629 37630 37631 37632 37633 37634 37635 37636 37637 37638 37639 37640 37641 37642 37643 37644 37645 37646 37647 37648 37649 37650 37651 37652 37653 37654 37655 37656 37657 | ); if( rc==SQLITE_OK && pPager->pBackup ){ PgHdr *p; for(p=pList; p; p=p->pDirty){ sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); } } #ifdef SQLITE_CHECK_PAGES { PgHdr *p; for(p=pList; p; p=p->pDirty) pager_set_pagehash(p); } #endif return rc; } /* ** Begin a read transaction on the WAL. ** ** This routine used to be called "pagerOpenSnapshot()" because it essentially ** makes a snapshot of the database at the current point in time and preserves ** that snapshot for use by the reader in spite of concurrently changes by ** other writers or checkpointers. */ static int pagerBeginReadTransaction(Pager *pPager){ int rc; /* Return code */ int changed = 0; /* True if cache must be reset */ assert( pagerUseWal(pPager) ); assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); /* sqlite3WalEndReadTransaction() was not called for the previous ** transaction in locking_mode=EXCLUSIVE. So call it now. If we ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ sqlite3WalEndReadTransaction(pPager->pWal); rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); if( rc!=SQLITE_OK || changed ){ pager_reset(pPager); } return rc; } #endif /* ** This function is called as part of the transition from PAGER_OPEN ** to PAGER_READER state to determine the size of the database file ** in pages (assuming the page size currently stored in Pager.pageSize). ** ** If no error occurs, SQLITE_OK is returned and the size of the database ** in pages is stored in *pnPage. Otherwise, an error code (perhaps ** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified. */ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ Pgno nPage; /* Value to return via *pnPage */ /* Query the WAL sub-system for the database size. The WalDbsize() ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or ** if the database size is not available. The database size is not ** available from the WAL sub-system if the log file is empty or ** contains no valid committed transactions. */ assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock ); nPage = sqlite3WalDbsize(pPager->pWal); /* If the database size was not available from the WAL sub-system, ** determine it based on the size of the database file. If the size ** of the database file is not an integer multiple of the page-size, ** round down to the nearest page. Except, any file larger than 0 ** bytes in size is considered to contain at least one page. */ if( nPage==0 ){ i64 n = 0; /* Size of db file in bytes */ assert( isOpen(pPager->fd) || pPager->tempFile ); if( isOpen(pPager->fd) ){ int rc = sqlite3OsFileSize(pPager->fd, &n); if( rc!=SQLITE_OK ){ return rc; } } nPage = (Pgno)(n / pPager->pageSize); if( nPage==0 && n>0 ){ nPage = 1; } } /* If the current number of pages in the file is greater than the ** configured maximum pager number, increase the allowed limit so ** that the file can be read. */ if( nPage>pPager->mxPgno ){ pPager->mxPgno = (Pgno)nPage; } *pnPage = nPage; return SQLITE_OK; } #ifndef SQLITE_OMIT_WAL /* ** Check if the *-wal file that corresponds to the database opened by pPager ** exists if the database is not empy, or verify that the *-wal file does ** not exist (by deleting it) if the database file is empty. ** ** If the database is not empty and the *-wal file exists, open the pager ** in WAL mode. If the database is empty or if no *-wal file exists and ** if no error occurs, make sure Pager.journalMode is not set to ** PAGER_JOURNALMODE_WAL. ** ** Return SQLITE_OK or an error code. ** ** The caller must hold a SHARED lock on the database file to call this ** function. Because an EXCLUSIVE lock on the db file is required to delete ** a WAL on a none-empty database, this ensures there is no race condition ** between the xAccess() below and an xDelete() being executed by some ** other connection. */ static int pagerOpenWalIfPresent(Pager *pPager){ int rc = SQLITE_OK; assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock ); if( !pPager->tempFile ){ int isWal; /* True if WAL file exists */ Pgno nPage; /* Size of the database file */ rc = pagerPagecount(pPager, &nPage); if( rc ) return rc; if( nPage==0 ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); isWal = 0; }else{ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal ); } if( rc==SQLITE_OK ){ if( isWal ){ testcase( sqlite3PcachePagecount(pPager->pPCache)==0 ); rc = sqlite3PagerOpenWal(pPager, 0); }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ pPager->journalMode = PAGER_JOURNALMODE_DELETE; } } } return rc; } |
︙ | ︙ | |||
36565 36566 36567 36568 36569 36570 36571 | */ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ i64 szJ; /* Effective size of the main journal */ i64 iHdrOff; /* End of first segment of main-journal records */ int rc = SQLITE_OK; /* Return code */ Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */ | | > | 37695 37696 37697 37698 37699 37700 37701 37702 37703 37704 37705 37706 37707 37708 37709 37710 | */ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ i64 szJ; /* Effective size of the main journal */ i64 iHdrOff; /* End of first segment of main-journal records */ int rc = SQLITE_OK; /* Return code */ Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */ assert( pPager->eState!=PAGER_ERROR ); assert( pPager->eState>=PAGER_WRITER_LOCKED ); /* Allocate a bitvec to use to store the set of pages rolled back */ if( pSavepoint ){ pDone = sqlite3BitvecCreate(pSavepoint->nOrig); if( !pDone ){ return SQLITE_NOMEM; } |
︙ | ︙ | |||
36704 36705 36706 36707 36708 36709 36710 | ** and FULL=3. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){ pPager->noSync = (level==1 || pPager->tempFile) ?1:0; pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0; pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL); | < | 37835 37836 37837 37838 37839 37840 37841 37842 37843 37844 37845 37846 37847 37848 | ** and FULL=3. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){ pPager->noSync = (level==1 || pPager->tempFile) ?1:0; pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0; pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL); } #endif /* ** The following global variable is incremented whenever the library ** attempts to open a temporary file. This information is used for ** testing and analysis only. |
︙ | ︙ | |||
36786 36787 36788 36789 36790 36791 36792 | /* ** Change the page size used by the Pager object. The new page size ** is passed in *pPageSize. ** ** If the pager is in the error state when this function is called, it ** is a no-op. The value returned is the error state error code (i.e. | | | 37916 37917 37918 37919 37920 37921 37922 37923 37924 37925 37926 37927 37928 37929 37930 | /* ** Change the page size used by the Pager object. The new page size ** is passed in *pPageSize. ** ** If the pager is in the error state when this function is called, it ** is a no-op. The value returned is the error state error code (i.e. ** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL). ** ** Otherwise, if all of the following are true: ** ** * the new page size (value of *pPageSize) is valid (a power ** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and ** ** * there are no outstanding page references, and |
︙ | ︙ | |||
36810 36811 36812 36813 36814 36815 36816 | ** In all other cases, SQLITE_OK is returned. ** ** If the page size is not changed, either because one of the enumerated ** conditions above is not true, the pager was in error state when this ** function was called, or because the memory allocation attempt failed, ** then *pPageSize is set to the old, retained page size before returning. */ | | | > > > > > > > > > | | | | | | | > > > > > > > | | > > | < | > | | | | | | > | > | 37940 37941 37942 37943 37944 37945 37946 37947 37948 37949 37950 37951 37952 37953 37954 37955 37956 37957 37958 37959 37960 37961 37962 37963 37964 37965 37966 37967 37968 37969 37970 37971 37972 37973 37974 37975 37976 37977 37978 37979 37980 37981 37982 37983 37984 37985 37986 37987 37988 37989 37990 37991 37992 37993 37994 37995 | ** In all other cases, SQLITE_OK is returned. ** ** If the page size is not changed, either because one of the enumerated ** conditions above is not true, the pager was in error state when this ** function was called, or because the memory allocation attempt failed, ** then *pPageSize is set to the old, retained page size before returning. */ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ int rc = SQLITE_OK; /* It is not possible to do a full assert_pager_state() here, as this ** function may be called from within PagerOpen(), before the state ** of the Pager object is internally consistent. ** ** At one point this function returned an error if the pager was in ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that ** there is at least one outstanding page reference, this function ** is a no-op for that case anyhow. */ u32 pageSize = *pPageSize; assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); if( (pPager->memDb==0 || pPager->dbSize==0) && sqlite3PcacheRefCount(pPager->pPCache)==0 && pageSize && pageSize!=(u32)pPager->pageSize ){ char *pNew = NULL; /* New temp space */ i64 nByte = 0; if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ rc = sqlite3OsFileSize(pPager->fd, &nByte); } if( rc==SQLITE_OK ){ pNew = (char *)sqlite3PageMalloc(pageSize); if( !pNew ) rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ pager_reset(pPager); pPager->dbSize = (Pgno)(nByte/pageSize); pPager->pageSize = pageSize; sqlite3PageFree(pPager->pTmpSpace); pPager->pTmpSpace = pNew; sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); } } *pPageSize = pPager->pageSize; if( rc==SQLITE_OK ){ if( nReserve<0 ) nReserve = pPager->nReserve; assert( nReserve>=0 && nReserve<1000 ); pPager->nReserve = (i16)nReserve; pagerReportSize(pPager); } return rc; } |
︙ | ︙ | |||
36860 36861 36862 36863 36864 36865 36866 | ** Attempt to set the maximum database page count if mxPage is positive. ** Make no changes if mxPage is zero or negative. And never reduce the ** maximum page count below the current size of the database. ** ** Regardless of mxPage, return the current maximum page count. */ SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ | < | < | | 38010 38011 38012 38013 38014 38015 38016 38017 38018 38019 38020 38021 38022 38023 38024 38025 38026 38027 38028 | ** Attempt to set the maximum database page count if mxPage is positive. ** Make no changes if mxPage is zero or negative. And never reduce the ** maximum page count below the current size of the database. ** ** Regardless of mxPage, return the current maximum page count. */ SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ if( mxPage>0 ){ pPager->mxPgno = mxPage; } if( pPager->eState!=PAGER_OPEN && pPager->mxPgno<pPager->dbSize ){ pPager->mxPgno = pPager->dbSize; } return pPager->mxPgno; } /* ** The following set of routines are used to disable the simulated ** I/O error mechanism. These routines are used to avoid simulated |
︙ | ︙ | |||
36931 36932 36933 36934 36935 36936 36937 | rc = SQLITE_OK; } } return rc; } /* | > | < > < < < < < < < < < | < | < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < | | | < < < | | | < < < < < | 38079 38080 38081 38082 38083 38084 38085 38086 38087 38088 38089 38090 38091 38092 38093 38094 38095 38096 38097 38098 38099 38100 38101 38102 38103 38104 38105 38106 38107 38108 38109 38110 38111 38112 38113 38114 38115 38116 38117 38118 38119 38120 38121 38122 38123 38124 38125 38126 38127 38128 38129 38130 38131 38132 38133 38134 38135 | rc = SQLITE_OK; } } return rc; } /* ** This function may only be called when a read-transaction is open on ** the pager. It returns the total number of pages in the database. ** ** However, if the file is between 1 and <page-size> bytes in size, then ** this is considered a 1 page file. */ SQLITE_PRIVATE void sqlite3PagerPagecount(Pager *pPager, int *pnPage){ assert( pPager->eState>=PAGER_READER ); assert( pPager->eState!=PAGER_WRITER_FINISHED ); *pnPage = (int)pPager->dbSize; } /* ** Try to obtain a lock of type locktype on the database file. If ** a similar or greater lock is already held, this function is a no-op ** (returning SQLITE_OK immediately). ** ** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke ** the busy callback if the lock is currently not available. Repeat ** until the busy callback returns false or until the attempt to ** obtain the lock succeeds. ** ** Return SQLITE_OK on success and an error code if we cannot obtain ** the lock. If the lock is obtained successfully, set the Pager.state ** variable to locktype before returning. */ static int pager_wait_on_lock(Pager *pPager, int locktype){ int rc; /* Return code */ /* Check that this is either a no-op (because the requested lock is ** already held, or one of the transistions that the busy-handler ** may be invoked during, according to the comment above ** sqlite3PagerSetBusyhandler(). */ assert( (pPager->eLock>=locktype) || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK) || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK) ); do { rc = pagerLockDb(pPager, locktype); }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) ); return rc; } /* ** Function assertTruncateConstraint(pPager) checks that one of the ** following is true for all dirty pages currently in the page-cache: ** |
︙ | ︙ | |||
37087 37088 37089 37090 37091 37092 37093 | /* ** Truncate the in-memory database file image to nPage pages. This ** function does not actually modify the database file on disk. It ** just sets the internal state of the pager object so that the ** truncation will be done when the current transaction is committed. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ | < | | 38166 38167 38168 38169 38170 38171 38172 38173 38174 38175 38176 38177 38178 38179 38180 38181 | /* ** Truncate the in-memory database file image to nPage pages. This ** function does not actually modify the database file on disk. It ** just sets the internal state of the pager object so that the ** truncation will be done when the current transaction is committed. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ assert( pPager->dbSize>=nPage ); assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); pPager->dbSize = nPage; assertTruncateConstraint(pPager); } /* ** This function is called before attempting a hot-journal rollback. It |
︙ | ︙ | |||
37139 37140 37141 37142 37143 37144 37145 | ** to the caller. */ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){ u8 *pTmp = (u8 *)pPager->pTmpSpace; disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); | | < | | | | > > > > > > | | 38217 38218 38219 38220 38221 38222 38223 38224 38225 38226 38227 38228 38229 38230 38231 38232 38233 38234 38235 38236 38237 38238 38239 38240 38241 38242 38243 38244 38245 38246 38247 38248 38249 38250 38251 38252 38253 38254 38255 38256 | ** to the caller. */ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){ u8 *pTmp = (u8 *)pPager->pTmpSpace; disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, pTmp ); pPager->pWal = 0; #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* If it is open, sync the journal file before calling UnlockAndRollback. ** If this is not done, then an unsynced portion of the open journal ** file may be played back into the database. If a power failure occurs ** while this is happening, the database could become corrupt. ** ** If an error occurs while trying to sync the journal, shift the pager ** into the ERROR state. This causes UnlockAndRollback to unlock the ** database and close the journal file without attempting to roll it ** back or finalize it. The next database user will have to do hot-journal ** rollback before accessing the database file. */ if( isOpen(pPager->jfd) ){ pager_error(pPager, pagerSyncHotJournal(pPager)); } pagerUnlockAndRollback(pPager); } sqlite3EndBenignMalloc(); enable_simulated_io_errors(); PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); IOTRACE(("CLOSE %p\n", pPager)) |
︙ | ︙ | |||
37204 37205 37206 37207 37208 37209 37210 | } /* ** Sync the journal. In other words, make sure all the pages that have ** been written to the journal have actually reached the surface of the ** disk and can be restored in the event of a hot-journal rollback. ** | | | | | 38287 38288 38289 38290 38291 38292 38293 38294 38295 38296 38297 38298 38299 38300 38301 38302 38303 | } /* ** Sync the journal. In other words, make sure all the pages that have ** been written to the journal have actually reached the surface of the ** disk and can be restored in the event of a hot-journal rollback. ** ** If the Pager.noSync flag is set, then this function is a no-op. ** Otherwise, the actions required depend on the journal-mode and the ** device characteristics of the the file-system, as follows: ** ** * If the journal file is an in-memory journal file, no action need ** be taken. ** ** * Otherwise, if the device does not support the SAFE_APPEND property, ** then the nRec field of the most recently written journal header ** is updated to contain the number of journal records that have |
︙ | ︙ | |||
37230 37231 37232 37233 37234 37235 37236 | ** if( NOT SAFE_APPEND ){ ** if( <full-sync mode> ) xSync(<journal file>); ** <update nRec field> ** } ** if( NOT SEQUENTIAL ) xSync(<journal file>); ** } ** | < < < | > > > > > > > > > > > | | < | 38313 38314 38315 38316 38317 38318 38319 38320 38321 38322 38323 38324 38325 38326 38327 38328 38329 38330 38331 38332 38333 38334 38335 38336 38337 38338 38339 38340 38341 38342 38343 38344 38345 | ** if( NOT SAFE_APPEND ){ ** if( <full-sync mode> ) xSync(<journal file>); ** <update nRec field> ** } ** if( NOT SEQUENTIAL ) xSync(<journal file>); ** } ** ** If successful, this routine clears the PGHDR_NEED_SYNC flag of every ** page currently held in memory before returning SQLITE_OK. If an IO ** error is encountered, then the IO error code is returned to the caller. */ static int syncJournal(Pager *pPager, int newHdr){ int rc; /* Return code */ assert( pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); assert( !pagerUseWal(pPager) ); rc = sqlite3PagerExclusiveLock(pPager); if( rc!=SQLITE_OK ) return rc; if( !pPager->noSync ){ assert( !pPager->tempFile ); if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); assert( isOpen(pPager->jfd) ); if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ /* This block deals with an obscure problem. If the last connection ** that wrote to this database was operating in persistent-journal ** mode, then the journal file may at this point actually be larger |
︙ | ︙ | |||
37316 37317 37318 37319 37320 37321 37322 | PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) ); if( rc!=SQLITE_OK ) return rc; } | | | | < < | | > > > | < | | > > > > > > > > | 38406 38407 38408 38409 38410 38411 38412 38413 38414 38415 38416 38417 38418 38419 38420 38421 38422 38423 38424 38425 38426 38427 38428 38429 38430 38431 38432 38433 38434 38435 38436 38437 38438 | PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) ); if( rc!=SQLITE_OK ) return rc; } pPager->journalHdr = pPager->journalOff; if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ pPager->nRec = 0; rc = writeJournalHdr(pPager); if( rc!=SQLITE_OK ) return rc; } }else{ pPager->journalHdr = pPager->journalOff; } } /* Unless the pager is in noSync mode, the journal file was just ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on ** all pages. */ sqlite3PcacheClearSyncFlags(pPager->pPCache); pPager->eState = PAGER_WRITER_DBMOD; assert( assert_pager_state(pPager) ); return SQLITE_OK; } /* ** The argument is the first in a linked list of dirty pages connected ** by the PgHdr.pDirty pointer. This function writes each one of the ** in-memory pages in the list to the database file. The argument may |
︙ | ︙ | |||
37363 37364 37365 37366 37367 37368 37369 | ** the database file. ** ** If everything is successful, SQLITE_OK is returned. If an IO error ** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot ** be obtained, SQLITE_BUSY is returned. */ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ | | | < < < < < < < < < < < < < < < | | | > > > | 38461 38462 38463 38464 38465 38466 38467 38468 38469 38470 38471 38472 38473 38474 38475 38476 38477 38478 38479 38480 38481 38482 38483 38484 38485 38486 38487 38488 38489 38490 38491 38492 38493 38494 38495 38496 38497 38498 38499 38500 38501 38502 38503 38504 38505 38506 38507 38508 38509 38510 38511 38512 38513 38514 38515 38516 | ** the database file. ** ** If everything is successful, SQLITE_OK is returned. If an IO error ** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot ** be obtained, SQLITE_BUSY is returned. */ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ int rc = SQLITE_OK; /* Return code */ /* This function is only called for rollback pagers in WRITER_DBMOD state. */ assert( !pagerUseWal(pPager) ); assert( pPager->eState==PAGER_WRITER_DBMOD ); assert( pPager->eLock==EXCLUSIVE_LOCK ); /* If the file is a temp-file has not yet been opened, open it now. It ** is not possible for rc to be other than SQLITE_OK if this branch ** is taken, as pager_wait_on_lock() is a no-op for temp-files. */ if( !isOpen(pPager->fd) ){ assert( pPager->tempFile && rc==SQLITE_OK ); rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); } /* Before the first write, give the VFS a hint of what the final ** file size will be. */ assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){ sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize; sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); pPager->dbHintSize = pPager->dbSize; } while( rc==SQLITE_OK && pList ){ Pgno pgno = pList->pgno; /* If there are dirty pages in the page cache with page numbers greater ** than Pager.dbSize, this means sqlite3PagerTruncateImage() was called to ** make the file smaller (presumably by auto-vacuum code). Do not write ** any such pages to the file. ** ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag ** set (set by sqlite3PagerDontWrite()). */ if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ char *pData; /* Data to write */ assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); /* Encode the database */ CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData); /* Write out the page data. */ rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); |
︙ | ︙ | |||
37446 37447 37448 37449 37450 37451 37452 | PAGERID(pPager), pgno, pager_pagehash(pList))); IOTRACE(("PGOUT %p %d\n", pPager, pgno)); PAGER_INCR(sqlite3_pager_writedb_count); PAGER_INCR(pPager->nWrite); }else{ PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); } | < | < | 38532 38533 38534 38535 38536 38537 38538 38539 38540 38541 38542 38543 38544 38545 38546 | PAGERID(pPager), pgno, pager_pagehash(pList))); IOTRACE(("PGOUT %p %d\n", pPager, pgno)); PAGER_INCR(sqlite3_pager_writedb_count); PAGER_INCR(pPager->nWrite); }else{ PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); } pager_set_pagehash(pList); pList = pList->pDirty; } return rc; } /* |
︙ | ︙ | |||
37560 37561 37562 37563 37564 37565 37566 | ** journal (and adding a new header) is not allowed. This occurs ** during calls to sqlite3PagerWrite() while trying to journal multiple ** pages belonging to the same sector. ** ** The doNotSpill flag inhibits all cache spilling regardless of whether ** or not a sync is required. This is set during a rollback. ** | | > > > > > | | | < < < < | | < < | 38644 38645 38646 38647 38648 38649 38650 38651 38652 38653 38654 38655 38656 38657 38658 38659 38660 38661 38662 38663 38664 38665 38666 38667 38668 38669 38670 38671 38672 38673 38674 38675 38676 38677 38678 38679 38680 38681 38682 38683 38684 38685 38686 | ** journal (and adding a new header) is not allowed. This occurs ** during calls to sqlite3PagerWrite() while trying to journal multiple ** pages belonging to the same sector. ** ** The doNotSpill flag inhibits all cache spilling regardless of whether ** or not a sync is required. This is set during a rollback. ** ** Spilling is also prohibited when in an error state since that could ** lead to database corruption. In the current implementaton it ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1 ** while in the error state, hence it is impossible for this routine to ** be called in the error state. Nevertheless, we include a NEVER() ** test for the error state as a safeguard against future changes. */ if( NEVER(pPager->errCode) ) return SQLITE_OK; if( pPager->doNotSpill ) return SQLITE_OK; if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){ return SQLITE_OK; } pPg->pDirty = 0; if( pagerUseWal(pPager) ){ /* Write a single frame for this page to the log. */ if( subjRequiresPage(pPg) ){ rc = subjournalPage(pPg); } if( rc==SQLITE_OK ){ rc = pagerWalFrames(pPager, pPg, 0, 0, 0); } }else{ /* Sync the journal file if required. */ if( pPg->flags&PGHDR_NEED_SYNC || pPager->eState==PAGER_WRITER_CACHEMOD ){ rc = syncJournal(pPager, 1); } /* If the page number of this page is larger than the current size of ** the database image, it may need to be written to the sub-journal. ** This is because the call to pager_write_pagelist() below will not ** actually write data to the file in this case. ** |
︙ | ︙ | |||
37627 37628 37629 37630 37631 37632 37633 37634 37635 37636 37637 37638 37639 37640 37641 37642 37643 | rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) ) ){ rc = subjournalPage(pPg); } /* Write the contents of the page out to the database file. */ if( rc==SQLITE_OK ){ rc = pager_write_pagelist(pPager, pPg); } } /* Mark the page as clean. */ if( rc==SQLITE_OK ){ PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); sqlite3PcacheMakeClean(pPg); } | > | | 38710 38711 38712 38713 38714 38715 38716 38717 38718 38719 38720 38721 38722 38723 38724 38725 38726 38727 38728 38729 38730 38731 38732 38733 38734 38735 | rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) ) ){ rc = subjournalPage(pPg); } /* Write the contents of the page out to the database file. */ if( rc==SQLITE_OK ){ assert( (pPg->flags&PGHDR_NEED_SYNC)==0 ); rc = pager_write_pagelist(pPager, pPg); } } /* Mark the page as clean. */ if( rc==SQLITE_OK ){ PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); sqlite3PcacheMakeClean(pPg); } return pager_error(pPager, rc); } /* ** Allocate and initialize a new Pager object and put a pointer to it ** in *ppPager. The pager should eventually be freed by passing it ** to sqlite3PagerClose(). |
︙ | ︙ | |||
37692 37693 37694 37695 37696 37697 37698 | int readOnly = 0; /* True if this is a read-only file */ int journalFileSize; /* Bytes to allocate for each journal fd */ char *zPathname = 0; /* Full path to database file */ int nPathname = 0; /* Number of bytes in zPathname */ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ | | > > > > > > > < < < < < < < | | < < | 38776 38777 38778 38779 38780 38781 38782 38783 38784 38785 38786 38787 38788 38789 38790 38791 38792 38793 38794 38795 38796 38797 38798 38799 38800 38801 38802 38803 38804 38805 38806 38807 38808 38809 38810 38811 38812 38813 38814 38815 38816 38817 38818 38819 38820 38821 38822 38823 38824 38825 38826 38827 | int readOnly = 0; /* True if this is a read-only file */ int journalFileSize; /* Bytes to allocate for each journal fd */ char *zPathname = 0; /* Full path to database file */ int nPathname = 0; /* Number of bytes in zPathname */ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ /* Figure out how much space is required for each journal file-handle ** (there are two of them, the main journal and the sub-journal). This ** is the maximum space required for an in-memory journal file handle ** and a regular journal file-handle. Note that a "regular journal-handle" ** may be a wrapper capable of caching the first portion of the journal ** file in memory to implement the atomic-write optimization (see ** source file journal.c). */ if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){ journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); }else{ journalFileSize = ROUND8(sqlite3MemJournalSize()); } /* Set the output variable to NULL in case an error occurs. */ *ppPager = 0; #ifndef SQLITE_OMIT_MEMORYDB if( flags & PAGER_MEMORY ){ memDb = 1; zFilename = 0; } #endif /* Compute and store the full pathname in an allocated buffer pointed ** to by zPathname, length nPathname. Or, if this is a temporary file, ** leave both nPathname and zPathname set to 0. */ if( zFilename && zFilename[0] ){ nPathname = pVfs->mxPathname+1; zPathname = sqlite3Malloc(nPathname*2); if( zPathname==0 ){ return SQLITE_NOMEM; } zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); nPathname = sqlite3Strlen30(zPathname); if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ /* This branch is taken when the journal path required by ** the database being opened will be more than pVfs->mxPathname ** bytes in length. This means the database cannot be opened, ** as it will not be possible to open the journal file or even ** check for a hot-journal before reading. |
︙ | ︙ | |||
37786 37787 37788 37789 37790 37791 37792 37793 37794 37795 37796 | pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); pPager->zFilename = (char*)(pPtr += journalFileSize); assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); | > < < < < | | | < | > | | 38868 38869 38870 38871 38872 38873 38874 38875 38876 38877 38878 38879 38880 38881 38882 38883 38884 38885 38886 38887 38888 38889 38890 38891 38892 38893 38894 38895 38896 38897 38898 38899 38900 38901 38902 38903 38904 38905 38906 38907 38908 38909 38910 38911 38912 38913 38914 38915 38916 38917 38918 38919 38920 | pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); pPager->zFilename = (char*)(pPtr += journalFileSize); assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ assert( nPathname>0 ); pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); #ifndef SQLITE_OMIT_WAL pPager->zWal = &pPager->zJournal[nPathname+8+1]; memcpy(pPager->zWal, zPathname, nPathname); memcpy(&pPager->zWal[nPathname], "-wal", 4); #endif sqlite3_free(zPathname); } pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; /* Open the pager file. */ if( zFilename && zFilename[0] ){ int fout = 0; /* VFS flags returned by xOpen() */ rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); assert( !memDb ); readOnly = (fout&SQLITE_OPEN_READONLY); /* If the file was successfully opened for read/write access, ** choose a default page size in case we have to create the ** database file. The default page size is the maximum of: ** ** + SQLITE_DEFAULT_PAGE_SIZE, ** + The value returned by sqlite3OsSectorSize() ** + The largest page size that can be written atomically. */ if( rc==SQLITE_OK && !readOnly ){ setSectorSize(pPager); assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE); if( szPageDflt<pPager->sectorSize ){ if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; }else{ szPageDflt = (u32)pPager->sectorSize; } } #ifdef SQLITE_ENABLE_ATOMIC_WRITE { int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); int ii; assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); |
︙ | ︙ | |||
37855 37856 37857 37858 37859 37860 37861 | ** opening the file until the first call to OsWrite(). ** ** This branch is also run for an in-memory database. An in-memory ** database is the same as a temp-file that is never written out to ** disk and uses an in-memory rollback journal. */ tempFile = 1; | | > | 38934 38935 38936 38937 38938 38939 38940 38941 38942 38943 38944 38945 38946 38947 38948 38949 | ** opening the file until the first call to OsWrite(). ** ** This branch is also run for an in-memory database. An in-memory ** database is the same as a temp-file that is never written out to ** disk and uses an in-memory rollback journal. */ tempFile = 1; pPager->eState = PAGER_READER; pPager->eLock = EXCLUSIVE_LOCK; readOnly = (vfsFlags&SQLITE_OPEN_READONLY); } /* The following call to PagerSetPagesize() serves to set the value of ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer. */ if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
37892 37893 37894 37895 37896 37897 37898 | IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) pPager->useJournal = (u8)useJournal; pPager->noReadlock = (noReadlock && readOnly) ?1:0; /* pPager->stmtOpen = 0; */ /* pPager->stmtInUse = 0; */ /* pPager->nRef = 0; */ | < > > < | 38972 38973 38974 38975 38976 38977 38978 38979 38980 38981 38982 38983 38984 38985 38986 38987 38988 38989 38990 38991 38992 38993 38994 38995 38996 38997 38998 38999 39000 39001 39002 | IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) pPager->useJournal = (u8)useJournal; pPager->noReadlock = (noReadlock && readOnly) ?1:0; /* pPager->stmtOpen = 0; */ /* pPager->stmtInUse = 0; */ /* pPager->nRef = 0; */ /* pPager->stmtSize = 0; */ /* pPager->stmtJSize = 0; */ /* pPager->nPage = 0; */ pPager->mxPgno = SQLITE_MAX_PAGE_COUNT; /* pPager->state = PAGER_UNLOCK; */ #if 0 assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) ); #endif /* pPager->errMask = 0; */ pPager->tempFile = (u8)tempFile; assert( tempFile==PAGER_LOCKINGMODE_NORMAL || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE ); assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 ); pPager->exclusiveMode = (u8)tempFile; pPager->changeCountDone = pPager->tempFile; pPager->memDb = (u8)memDb; pPager->readOnly = (u8)readOnly; assert( useJournal || pPager->tempFile ); pPager->noSync = pPager->tempFile; pPager->fullSync = pPager->noSync ?0:1; pPager->sync_flags = SQLITE_SYNC_NORMAL; /* pPager->pFirst = 0; */ /* pPager->pFirstSynced = 0; */ /* pPager->pLast = 0; */ |
︙ | ︙ | |||
37973 37974 37975 37976 37977 37978 37979 | */ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3_vfs * const pVfs = pPager->pVfs; int rc = SQLITE_OK; /* Return code */ int exists = 1; /* True if a journal file is present */ int jrnlOpen = !!isOpen(pPager->jfd); | < | > | | | | | | 39053 39054 39055 39056 39057 39058 39059 39060 39061 39062 39063 39064 39065 39066 39067 39068 39069 39070 39071 39072 39073 39074 39075 39076 39077 39078 39079 39080 39081 39082 39083 39084 39085 39086 39087 39088 39089 39090 39091 39092 39093 39094 39095 39096 39097 39098 39099 39100 39101 39102 39103 39104 39105 39106 | */ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3_vfs * const pVfs = pPager->pVfs; int rc = SQLITE_OK; /* Return code */ int exists = 1; /* True if a journal file is present */ int jrnlOpen = !!isOpen(pPager->jfd); assert( pPager->useJournal ); assert( isOpen(pPager->fd) ); assert( pPager->eState==PAGER_OPEN ); assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN )); *pExists = 0; if( !jrnlOpen ){ rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); } if( rc==SQLITE_OK && exists ){ int locked = 0; /* True if some process holds a RESERVED lock */ /* Race condition here: Another process might have been holding the ** the RESERVED lock and have a journal open at the sqlite3OsAccess() ** call above, but then delete the journal and drop the lock before ** we get to the following sqlite3OsCheckReservedLock() call. If that ** is the case, this routine might think there is a hot journal when ** in fact there is none. This results in a false-positive which will ** be dealt with by the playback routine. Ticket #3883. */ rc = sqlite3OsCheckReservedLock(pPager->fd, &locked); if( rc==SQLITE_OK && !locked ){ Pgno nPage; /* Number of pages in database file */ /* Check the size of the database file. If it consists of 0 pages, ** then delete the journal file. See the header comment above for ** the reasoning here. Delete the obsolete journal file under ** a RESERVED lock to avoid race conditions and to avoid violating ** [H33020]. */ rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ if( nPage==0 ){ sqlite3BeginBenignMalloc(); if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); pagerUnlockDb(pPager, SHARED_LOCK); } sqlite3EndBenignMalloc(); }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is ** at least one non-zero bytes at the start of the journal file. ** If there is, then we consider this journal to be hot. If not, |
︙ | ︙ | |||
38065 38066 38067 38068 38069 38070 38071 | ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerAcquire() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** ** The following operations are also performed by this function. ** | | < < < < < | | | < | > > > > > < < < < | | < | < < < < < < < < < > | | < < < | < > < < | | > | | | < | > > > < | | < | | < | | | | | | > | > > > > > > | | > | < | | | | | | | | | < < < < < < < | | < < < < < < < < < < < < < > > > > > > | > > > > > > > > > > > > > > > > | | | | > | | > | > | < < | | < < > > > > > > > > > > > > | > > > | < < | 39145 39146 39147 39148 39149 39150 39151 39152 39153 39154 39155 39156 39157 39158 39159 39160 39161 39162 39163 39164 39165 39166 39167 39168 39169 39170 39171 39172 39173 39174 39175 39176 39177 39178 39179 39180 39181 39182 39183 39184 39185 39186 39187 39188 39189 39190 39191 39192 39193 39194 39195 39196 39197 39198 39199 39200 39201 39202 39203 39204 39205 39206 39207 39208 39209 39210 39211 39212 39213 39214 39215 39216 39217 39218 39219 39220 39221 39222 39223 39224 39225 39226 39227 39228 39229 39230 39231 39232 39233 39234 39235 39236 39237 39238 39239 39240 39241 39242 39243 39244 39245 39246 39247 39248 39249 39250 39251 39252 39253 39254 39255 39256 39257 39258 39259 39260 39261 39262 39263 39264 39265 39266 39267 39268 39269 39270 39271 39272 39273 39274 39275 39276 39277 39278 39279 39280 39281 39282 39283 39284 39285 39286 39287 39288 39289 39290 39291 39292 39293 39294 39295 39296 39297 39298 39299 39300 39301 39302 39303 39304 39305 39306 39307 39308 39309 39310 39311 39312 39313 39314 39315 39316 39317 39318 39319 39320 39321 39322 39323 39324 39325 39326 39327 39328 39329 39330 39331 39332 39333 39334 39335 39336 39337 39338 39339 39340 39341 39342 39343 39344 39345 39346 39347 39348 39349 39350 39351 39352 39353 39354 39355 39356 39357 39358 39359 39360 39361 39362 39363 39364 39365 39366 39367 39368 39369 39370 39371 39372 39373 39374 39375 39376 39377 39378 39379 39380 39381 39382 39383 39384 39385 39386 39387 39388 39389 39390 | ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerAcquire() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** ** The following operations are also performed by this function. ** ** 1) If the pager is currently in PAGER_OPEN state (no lock held ** on the database file), then an attempt is made to obtain a ** SHARED lock on the database file. Immediately after obtaining ** the SHARED lock, the file-system is checked for a hot-journal, ** which is played back if present. Following any hot-journal ** rollback, the contents of the cache are validated by checking ** the 'change-counter' field of the database file header and ** discarded if they are found to be invalid. ** ** 2) If the pager is running in exclusive-mode, and there are currently ** no outstanding references to any pages, and is in the error state, ** then an attempt is made to clear the error state by discarding ** the contents of the page cache and rolling back any open journal ** file. ** ** If everything is successful, SQLITE_OK is returned. If an IO error ** occurs while locking the database, checking for a hot-journal file or ** rolling back a journal file, the IO error code is returned. */ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ /* This routine is only called from b-tree and only when there are no ** outstanding pages. This implies that the pager state should either ** be OPEN or READER. READER is only possible if the pager is or was in ** exclusive access mode. */ assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); assert( assert_pager_state(pPager) ); assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; } if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){ int bHotJournal = 1; /* True if there exists a hot journal-file */ assert( !MEMDB ); assert( pPager->noReadlock==0 || pPager->readOnly ); if( pPager->noReadlock==0 ){ rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); goto failed; } } /* If a journal file exists, and there is no RESERVED lock on the ** database file, then it either needs to be played back or deleted. */ if( pPager->eLock<=SHARED_LOCK ){ rc = hasHotJournal(pPager, &bHotJournal); } if( rc!=SQLITE_OK ){ goto failed; } if( bHotJournal ){ /* Get an EXCLUSIVE lock on the database file. At this point it is ** important that a RESERVED lock is not obtained on the way to the ** EXCLUSIVE lock. If it were, another process might open the ** database file, detect the RESERVED lock, and conclude that the ** database is safe to read while this process is still rolling the ** hot-journal back. ** ** Because the intermediate RESERVED lock is not requested, any ** other process attempting to access the database file will get to ** this point in the code and fail to obtain its own EXCLUSIVE lock ** on the database file. ** ** Unless the pager is in locking_mode=exclusive mode, the lock is ** downgraded to SHARED_LOCK before this function returns. */ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ goto failed; } /* If it is not already open and the file exists on disk, open the ** journal for read/write access. Write access is required because ** in exclusive-access mode the file descriptor will be kept open ** and possibly used for a transaction later on. Also, write-access ** is usually required to finalize the journal in journal_mode=persist ** mode (and also for journal_mode=truncate on some systems). ** ** If the journal does not exist, it usually means that some ** other connection managed to get in and roll it back before ** this connection obtained the exclusive lock above. Or, it ** may mean that the pager was in the error-state when this ** function was called and the journal file does not exist. */ if( !isOpen(pPager->jfd) ){ sqlite3_vfs * const pVfs = pPager->pVfs; int bExists; /* True if journal file exists */ rc = sqlite3OsAccess( pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists); if( rc==SQLITE_OK && bExists ){ int fout = 0; int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; assert( !pPager->tempFile ); rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){ rc = SQLITE_CANTOPEN_BKPT; sqlite3OsClose(pPager->jfd); } } } /* Playback and delete the journal. Drop the database write ** lock and reacquire the read lock. Purge the cache before ** playing back the hot-journal so that we don't end up with ** an inconsistent cache. Sync the hot journal before playing ** it back since the process that crashed and left the hot journal ** probably did not sync it and we are required to always sync ** the journal before playing it back. */ if( isOpen(pPager->jfd) ){ assert( rc==SQLITE_OK ); rc = pagerSyncHotJournal(pPager); if( rc==SQLITE_OK ){ rc = pager_playback(pPager, 1); pPager->eState = PAGER_OPEN; } }else if( !pPager->exclusiveMode ){ pagerUnlockDb(pPager, SHARED_LOCK); } if( rc!=SQLITE_OK ){ /* This branch is taken if an error occurs while trying to open ** or roll back a hot-journal while holding an EXCLUSIVE lock. The ** pager_unlock() routine will be called before returning to unlock ** the file. If the unlock attempt fails, then Pager.eLock must be ** set to UNKNOWN_LOCK (see the comment above the #define for ** UNKNOWN_LOCK above for an explanation). ** ** In order to get pager_unlock() to do this, set Pager.eState to ** PAGER_ERROR now. This is not actually counted as a transition ** to ERROR state in the state diagram at the top of this file, ** since we know that the same call to pager_unlock() will very ** shortly transition the pager object to the OPEN state. Calling ** assert_pager_state() would fail now, as it should not be possible ** to be in ERROR state when there are zero outstanding page ** references. */ pager_error(pPager, rc); goto failed; } assert( pPager->eState==PAGER_OPEN ); assert( (pPager->eLock==SHARED_LOCK) || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) ); } if( !pPager->tempFile && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0) ){ /* The shared-lock has just been acquired on the database file ** and there are already pages in the cache (from a previous ** read or write transaction). Check to see if the database ** has been modified. If the database has changed, flush the ** cache. ** ** Database changes is detected by looking at 15 bytes beginning ** at offset 24 into the file. The first 4 of these 16 bytes are ** a 32-bit counter that is incremented with each change. The ** other bytes change randomly with each file change when ** a codec is in use. ** ** There is a vanishingly small chance that a change will not be ** detected. The chance of an undetected change is so small that ** it can be neglected. */ Pgno nPage = 0; char dbFileVers[sizeof(pPager->dbFileVers)]; rc = pagerPagecount(pPager, &nPage); if( rc ) goto failed; if( nPage>0 ){ IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); if( rc!=SQLITE_OK ){ goto failed; } }else{ memset(dbFileVers, 0, sizeof(dbFileVers)); } if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); } } /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); #ifndef SQLITE_OMIT_WAL assert( pPager->pWal==0 || rc==SQLITE_OK ); #endif } if( pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); } if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ rc = pagerPagecount(pPager, &pPager->dbSize); } failed: if( rc!=SQLITE_OK ){ assert( !MEMDB ); pager_unlock(pPager); assert( pPager->eState==PAGER_OPEN ); }else{ pPager->eState = PAGER_READER; } return rc; } /* ** If the reference count has reached zero, rollback any active ** transaction and unlock the pager. ** ** Except, in locking_mode=EXCLUSIVE when there is nothing to in ** the rollback journal, the unlock is not performed and there is ** nothing to rollback, so this routine is a no-op. */ static void pagerUnlockIfUnused(Pager *pPager){ if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){ pagerUnlockAndRollback(pPager); } } /* ** Acquire a reference to page number pgno in pager pPager (a page ** reference has type DbPage*). If the requested reference is |
︙ | ︙ | |||
38358 38359 38360 38361 38362 38363 38364 38365 | Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int noContent /* Do not bother reading content from disk if true */ ){ int rc; PgHdr *pPg; assert( assert_pager_state(pPager) ); | > < | | 39440 39441 39442 39443 39444 39445 39446 39447 39448 39449 39450 39451 39452 39453 39454 39455 39456 39457 39458 39459 39460 39461 39462 39463 | Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int noContent /* Do not bother reading content from disk if true */ ){ int rc; PgHdr *pPg; assert( pPager->eState>=PAGER_READER ); assert( assert_pager_state(pPager) ); if( pgno==0 ){ return SQLITE_CORRUPT_BKPT; } /* If the pager is in the error state, return an error immediately. ** Otherwise, request the page from the PCache layer. */ if( pPager->errCode!=SQLITE_OK ){ rc = pPager->errCode; }else{ rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage); } if( rc!=SQLITE_OK ){ /* Either the call to sqlite3PcacheFetch() returned an error or the |
︙ | ︙ | |||
38393 38394 38395 38396 38397 38398 38399 | assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); PAGER_INCR(pPager->nHit); return SQLITE_OK; }else{ /* The pager cache has created a new page. Its content needs to ** be initialized. */ | < < < < < < | | 39475 39476 39477 39478 39479 39480 39481 39482 39483 39484 39485 39486 39487 39488 39489 39490 39491 39492 39493 39494 39495 39496 39497 39498 39499 39500 39501 | assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); PAGER_INCR(pPager->nHit); return SQLITE_OK; }else{ /* The pager cache has created a new page. Its content needs to ** be initialized. */ PAGER_INCR(pPager->nMiss); pPg = *ppPage; pPg->pPager = pPager; /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page ** number greater than this, or the unused locking-page, is requested. */ if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ rc = SQLITE_CORRUPT_BKPT; goto pager_acquire_err; } if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){ if( pgno>pPager->mxPgno ){ rc = SQLITE_FULL; goto pager_acquire_err; } if( noContent ){ /* Failure to set the bits in the InJournal bit-vectors is benign. ** It merely means that we might do some extra work to journal a |
︙ | ︙ | |||
38441 38442 38443 38444 38445 38446 38447 | }else{ assert( pPg->pPager==pPager ); rc = readDbPage(pPg); if( rc!=SQLITE_OK ){ goto pager_acquire_err; } } | < | < | < < | | 39517 39518 39519 39520 39521 39522 39523 39524 39525 39526 39527 39528 39529 39530 39531 39532 39533 39534 39535 39536 39537 39538 39539 39540 39541 39542 39543 39544 39545 39546 39547 39548 39549 39550 39551 39552 39553 39554 39555 39556 39557 39558 39559 39560 39561 39562 39563 | }else{ assert( pPg->pPager==pPager ); rc = readDbPage(pPg); if( rc!=SQLITE_OK ){ goto pager_acquire_err; } } pager_set_pagehash(pPg); } return SQLITE_OK; pager_acquire_err: assert( rc!=SQLITE_OK ); if( pPg ){ sqlite3PcacheDrop(pPg); } pagerUnlockIfUnused(pPager); *ppPage = 0; return rc; } /* ** Acquire a page if it is already in the in-memory cache. Do ** not read the page from disk. Return a pointer to the page, ** or 0 if the page is not in cache. ** ** See also sqlite3PagerGet(). The difference between this routine ** and sqlite3PagerGet() is that _get() will go to the disk and read ** in the page if the page is not already in cache. This routine ** returns NULL if the page is not in cache or if a disk I/O error ** has ever happened. */ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ PgHdr *pPg = 0; assert( pPager!=0 ); assert( pgno!=0 ); assert( pPager->pPCache!=0 ); assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR ); sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); return pPg; } /* ** Release a page reference. ** |
︙ | ︙ | |||
38522 38523 38524 38525 38526 38527 38528 | ** ** Return SQLITE_OK if everything is successful. Otherwise, return ** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or ** an IO error code if opening or writing the journal file fails. */ static int pager_open_journal(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ | < < | | | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < | | | | | > > > > > < < < < < < < < | > > | < | < < < < < < < < < < < < < < < | | < < | | | | | > > | | > | > | | > > > > > | < < < < < < < < | > > > | | > | > > > > | < > | > > > > > > > > > > > > | > | < < < < < < < < < < < < < < < < < < < < | | | > | > > > > > > > > > > | | | < < < | | | | < < < < < < < < < < < | < < < < < < < | < < | 39594 39595 39596 39597 39598 39599 39600 39601 39602 39603 39604 39605 39606 39607 39608 39609 39610 39611 39612 39613 39614 39615 39616 39617 39618 39619 39620 39621 39622 39623 39624 39625 39626 39627 39628 39629 39630 39631 39632 39633 39634 39635 39636 39637 39638 39639 39640 39641 39642 39643 39644 39645 39646 39647 39648 39649 39650 39651 39652 39653 39654 39655 39656 39657 39658 39659 39660 39661 39662 39663 39664 39665 39666 39667 39668 39669 39670 39671 39672 39673 39674 39675 39676 39677 39678 39679 39680 39681 39682 39683 39684 39685 39686 39687 39688 39689 39690 39691 39692 39693 39694 39695 39696 39697 39698 39699 39700 39701 39702 39703 39704 39705 39706 39707 39708 39709 39710 39711 39712 39713 39714 39715 39716 39717 39718 39719 39720 39721 39722 39723 39724 39725 39726 39727 39728 39729 39730 39731 39732 39733 39734 39735 39736 39737 39738 39739 39740 39741 39742 39743 39744 39745 39746 39747 39748 39749 39750 39751 39752 39753 39754 39755 39756 39757 39758 39759 39760 39761 39762 39763 39764 39765 39766 39767 39768 39769 39770 39771 39772 39773 39774 39775 39776 39777 39778 39779 39780 39781 39782 39783 39784 39785 39786 39787 39788 39789 39790 39791 39792 39793 39794 39795 39796 39797 39798 39799 39800 39801 39802 39803 39804 39805 39806 39807 39808 39809 39810 39811 39812 39813 39814 39815 39816 39817 39818 39819 39820 39821 39822 39823 39824 39825 39826 39827 39828 39829 39830 39831 39832 39833 39834 39835 39836 39837 39838 39839 39840 39841 39842 39843 39844 39845 39846 39847 39848 39849 39850 39851 39852 39853 39854 39855 39856 39857 39858 39859 39860 39861 39862 39863 39864 39865 39866 39867 39868 39869 39870 39871 39872 39873 39874 39875 39876 39877 39878 39879 39880 39881 39882 39883 39884 39885 39886 | ** ** Return SQLITE_OK if everything is successful. Otherwise, return ** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or ** an IO error code if opening or writing the journal file fails. */ static int pager_open_journal(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ assert( pPager->eState==PAGER_WRITER_LOCKED ); assert( assert_pager_state(pPager) ); assert( pPager->pInJournal==0 ); /* If already in the error state, this function is a no-op. But on ** the other hand, this routine is never called if we are already in ** an error state. */ if( NEVER(pPager->errCode) ) return pPager->errCode; if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize); if( pPager->pInJournal==0 ){ return SQLITE_NOMEM; } /* Open the journal file if it is not already open. */ if( !isOpen(pPager->jfd) ){ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ sqlite3MemJournalOpen(pPager->jfd); }else{ const int flags = /* VFS flags to open journal file */ SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| (pPager->tempFile ? (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): (SQLITE_OPEN_MAIN_JOURNAL) ); #ifdef SQLITE_ENABLE_ATOMIC_WRITE rc = sqlite3JournalOpen( pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) ); #else rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); #endif } assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); } /* Write the first journal header to the journal file and open ** the sub-journal if necessary. */ if( rc==SQLITE_OK ){ /* TODO: Check if all of these are really required. */ pPager->nRec = 0; pPager->journalOff = 0; pPager->setMaster = 0; pPager->journalHdr = 0; rc = writeJournalHdr(pPager); } } if( rc!=SQLITE_OK ){ sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; }else{ assert( pPager->eState==PAGER_WRITER_LOCKED ); pPager->eState = PAGER_WRITER_CACHEMOD; } return rc; } /* ** Begin a write-transaction on the specified pager object. If a ** write-transaction has already been opened, this function is a no-op. ** ** If the exFlag argument is false, then acquire at least a RESERVED ** lock on the database file. If exFlag is true, then acquire at least ** an EXCLUSIVE lock. If such a lock is already held, no locking ** functions need be called. ** ** If the subjInMemory argument is non-zero, then any sub-journal opened ** within this transaction will be opened as an in-memory file. This ** has no effect if the sub-journal is already opened (as it may be when ** running in exclusive mode) or if the transaction does not require a ** sub-journal. If the subjInMemory argument is zero, then any required ** sub-journal is implemented in-memory if pPager is an in-memory database, ** or using a temporary file otherwise. */ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; if( pPager->errCode ) return pPager->errCode; assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR ); pPager->subjInMemory = (u8)subjInMemory; if( ALWAYS(pPager->eState==PAGER_READER) ){ assert( pPager->pInJournal==0 ); if( pagerUseWal(pPager) ){ /* If the pager is configured to use locking_mode=exclusive, and an ** exclusive lock on the database is not already held, obtain it now. */ if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ return rc; } sqlite3WalExclusiveMode(pPager->pWal, 1); } /* Grab the write lock on the log file. If successful, upgrade to ** PAGER_RESERVED state. Otherwise, return an error code to the caller. ** The busy-handler is not invoked if another connection already ** holds the write-lock. If possible, the upper layer will call it. */ rc = sqlite3WalBeginWriteTransaction(pPager->pWal); }else{ /* Obtain a RESERVED lock on the database file. If the exFlag parameter ** is true, then immediately upgrade this to an EXCLUSIVE lock. The ** busy-handler callback can be used when upgrading to the EXCLUSIVE ** lock, but not when obtaining the RESERVED lock. */ rc = pagerLockDb(pPager, RESERVED_LOCK); if( rc==SQLITE_OK && exFlag ){ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); } } if( rc==SQLITE_OK ){ /* Change to WRITER_LOCKED state. ** ** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD ** when it has an open transaction, but never to DBMOD or FINISHED. ** This is because in those states the code to roll back savepoint ** transactions may copy data from the sub-journal into the database ** file as well as into the page cache. Which would be incorrect in ** WAL mode. */ pPager->eState = PAGER_WRITER_LOCKED; pPager->dbHintSize = pPager->dbSize; pPager->dbFileSize = pPager->dbSize; pPager->dbOrigSize = pPager->dbSize; pPager->journalOff = 0; } assert( rc==SQLITE_OK || pPager->eState==PAGER_READER ); assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED ); assert( assert_pager_state(pPager) ); } PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); return rc; } /* ** Mark a single data page as writeable. The page is written into the ** main journal or sub-journal as required. If the page is written into ** one of the journals, the corresponding bit is set in the ** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs ** of any open savepoints as appropriate. */ static int pager_write(PgHdr *pPg){ void *pData = pPg->pData; Pager *pPager = pPg->pPager; int rc = SQLITE_OK; /* This routine is not called unless a write-transaction has already ** been started. The journal file may or may not be open at this point. ** It is never called in the ERROR state. */ assert( pPager->eState==PAGER_WRITER_LOCKED || pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); /* If an error has been previously detected, report the same error ** again. This should not happen, but the check provides robustness. */ if( NEVER(pPager->errCode) ) return pPager->errCode; /* Higher-level routines never call this function if database is not ** writable. But check anyway, just for robustness. */ if( NEVER(pPager->readOnly) ) return SQLITE_PERM; CHECK_PAGE(pPg); /* The journal file needs to be opened. Higher level routines have already ** obtained the necessary locks to begin the write-transaction, but the ** rollback journal might not yet be open. Open it now if this is the case. ** ** This is done before calling sqlite3PcacheMakeDirty() on the page. ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then ** an error might occur and the pager would end up in WRITER_LOCKED state ** with pages marked as dirty in the cache. */ if( pPager->eState==PAGER_WRITER_LOCKED ){ rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); assert( assert_pager_state(pPager) ); /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ sqlite3PcacheMakeDirty(pPg); if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ assert( !pagerUseWal(pPager) ); }else{ /* The transaction journal now exists and we have a RESERVED or an ** EXCLUSIVE lock on the main database file. Write the current page to ** the transaction journal if it is not there already. */ if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){ assert( pagerUseWal(pPager)==0 ); if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){ u32 cksum; char *pData2; i64 iOff = pPager->journalOff; /* We should never write to the journal file the page that ** contains the database locks. The following assert verifies ** that we do not. */ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); assert( pPager->journalHdr<=pPager->journalOff ); CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); cksum = pager_cksum(pPager, (u8*)pData2); /* Even if an IO or diskfull error occurs while journalling the ** page in the block above, set the need-sync flag for the page. ** Otherwise, when the transaction is rolled back, the logic in ** playback_one_page() will think that the page needs to be restored ** in the database file. And if an IO error occurs while doing so, ** then corruption may follow. */ pPg->flags |= PGHDR_NEED_SYNC; rc = write32bits(pPager->jfd, iOff, pPg->pgno); if( rc!=SQLITE_OK ) return rc; rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4); if( rc!=SQLITE_OK ) return rc; rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum); if( rc!=SQLITE_OK ) return rc; IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, pPager->journalOff, pPager->pageSize)); PAGER_INCR(sqlite3_pager_writej_count); PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n", PAGERID(pPager), pPg->pgno, ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg))); pPager->journalOff += 8 + pPager->pageSize; pPager->nRec++; assert( pPager->pInJournal!=0 ); rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno); testcase( rc==SQLITE_NOMEM ); assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); rc |= addToSavepointBitvecs(pPager, pPg->pgno); if( rc!=SQLITE_OK ){ assert( rc==SQLITE_NOMEM ); return rc; } }else{ if( pPager->eState!=PAGER_WRITER_DBMOD ){ pPg->flags |= PGHDR_NEED_SYNC; } PAGERTRACE(("APPEND %d page %d needSync=%d\n", PAGERID(pPager), pPg->pgno, ((pPg->flags&PGHDR_NEED_SYNC)?1:0))); } } /* If the statement journal is open and the page is not in it, ** then write the current page to the statement journal. Note that ** the statement journal format differs from the standard journal format ** in that it omits the checksums and the header. */ if( subjRequiresPage(pPg) ){ rc = subjournalPage(pPg); } } /* Update the database size and return. */ if( pPager->dbSize<pPg->pgno ){ pPager->dbSize = pPg->pgno; } return rc; } /* |
︙ | ︙ | |||
38864 38865 38866 38867 38868 38869 38870 38871 38872 38873 38874 38875 38876 38877 | SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){ int rc = SQLITE_OK; PgHdr *pPg = pDbPage; Pager *pPager = pPg->pPager; Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); if( nPagePerSector>1 ){ Pgno nPageCount; /* Total number of pages in database file */ Pgno pg1; /* First page of the sector pPg is located on. */ int nPage = 0; /* Number of pages starting at pg1 to journal */ int ii; /* Loop counter */ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ | > > > > | 39900 39901 39902 39903 39904 39905 39906 39907 39908 39909 39910 39911 39912 39913 39914 39915 39916 39917 | SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){ int rc = SQLITE_OK; PgHdr *pPg = pDbPage; Pager *pPager = pPg->pPager; Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); assert( pPager->eState>=PAGER_WRITER_LOCKED ); assert( pPager->eState!=PAGER_ERROR ); assert( assert_pager_state(pPager) ); if( nPagePerSector>1 ){ Pgno nPageCount; /* Total number of pages in database file */ Pgno pg1; /* First page of the sector pPg is located on. */ int nPage = 0; /* Number of pages starting at pg1 to journal */ int ii; /* Loop counter */ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ |
︙ | ︙ | |||
38885 38886 38887 38888 38889 38890 38891 | /* This trick assumes that both the page-size and sector-size are ** an integer power of 2. It sets variable pg1 to the identifier ** of the first page of the sector pPg is located on. */ pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; | | < | | | | | | | | | | < < | < | 39925 39926 39927 39928 39929 39930 39931 39932 39933 39934 39935 39936 39937 39938 39939 39940 39941 39942 39943 39944 39945 39946 39947 39948 39949 39950 39951 39952 39953 39954 39955 39956 39957 39958 39959 39960 39961 39962 39963 39964 39965 39966 39967 39968 39969 39970 39971 39972 39973 39974 39975 39976 39977 39978 39979 39980 39981 39982 39983 39984 39985 39986 39987 | /* This trick assumes that both the page-size and sector-size are ** an integer power of 2. It sets variable pg1 to the identifier ** of the first page of the sector pPg is located on. */ pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; nPageCount = pPager->dbSize; if( pPg->pgno>nPageCount ){ nPage = (pPg->pgno - pg1)+1; }else if( (pg1+nPagePerSector-1)>nPageCount ){ nPage = nPageCount+1-pg1; }else{ nPage = nPagePerSector; } assert(nPage>0); assert(pg1<=pPg->pgno); assert((pg1+nPage)>pPg->pgno); for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){ Pgno pg = pg1+ii; PgHdr *pPage; if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){ if( pg!=PAGER_MJ_PGNO(pPager) ){ rc = sqlite3PagerGet(pPager, pg, &pPage); if( rc==SQLITE_OK ){ rc = pager_write(pPage); if( pPage->flags&PGHDR_NEED_SYNC ){ needSync = 1; } sqlite3PagerUnref(pPage); } } }else if( (pPage = pager_lookup(pPager, pg))!=0 ){ if( pPage->flags&PGHDR_NEED_SYNC ){ needSync = 1; } sqlite3PagerUnref(pPage); } } /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages ** starting at pg1, then it needs to be set for all of them. Because ** writing to any of these nPage pages may damage the others, the ** journal file must contain sync()ed copies of all of them ** before any of them can be written out to the database file. */ if( rc==SQLITE_OK && needSync ){ assert( !MEMDB ); for(ii=0; ii<nPage; ii++){ PgHdr *pPage = pager_lookup(pPager, pg1+ii); if( pPage ){ pPage->flags |= PGHDR_NEED_SYNC; sqlite3PagerUnref(pPage); } } } assert( pPager->doNotSyncSpill==1 ); pPager->doNotSyncSpill--; }else{ rc = pager_write(pDbPage); } |
︙ | ︙ | |||
38979 38980 38981 38982 38983 38984 38985 | */ SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){ Pager *pPager = pPg->pPager; if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) pPg->flags |= PGHDR_DONT_WRITE; | < | < | 40015 40016 40017 40018 40019 40020 40021 40022 40023 40024 40025 40026 40027 40028 40029 | */ SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){ Pager *pPager = pPg->pPager; if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) pPg->flags |= PGHDR_DONT_WRITE; pager_set_pagehash(pPg); } } /* ** This routine is called to increment the value of the database file ** change-counter, stored as a 4-byte big-endian integer starting at ** byte offset 24 of the pager file. |
︙ | ︙ | |||
39003 39004 39005 39006 39007 39008 39009 39010 39011 39012 39013 39014 39015 39016 39017 39018 39019 39020 39021 39022 39023 39024 39025 39026 39027 39028 | ** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, ** if isDirect is non-zero, then the database file is updated directly ** by writing an updated version of page 1 using a call to the ** sqlite3OsWrite() function. */ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ int rc = SQLITE_OK; /* Declare and initialize constant integer 'isDirect'. If the ** atomic-write optimization is enabled in this build, then isDirect ** is initialized to the value passed as the isDirectMode parameter ** to this function. Otherwise, it is always set to zero. ** ** The idea is that if the atomic-write optimization is not ** enabled at compile time, the compiler can omit the tests of ** 'isDirect' below, as well as the block enclosed in the ** "if( isDirect )" condition. */ #ifndef SQLITE_ENABLE_ATOMIC_WRITE # define DIRECT_MODE 0 assert( isDirectMode==0 ); UNUSED_PARAMETER(isDirectMode); #else # define DIRECT_MODE isDirectMode #endif | > > > > > < | 40037 40038 40039 40040 40041 40042 40043 40044 40045 40046 40047 40048 40049 40050 40051 40052 40053 40054 40055 40056 40057 40058 40059 40060 40061 40062 40063 40064 40065 40066 40067 40068 40069 40070 40071 40072 40073 40074 | ** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, ** if isDirect is non-zero, then the database file is updated directly ** by writing an updated version of page 1 using a call to the ** sqlite3OsWrite() function. */ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ int rc = SQLITE_OK; assert( pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); /* Declare and initialize constant integer 'isDirect'. If the ** atomic-write optimization is enabled in this build, then isDirect ** is initialized to the value passed as the isDirectMode parameter ** to this function. Otherwise, it is always set to zero. ** ** The idea is that if the atomic-write optimization is not ** enabled at compile time, the compiler can omit the tests of ** 'isDirect' below, as well as the block enclosed in the ** "if( isDirect )" condition. */ #ifndef SQLITE_ENABLE_ATOMIC_WRITE # define DIRECT_MODE 0 assert( isDirectMode==0 ); UNUSED_PARAMETER(isDirectMode); #else # define DIRECT_MODE isDirectMode #endif if( !pPager->changeCountDone && pPager->dbSize>0 ){ PgHdr *pPgHdr; /* Reference to page 1 */ u32 change_counter; /* Initial value of change-counter field */ assert( !pPager->tempFile && isOpen(pPager->fd) ); /* Open page 1 of the file for writing. */ |
︙ | ︙ | |||
39107 39108 39109 39110 39111 39112 39113 | ** If the EXCLUSIVE lock is already held or the attempt to obtain it is ** successful, or the connection is in WAL mode, SQLITE_OK is returned. ** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is ** returned. */ SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){ int rc = SQLITE_OK; | | > > > > | | 40145 40146 40147 40148 40149 40150 40151 40152 40153 40154 40155 40156 40157 40158 40159 40160 40161 40162 40163 40164 40165 | ** If the EXCLUSIVE lock is already held or the attempt to obtain it is ** successful, or the connection is in WAL mode, SQLITE_OK is returned. ** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is ** returned. */ SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){ int rc = SQLITE_OK; assert( pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD || pPager->eState==PAGER_WRITER_LOCKED ); assert( assert_pager_state(pPager) ); if( 0==pagerUseWal(pPager) ){ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); } return rc; } /* ** Sync the database file for the pager pPager. zMaster points to the name ** of a master journal file that should be written into the individual |
︙ | ︙ | |||
39147 39148 39149 39150 39151 39152 39153 | SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( Pager *pPager, /* Pager object */ const char *zMaster, /* If not NULL, the master journal name */ int noSync /* True to omit the xSync on the db file */ ){ int rc = SQLITE_OK; /* Return code */ | < | > > > > > | > > > | | | 40189 40190 40191 40192 40193 40194 40195 40196 40197 40198 40199 40200 40201 40202 40203 40204 40205 40206 40207 40208 40209 40210 40211 40212 40213 40214 40215 40216 40217 40218 40219 40220 40221 40222 40223 40224 40225 | SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( Pager *pPager, /* Pager object */ const char *zMaster, /* If not NULL, the master journal name */ int noSync /* True to omit the xSync on the db file */ ){ int rc = SQLITE_OK; /* Return code */ assert( pPager->eState==PAGER_WRITER_LOCKED || pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD || pPager->eState==PAGER_ERROR ); assert( assert_pager_state(pPager) ); /* If a prior error occurred, report that error again. */ if( NEVER(pPager->errCode) ) return pPager->errCode; PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", pPager->zFilename, zMaster, pPager->dbSize)); /* If no database changes have been made, return early. */ if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK; if( MEMDB ){ /* If this is an in-memory db, or no pages have been written to, or this ** function has already been called, it is mostly a no-op. However, any ** backup in progress needs to be restarted. */ sqlite3BackupRestart(pPager->pBackup); }else{ if( pagerUseWal(pPager) ){ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); if( pList ){ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, (pPager->fullSync ? pPager->sync_flags : 0) ); } |
︙ | ︙ | |||
39205 39206 39207 39208 39209 39210 39211 | PgHdr *pPg; assert( isOpen(pPager->jfd) || pPager->journalMode==PAGER_JOURNALMODE_OFF || pPager->journalMode==PAGER_JOURNALMODE_WAL ); if( !zMaster && isOpen(pPager->jfd) && pPager->journalOff==jrnlBufferSize(pPager) | | | 40254 40255 40256 40257 40258 40259 40260 40261 40262 40263 40264 40265 40266 40267 40268 | PgHdr *pPg; assert( isOpen(pPager->jfd) || pPager->journalMode==PAGER_JOURNALMODE_OFF || pPager->journalMode==PAGER_JOURNALMODE_WAL ); if( !zMaster && isOpen(pPager->jfd) && pPager->journalOff==jrnlBufferSize(pPager) && pPager->dbSize>=pPager->dbOrigSize && (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) ){ /* Update the db file change counter via the direct-write method. The ** following call will modify the in-memory representation of page 1 ** to include the updated change counter and then write page 1 ** directly to the database file. Because of the atomic-write ** property of the host file-system, this is safe. |
︙ | ︙ | |||
39235 39236 39237 39238 39239 39240 39241 | ** file. This can only happen in auto-vacuum mode. ** ** Before reading the pages with page numbers larger than the ** current value of Pager.dbSize, set dbSize back to the value ** that it took at the start of the transaction. Otherwise, the ** calls to sqlite3PagerGet() return zeroed pages instead of ** reading data from the database file. | < < < | | 40284 40285 40286 40287 40288 40289 40290 40291 40292 40293 40294 40295 40296 40297 40298 40299 40300 40301 | ** file. This can only happen in auto-vacuum mode. ** ** Before reading the pages with page numbers larger than the ** current value of Pager.dbSize, set dbSize back to the value ** that it took at the start of the transaction. Otherwise, the ** calls to sqlite3PagerGet() return zeroed pages instead of ** reading data from the database file. */ #ifndef SQLITE_OMIT_AUTOVACUUM if( pPager->dbSize<pPager->dbOrigSize && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ Pgno i; /* Iterator variable */ const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ const Pgno dbSize = pPager->dbSize; /* Database image size */ pPager->dbSize = pPager->dbOrigSize; for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){ if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){ |
︙ | ︙ | |||
39268 39269 39270 39271 39272 39273 39274 | /* Write the master journal name into the journal file. If a master ** journal file name has already been written to the journal file, ** or if zMaster is NULL (no master journal), then this call is a no-op. */ rc = writeMasterJournal(pPager, zMaster); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; | > | | | > > > > > > | < | < < < > > > | 40314 40315 40316 40317 40318 40319 40320 40321 40322 40323 40324 40325 40326 40327 40328 40329 40330 40331 40332 40333 40334 40335 40336 40337 40338 40339 40340 40341 40342 40343 40344 40345 40346 40347 40348 40349 40350 40351 40352 40353 40354 40355 40356 40357 40358 40359 40360 40361 40362 40363 40364 40365 40366 40367 40368 40369 40370 | /* Write the master journal name into the journal file. If a master ** journal file name has already been written to the journal file, ** or if zMaster is NULL (no master journal), then this call is a no-op. */ rc = writeMasterJournal(pPager, zMaster); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; /* Sync the journal file and write all dirty pages to the database. ** If the atomic-update optimization is being used, this sync will not ** create the journal file or perform any real IO. ** ** Because the change-counter page was just modified, unless the ** atomic-update optimization is used it is almost certain that the ** journal requires a sync here. However, in locking_mode=exclusive ** on a system under memory pressure it is just possible that this is ** not the case. In this case it is likely enough that the redundant ** xSync() call will be changed to a no-op by the OS anyhow. */ rc = syncJournal(pPager, 0); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache)); if( rc!=SQLITE_OK ){ assert( rc!=SQLITE_IOERR_BLOCKED ); goto commit_phase_one_exit; } sqlite3PcacheCleanAll(pPager->pPCache); /* If the file on disk is not the same size as the database image, ** then use pager_truncate to grow or shrink the file here. */ if( pPager->dbSize!=pPager->dbFileSize ){ Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); assert( pPager->eState==PAGER_WRITER_DBMOD ); rc = pager_truncate(pPager, nNew); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } /* Finally, sync the database file. */ if( !pPager->noSync && !noSync ){ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags); } IOTRACE(("DBSYNC %p\n", pPager)) } } commit_phase_one_exit: if( rc==SQLITE_OK && !pagerUseWal(pPager) ){ pPager->eState = PAGER_WRITER_FINISHED; } return rc; } /* ** When this function is called, the database file has been completely ** updated to reflect the changes made by the current transaction and |
︙ | ︙ | |||
39332 39333 39334 39335 39336 39337 39338 | int rc = SQLITE_OK; /* Return code */ /* This routine should not be called if a prior error has occurred. ** But if (due to a coding error elsewhere in the system) it does get ** called, just return the same error code without doing anything. */ if( NEVER(pPager->errCode) ) return pPager->errCode; | | | | < < > | > | > < > > | > > > > | > < < | < < < < < < < < < | < < < < < < < < < > | < < < < < < < > > > > > > > > > > > < < | | < < < < < < < | < < < | < < | | < < > | < | | < | | < < | 40384 40385 40386 40387 40388 40389 40390 40391 40392 40393 40394 40395 40396 40397 40398 40399 40400 40401 40402 40403 40404 40405 40406 40407 40408 40409 40410 40411 40412 40413 40414 40415 40416 40417 40418 40419 40420 40421 40422 40423 40424 40425 40426 40427 40428 40429 40430 40431 40432 40433 40434 40435 40436 40437 40438 40439 40440 40441 40442 40443 40444 40445 40446 40447 40448 40449 40450 40451 40452 40453 40454 40455 40456 40457 40458 40459 40460 40461 40462 40463 40464 40465 40466 40467 40468 40469 40470 40471 40472 40473 40474 40475 40476 40477 40478 40479 40480 40481 40482 40483 40484 | int rc = SQLITE_OK; /* Return code */ /* This routine should not be called if a prior error has occurred. ** But if (due to a coding error elsewhere in the system) it does get ** called, just return the same error code without doing anything. */ if( NEVER(pPager->errCode) ) return pPager->errCode; assert( pPager->eState==PAGER_WRITER_LOCKED || pPager->eState==PAGER_WRITER_FINISHED || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD) ); assert( assert_pager_state(pPager) ); /* An optimization. If the database was not actually modified during ** this transaction, the pager is running in exclusive-mode and is ** using persistent journals, then this function is a no-op. ** ** The start of the journal file currently contains a single journal ** header with the nRec field set to 0. If such a journal is used as ** a hot-journal during hot-journal rollback, 0 changes will be made ** to the database file. So there is no need to zero the journal ** header. Since the pager is in exclusive mode, there is no need ** to drop any locks either. */ if( pPager->eState==PAGER_WRITER_LOCKED && pPager->exclusiveMode && pPager->journalMode==PAGER_JOURNALMODE_PERSIST ){ assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff ); pPager->eState = PAGER_READER; return SQLITE_OK; } PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); rc = pager_end_transaction(pPager, pPager->setMaster); return pager_error(pPager, rc); } /* ** If a write transaction is open, then all changes made within the ** transaction are reverted and the current write-transaction is closed. ** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR ** state if an error occurs. ** ** If the pager is already in PAGER_ERROR state when this function is called, ** it returns Pager.errCode immediately. No work is performed in this case. ** ** Otherwise, in rollback mode, this function performs two functions: ** ** 1) It rolls back the journal file, restoring all database file and ** in-memory cache pages to the state they were in when the transaction ** was opened, and ** ** 2) It finalizes the journal file, so that it is not used for hot ** rollback at any point in the future. ** ** Finalization of the journal file (task 2) is only performed if the ** rollback is successful. ** ** In WAL mode, all cache-entries containing data modified within the ** current transaction are either expelled from the cache or reverted to ** their pre-transaction state by re-reading data from the database or ** WAL files. The WAL transaction is then closed. */ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); /* PagerRollback() is a no-op if called in READER or OPEN state. If ** the pager is already in the ERROR state, the rollback is not ** attempted here. Instead, the error code is returned to the caller. */ assert( assert_pager_state(pPager) ); if( pPager->eState==PAGER_ERROR ) return pPager->errCode; if( pPager->eState<=PAGER_READER ) return SQLITE_OK; if( pagerUseWal(pPager) ){ int rc2; rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); rc2 = pager_end_transaction(pPager, pPager->setMaster); if( rc==SQLITE_OK ) rc = rc2; }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ rc = pager_end_transaction(pPager, 0); }else{ rc = pager_playback(pPager, 0); } assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR ); /* If an error occurs during a ROLLBACK, we can no longer trust the pager ** cache. So call pager_error() on the way out to make any error persistent. */ return pager_error(pPager, rc); } /* ** Return TRUE if the database file is opened read-only. Return FALSE ** if the database is (in theory) writable. */ SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager *pPager){ |
︙ | ︙ | |||
39491 39492 39493 39494 39495 39496 39497 | ** This routine is used for testing and analysis only. */ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ static int a[11]; a[0] = sqlite3PcacheRefCount(pPager->pPCache); a[1] = sqlite3PcachePagecount(pPager->pPCache); a[2] = sqlite3PcacheGetCachesize(pPager->pPCache); | | | | 40516 40517 40518 40519 40520 40521 40522 40523 40524 40525 40526 40527 40528 40529 40530 40531 | ** This routine is used for testing and analysis only. */ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ static int a[11]; a[0] = sqlite3PcacheRefCount(pPager->pPCache); a[1] = sqlite3PcachePagecount(pPager->pPCache); a[2] = sqlite3PcacheGetCachesize(pPager->pPCache); a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; a[4] = pPager->eState; a[5] = pPager->errCode; a[6] = pPager->nHit; a[7] = pPager->nMiss; a[8] = 0; /* Used to be pPager->nOvfl */ a[9] = pPager->nRead; a[10] = pPager->nWrite; return a; |
︙ | ︙ | |||
39523 39524 39525 39526 39527 39528 39529 39530 39531 39532 39533 | ** If a memory allocation fails, SQLITE_NOMEM is returned. If an error ** occurs while opening the sub-journal file, then an IO error code is ** returned. Otherwise, SQLITE_OK. */ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ int rc = SQLITE_OK; /* Return code */ int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ if( nSavepoint>nCurrent && pPager->useJournal ){ int ii; /* Iterator variable */ PagerSavepoint *aNew; /* New Pager.aSavepoint array */ | > > > < < < < | | | 40548 40549 40550 40551 40552 40553 40554 40555 40556 40557 40558 40559 40560 40561 40562 40563 40564 40565 40566 40567 40568 40569 40570 40571 40572 40573 40574 40575 40576 40577 40578 40579 40580 40581 40582 40583 40584 40585 40586 40587 40588 40589 40590 40591 40592 | ** If a memory allocation fails, SQLITE_NOMEM is returned. If an error ** occurs while opening the sub-journal file, then an IO error code is ** returned. Otherwise, SQLITE_OK. */ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ int rc = SQLITE_OK; /* Return code */ int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ assert( pPager->eState>=PAGER_WRITER_LOCKED ); assert( assert_pager_state(pPager) ); if( nSavepoint>nCurrent && pPager->useJournal ){ int ii; /* Iterator variable */ PagerSavepoint *aNew; /* New Pager.aSavepoint array */ /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM ** if the allocation fails. Otherwise, zero the new portion in case a ** malloc failure occurs while populating it in the for(...) loop below. */ aNew = (PagerSavepoint *)sqlite3Realloc( pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint ); if( !aNew ){ return SQLITE_NOMEM; } memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); pPager->aSavepoint = aNew; /* Populate the PagerSavepoint structures just allocated. */ for(ii=nCurrent; ii<nSavepoint; ii++){ aNew[ii].nOrig = pPager->dbSize; if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ aNew[ii].iOffset = pPager->journalOff; }else{ aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); } aNew[ii].iSubRec = pPager->nSubRec; aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); if( !aNew[ii].pInSavepoint ){ return SQLITE_NOMEM; } if( pagerUseWal(pPager) ){ sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); } pPager->nSavepoint = ii+1; |
︙ | ︙ | |||
39601 39602 39603 39604 39605 39606 39607 | ** then savepoint iSavepoint is also destroyed. ** ** This function may return SQLITE_NOMEM if a memory allocation fails, ** or an IO error code if an IO error occurs while rolling back a ** savepoint. If no errors occur, SQLITE_OK is returned. */ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ | | | | 40625 40626 40627 40628 40629 40630 40631 40632 40633 40634 40635 40636 40637 40638 40639 40640 40641 40642 40643 40644 | ** then savepoint iSavepoint is also destroyed. ** ** This function may return SQLITE_NOMEM if a memory allocation fails, ** or an IO error code if an IO error occurs while rolling back a ** savepoint. If no errors occur, SQLITE_OK is returned. */ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ int rc = pPager->errCode; /* Return code */ assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK ); if( rc==SQLITE_OK && iSavepoint<pPager->nSavepoint ){ int ii; /* Iterator variable */ int nNew; /* Number of remaining savepoints after this op. */ /* Figure out how many savepoints will still be active after this ** operation. Store this value in nNew. Then free resources associated ** with any savepoints that are destroyed by this operation. */ |
︙ | ︙ | |||
39642 39643 39644 39645 39646 39647 39648 | ** the database file, so the playback operation can be skipped. */ else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){ PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; rc = pagerPlaybackSavepoint(pPager, pSavepoint); assert(rc!=SQLITE_DONE); } | | | | 40666 40667 40668 40669 40670 40671 40672 40673 40674 40675 40676 40677 40678 40679 40680 40681 | ** the database file, so the playback operation can be skipped. */ else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){ PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; rc = pagerPlaybackSavepoint(pPager, pSavepoint); assert(rc!=SQLITE_DONE); } } return rc; } /* ** Return the full pathname of the database file. */ SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager){ |
︙ | ︙ | |||
39741 39742 39743 39744 39745 39746 39747 39748 39749 39750 39751 39752 39753 39754 | SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ PgHdr *pPgOld; /* The page being overwritten. */ Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ int rc; /* Return code */ Pgno origPgno; /* The original page number */ assert( pPg->nRef>0 ); /* In order to be able to rollback, an in-memory database must journal ** the page we are moving from. */ if( MEMDB ){ rc = sqlite3PagerWrite(pPg); if( rc ) return rc; | > > > > | 40765 40766 40767 40768 40769 40770 40771 40772 40773 40774 40775 40776 40777 40778 40779 40780 40781 40782 | SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ PgHdr *pPgOld; /* The page being overwritten. */ Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ int rc; /* Return code */ Pgno origPgno; /* The original page number */ assert( pPg->nRef>0 ); assert( pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); /* In order to be able to rollback, an in-memory database must journal ** the page we are moving from. */ if( MEMDB ){ rc = sqlite3PagerWrite(pPg); if( rc ) return rc; |
︙ | ︙ | |||
39790 39791 39792 39793 39794 39795 39796 | ** the journal needs to be sync()ed before database page pPg->pgno ** can be written to. The caller has already promised not to write to it. */ if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ needSyncPgno = pPg->pgno; assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); assert( pPg->flags&PGHDR_DIRTY ); | < | < | > > > > > > > > > | < < < < < < < < < < < < < < < < | 40818 40819 40820 40821 40822 40823 40824 40825 40826 40827 40828 40829 40830 40831 40832 40833 40834 40835 40836 40837 40838 40839 40840 40841 40842 40843 40844 40845 40846 40847 40848 40849 40850 40851 40852 40853 40854 40855 40856 40857 40858 40859 40860 40861 40862 40863 40864 40865 40866 40867 40868 40869 40870 40871 40872 40873 40874 40875 40876 40877 40878 40879 40880 40881 40882 40883 40884 40885 40886 40887 40888 40889 40890 40891 40892 40893 40894 40895 | ** the journal needs to be sync()ed before database page pPg->pgno ** can be written to. The caller has already promised not to write to it. */ if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ needSyncPgno = pPg->pgno; assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); assert( pPg->flags&PGHDR_DIRTY ); } /* If the cache contains a page with page-number pgno, remove it ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for ** page pgno before the 'move' operation, it needs to be retained ** for the page moved there. */ pPg->flags &= ~PGHDR_NEED_SYNC; pPgOld = pager_lookup(pPager, pgno); assert( !pPgOld || pPgOld->nRef==1 ); if( pPgOld ){ pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); if( MEMDB ){ /* Do not discard pages from an in-memory database since we might ** need to rollback later. Just move the page out of the way. */ sqlite3PcacheMove(pPgOld, pPager->dbSize+1); }else{ sqlite3PcacheDrop(pPgOld); } } origPgno = pPg->pgno; sqlite3PcacheMove(pPg, pgno); sqlite3PcacheMakeDirty(pPg); /* For an in-memory database, make sure the original page continues ** to exist, in case the transaction needs to roll back. Use pPgOld ** as the original page since it has already been allocated. */ if( MEMDB ){ assert( pPgOld ); sqlite3PcacheMove(pPgOld, origPgno); sqlite3PagerUnref(pPgOld); } if( needSyncPgno ){ /* If needSyncPgno is non-zero, then the journal file needs to be ** sync()ed before any data is written to database file page needSyncPgno. ** Currently, no such page exists in the page-cache and the ** "is journaled" bitvec flag has been set. This needs to be remedied by ** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC ** flag. ** ** If the attempt to load the page into the page-cache fails, (due ** to a malloc() or IO failure), clear the bit in the pInJournal[] ** array. Otherwise, if the page is loaded and written again in ** this transaction, it may be written to the database file before ** it is synced into the journal file. This way, it may end up in ** the journal file twice, but that is not a problem. */ PgHdr *pPgHdr; rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr); if( rc!=SQLITE_OK ){ if( needSyncPgno<=pPager->dbOrigSize ){ assert( pPager->pTmpSpace!=0 ); sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace); } return rc; } pPgHdr->flags |= PGHDR_NEED_SYNC; sqlite3PcacheMakeDirty(pPgHdr); sqlite3PagerUnref(pPgHdr); } return SQLITE_OK; } #endif /* ** Return a pointer to the data for the specified page. */ |
︙ | ︙ | |||
39927 39928 39929 39930 39931 39932 39933 39934 39935 39936 39937 39938 39939 39940 | ** ** * Temporary databases cannot have _WAL journalmode. ** ** The returned indicate the current (possibly updated) journal-mode. */ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ u8 eOld = pPager->journalMode; /* Prior journalmode */ /* The eMode parameter is always valid */ assert( eMode==PAGER_JOURNALMODE_DELETE || eMode==PAGER_JOURNALMODE_TRUNCATE || eMode==PAGER_JOURNALMODE_PERSIST || eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_WAL | > > > > > > > | 40946 40947 40948 40949 40950 40951 40952 40953 40954 40955 40956 40957 40958 40959 40960 40961 40962 40963 40964 40965 40966 | ** ** * Temporary databases cannot have _WAL journalmode. ** ** The returned indicate the current (possibly updated) journal-mode. */ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ u8 eOld = pPager->journalMode; /* Prior journalmode */ #ifdef SQLITE_DEBUG /* The print_pager_state() routine is intended to be used by the debugger ** only. We invoke it once here to suppress a compiler warning. */ print_pager_state(pPager); #endif /* The eMode parameter is always valid */ assert( eMode==PAGER_JOURNALMODE_DELETE || eMode==PAGER_JOURNALMODE_TRUNCATE || eMode==PAGER_JOURNALMODE_PERSIST || eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_WAL |
︙ | ︙ | |||
39953 39954 39955 39956 39957 39958 39959 | assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF ); if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){ eMode = eOld; } } if( eMode!=eOld ){ | < < < < < < < | < > | > > > > | | > | | | | | | | | | | | | | | | | > > | | 40979 40980 40981 40982 40983 40984 40985 40986 40987 40988 40989 40990 40991 40992 40993 40994 40995 40996 40997 40998 40999 41000 41001 41002 41003 41004 41005 41006 41007 41008 41009 41010 41011 41012 41013 41014 41015 41016 41017 41018 41019 41020 41021 41022 41023 41024 41025 41026 41027 41028 41029 41030 41031 41032 41033 41034 41035 41036 41037 41038 41039 41040 41041 41042 41043 41044 41045 41046 41047 41048 41049 41050 41051 41052 41053 41054 41055 41056 41057 41058 41059 41060 41061 41062 41063 41064 41065 | assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF ); if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){ eMode = eOld; } } if( eMode!=eOld ){ /* Change the journal mode. */ assert( pPager->eState!=PAGER_ERROR ); pPager->journalMode = (u8)eMode; /* When transistioning from TRUNCATE or PERSIST to any other journal ** mode except WAL, unless the pager is in locking_mode=exclusive mode, ** delete the journal file. */ assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); assert( (PAGER_JOURNALMODE_DELETE & 5)==0 ); assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 ); assert( (PAGER_JOURNALMODE_OFF & 5)==0 ); assert( (PAGER_JOURNALMODE_WAL & 5)==5 ); assert( isOpen(pPager->fd) || pPager->exclusiveMode ); if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ /* In this case we would like to delete the journal file. If it is ** not possible, then that is not a problem. Deleting the journal file ** here is an optimization only. ** ** Before deleting the journal file, obtain a RESERVED lock on the ** database file. This ensures that the journal file is not deleted ** while it is in use by some other client. */ sqlite3OsClose(pPager->jfd); if( pPager->eLock>=RESERVED_LOCK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); }else{ int rc = SQLITE_OK; int state = pPager->eState; assert( state==PAGER_OPEN || state==PAGER_READER ); if( state==PAGER_OPEN ){ rc = sqlite3PagerSharedLock(pPager); } if( pPager->eState==PAGER_READER ){ assert( rc==SQLITE_OK ); rc = pagerLockDb(pPager, RESERVED_LOCK); } if( rc==SQLITE_OK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } if( rc==SQLITE_OK && state==PAGER_READER ){ pagerUnlockDb(pPager, SHARED_LOCK); }else if( state==PAGER_OPEN ){ pager_unlock(pPager); } assert( state==pPager->eState ); } } } /* Return the new journal mode */ return (int)pPager->journalMode; } /* ** Return the current journal mode. */ SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager *pPager){ return (int)pPager->journalMode; } /* ** Return TRUE if the pager is in a state where it is OK to change the ** journalmode. Journalmode changes can only happen when the database ** is unmodified. */ SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){ assert( assert_pager_state(pPager) ); if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0; if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0; return 1; } /* ** Get/set the size-limit used for persistent journal files. ** |
︙ | ︙ | |||
40090 40091 40092 40093 40094 40095 40096 | ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file ** is not already open, make an attempt to open it now. If successful, ** return SQLITE_OK. If an error occurs or the VFS used by the pager does | | | | > | > > | > > > > | | 41116 41117 41118 41119 41120 41121 41122 41123 41124 41125 41126 41127 41128 41129 41130 41131 41132 41133 41134 41135 41136 41137 41138 41139 41140 41141 41142 41143 41144 41145 41146 41147 41148 41149 41150 41151 41152 41153 41154 41155 41156 41157 41158 41159 41160 41161 41162 41163 41164 41165 | ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file ** is not already open, make an attempt to open it now. If successful, ** return SQLITE_OK. If an error occurs or the VFS used by the pager does ** not support the xShmXXX() methods, return an error code. *pbOpen is ** not modified in either case. ** ** If the pager is open on a temp-file (or in-memory database), or if ** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK ** without doing anything. */ SQLITE_PRIVATE int sqlite3PagerOpenWal( Pager *pPager, /* Pager object */ int *pbOpen /* OUT: Set to true if call is a no-op */ ){ int rc = SQLITE_OK; /* Return code */ assert( assert_pager_state(pPager) ); assert( pPager->eState==PAGER_OPEN || pbOpen ); assert( pPager->eState==PAGER_READER || !pbOpen ); assert( pbOpen==0 || *pbOpen==0 ); assert( pbOpen!=0 || (!pPager->tempFile && !pPager->pWal) ); if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Close any rollback journal previously open */ sqlite3OsClose(pPager->jfd); /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), unlock the database file and ** return an error code. */ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; pPager->eState = PAGER_OPEN; } }else{ *pbOpen = 1; } return rc; } /* ** This function is called to close the connection to the log file prior |
︙ | ︙ | |||
40144 40145 40146 40147 40148 40149 40150 | /* If the log file is not already open, but does exist in the file-system, ** it may need to be checkpointed before the connection can switch to ** rollback mode. Open it now so this can happen. */ if( !pPager->pWal ){ int logexists = 0; | | | | | 41177 41178 41179 41180 41181 41182 41183 41184 41185 41186 41187 41188 41189 41190 41191 41192 41193 41194 41195 41196 41197 41198 41199 41200 41201 41202 41203 41204 41205 41206 41207 41208 41209 41210 41211 41212 41213 41214 41215 41216 41217 | /* If the log file is not already open, but does exist in the file-system, ** it may need to be checkpointed before the connection can switch to ** rollback mode. Open it now so this can happen. */ if( !pPager->pWal ){ int logexists = 0; rc = pagerLockDb(pPager, SHARED_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal); } } /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, (u8*)pPager->pTmpSpace ); pPager->pWal = 0; }else{ /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock ** that we did get back to SHARED. */ pagerUnlockDb(pPager, SQLITE_LOCK_SHARED); } } return rc; } #ifdef SQLITE_HAS_CODEC /* |
︙ | ︙ | |||
40490 40491 40492 40493 40494 40495 40496 40497 40498 40499 40500 40501 40502 40503 | /* ** The following object holds a copy of the wal-index header content. ** ** The actual header in the wal-index consists of two copies of this ** object. */ struct WalIndexHdr { u32 iVersion; /* Wal-index version */ u32 unused; /* Unused (padding) field */ u32 iChange; /* Counter incremented each transaction */ u8 isInit; /* 1 when initialized */ u8 bigEndCksum; /* True if checksums in WAL are big-endian */ | > > > > | | 41523 41524 41525 41526 41527 41528 41529 41530 41531 41532 41533 41534 41535 41536 41537 41538 41539 41540 41541 41542 41543 41544 41545 41546 41547 41548 | /* ** The following object holds a copy of the wal-index header content. ** ** The actual header in the wal-index consists of two copies of this ** object. ** ** The szPage value can be any power of 2 between 512 and 32768, inclusive. ** Or it can be 1 to represent a 65536-byte page. The latter case was ** added in 3.7.1 when support for 64K pages was added. */ struct WalIndexHdr { u32 iVersion; /* Wal-index version */ u32 unused; /* Unused (padding) field */ u32 iChange; /* Counter incremented each transaction */ u8 isInit; /* 1 when initialized */ u8 bigEndCksum; /* True if checksums in WAL are big-endian */ u16 szPage; /* Database page size in bytes. 1==64K */ u32 mxFrame; /* Index of last valid frame in the WAL */ u32 nPage; /* Size of database in pages */ u32 aFrameCksum[2]; /* Checksum of last frame in log */ u32 aSalt[2]; /* Two salt values copied from WAL header */ u32 aCksum[2]; /* Checksum over all prior fields */ }; |
︙ | ︙ | |||
40608 40609 40610 40611 40612 40613 40614 | struct Wal { sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ sqlite3_file *pDbFd; /* File handle for the database file */ sqlite3_file *pWalFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ int nWiData; /* Size of array apWiData */ volatile u32 **apWiData; /* Pointer to wal-index content in memory */ | | | 41645 41646 41647 41648 41649 41650 41651 41652 41653 41654 41655 41656 41657 41658 41659 | struct Wal { sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ sqlite3_file *pDbFd; /* File handle for the database file */ sqlite3_file *pWalFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ int nWiData; /* Size of array apWiData */ volatile u32 **apWiData; /* Pointer to wal-index content in memory */ u32 szPage; /* Database page size */ i16 readLock; /* Which read lock is being held. -1 for none */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ u8 readOnly; /* True if the WAL file is open read-only */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ |
︙ | ︙ | |||
41279 41280 41281 41282 41283 41284 41285 | || szPage&(szPage-1) || szPage>SQLITE_MAX_PAGE_SIZE || szPage<512 ){ goto finished; } pWal->hdr.bigEndCksum = (u8)(magic&0x00000001); | | | 42316 42317 42318 42319 42320 42321 42322 42323 42324 42325 42326 42327 42328 42329 42330 | || szPage&(szPage-1) || szPage>SQLITE_MAX_PAGE_SIZE || szPage<512 ){ goto finished; } pWal->hdr.bigEndCksum = (u8)(magic&0x00000001); pWal->szPage = szPage; pWal->nCkpt = sqlite3Get4byte(&aBuf[12]); memcpy(&pWal->hdr.aSalt, &aBuf[16], 8); /* Verify that the WAL header checksum is correct */ walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum ); |
︙ | ︙ | |||
41329 41330 41331 41332 41333 41334 41335 | rc = walIndexAppend(pWal, ++iFrame, pgno); if( rc!=SQLITE_OK ) break; /* If nTruncate is non-zero, this is a commit record. */ if( nTruncate ){ pWal->hdr.mxFrame = iFrame; pWal->hdr.nPage = nTruncate; | | > > | 42366 42367 42368 42369 42370 42371 42372 42373 42374 42375 42376 42377 42378 42379 42380 42381 42382 | rc = walIndexAppend(pWal, ++iFrame, pgno); if( rc!=SQLITE_OK ) break; /* If nTruncate is non-zero, this is a commit record. */ if( nTruncate ){ pWal->hdr.mxFrame = iFrame; pWal->hdr.nPage = nTruncate; pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); testcase( szPage<=32768 ); testcase( szPage>=65536 ); aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; } } sqlite3_free(aFrame); } |
︙ | ︙ | |||
41354 41355 41356 41357 41358 41359 41360 41361 41362 41363 41364 41365 41366 41367 | ** currently holding locks that exclude all other readers, writers and ** checkpointers. */ pInfo = walCkptInfo(pWal); pInfo->nBackfill = 0; pInfo->aReadMark[0] = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; } recovery_error: WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); walUnlockExclusive(pWal, iLock, nLock); return rc; } | > > > > > > > > > > > | 42393 42394 42395 42396 42397 42398 42399 42400 42401 42402 42403 42404 42405 42406 42407 42408 42409 42410 42411 42412 42413 42414 42415 42416 42417 | ** currently holding locks that exclude all other readers, writers and ** checkpointers. */ pInfo = walCkptInfo(pWal); pInfo->nBackfill = 0; pInfo->aReadMark[0] = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; /* If more than one frame was recovered from the log file, report an ** event via sqlite3_log(). This is to help with identifying performance ** problems caused by applications routinely shutting down without ** checkpointing the log file. */ if( pWal->hdr.nPage ){ sqlite3_log(SQLITE_OK, "Recovered %d frames from WAL file %s", pWal->hdr.nPage, pWal->zWalName ); } } recovery_error: WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); walUnlockExclusive(pWal, iLock, nLock); return rc; } |
︙ | ︙ | |||
41714 41715 41716 41717 41718 41719 41720 | static int walCheckpoint( Wal *pWal, /* Wal connection */ 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 */ | | > > > > | > > > > > > > > > > > > > | | 42764 42765 42766 42767 42768 42769 42770 42771 42772 42773 42774 42775 42776 42777 42778 42779 42780 42781 42782 42783 42784 42785 42786 42787 42788 42789 42790 42791 42792 42793 42794 42795 42796 42797 42798 42799 42800 42801 42802 42803 42804 42805 42806 42807 42808 42809 42810 42811 42812 42813 42814 42815 42816 42817 42818 42819 42820 42821 42822 42823 42824 42825 42826 42827 42828 42829 42830 42831 42832 42833 42834 42835 42836 42837 42838 42839 42840 42841 42842 42843 42844 42845 42846 42847 42848 42849 42850 42851 42852 42853 42854 42855 | static int walCheckpoint( Wal *pWal, /* Wal connection */ 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 */ int szPage; /* Database page-size */ WalIterator *pIter = 0; /* Wal iterator context */ u32 iDbpage = 0; /* Next database page to write */ u32 iFrame = 0; /* Wal frame containing data for iDbpage */ u32 mxSafeFrame; /* Max frame that can be backfilled */ u32 mxPage; /* Max database page to write */ int i; /* Loop counter */ volatile WalCkptInfo *pInfo; /* The checkpoint status information */ szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); testcase( szPage<=32768 ); testcase( szPage>=65536 ); if( pWal->hdr.mxFrame==0 ) return SQLITE_OK; /* Allocate the iterator */ rc = walIteratorInit(pWal, &pIter); if( rc!=SQLITE_OK ){ return rc; } 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; } /* Compute in mxSafeFrame the index of the last frame of the WAL that is ** safe to write into the database. Frames beyond mxSafeFrame might ** overwrite database pages that are in use by active readers and thus ** cannot be backfilled from the WAL. */ mxSafeFrame = pWal->hdr.mxFrame; mxPage = pWal->hdr.nPage; pInfo = walCkptInfo(pWal); for(i=1; i<WAL_NREADER; i++){ u32 y = pInfo->aReadMark[i]; if( mxSafeFrame>=y ){ assert( y<=pWal->hdr.mxFrame ); rc = walLockExclusive(pWal, 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; }else{ goto walcheckpoint_out; } } } if( pInfo->nBackfill<mxSafeFrame && (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK ){ i64 nSize; /* Current size of database file */ u32 nBackfill = pInfo->nBackfill; /* Sync the WAL to disk */ if( sync_flags ){ rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } /* If the database file may grow as a result of this checkpoint, hint ** about the eventual size of the db file to the VFS layer. */ if( rc==SQLITE_OK ){ i64 nReq = ((i64)mxPage * szPage); rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); if( rc==SQLITE_OK && nSize<nReq ){ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); } } /* Iterate through the contents of the WAL, copying data to the db file. */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; assert( walFramePgno(pWal, iFrame)==iDbpage ); if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue; iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); if( rc!=SQLITE_OK ) break; iOffset = (iDbpage-1)*(i64)szPage; testcase( IS_BIG_INT(iOffset) ); rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); |
︙ | ︙ | |||
41881 41882 41883 41884 41885 41886 41887 | u32 aCksum[2]; /* Checksum on the header content */ WalIndexHdr h1, h2; /* Two copies of the header content */ WalIndexHdr volatile *aHdr; /* Header in shared memory */ /* The first page of the wal-index must be mapped at this point. */ assert( pWal->nWiData>0 && pWal->apWiData[0] ); | | | 42948 42949 42950 42951 42952 42953 42954 42955 42956 42957 42958 42959 42960 42961 42962 | u32 aCksum[2]; /* Checksum on the header content */ WalIndexHdr h1, h2; /* Two copies of the header content */ WalIndexHdr volatile *aHdr; /* Header in shared memory */ /* The first page of the wal-index must be mapped at this point. */ assert( pWal->nWiData>0 && pWal->apWiData[0] ); /* Read the header. This might happen concurrently with a write to the ** same area of shared memory on a different CPU in a SMP, ** meaning it is possible that an inconsistent snapshot is read ** from the file. If this happens, return non-zero. ** ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from |
︙ | ︙ | |||
41910 41911 41912 41913 41914 41915 41916 | if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){ return 1; /* Checksum does not match */ } if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){ *pChanged = 1; memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr)); | > | > | 42977 42978 42979 42980 42981 42982 42983 42984 42985 42986 42987 42988 42989 42990 42991 42992 42993 | if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){ return 1; /* Checksum does not match */ } if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){ *pChanged = 1; memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr)); pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); testcase( pWal->szPage<=32768 ); testcase( pWal->szPage>=65536 ); } /* The header was successfully read. Return zero. */ return 0; } /* |
︙ | ︙ | |||
42229 42230 42231 42232 42233 42234 42235 42236 42237 42238 42239 42240 42241 42242 | } /* ** Finish with a read transaction. All this does is release the ** read-lock. */ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ if( pWal->readLock>=0 ){ walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); pWal->readLock = -1; } } /* | > | 43298 43299 43300 43301 43302 43303 43304 43305 43306 43307 43308 43309 43310 43311 43312 | } /* ** Finish with a read transaction. All this does is release the ** read-lock. */ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ sqlite3WalEndWriteTransaction(pWal); if( pWal->readLock>=0 ){ walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); pWal->readLock = -1; } } /* |
︙ | ︙ | |||
42339 42340 42341 42342 42343 42344 42345 | } #endif /* If iRead is non-zero, then it is the log frame number that contains the ** required page. Read and return data from the log file. */ if( iRead ){ | > > > > > > | | | | | > > | 43409 43410 43411 43412 43413 43414 43415 43416 43417 43418 43419 43420 43421 43422 43423 43424 43425 43426 43427 43428 43429 43430 43431 43432 43433 43434 43435 43436 43437 43438 43439 43440 43441 43442 43443 43444 43445 43446 43447 | } #endif /* If iRead is non-zero, then it is the log frame number that contains the ** required page. Read and return data from the log file. */ if( iRead ){ int sz; i64 iOffset; sz = pWal->hdr.szPage; sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); testcase( sz<=32768 ); testcase( sz>=65536 ); iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE; *pInWal = 1; /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset); } *pInWal = 0; return SQLITE_OK; } /* ** Return the size of the database in pages (or zero, if unknown). */ SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){ if( pWal && ALWAYS(pWal->readLock>=0) ){ return pWal->hdr.nPage; } return 0; } /* ** This function starts a write transaction on the WAL. ** ** A read transaction must have already been started by a prior call |
︙ | ︙ | |||
42431 42432 42433 42434 42435 42436 42437 | ** returned to the caller. ** ** Otherwise, if the callback function does not return an error, this ** function returns SQLITE_OK. */ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; | | | 43509 43510 43511 43512 43513 43514 43515 43516 43517 43518 43519 43520 43521 43522 43523 | ** returned to the caller. ** ** Otherwise, if the callback function does not return an error, this ** function returns SQLITE_OK. */ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; if( ALWAYS(pWal->writeLock) ){ Pgno iMax = pWal->hdr.mxFrame; Pgno iFrame; /* Restore the clients cache of the wal-index header to the state it ** was in before the client began writing to the database. */ memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); |
︙ | ︙ | |||
42519 42520 42521 42522 42523 42524 42525 | ** to the current log file, it is possible to overwrite the start of the ** existing log file with the new frames (i.e. "reset" the log). If so, ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left ** unchanged. ** ** SQLITE_OK is returned if no error is encountered (regardless of whether ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned | | | 43597 43598 43599 43600 43601 43602 43603 43604 43605 43606 43607 43608 43609 43610 43611 | ** to the current log file, it is possible to overwrite the start of the ** existing log file with the new frames (i.e. "reset" the log). If so, ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left ** unchanged. ** ** SQLITE_OK is returned if no error is encountered (regardless of whether ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned ** if an error occurs. */ static int walRestartLog(Wal *pWal){ int rc = SQLITE_OK; int cnt; if( pWal->readLock==0 ){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); |
︙ | ︙ | |||
42552 42553 42554 42555 42556 42557 42558 42559 42560 42561 42562 42563 42564 42565 | sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); sqlite3_randomness(4, &aSalt[1]); walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; assert( pInfo->aReadMark[0]==0 ); walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; | > > | 43630 43631 43632 43633 43634 43635 43636 43637 43638 43639 43640 43641 43642 43643 43644 43645 | sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); sqlite3_randomness(4, &aSalt[1]); walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; assert( pInfo->aReadMark[0]==0 ); walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); }else if( rc!=SQLITE_BUSY ){ return rc; } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; |
︙ | ︙ | |||
42620 42621 42622 42623 42624 42625 42626 | sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); sqlite3_randomness(8, pWal->hdr.aSalt); memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); sqlite3Put4byte(&aWalHdr[24], aCksum[0]); sqlite3Put4byte(&aWalHdr[28], aCksum[1]); | | | | 43700 43701 43702 43703 43704 43705 43706 43707 43708 43709 43710 43711 43712 43713 43714 43715 43716 43717 43718 43719 43720 43721 43722 43723 43724 43725 | sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); sqlite3_randomness(8, pWal->hdr.aSalt); memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); sqlite3Put4byte(&aWalHdr[24], aCksum[0]); sqlite3Put4byte(&aWalHdr[28], aCksum[1]); pWal->szPage = szPage; pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; pWal->hdr.aFrameCksum[0] = aCksum[0]; pWal->hdr.aFrameCksum[1] = aCksum[1]; rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); if( rc!=SQLITE_OK ){ return rc; } } assert( (int)pWal->szPage==szPage ); /* Write the log file. */ for(p=pList; p; p=p->pDirty){ u32 nDbsize; /* Db-size field for frame header */ i64 iOffset; /* Write offset in log file */ void *pData; |
︙ | ︙ | |||
42715 42716 42717 42718 42719 42720 42721 | iFrame++; nLast--; rc = walIndexAppend(pWal, iFrame, pLast->pgno); } if( rc==SQLITE_OK ){ /* Update the private copy of the header. */ | | > > | 43795 43796 43797 43798 43799 43800 43801 43802 43803 43804 43805 43806 43807 43808 43809 43810 43811 | iFrame++; nLast--; rc = walIndexAppend(pWal, iFrame, pLast->pgno); } if( rc==SQLITE_OK ){ /* Update the private copy of the header. */ pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); testcase( szPage<=32768 ); testcase( szPage>=65536 ); pWal->hdr.mxFrame = iFrame; if( isCommit ){ pWal->hdr.iChange++; pWal->hdr.nPage = nTruncate; } /* If this is a commit, update the wal-index header too. */ if( isCommit ){ |
︙ | ︙ | |||
42927 42928 42929 42930 42931 42932 42933 | ** page has a small header which contains the Ptr(N) pointer and other ** information such as the size of key and data. ** ** FORMAT DETAILS ** ** The file is divided into pages. The first page is called page 1, ** the second is page 2, and so forth. A page number of zero indicates | | | 44009 44010 44011 44012 44013 44014 44015 44016 44017 44018 44019 44020 44021 44022 44023 | ** page has a small header which contains the Ptr(N) pointer and other ** information such as the size of key and data. ** ** FORMAT DETAILS ** ** The file is divided into pages. The first page is called page 1, ** the second is page 2, and so forth. A page number of zero indicates ** "no such page". The page size can be any power of 2 between 512 and 65536. ** Each page can be either a btree page, a freelist page, an overflow ** page, or a pointer-map page. ** ** The first page is always a btree page. The first 100 bytes of the first ** page contain a special header (the "file header") that describes the file. ** The format of the file header is as follows: ** |
︙ | ︙ | |||
43289 43290 43291 43292 43293 43294 43295 43296 43297 43298 43299 | sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif | > < < > > | 44371 44372 44373 44374 44375 44376 44377 44378 44379 44380 44381 44382 44383 44384 44385 44386 44387 44388 44389 44390 44391 44392 44393 44394 44395 44396 44397 | sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ u8 openFlags; /* Flags to sqlite3BtreeOpen() */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u8 inTransaction; /* Transaction state */ u8 doNotUseWAL; /* If true, do not open write-ahead-log file */ u32 pageSize; /* Total number of bytes on a page */ u32 usableSize; /* Number of usable bytes on each page */ int nTransaction; /* Number of open transactions (read + write) */ 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 */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE |
︙ | ︙ | |||
43899 43900 43901 43902 43903 43904 43905 | #if 0 int sqlite3BtreeTrace=1; /* True to enable tracing */ # define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);} #else # define TRACE(X) #endif | | > > > > > > > > > | 44982 44983 44984 44985 44986 44987 44988 44989 44990 44991 44992 44993 44994 44995 44996 44997 44998 44999 45000 45001 45002 45003 45004 45005 | #if 0 int sqlite3BtreeTrace=1; /* True to enable tracing */ # define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);} #else # define TRACE(X) #endif /* ** Extract a 2-byte big-endian integer from an array of unsigned bytes. ** But if the value is zero, make it 65536. ** ** This routine is used to extract the "offset to cell content area" value ** from the header of a btree page. If the page size is 65536 and the page ** is empty, the offset should be 65536, but the 2-byte value stores zero. ** This routine makes the necessary adjustment to 65536. */ #define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1) #ifndef SQLITE_OMIT_SHARED_CACHE /* ** A list of BtShared objects that are eligible for participation ** in shared cache. This variable has file scope during normal builds, ** but the test harness needs to access it so we make it global for ** test builds. |
︙ | ︙ | |||
44588 44589 44590 44591 44592 44593 44594 44595 44596 44597 44598 44599 44600 44601 44602 44603 44604 44605 44606 | } #ifndef SQLITE_OMIT_AUTOVACUUM /* ** Given a page number of a regular database page, return the page ** number for the pointer-map page that contains the entry for the ** input page number. */ static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ int nPagesPerMapPage; Pgno iPtrMap, ret; assert( sqlite3_mutex_held(pBt->mutex) ); nPagesPerMapPage = (pBt->usableSize/5)+1; iPtrMap = (pgno-2)/nPagesPerMapPage; ret = (iPtrMap*nPagesPerMapPage) + 2; if( ret==PENDING_BYTE_PAGE(pBt) ){ ret++; } return ret; | > > > > > | 45680 45681 45682 45683 45684 45685 45686 45687 45688 45689 45690 45691 45692 45693 45694 45695 45696 45697 45698 45699 45700 45701 45702 45703 | } #ifndef SQLITE_OMIT_AUTOVACUUM /* ** Given a page number of a regular database page, return the page ** number for the pointer-map page that contains the entry for the ** input page number. ** ** Return 0 (not a valid page) for pgno==1 since there is ** no pointer map associated with page 1. The integrity_check logic ** requires that ptrmapPageno(*,1)!=1. */ static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ int nPagesPerMapPage; Pgno iPtrMap, ret; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno<2 ) return 0; nPagesPerMapPage = (pBt->usableSize/5)+1; iPtrMap = (pgno-2)/nPagesPerMapPage; ret = (iPtrMap*nPagesPerMapPage) + 2; if( ret==PENDING_BYTE_PAGE(pBt) ){ ret++; } return ret; |
︙ | ︙ | |||
45021 45022 45023 45024 45025 45026 45027 | assert( pPage->nOverflow==0 ); usableSize = pPage->pBt->usableSize; assert( nByte < usableSize-8 ); nFrag = data[hdr+7]; assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); gap = pPage->cellOffset + 2*pPage->nCell; | | | | 46118 46119 46120 46121 46122 46123 46124 46125 46126 46127 46128 46129 46130 46131 46132 46133 46134 46135 46136 46137 46138 46139 46140 46141 46142 | assert( pPage->nOverflow==0 ); usableSize = pPage->pBt->usableSize; assert( nByte < usableSize-8 ); nFrag = data[hdr+7]; assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); gap = pPage->cellOffset + 2*pPage->nCell; top = get2byteNotZero(&data[hdr+5]); if( gap>top ) return SQLITE_CORRUPT_BKPT; testcase( gap+2==top ); testcase( gap+1==top ); testcase( gap==top ); if( nFrag>=60 ){ /* Always defragment highly fragmented pages */ rc = defragmentPage(pPage); if( rc ) return rc; top = get2byteNotZero(&data[hdr+5]); }else if( gap+2<=top ){ /* Search the freelist looking for a free slot big enough to satisfy ** the request. The allocation is made from the first free slot in ** the list that is large enough to accomadate it. */ int pc, addr; for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){ |
︙ | ︙ | |||
45073 45074 45075 45076 45077 45078 45079 | /* Check to make sure there is enough space in the gap to satisfy ** the allocation. If not, defragment. */ testcase( gap+2+nByte==top ); if( gap+2+nByte>top ){ rc = defragmentPage(pPage); if( rc ) return rc; | | | 46170 46171 46172 46173 46174 46175 46176 46177 46178 46179 46180 46181 46182 46183 46184 | /* Check to make sure there is enough space in the gap to satisfy ** the allocation. If not, defragment. */ testcase( gap+2+nByte==top ); if( gap+2+nByte>top ){ rc = defragmentPage(pPage); if( rc ) return rc; top = get2byteNotZero(&data[hdr+5]); assert( gap+nByte<=top ); } /* Allocate memory from the gap in between the cell pointer array ** and the cell content area. The btreeInitPage() call has already ** validated the freelist. Given that the freelist is valid, there |
︙ | ︙ | |||
45239 45240 45241 45242 45243 45244 45245 | assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); if( !pPage->isInit ){ u16 pc; /* Address of a freeblock within pPage->aData[] */ u8 hdr; /* Offset to beginning of page header */ u8 *data; /* Equal to pPage->aData */ BtShared *pBt; /* The main btree structure */ | | | | | | | | 46336 46337 46338 46339 46340 46341 46342 46343 46344 46345 46346 46347 46348 46349 46350 46351 46352 46353 46354 46355 46356 46357 46358 46359 46360 46361 46362 46363 46364 46365 46366 46367 | assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); if( !pPage->isInit ){ u16 pc; /* Address of a freeblock within pPage->aData[] */ u8 hdr; /* Offset to beginning of page header */ u8 *data; /* Equal to pPage->aData */ BtShared *pBt; /* The main btree structure */ int usableSize; /* Amount of usable space on each page */ u16 cellOffset; /* Offset from start of page to first cell pointer */ int nFree; /* Number of unused bytes on the page */ int top; /* First byte of the cell content area */ int iCellFirst; /* First allowable cell or freeblock offset */ int iCellLast; /* Last possible cell or freeblock offset */ pBt = pPage->pBt; hdr = pPage->hdrOffset; data = pPage->aData; if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT; assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); pPage->maskPage = (u16)(pBt->pageSize - 1); pPage->nOverflow = 0; usableSize = pBt->usableSize; pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf; top = get2byteNotZero(&data[hdr+5]); pPage->nCell = get2byte(&data[hdr+3]); if( pPage->nCell>MX_CELL(pBt) ){ /* To many cells for a single page. The page must be corrupt */ return SQLITE_CORRUPT_BKPT; } testcase( pPage->nCell==MX_CELL(pBt) ); |
︙ | ︙ | |||
45355 45356 45357 45358 45359 45360 45361 | memset(&data[hdr], 0, pBt->usableSize - hdr); } data[hdr] = (char)flags; first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0); memset(&data[hdr+1], 0, 4); data[hdr+7] = 0; put2byte(&data[hdr+5], pBt->usableSize); | | | | | 46452 46453 46454 46455 46456 46457 46458 46459 46460 46461 46462 46463 46464 46465 46466 46467 46468 46469 46470 46471 46472 | memset(&data[hdr], 0, pBt->usableSize - hdr); } data[hdr] = (char)flags; first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0); memset(&data[hdr+1], 0, 4); data[hdr+7] = 0; put2byte(&data[hdr+5], pBt->usableSize); pPage->nFree = (u16)(pBt->usableSize - first); decodeFlags(pPage, flags); pPage->hdrOffset = hdr; pPage->cellOffset = first; pPage->nOverflow = 0; assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); pPage->maskPage = (u16)(pBt->pageSize - 1); pPage->nCell = 0; pPage->isInit = 1; } /* ** Convert a DbPage obtained from the pager into a MemPage used by |
︙ | ︙ | |||
45525 45526 45527 45528 45529 45530 45531 | return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); } /* ** Open a database file. ** ** zFilename is the name of the database file. If zFilename is NULL | | > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | 46622 46623 46624 46625 46626 46627 46628 46629 46630 46631 46632 46633 46634 46635 46636 46637 46638 46639 46640 46641 46642 46643 46644 46645 46646 46647 46648 46649 46650 46651 46652 46653 46654 46655 46656 46657 46658 46659 46660 46661 46662 46663 46664 46665 46666 46667 46668 46669 46670 46671 46672 46673 46674 46675 46676 46677 46678 46679 46680 46681 46682 46683 46684 46685 46686 46687 46688 46689 46690 46691 46692 46693 46694 46695 46696 46697 46698 46699 46700 46701 46702 46703 46704 46705 46706 46707 46708 46709 46710 46711 46712 46713 46714 46715 46716 46717 46718 46719 46720 46721 46722 46723 46724 | return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); } /* ** Open a database file. ** ** zFilename is the name of the database file. If zFilename is NULL ** then an ephemeral database is created. The ephemeral database might ** be exclusively in memory, or it might use a disk-based memory cache. ** Either way, the ephemeral database will be automatically deleted ** when sqlite3BtreeClose() is called. ** ** If zFilename is ":memory:" then an in-memory database is created ** that is automatically destroyed when it is closed. ** ** The "flags" parameter is a bitmask that might contain bits ** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK ** bit is also set if the SQLITE_NoReadlock flags is set in db->flags. ** These flags are passed through into sqlite3PagerOpen() and must ** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK. ** ** If the database is already opened in the same database connection ** and we are in shared cache mode, then the open will fail with an ** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared ** objects in the same database connection since doing so will lead ** to problems with locking. */ SQLITE_PRIVATE int sqlite3BtreeOpen( const char *zFilename, /* Name of the file containing the BTree database */ sqlite3 *db, /* Associated database handle */ Btree **ppBtree, /* Pointer to new Btree object written here */ int flags, /* Options */ int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */ ){ sqlite3_vfs *pVfs; /* The VFS to use for this btree */ BtShared *pBt = 0; /* Shared part of btree structure */ Btree *p; /* Handle to return */ sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ int rc = SQLITE_OK; /* Result code from this function */ u8 nReserve; /* Byte of unused space on each page */ unsigned char zDbHeader[100]; /* Database header content */ /* True if opening an ephemeral, temporary database */ const int isTempDb = zFilename==0 || zFilename[0]==0; /* Set the variable isMemdb to true for an in-memory database, or ** false for a file-based database. This symbol is only required if ** either of the shared-data or autovacuum features are compiled ** into the library. */ #if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM) #ifdef SQLITE_OMIT_MEMORYDB const int isMemdb = 0; #else const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0) || (isTempDb && sqlite3TempInMemory(db)); #endif #endif assert( db!=0 ); assert( sqlite3_mutex_held(db->mutex) ); assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */ assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 ); /* A BTREE_SINGLE database is always a temporary and/or ephemeral */ assert( (flags & BTREE_SINGLE)==0 || isTempDb ); if( db->flags & SQLITE_NoReadlock ){ flags |= BTREE_NO_READLOCK; } if( isMemdb ){ flags |= BTREE_MEMORY; } if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; } pVfs = db->pVfs; p = sqlite3MallocZero(sizeof(Btree)); if( !p ){ return SQLITE_NOMEM; } p->inTrans = TRANS_NONE; p->db = db; #ifndef SQLITE_OMIT_SHARED_CACHE p->lock.pBtree = p; p->lock.iTable = 1; #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* ** If this Btree is a candidate for shared cache, try to find an ** existing BtShared object that we can share with */ if( isMemdb==0 && isTempDb==0 ){ if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = sqlite3Malloc(nFullPathname); sqlite3_mutex *mutexShared; p->sharable = 1; if( !zFullPathname ){ sqlite3_free(p); |
︙ | ︙ | |||
45659 45660 45661 45662 45663 45664 45665 45666 45667 45668 45669 45670 45671 45672 45673 45674 45675 | EXTRA_SIZE, flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; } pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; pBt->pPage1 = 0; pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); #ifdef SQLITE_SECURE_DELETE pBt->secureDelete = 1; #endif | > | | 46785 46786 46787 46788 46789 46790 46791 46792 46793 46794 46795 46796 46797 46798 46799 46800 46801 46802 46803 46804 46805 46806 46807 46808 46809 46810 | EXTRA_SIZE, flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; } pBt->openFlags = (u8)flags; pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; pBt->pPage1 = 0; pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); #ifdef SQLITE_SECURE_DELETE pBt->secureDelete = 1; #endif pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16); if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ pBt->pageSize = 0; #ifndef SQLITE_OMIT_AUTOVACUUM /* If the magic name ":memory:" will create an in-memory database, then ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if |
︙ | ︙ | |||
45763 45764 45765 45766 45767 45768 45769 45770 45771 45772 45773 45774 45775 45776 | if( rc!=SQLITE_OK ){ if( pBt && pBt->pPager ){ sqlite3PagerClose(pBt->pPager); } sqlite3_free(pBt); sqlite3_free(p); *ppBtree = 0; } if( mutexOpen ){ assert( sqlite3_mutex_held(mutexOpen) ); sqlite3_mutex_leave(mutexOpen); } return rc; } | > > > > > > > > | 46890 46891 46892 46893 46894 46895 46896 46897 46898 46899 46900 46901 46902 46903 46904 46905 46906 46907 46908 46909 46910 46911 | if( rc!=SQLITE_OK ){ if( pBt && pBt->pPager ){ sqlite3PagerClose(pBt->pPager); } sqlite3_free(pBt); sqlite3_free(p); *ppBtree = 0; }else{ /* If the B-Tree was successfully opened, set the pager-cache size to the ** default value. Except, when opening on an existing shared pager-cache, ** do not change the pager-cache size. */ if( sqlite3BtreeSchema(p, 0, 0)==0 ){ sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); } } if( mutexOpen ){ assert( sqlite3_mutex_held(mutexOpen) ); sqlite3_mutex_leave(mutexOpen); } return rc; } |
︙ | ︙ | |||
45983 45984 45985 45986 45987 45988 45989 | nReserve = pBt->pageSize - pBt->usableSize; } assert( nReserve>=0 && nReserve<=255 ); if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && ((pageSize-1)&pageSize)==0 ){ assert( (pageSize & 7)==0 ); assert( !pBt->pPage1 && !pBt->pCursor ); | | | 47118 47119 47120 47121 47122 47123 47124 47125 47126 47127 47128 47129 47130 47131 47132 | nReserve = pBt->pageSize - pBt->usableSize; } assert( nReserve>=0 && nReserve<=255 ); if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && ((pageSize-1)&pageSize)==0 ){ assert( (pageSize & 7)==0 ); assert( !pBt->pPage1 && !pBt->pCursor ); pBt->pageSize = (u32)pageSize; freeTempSpace(pBt); } rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); pBt->usableSize = pBt->pageSize - (u16)nReserve; if( iFix ) pBt->pageSizeFixed = 1; sqlite3BtreeLeave(p); return rc; |
︙ | ︙ | |||
46118 46119 46120 46121 46122 46123 46124 | rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); | | < < | | | 47253 47254 47255 47256 47257 47258 47259 47260 47261 47262 47263 47264 47265 47266 47267 47268 47269 47270 47271 47272 47273 | rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); sqlite3PagerPagecount(pBt->pPager, &nPageFile); if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ nPage = nPageFile; } if( nPage>0 ){ u32 pageSize; u32 usableSize; u8 *page1 = pPage1->aData; rc = SQLITE_NOTADB; if( memcmp(page1, zMagicHeader, 16)!=0 ){ goto page1_init_failed; } #ifdef SQLITE_OMIT_WAL |
︙ | ︙ | |||
46177 46178 46179 46180 46181 46182 46183 | ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. ** The original design allowed these amounts to vary, but as of ** version 3.6.0, we require them to be fixed. */ if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ goto page1_init_failed; } | | | | > | | | | | | | | | | | 47310 47311 47312 47313 47314 47315 47316 47317 47318 47319 47320 47321 47322 47323 47324 47325 47326 47327 47328 47329 47330 47331 47332 47333 47334 47335 47336 47337 47338 47339 47340 47341 47342 47343 47344 47345 47346 47347 47348 47349 47350 47351 47352 47353 47354 47355 47356 47357 47358 47359 47360 47361 47362 47363 47364 47365 47366 47367 47368 47369 47370 47371 47372 47373 47374 47375 47376 47377 47378 47379 | ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. ** The original design allowed these amounts to vary, but as of ** version 3.6.0, we require them to be fixed. */ if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ goto page1_init_failed; } pageSize = (page1[16]<<8) | (page1[17]<<16); if( ((pageSize-1)&pageSize)!=0 || pageSize>SQLITE_MAX_PAGE_SIZE || pageSize<=256 ){ goto page1_init_failed; } assert( (pageSize & 7)==0 ); usableSize = pageSize - page1[20]; if( (u32)pageSize!=pBt->pageSize ){ /* After reading the first page of the database assuming a page size ** of BtShared.pageSize, we have discovered that the page-size is ** actually pageSize. Unlock the database, leave pBt->pPage1 at ** zero and return SQLITE_OK. The caller will call this function ** again with the correct page-size. */ releasePage(pPage1); pBt->usableSize = usableSize; pBt->pageSize = pageSize; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); return rc; } if( nPageHeader>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } if( usableSize<480 ){ goto page1_init_failed; } pBt->pageSize = pageSize; pBt->usableSize = usableSize; #ifndef SQLITE_OMIT_AUTOVACUUM pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0); #endif } /* maxLocal is the maximum amount of payload to store locally for ** a cell. Make sure it is small enough so that at least minFanout ** cells can will fit on one page. We assume a 10-byte page header. ** Besides the payload, the cell must store: ** 2-byte pointer to the cell ** 4-byte child pointer ** 9-byte nKey value ** 4-byte nData value ** 4-byte overflow page pointer ** So a cell consists of a 2-byte pointer, a header which is as much as ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow ** page pointer. */ pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23); pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23); pBt->maxLeaf = (u16)(pBt->usableSize - 35); pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23); assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; pBt->nPage = nPage; return SQLITE_OK; page1_init_failed: releasePage(pPage1); |
︙ | ︙ | |||
46284 46285 46286 46287 46288 46289 46290 | pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; rc = sqlite3PagerWrite(pP1->pDbPage); if( rc ) return rc; memcpy(data, zMagicHeader, sizeof(zMagicHeader)); assert( sizeof(zMagicHeader)==16 ); | | > | 47418 47419 47420 47421 47422 47423 47424 47425 47426 47427 47428 47429 47430 47431 47432 47433 | pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; rc = sqlite3PagerWrite(pP1->pDbPage); if( rc ) return rc; memcpy(data, zMagicHeader, sizeof(zMagicHeader)); assert( sizeof(zMagicHeader)==16 ); data[16] = (u8)((pBt->pageSize>>8)&0xff); data[17] = (u8)((pBt->pageSize>>16)&0xff); data[18] = 1; data[19] = 1; assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize); data[20] = (u8)(pBt->pageSize - pBt->usableSize); data[21] = 64; data[22] = 32; data[23] = 32; |
︙ | ︙ | |||
48295 48296 48297 48298 48299 48300 48301 | }else{ assert( nCellKey>intKey ); c = +1; } pCur->validNKey = 1; pCur->info.nKey = nCellKey; }else{ | | | | 49430 49431 49432 49433 49434 49435 49436 49437 49438 49439 49440 49441 49442 49443 49444 49445 49446 | }else{ assert( nCellKey>intKey ); c = +1; } pCur->validNKey = 1; pCur->info.nKey = nCellKey; }else{ /* The maximum supported page-size is 65536 bytes. This means that ** the maximum number of record bytes stored on an index B-Tree ** page is less than 16384 bytes and may be stored as a 2-byte ** varint. This information is used to attempt to avoid parsing ** the entire cell by checking for the cases where the record is ** stored entirely within the b-tree page by inspecting the first ** 2 bytes of the cell. */ int nCell = pCell[0]; if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){ |
︙ | ︙ | |||
48660 48661 48662 48663 48664 48665 48666 48667 48668 48669 48670 48671 48672 48673 | if( rc ){ goto end_allocate_page; } if( k==0 ){ if( !pPrevTrunk ){ memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); }else{ memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4); } }else{ /* The trunk page is required by the caller but it contains ** pointers to free-list leaves. The first leaf becomes a trunk ** page in this case. */ | > > > > | 49795 49796 49797 49798 49799 49800 49801 49802 49803 49804 49805 49806 49807 49808 49809 49810 49811 49812 | if( rc ){ goto end_allocate_page; } if( k==0 ){ if( !pPrevTrunk ){ memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); }else{ rc = sqlite3PagerWrite(pPrevTrunk->pDbPage); if( rc!=SQLITE_OK ){ goto end_allocate_page; } memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4); } }else{ /* The trunk page is required by the caller but it contains ** pointers to free-list leaves. The first leaf becomes a trunk ** page in this case. */ |
︙ | ︙ | |||
48966 48967 48968 48969 48970 48971 48972 | */ static int clearCell(MemPage *pPage, unsigned char *pCell){ BtShared *pBt = pPage->pBt; CellInfo info; Pgno ovflPgno; int rc; int nOvfl; | | | 50105 50106 50107 50108 50109 50110 50111 50112 50113 50114 50115 50116 50117 50118 50119 | */ static int clearCell(MemPage *pPage, unsigned char *pCell){ BtShared *pBt = pPage->pBt; CellInfo info; Pgno ovflPgno; int rc; int nOvfl; u32 ovflPageSize; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); btreeParseCellPtr(pPage, pCell, &info); if( info.iOverflow==0 ){ return SQLITE_OK; /* No overflow pages. Return without doing anything */ } ovflPgno = get4byte(&pCell[info.iOverflow]); |
︙ | ︙ | |||
49191 49192 49193 49194 49195 49196 49197 | ** the cell content has been copied someplace else. This routine just ** removes the reference to the cell from pPage. ** ** "sz" must be the number of bytes in the cell. */ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ int i; /* Loop counter */ | | | | 50330 50331 50332 50333 50334 50335 50336 50337 50338 50339 50340 50341 50342 50343 50344 50345 50346 50347 50348 50349 50350 50351 50352 50353 50354 50355 50356 50357 50358 50359 50360 50361 50362 | ** the cell content has been copied someplace else. This routine just ** removes the reference to the cell from pPage. ** ** "sz" must be the number of bytes in the cell. */ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ int i; /* Loop counter */ u32 pc; /* Offset to cell content of cell being deleted */ u8 *data; /* pPage->aData */ u8 *ptr; /* Used to move bytes around within data[] */ int rc; /* The return code */ int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ if( *pRC ) return; assert( idx>=0 && idx<pPage->nCell ); assert( sz==cellSize(pPage, idx) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); data = pPage->aData; ptr = &data[pPage->cellOffset + 2*idx]; pc = get2byte(ptr); hdr = pPage->hdrOffset; testcase( pc==get2byte(&data[hdr+5]) ); testcase( pc+sz==pPage->pBt->usableSize ); if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){ *pRC = SQLITE_CORRUPT_BKPT; return; } rc = freeSpace(pPage, pc, sz); if( rc ){ *pRC = rc; return; |
︙ | ︙ | |||
49266 49267 49268 49269 49270 49271 49272 | u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); | | | 50405 50406 50407 50408 50409 50410 50411 50412 50413 50414 50415 50416 50417 50418 50419 | u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); /* The cell should normally be sized correctly. However, when moving a ** malformed cell from a leaf page to an interior page, if the cell size ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size ** might be less than 8 (leaf-size + pointer) on the interior node. Hence ** the term after the || in the following assert(). */ |
︙ | ︙ | |||
49346 49347 49348 49349 49350 49351 49352 | int cellbody; /* Address of next cell body */ u8 * const data = pPage->aData; /* Pointer to data for pPage */ const int hdr = pPage->hdrOffset; /* Offset of header on pPage */ const int nUsable = pPage->pBt->usableSize; /* Usable size of page */ assert( pPage->nOverflow==0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); | | | | 50485 50486 50487 50488 50489 50490 50491 50492 50493 50494 50495 50496 50497 50498 50499 50500 50501 50502 50503 50504 | int cellbody; /* Address of next cell body */ u8 * const data = pPage->aData; /* Pointer to data for pPage */ const int hdr = pPage->hdrOffset; /* Offset of header on pPage */ const int nUsable = pPage->pBt->usableSize; /* Usable size of page */ 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( sqlite3PagerIswriteable(pPage->pDbPage) ); /* Check that the page has just been zeroed by zeroPage() */ assert( pPage->nCell==0 ); assert( get2byteNotZero(&data[hdr+5])==nUsable ); pCellptr = &data[pPage->cellOffset + nCell*2]; cellbody = nUsable; for(i=nCell-1; i>=0; i--){ pCellptr -= 2; cellbody -= aSize[i]; put2byte(pCellptr, cellbody); |
︙ | ︙ | |||
49417 49418 49419 49420 49421 49422 49423 49424 49425 49426 49427 49428 49429 49430 | int rc; /* Return Code */ Pgno pgnoNew; /* Page number of pNew */ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT; /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell ** may be inserted. If both these operations are successful, proceed. */ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); | > | 50556 50557 50558 50559 50560 50561 50562 50563 50564 50565 50566 50567 50568 50569 50570 | int rc; /* Return Code */ Pgno pgnoNew; /* Page number of pNew */ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); /* This error condition is now caught prior to reaching this function */ if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT; /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell ** may be inserted. If both these operations are successful, proceed. */ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); |
︙ | ︙ | |||
49746 49747 49748 49749 49750 49751 49752 | ** Unless SQLite is compiled in secure-delete mode. In this case, ** the dropCell() routine will overwrite the entire cell with zeroes. ** In this case, temporarily copy the cell into the aOvflSpace[] ** buffer. It will be copied out again as soon as the aSpace[] buffer ** is allocated. */ if( pBt->secureDelete ){ int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); | | | 50886 50887 50888 50889 50890 50891 50892 50893 50894 50895 50896 50897 50898 50899 50900 | ** Unless SQLite is compiled in secure-delete mode. In this case, ** the dropCell() routine will overwrite the entire cell with zeroes. ** In this case, temporarily copy the cell into the aOvflSpace[] ** buffer. It will be copied out again as soon as the aSpace[] buffer ** is allocated. */ if( pBt->secureDelete ){ int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); if( (iOff+szNew[i])>(int)pBt->usableSize ){ rc = SQLITE_CORRUPT_BKPT; memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; }else{ memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; } |
︙ | ︙ | |||
49825 49826 49827 49828 49829 49830 49831 | if( i<nOld-1 && !leafData){ u16 sz = (u16)szNew[i]; u8 *pTemp; assert( nCell<nMaxCells ); szCell[nCell] = sz; pTemp = &aSpace1[iSpace1]; iSpace1 += sz; | | | 50965 50966 50967 50968 50969 50970 50971 50972 50973 50974 50975 50976 50977 50978 50979 | if( i<nOld-1 && !leafData){ u16 sz = (u16)szNew[i]; u8 *pTemp; assert( nCell<nMaxCells ); szCell[nCell] = sz; pTemp = &aSpace1[iSpace1]; iSpace1 += sz; assert( sz<=pBt->maxLocal+23 ); assert( iSpace1<=pBt->pageSize ); memcpy(pTemp, apDiv[i], sz); apCell[nCell] = pTemp+leafCorrection; assert( leafCorrection==0 || leafCorrection==4 ); szCell[nCell] = szCell[nCell] - leafCorrection; if( !pOld->leaf ){ assert( leafCorrection==0 ); |
︙ | ︙ | |||
50071 50072 50073 50074 50075 50076 50077 | */ if( szCell[j]==4 ){ assert(leafCorrection==4); sz = cellSizePtr(pParent, pCell); } } iOvflSpace += sz; | | | 51211 51212 51213 51214 51215 51216 51217 51218 51219 51220 51221 51222 51223 51224 51225 | */ if( szCell[j]==4 ){ assert(leafCorrection==4); sz = cellSizePtr(pParent, pCell); } } iOvflSpace += sz; assert( sz<=pBt->maxLocal+23 ); assert( iOvflSpace<=pBt->pageSize ); insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc); if( rc!=SQLITE_OK ) goto balance_cleanup; assert( sqlite3PagerIswriteable(pParent->pDbPage) ); j++; nxDiv++; |
︙ | ︙ | |||
50708 50709 50710 50711 50712 50713 50714 | ** The type of type is determined by the flags parameter. Only the ** following values of flags are currently in use. Other values for ** flags might not work: ** ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys ** BTREE_ZERODATA Used for SQL indices */ | | > | 51848 51849 51850 51851 51852 51853 51854 51855 51856 51857 51858 51859 51860 51861 51862 51863 51864 51865 51866 51867 | ** The type of type is determined by the flags parameter. Only the ** following values of flags are currently in use. Other values for ** flags might not work: ** ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys ** BTREE_ZERODATA Used for SQL indices */ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ BtShared *pBt = p->pBt; MemPage *pRoot; Pgno pgnoRoot; int rc; int ptfFlags; /* Page-type flage for the root page of new table */ assert( sqlite3BtreeHoldsMutex(p) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); #ifdef SQLITE_OMIT_AUTOVACUUM rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); |
︙ | ︙ | |||
50831 50832 50833 50834 50835 50836 50837 | }else{ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); if( rc ) return rc; } #endif assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); | > > > > > | > | 51972 51973 51974 51975 51976 51977 51978 51979 51980 51981 51982 51983 51984 51985 51986 51987 51988 51989 51990 51991 51992 51993 | }else{ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); if( rc ) return rc; } #endif assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); if( createTabFlags & BTREE_INTKEY ){ ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; }else{ ptfFlags = PTF_ZERODATA | PTF_LEAF; } zeroPage(pRoot, ptfFlags); sqlite3PagerUnref(pRoot->pDbPage); assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); *piTable = (int)pgnoRoot; return SQLITE_OK; } SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ int rc; sqlite3BtreeEnter(p); rc = btreeCreateTable(p, piTable, flags); |
︙ | ︙ | |||
51315 51316 51317 51318 51319 51320 51321 | if( isFreeList ){ int n = get4byte(&pOvflData[4]); #ifndef SQLITE_OMIT_AUTOVACUUM if( pCheck->pBt->autoVacuum ){ checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext); } #endif | | | 52462 52463 52464 52465 52466 52467 52468 52469 52470 52471 52472 52473 52474 52475 52476 | if( isFreeList ){ int n = get4byte(&pOvflData[4]); #ifndef SQLITE_OMIT_AUTOVACUUM if( pCheck->pBt->autoVacuum ){ checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext); } #endif if( n>(int)pCheck->pBt->usableSize/4-2 ){ checkAppendMsg(pCheck, zContext, "freelist leaf count too big on page %d", iPage); N--; }else{ for(i=0; i<n; i++){ Pgno iFreePage = get4byte(&pOvflData[8+i*4]); #ifndef SQLITE_OMIT_AUTOVACUUM |
︙ | ︙ | |||
51526 51527 51528 51529 51530 51531 51532 | */ data = pPage->aData; hdr = pPage->hdrOffset; hit = sqlite3PageMalloc( pBt->pageSize ); if( hit==0 ){ pCheck->mallocFailed = 1; }else{ | | | | | 52673 52674 52675 52676 52677 52678 52679 52680 52681 52682 52683 52684 52685 52686 52687 52688 52689 52690 52691 52692 52693 52694 52695 52696 52697 52698 52699 52700 | */ data = pPage->aData; hdr = pPage->hdrOffset; hit = sqlite3PageMalloc( pBt->pageSize ); if( hit==0 ){ pCheck->mallocFailed = 1; }else{ int contentOffset = get2byteNotZero(&data[hdr+5]); assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ memset(hit+contentOffset, 0, usableSize-contentOffset); memset(hit, 1, contentOffset); nCell = get2byte(&data[hdr+3]); cellStart = hdr + 12 - 4*pPage->leaf; for(i=0; i<nCell; i++){ int pc = get2byte(&data[cellStart+i*2]); u32 size = 65536; int j; if( pc<=usableSize-4 ){ size = cellSizePtr(pPage, &data[pc]); } if( (int)(pc+size-1)>=usableSize ){ checkAppendMsg(pCheck, 0, "Corruption detected in cell %d on page %d",i,iPage); }else{ for(j=pc+size-1; j>=pc; j--) hit[j]++; } } i = get2byte(&data[hdr+1]); |
︙ | ︙ | |||
52092 52093 52094 52095 52096 52097 52098 | if( pSrcDb==pDestDb ){ sqlite3Error( pDestDb, SQLITE_ERROR, "source and destination must be distinct" ); p = 0; }else { | | > > > | 53239 53240 53241 53242 53243 53244 53245 53246 53247 53248 53249 53250 53251 53252 53253 53254 53255 53256 | if( pSrcDb==pDestDb ){ sqlite3Error( pDestDb, SQLITE_ERROR, "source and destination must be distinct" ); p = 0; }else { /* Allocate space for a new sqlite3_backup object... ** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a ** call to sqlite3_backup_init() and is destroyed by a call to ** sqlite3_backup_finish(). */ p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup)); if( !p ){ sqlite3Error(pDestDb, SQLITE_NOMEM, 0); } } /* If the allocation succeeded, populate the new object. */ |
︙ | ︙ | |||
52162 52163 52164 52165 52166 52167 52168 52169 52170 52171 52172 52173 52174 52175 | /* Catch the case where the destination is an in-memory database and the ** page sizes of the source and destination differ. */ if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ rc = SQLITE_READONLY; } /* This loop runs once for each destination page spanned by the source ** page. For each iteration, variable iOff is set to the byte offset ** of the destination page. */ for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){ DbPage *pDestPg = 0; | > > > > > > > > > | 53312 53313 53314 53315 53316 53317 53318 53319 53320 53321 53322 53323 53324 53325 53326 53327 53328 53329 53330 53331 53332 53333 53334 | /* Catch the case where the destination is an in-memory database and the ** page sizes of the source and destination differ. */ if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ rc = SQLITE_READONLY; } #ifdef SQLITE_HAS_CODEC /* Backup is not possible if the page size of the destination is changing ** a a codec is in use. */ if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){ rc = SQLITE_READONLY; } #endif /* This loop runs once for each destination page spanned by the source ** page. For each iteration, variable iOff is set to the byte offset ** of the destination page. */ for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){ DbPage *pDestPg = 0; |
︙ | ︙ | |||
52466 52467 52468 52469 52470 52471 52472 52473 52474 52475 52476 52477 52478 52479 | /* Exit the mutexes and free the backup context structure. */ if( p->pDestDb ){ sqlite3_mutex_leave(p->pDestDb->mutex); } sqlite3BtreeLeave(p->pSrc); if( p->pDestDb ){ sqlite3_free(p); } sqlite3_mutex_leave(mutex); return rc; } /* | > > > | 53625 53626 53627 53628 53629 53630 53631 53632 53633 53634 53635 53636 53637 53638 53639 53640 53641 | /* Exit the mutexes and free the backup context structure. */ if( p->pDestDb ){ sqlite3_mutex_leave(p->pDestDb->mutex); } sqlite3BtreeLeave(p->pSrc); if( p->pDestDb ){ /* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a ** call to sqlite3_backup_init() and is destroyed by a call to ** sqlite3_backup_finish(). */ sqlite3_free(p); } sqlite3_mutex_leave(mutex); return rc; } /* |
︙ | ︙ | |||
52717 52718 52719 52720 52721 52722 52723 52724 52725 52726 52727 52728 52729 52730 | if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){ return SQLITE_NOMEM; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; } return SQLITE_OK; } /* ** If the given Mem* has a zero-filled tail, turn it into an ordinary | > > > | 53879 53880 53881 53882 53883 53884 53885 53886 53887 53888 53889 53890 53891 53892 53893 53894 53895 | if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){ return SQLITE_NOMEM; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; #ifdef SQLITE_DEBUG pMem->pScopyFrom = 0; #endif } return SQLITE_OK; } /* ** If the given Mem* has a zero-filled tail, turn it into an ordinary |
︙ | ︙ | |||
52837 52838 52839 52840 52841 52842 52843 | assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); memset(&ctx, 0, sizeof(ctx)); ctx.s.flags = MEM_Null; ctx.s.db = pMem->db; ctx.pMem = pMem; ctx.pFunc = pFunc; | | | 54002 54003 54004 54005 54006 54007 54008 54009 54010 54011 54012 54013 54014 54015 54016 | assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); memset(&ctx, 0, sizeof(ctx)); ctx.s.flags = MEM_Null; ctx.s.db = pMem->db; ctx.pMem = pMem; ctx.pFunc = pFunc; pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel ); sqlite3DbFree(pMem->db, pMem->zMalloc); memcpy(pMem, &ctx.s, sizeof(ctx.s)); rc = ctx.isError; } return rc; } |
︙ | ︙ | |||
52950 52951 52952 52953 52954 52955 52956 | flags = pMem->flags; if( flags & MEM_Int ){ return pMem->u.i; }else if( flags & MEM_Real ){ return doubleToInt64(pMem->r); }else if( flags & (MEM_Str|MEM_Blob) ){ i64 value; | < < < < < | > | | 54115 54116 54117 54118 54119 54120 54121 54122 54123 54124 54125 54126 54127 54128 54129 54130 54131 | flags = pMem->flags; if( flags & MEM_Int ){ return pMem->u.i; }else if( flags & MEM_Real ){ return doubleToInt64(pMem->r); }else if( flags & (MEM_Str|MEM_Blob) ){ i64 value; assert( pMem->z || pMem->n==0 ); testcase( pMem->z==0 ); sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); return value; }else{ return 0; } } /* |
︙ | ︙ | |||
52979 52980 52981 52982 52983 52984 52985 | if( pMem->flags & MEM_Real ){ return pMem->r; }else if( pMem->flags & MEM_Int ){ return (double)pMem->u.i; }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ double val = (double)0; | < < < < < < < | | 54140 54141 54142 54143 54144 54145 54146 54147 54148 54149 54150 54151 54152 54153 54154 | if( pMem->flags & MEM_Real ){ return pMem->r; }else if( pMem->flags & MEM_Int ){ return (double)pMem->u.i; }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ double val = (double)0; sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); return val; }else{ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ return (double)0; } } |
︙ | ︙ | |||
53059 53060 53061 53062 53063 53064 53065 | ** Invalidate any prior representations. ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ | < | | | < < < < | | | | | | | > > > | 54213 54214 54215 54216 54217 54218 54219 54220 54221 54222 54223 54224 54225 54226 54227 54228 54229 54230 54231 54232 54233 54234 54235 54236 54237 54238 54239 | ** Invalidate any prior representations. ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){ MemSetTypeFlag(pMem, MEM_Int); }else{ pMem->r = sqlite3VdbeRealValue(pMem); MemSetTypeFlag(pMem, MEM_Real); sqlite3VdbeIntegerAffinity(pMem); } } assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); pMem->flags &= ~(MEM_Str|MEM_Blob); return SQLITE_OK; } /* ** Delete any previous value and set the value stored in *pMem to NULL. */ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){ |
︙ | ︙ | |||
53178 53179 53180 53181 53182 53183 53184 53185 53186 53187 53188 53189 53190 53191 | n += p->u.nZero; } return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; } return 0; } /* ** Size of struct Mem not including the Mem.zMalloc member. */ #define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc)) /* ** Make an shallow copy of pFrom into pTo. Prior contents of | > > > > > > > > > > > > > > > > > > > > > > | 54330 54331 54332 54333 54334 54335 54336 54337 54338 54339 54340 54341 54342 54343 54344 54345 54346 54347 54348 54349 54350 54351 54352 54353 54354 54355 54356 54357 54358 54359 54360 54361 54362 54363 54364 54365 | n += p->u.nZero; } return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; } return 0; } #ifdef SQLITE_DEBUG /* ** This routine prepares a memory cell for modication by breaking ** its link to a shallow copy and by marking any current shallow ** copies of this cell as invalid. ** ** This is used for testing and debugging only - to make sure shallow ** copies are not misused. */ SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){ int i; Mem *pX; for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){ if( pX->pScopyFrom==pMem ){ pX->flags |= MEM_Invalid; pX->pScopyFrom = 0; } } pMem->pScopyFrom = 0; } #endif /* SQLITE_DEBUG */ /* ** Size of struct Mem not including the Mem.zMalloc member. */ #define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc)) /* ** Make an shallow copy of pFrom into pTo. Prior contents of |
︙ | ︙ | |||
53546 53547 53548 53549 53550 53551 53552 | sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ return 0; } } | | | 54720 54721 54722 54723 54724 54725 54726 54727 54728 54729 54730 54731 54732 54733 54734 | sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ return 0; } } sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */ }else{ assert( (pVal->flags&MEM_Blob)==0 ); sqlite3VdbeMemStringify(pVal, enc); assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) ); } assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0 || pVal->db->mallocFailed ); |
︙ | ︙ | |||
53594 53595 53596 53597 53598 53599 53600 53601 53602 53603 53604 53605 53606 53607 53608 53609 53610 53611 53612 53613 53614 53615 53616 53617 53618 53619 53620 53621 | u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ sqlite3_value **ppVal /* Write the new value here */ ){ int op; char *zVal = 0; sqlite3_value *pVal = 0; if( !pExpr ){ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2. ** The ifdef here is to enable us to achieve 100% branch test coverage even ** when SQLITE_ENABLE_STAT2 is omitted. */ #ifdef SQLITE_ENABLE_STAT2 if( op==TK_REGISTER ) op = pExpr->op2; #else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; #endif if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = sqlite3ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ | > > > > > > > > > > > > > | | > > > > | 54768 54769 54770 54771 54772 54773 54774 54775 54776 54777 54778 54779 54780 54781 54782 54783 54784 54785 54786 54787 54788 54789 54790 54791 54792 54793 54794 54795 54796 54797 54798 54799 54800 54801 54802 54803 54804 54805 54806 54807 54808 54809 54810 54811 54812 54813 54814 54815 54816 54817 54818 54819 54820 54821 54822 54823 54824 54825 54826 54827 54828 54829 54830 54831 54832 54833 54834 54835 54836 54837 54838 54839 | u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ sqlite3_value **ppVal /* Write the new value here */ ){ int op; char *zVal = 0; sqlite3_value *pVal = 0; int negInt = 1; const char *zNeg = ""; if( !pExpr ){ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2. ** The ifdef here is to enable us to achieve 100% branch test coverage even ** when SQLITE_ENABLE_STAT2 is omitted. */ #ifdef SQLITE_ENABLE_STAT2 if( op==TK_REGISTER ) op = pExpr->op2; #else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; #endif /* Handle negative integers in a single step. This is needed in the ** case when the value is -9223372036854775808. */ if( op==TK_UMINUS && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ pExpr = pExpr->pLeft; op = pExpr->op; negInt = -1; zNeg = "-"; } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = sqlite3ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; } if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); }else{ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); } if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; if( enc!=SQLITE_UTF8 ){ sqlite3VdbeChangeEncoding(pVal, enc); } }else if( op==TK_UMINUS ) { /* 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; sqlite3ValueApplyAffinity(pVal, affinity, enc); } } #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ int nVal; assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); assert( pExpr->u.zToken[1]=='\'' ); |
︙ | ︙ | |||
55461 55462 55463 55464 55465 55466 55467 | ** still have 'null' as the master journal pointer, so they will roll ** back independently if a failure occurs. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ char const *zFile = sqlite3BtreeGetJournalname(pBt); | | > | 56652 56653 56654 56655 56656 56657 56658 56659 56660 56661 56662 56663 56664 56665 56666 56667 56668 56669 | ** still have 'null' as the master journal pointer, so they will roll ** back independently if a failure occurs. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ char const *zFile = sqlite3BtreeGetJournalname(pBt); if( zFile==0 ){ continue; /* Ignore TEMP and :memory: databases */ } assert( zFile[0]!=0 ); if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){ needSync = 1; } rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); offset += sqlite3Strlen30(zFile)+1; if( rc!=SQLITE_OK ){ sqlite3OsCloseFree(pMaster); |
︙ | ︙ | |||
55767 55768 55769 55770 55771 55772 55773 | /* Check for one of the special errors */ mrc = p->rc & 0xff; assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; if( isSpecialError ){ | | > > > > > > > > > | | 56959 56960 56961 56962 56963 56964 56965 56966 56967 56968 56969 56970 56971 56972 56973 56974 56975 56976 56977 56978 56979 56980 56981 56982 56983 | /* Check for one of the special errors */ mrc = p->rc & 0xff; assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; if( isSpecialError ){ /* If the query was read-only and the error code is SQLITE_INTERRUPT, ** no rollback is necessary. Otherwise, at least a savepoint ** transaction must be rolled back to restore the database to a ** consistent state. ** ** Even if the statement is read-only, it is important to perform ** a statement or transaction rollback operation. If the error ** occured while writing to the journal, sub-journal or database ** file as part of an effort to free up cache space (see function ** pagerStress() in pager.c), the rollback is required to restore ** the pager to a consistent state. */ if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ eStatementOp = SAVEPOINT_ROLLBACK; }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. |
︙ | ︙ | |||
56918 56919 56920 56921 56922 56923 56924 56925 56926 56927 56928 56929 56930 56931 | ** ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ rc = SQLITE_OK; }else{ Vdbe *v = (Vdbe*)pStmt; sqlite3 *db = v->db; #if SQLITE_THREADSAFE sqlite3_mutex *mutex; #endif | > > | 58119 58120 58121 58122 58123 58124 58125 58126 58127 58128 58129 58130 58131 58132 58133 58134 | ** ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ /* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL ** pointer is a harmless no-op. */ rc = SQLITE_OK; }else{ Vdbe *v = (Vdbe*)pStmt; sqlite3 *db = v->db; #if SQLITE_THREADSAFE sqlite3_mutex *mutex; #endif |
︙ | ︙ | |||
56994 56995 56996 56997 56998 56999 57000 | */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ sqlite3VdbeMemExpandBlob(p); p->flags &= ~MEM_Str; p->flags |= MEM_Blob; | | | 58197 58198 58199 58200 58201 58202 58203 58204 58205 58206 58207 58208 58209 58210 58211 | */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ sqlite3VdbeMemExpandBlob(p); p->flags &= ~MEM_Str; p->flags |= MEM_Blob; return p->n ? p->z : 0; }else{ return sqlite3_value_text(pVal); } } SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF8); } |
︙ | ︙ | |||
57348 57349 57350 57351 57352 57353 57354 57355 57356 57357 57358 57359 57360 57361 | assert( p && p->pFunc ); return p->pFunc->pUserData; } /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ assert( p && p->pFunc ); return p->s.db; } /* | > > > > > > | 58551 58552 58553 58554 58555 58556 58557 58558 58559 58560 58561 58562 58563 58564 58565 58566 58567 58568 58569 58570 | assert( p && p->pFunc ); return p->pFunc->pUserData; } /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. ** ** IMPLEMENTATION-OF: R-46798-50301 The sqlite3_context_db_handle() interface ** returns a copy of the pointer to the database connection (the 1st ** parameter) of the sqlite3_create_function() and ** sqlite3_create_function16() routines that originally registered the ** application defined function. */ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ assert( p && p->pFunc ); return p->s.db; } /* |
︙ | ︙ | |||
57557 57558 57559 57560 57561 57562 57563 | ** sqlite3_column_int() ** sqlite3_column_int64() ** sqlite3_column_text() ** sqlite3_column_text16() ** sqlite3_column_real() ** sqlite3_column_bytes() ** sqlite3_column_bytes16() | < | | 58766 58767 58768 58769 58770 58771 58772 58773 58774 58775 58776 58777 58778 58779 58780 | ** sqlite3_column_int() ** sqlite3_column_int64() ** sqlite3_column_text() ** sqlite3_column_text16() ** sqlite3_column_real() ** sqlite3_column_bytes() ** sqlite3_column_bytes16() ** sqiite3_column_blob() */ static void columnMallocFailure(sqlite3_stmt *pStmt) { /* If malloc() failed during an encoding conversion within an ** sqlite3_column_XXX API, then set the return code of the statement to ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR ** and _finalize() will return NOMEM. |
︙ | ︙ | |||
57826 57827 57828 57829 57830 57831 57832 57833 57834 57835 57836 57837 57838 57839 | pVar = &p->aVar[i]; sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; sqlite3Error(p->db, SQLITE_OK, 0); /* If the bit corresponding to this variable in Vdbe.expmask is set, then ** binding a new value to this variable invalidates the current query plan. */ if( p->isPrepareV2 && ((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff) ){ p->expired = 1; } return SQLITE_OK; | > > > > > > | 59034 59035 59036 59037 59038 59039 59040 59041 59042 59043 59044 59045 59046 59047 59048 59049 59050 59051 59052 59053 | pVar = &p->aVar[i]; sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; sqlite3Error(p->db, SQLITE_OK, 0); /* If the bit corresponding to this variable in Vdbe.expmask is set, then ** binding a new value to this variable invalidates the current query plan. ** ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host ** parameter in the WHERE clause might influence the choice of query plan ** for a statement, then the statement will be automatically recompiled, ** as if there had been a schema change, on the first sqlite3_step() call ** following any change to the bindings of that parameter. */ if( p->isPrepareV2 && ((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff) ){ p->expired = 1; } return SQLITE_OK; |
︙ | ︙ | |||
58323 58324 58325 58326 58327 58328 58329 58330 58331 58332 58333 58334 58335 58336 | ** Various scripts scan this source file in order to generate HTML ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. */ /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test ** procedures use this information to make sure that indices are ** working correctly. This variable has no function other than to ** help verify the correct operation of the library. */ | > > > > > > > > > > > | 59537 59538 59539 59540 59541 59542 59543 59544 59545 59546 59547 59548 59549 59550 59551 59552 59553 59554 59555 59556 59557 59558 59559 59560 59561 | ** Various scripts scan this source file in order to generate HTML ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. */ /* ** Invoke this macro on memory cells just prior to changing the ** value of the cell. This macro verifies that shallow copies are ** not misused. */ #ifdef SQLITE_DEBUG # define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M) #else # define memAboutToChange(P,M) #endif /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test ** procedures use this information to make sure that indices are ** working correctly. This variable has no function other than to ** help verify the correct operation of the library. */ |
︙ | ︙ | |||
58515 58516 58517 58518 58519 58520 58521 | ** Try to convert a value into a numeric representation if we can ** do so without loss of information. In other words, if the string ** looks like a number, convert it into a number. If it does not ** look like a number, leave it alone. */ static void applyNumericAffinity(Mem *pRec){ if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ | > | < | < | < < < < < < < | | | | | | < < < < < < | 59740 59741 59742 59743 59744 59745 59746 59747 59748 59749 59750 59751 59752 59753 59754 59755 59756 59757 59758 59759 59760 59761 59762 59763 59764 | ** Try to convert a value into a numeric representation if we can ** do so without loss of information. In other words, if the string ** looks like a number, convert it into a number. If it does not ** look like a number, leave it alone. */ static void applyNumericAffinity(Mem *pRec){ if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ double rValue; i64 iValue; u8 enc = pRec->enc; if( (pRec->flags&MEM_Str)==0 ) return; if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return; if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){ pRec->u.i = iValue; pRec->flags |= MEM_Int; }else{ pRec->r = rValue; pRec->flags |= MEM_Real; } } } /* ** Processing is determine by the affinity parameter: ** |
︙ | ︙ | |||
59432 59433 59434 59435 59436 59437 59438 59439 59440 59441 59442 59443 59444 59445 59446 59447 59448 59449 59450 59451 59452 59453 59454 59455 59456 59457 59458 59459 59460 59461 59462 59463 59464 59465 59466 59467 59468 59469 59470 59471 59472 59473 | ** value or convert mem[p2] to a different type. */ assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); pOut = &aMem[pOp->p2]; sqlite3VdbeMemReleaseExternal(pOut); pOut->flags = MEM_Int; } /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=p->nMem ); REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } if( (pOp->opflags & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); } if( (pOp->opflags & OPFLG_OUT2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); } if( (pOp->opflags & OPFLG_OUT3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); } #endif switch( pOp->opcode ){ /***************************************************************************** ** What follows is a massive switch statement where each case implements a | > > > > > > | 60643 60644 60645 60646 60647 60648 60649 60650 60651 60652 60653 60654 60655 60656 60657 60658 60659 60660 60661 60662 60663 60664 60665 60666 60667 60668 60669 60670 60671 60672 60673 60674 60675 60676 60677 60678 60679 60680 60681 60682 60683 60684 60685 60686 60687 60688 60689 60690 | ** value or convert mem[p2] to a different type. */ assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); sqlite3VdbeMemReleaseExternal(pOut); pOut->flags = MEM_Int; } /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=p->nMem ); assert( memIsValid(&aMem[pOp->p1]) ); REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } if( (pOp->opflags & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); assert( memIsValid(&aMem[pOp->p2]) ); REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); assert( memIsValid(&aMem[pOp->p3]) ); REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); } if( (pOp->opflags & OPFLG_OUT2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); memAboutToChange(p, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_OUT3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); memAboutToChange(p, &aMem[pOp->p3]); } #endif switch( pOp->opcode ){ /***************************************************************************** ** What follows is a massive switch statement where each case implements a |
︙ | ︙ | |||
59521 59522 59523 59524 59525 59526 59527 59528 59529 59530 59531 59532 59533 59534 | ** ** Write the current address onto register P1 ** and then jump to address P2. */ case OP_Gosub: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); pIn1->flags = MEM_Int; pIn1->u.i = pc; REGISTER_TRACE(pOp->p1, pIn1); pc = pOp->p2 - 1; break; } | > | 60738 60739 60740 60741 60742 60743 60744 60745 60746 60747 60748 60749 60750 60751 60752 | ** ** Write the current address onto register P1 ** and then jump to address P2. */ case OP_Gosub: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; pIn1->u.i = pc; REGISTER_TRACE(pOp->p1, pIn1); pc = pOp->p2 - 1; break; } |
︙ | ︙ | |||
59728 59729 59730 59731 59732 59733 59734 | break; } /* Opcode: Blob P1 P2 * P4 ** ** P4 points to a blob of data P1 bytes long. Store this | | < < < < | 60946 60947 60948 60949 60950 60951 60952 60953 60954 60955 60956 60957 60958 60959 60960 | break; } /* Opcode: Blob P1 P2 * P4 ** ** P4 points to a blob of data P1 bytes long. Store this ** blob in register P2. */ case OP_Blob: { /* out2-prerelease */ assert( pOp->p1 <= SQLITE_MAX_LENGTH ); sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; |
︙ | ︙ | |||
59790 59791 59792 59793 59794 59795 59796 59797 59798 59799 59800 59801 59802 59803 | assert( u.ac.p1+u.ac.n<=u.ac.p2 || u.ac.p2+u.ac.n<=u.ac.p1 ); pIn1 = &aMem[u.ac.p1]; pOut = &aMem[u.ac.p2]; while( u.ac.n-- ){ assert( pOut<=&aMem[p->nMem] ); assert( pIn1<=&aMem[p->nMem] ); u.ac.zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); pIn1->zMalloc = u.ac.zMalloc; REGISTER_TRACE(u.ac.p2++, pOut); pIn1++; pOut++; | > > | 61004 61005 61006 61007 61008 61009 61010 61011 61012 61013 61014 61015 61016 61017 61018 61019 | assert( u.ac.p1+u.ac.n<=u.ac.p2 || u.ac.p2+u.ac.n<=u.ac.p1 ); pIn1 = &aMem[u.ac.p1]; pOut = &aMem[u.ac.p2]; while( u.ac.n-- ){ assert( pOut<=&aMem[p->nMem] ); assert( pIn1<=&aMem[p->nMem] ); assert( memIsValid(pIn1) ); memAboutToChange(p, pOut); u.ac.zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); pIn1->zMalloc = u.ac.zMalloc; REGISTER_TRACE(u.ac.p2++, pOut); pIn1++; pOut++; |
︙ | ︙ | |||
59835 59836 59837 59838 59839 59840 59841 59842 59843 59844 59845 59846 59847 59848 | ** copy. */ case OP_SCopy: { /* in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: ResultRow P1 P2 * * * ** ** The registers P1 through P1+P2-1 contain a single row of | > > > | 61051 61052 61053 61054 61055 61056 61057 61058 61059 61060 61061 61062 61063 61064 61065 61066 61067 | ** copy. */ case OP_SCopy: { /* in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); #ifdef SQLITE_DEBUG if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1; #endif REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: ResultRow P1 P2 * * * ** ** The registers P1 through P1+P2-1 contain a single row of |
︙ | ︙ | |||
59895 59896 59897 59898 59899 59900 59901 59902 59903 59904 59905 59906 59907 59908 | /* Make sure the results of the current row are \000 terminated ** and have an assigned type. The results are de-ephemeralized as ** as side effect. */ u.ad.pMem = p->pResultSet = &aMem[pOp->p1]; for(u.ad.i=0; u.ad.i<pOp->p2; u.ad.i++){ sqlite3VdbeMemNulTerminate(&u.ad.pMem[u.ad.i]); sqlite3VdbeMemStoreType(&u.ad.pMem[u.ad.i]); REGISTER_TRACE(pOp->p1+u.ad.i, &u.ad.pMem[u.ad.i]); } if( db->mallocFailed ) goto no_mem; /* Return SQLITE_ROW | > > > > | 61114 61115 61116 61117 61118 61119 61120 61121 61122 61123 61124 61125 61126 61127 61128 61129 61130 61131 | /* Make sure the results of the current row are \000 terminated ** and have an assigned type. The results are de-ephemeralized as ** as side effect. */ u.ad.pMem = p->pResultSet = &aMem[pOp->p1]; for(u.ad.i=0; u.ad.i<pOp->p2; u.ad.i++){ assert( memIsValid(&u.ad.pMem[u.ad.i]) ); Deephemeralize(&u.ad.pMem[u.ad.i]); assert( (u.ad.pMem[u.ad.i].flags & MEM_Ephem)==0 || (u.ad.pMem[u.ad.i].flags & (MEM_Str|MEM_Blob))==0 ); sqlite3VdbeMemNulTerminate(&u.ad.pMem[u.ad.i]); sqlite3VdbeMemStoreType(&u.ad.pMem[u.ad.i]); REGISTER_TRACE(pOp->p1+u.ad.i, &u.ad.pMem[u.ad.i]); } if( db->mallocFailed ) goto no_mem; /* Return SQLITE_ROW |
︙ | ︙ | |||
60126 60127 60128 60129 60130 60131 60132 60133 60134 60135 60136 60137 60138 60139 60140 60141 60142 60143 60144 60145 60146 60147 60148 60149 60150 60151 | sqlite3_value **apVal; int n; #endif /* local variables moved into u.ag */ u.ag.n = pOp->p5; u.ag.apVal = p->apArg; assert( u.ag.apVal || u.ag.n==0 ); assert( u.ag.n==0 || (pOp->p2>0 && pOp->p2+u.ag.n<=p->nMem+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+u.ag.n ); u.ag.pArg = &aMem[pOp->p2]; for(u.ag.i=0; u.ag.i<u.ag.n; u.ag.i++, u.ag.pArg++){ u.ag.apVal[u.ag.i] = u.ag.pArg; sqlite3VdbeMemStoreType(u.ag.pArg); REGISTER_TRACE(pOp->p2+u.ag.i, u.ag.pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); if( pOp->p4type==P4_FUNCDEF ){ u.ag.ctx.pFunc = pOp->p4.pFunc; u.ag.ctx.pVdbeFunc = 0; }else{ u.ag.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; u.ag.ctx.pFunc = u.ag.ctx.pVdbeFunc->pFunc; } | > > > > > < < | | 61349 61350 61351 61352 61353 61354 61355 61356 61357 61358 61359 61360 61361 61362 61363 61364 61365 61366 61367 61368 61369 61370 61371 61372 61373 61374 61375 61376 61377 61378 61379 61380 61381 61382 61383 61384 61385 61386 61387 61388 61389 61390 61391 61392 61393 61394 61395 61396 61397 61398 61399 61400 61401 61402 61403 61404 61405 61406 | sqlite3_value **apVal; int n; #endif /* local variables moved into u.ag */ u.ag.n = pOp->p5; u.ag.apVal = p->apArg; assert( u.ag.apVal || u.ag.n==0 ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); assert( u.ag.n==0 || (pOp->p2>0 && pOp->p2+u.ag.n<=p->nMem+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+u.ag.n ); u.ag.pArg = &aMem[pOp->p2]; for(u.ag.i=0; u.ag.i<u.ag.n; u.ag.i++, u.ag.pArg++){ assert( memIsValid(u.ag.pArg) ); u.ag.apVal[u.ag.i] = u.ag.pArg; Deephemeralize(u.ag.pArg); sqlite3VdbeMemStoreType(u.ag.pArg); REGISTER_TRACE(pOp->p2+u.ag.i, u.ag.pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); if( pOp->p4type==P4_FUNCDEF ){ u.ag.ctx.pFunc = pOp->p4.pFunc; u.ag.ctx.pVdbeFunc = 0; }else{ u.ag.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; u.ag.ctx.pFunc = u.ag.ctx.pVdbeFunc->pFunc; } u.ag.ctx.s.flags = MEM_Null; u.ag.ctx.s.db = db; u.ag.ctx.s.xDel = 0; u.ag.ctx.s.zMalloc = 0; /* The output cell may already have a buffer allocated. Move ** the pointer to u.ag.ctx.s so in case the user-function can use ** the already allocated buffer instead of allocating a new one. */ sqlite3VdbeMemMove(&u.ag.ctx.s, pOut); MemSetTypeFlag(&u.ag.ctx.s, MEM_Null); u.ag.ctx.isError = 0; if( u.ag.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); u.ag.ctx.pColl = pOp[-1].p4.pColl; } (*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */ if( db->mallocFailed ){ /* Even though a malloc() has failed, the implementation of the ** user function may have called an sqlite3_result_XXX() function ** to return a value. The following call releases any resources ** associated with such a value. */ sqlite3VdbeMemRelease(&u.ag.ctx.s); |
︙ | ︙ | |||
60218 60219 60220 60221 60222 60223 60224 | ** Take the bit-wise OR of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftLeft P1 P2 P3 * * ** ** Shift the integer value in register P2 to the left by the | | | 61444 61445 61446 61447 61448 61449 61450 61451 61452 61453 61454 61455 61456 61457 61458 | ** Take the bit-wise OR of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftLeft P1 P2 P3 * * ** ** Shift the integer value in register P2 to the left by the ** number of bits specified by the integer in register P1. ** Store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftRight P1 P2 P3 * * ** ** Shift the integer value in register P2 to the right by the ** number of bits specified by the integer in register P1. |
︙ | ︙ | |||
60268 60269 60270 60271 60272 60273 60274 60275 60276 60277 60278 60279 60280 60281 60282 60283 60284 60285 60286 60287 60288 60289 60290 60291 60292 60293 60294 60295 | ** Add the constant P2 to the value in register P1. ** The result is always an integer. ** ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; sqlite3VdbeMemIntegerify(pIn1); pIn1->u.i += pOp->p2; break; } /* Opcode: MustBeInt P1 P2 * * * ** ** Force the value in register P1 to be an integer. If the value ** in P1 is not an integer and cannot be converted into an integer ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ pc = pOp->p2 - 1; | > > | 61494 61495 61496 61497 61498 61499 61500 61501 61502 61503 61504 61505 61506 61507 61508 61509 61510 61511 61512 61513 61514 61515 61516 61517 61518 61519 61520 61521 61522 61523 | ** Add the constant P2 to the value in register P1. ** The result is always an integer. ** ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite3VdbeMemIntegerify(pIn1); pIn1->u.i += pOp->p2; break; } /* Opcode: MustBeInt P1 P2 * * * ** ** Force the value in register P1 to be an integer. If the value ** in P1 is not an integer and cannot be converted into an integer ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ pc = pOp->p2 - 1; |
︙ | ︙ | |||
60327 60328 60329 60330 60331 60332 60333 60334 60335 60336 60337 60338 60339 60340 | ** equivalent of printf(). Blob values are unchanged and ** are afterwards simply interpreted as text. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToText: { /* same as TK_TO_TEXT, in1 */ pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); rc = ExpandBlob(pIn1); assert( pIn1->flags & MEM_Str || db->mallocFailed ); pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero); | > | 61555 61556 61557 61558 61559 61560 61561 61562 61563 61564 61565 61566 61567 61568 61569 | ** equivalent of printf(). Blob values are unchanged and ** are afterwards simply interpreted as text. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToText: { /* same as TK_TO_TEXT, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); rc = ExpandBlob(pIn1); assert( pIn1->flags & MEM_Str || db->mallocFailed ); pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero); |
︙ | ︙ | |||
60373 60374 60375 60376 60377 60378 60379 | ** equivalent of atoi() or atof() and store 0 if no such conversion ** is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ pIn1 = &aMem[pOp->p1]; | < | < | | 61602 61603 61604 61605 61606 61607 61608 61609 61610 61611 61612 61613 61614 61615 61616 61617 61618 61619 61620 61621 61622 61623 | ** equivalent of atoi() or atof() and store 0 if no such conversion ** is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ pIn1 = &aMem[pOp->p1]; sqlite3VdbeMemNumerify(pIn1); break; } #endif /* SQLITE_OMIT_CAST */ /* Opcode: ToInt P1 * * * * ** ** Force the value in register P1 to be an integer. If ** The value is currently a real number, drop its fractional part. ** If the value is text or blob, try to convert it to an integer using the ** equivalent of atoi() and store 0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToInt: { /* same as TK_TO_INT, in1 */ |
︙ | ︙ | |||
60409 60410 60411 60412 60413 60414 60415 60416 60417 60418 60419 60420 60421 60422 60423 60424 60425 60426 60427 60428 60429 | ** If the value is text or blob, try to convert it to an integer using the ** equivalent of atoi() and store 0.0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemRealify(pIn1); } break; } #endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 ** ** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then ** jump to address P2. ** ** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or ** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL | > | | 61636 61637 61638 61639 61640 61641 61642 61643 61644 61645 61646 61647 61648 61649 61650 61651 61652 61653 61654 61655 61656 61657 61658 61659 61660 61661 61662 61663 61664 61665 | ** If the value is text or blob, try to convert it to an integer using the ** equivalent of atoi() and store 0.0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemRealify(pIn1); } break; } #endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 ** ** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then ** jump to address P2. ** ** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or ** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL ** bit is clear then fall through if either operand is NULL. ** ** The SQLITE_AFF_MASK portion of P5 must be an affinity character - ** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made ** to coerce both inputs according to this affinity before the ** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric ** affinity is used. Note that the affinity conversions are stored ** back into the input registers P1 and P3. So this opcode can cause |
︙ | ︙ | |||
60553 60554 60555 60556 60557 60558 60559 60560 60561 60562 60563 60564 60565 60566 | case OP_Le: u.ai.res = u.ai.res<=0; break; case OP_Gt: u.ai.res = u.ai.res>0; break; default: u.ai.res = u.ai.res>=0; break; } if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = u.ai.res; REGISTER_TRACE(pOp->p2, pOut); }else if( u.ai.res ){ pc = pOp->p2-1; } | > | 61781 61782 61783 61784 61785 61786 61787 61788 61789 61790 61791 61792 61793 61794 61795 | case OP_Le: u.ai.res = u.ai.res<=0; break; case OP_Gt: u.ai.res = u.ai.res>0; break; default: u.ai.res = u.ai.res>=0; break; } if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = u.ai.res; REGISTER_TRACE(pOp->p2, pOut); }else if( u.ai.res ){ pc = pOp->p2-1; } |
︙ | ︙ | |||
60584 60585 60586 60587 60588 60589 60590 | assert( pOp->p4.ai ); aPermute = pOp->p4.ai; break; } /* Opcode: Compare P1 P2 P3 P4 * ** | | | | 61813 61814 61815 61816 61817 61818 61819 61820 61821 61822 61823 61824 61825 61826 61827 61828 | assert( pOp->p4.ai ); aPermute = pOp->p4.ai; break; } /* Opcode: Compare P1 P2 P3 P4 * ** ** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this ** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of ** the comparison for use by the next OP_Jump instruct. ** ** P4 is a KeyInfo structure that defines collating sequences and sort ** orders for the comparison. The permutation applies to registers ** only. The KeyInfo elements are used sequentially. ** ** The comparison is a sort comparison, so NULLs compare equal, |
︙ | ︙ | |||
60627 60628 60629 60630 60631 60632 60633 60634 60635 60636 60637 60638 60639 60640 | }else{ assert( u.aj.p1>0 && u.aj.p1+u.aj.n<=p->nMem+1 ); assert( u.aj.p2>0 && u.aj.p2+u.aj.n<=p->nMem+1 ); } #endif /* SQLITE_DEBUG */ for(u.aj.i=0; u.aj.i<u.aj.n; u.aj.i++){ u.aj.idx = aPermute ? aPermute[u.aj.i] : u.aj.i; REGISTER_TRACE(u.aj.p1+u.aj.idx, &aMem[u.aj.p1+u.aj.idx]); REGISTER_TRACE(u.aj.p2+u.aj.idx, &aMem[u.aj.p2+u.aj.idx]); assert( u.aj.i<u.aj.pKeyInfo->nField ); u.aj.pColl = u.aj.pKeyInfo->aColl[u.aj.i]; u.aj.bRev = u.aj.pKeyInfo->aSortOrder[u.aj.i]; iCompare = sqlite3MemCompare(&aMem[u.aj.p1+u.aj.idx], &aMem[u.aj.p2+u.aj.idx], u.aj.pColl); if( iCompare ){ | > > | 61856 61857 61858 61859 61860 61861 61862 61863 61864 61865 61866 61867 61868 61869 61870 61871 | }else{ assert( u.aj.p1>0 && u.aj.p1+u.aj.n<=p->nMem+1 ); assert( u.aj.p2>0 && u.aj.p2+u.aj.n<=p->nMem+1 ); } #endif /* SQLITE_DEBUG */ for(u.aj.i=0; u.aj.i<u.aj.n; u.aj.i++){ u.aj.idx = aPermute ? aPermute[u.aj.i] : u.aj.i; assert( memIsValid(&aMem[u.aj.p1+u.aj.idx]) ); assert( memIsValid(&aMem[u.aj.p2+u.aj.idx]) ); REGISTER_TRACE(u.aj.p1+u.aj.idx, &aMem[u.aj.p1+u.aj.idx]); REGISTER_TRACE(u.aj.p2+u.aj.idx, &aMem[u.aj.p2+u.aj.idx]); assert( u.aj.i<u.aj.pKeyInfo->nField ); u.aj.pColl = u.aj.pKeyInfo->aColl[u.aj.i]; u.aj.bRev = u.aj.pKeyInfo->aSortOrder[u.aj.i]; iCompare = sqlite3MemCompare(&aMem[u.aj.p1+u.aj.idx], &aMem[u.aj.p2+u.aj.idx], u.aj.pColl); if( iCompare ){ |
︙ | ︙ | |||
60858 60859 60860 60861 60862 60863 60864 60865 60866 60867 60868 60869 60870 60871 | u.am.p1 = pOp->p1; u.am.p2 = pOp->p2; u.am.pC = 0; memset(&u.am.sMem, 0, sizeof(u.am.sMem)); assert( u.am.p1<p->nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); u.am.pDest = &aMem[pOp->p3]; MemSetTypeFlag(u.am.pDest, MEM_Null); u.am.zRec = 0; /* This block sets the variable u.am.payloadSize to be the total number of ** bytes in the record. ** ** u.am.zRec is set to be the complete text of the record if it is available. | > | 62089 62090 62091 62092 62093 62094 62095 62096 62097 62098 62099 62100 62101 62102 62103 | u.am.p1 = pOp->p1; u.am.p2 = pOp->p2; u.am.pC = 0; memset(&u.am.sMem, 0, sizeof(u.am.sMem)); assert( u.am.p1<p->nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); u.am.pDest = &aMem[pOp->p3]; memAboutToChange(p, u.am.pDest); MemSetTypeFlag(u.am.pDest, MEM_Null); u.am.zRec = 0; /* This block sets the variable u.am.payloadSize to be the total number of ** bytes in the record. ** ** u.am.zRec is set to be the complete text of the record if it is available. |
︙ | ︙ | |||
60905 60906 60907 60908 60909 60910 60911 60912 60913 60914 60915 60916 60917 60918 | assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) ); rc = sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize); assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } }else if( u.am.pC->pseudoTableReg>0 ){ u.am.pReg = &aMem[u.am.pC->pseudoTableReg]; assert( u.am.pReg->flags & MEM_Blob ); u.am.payloadSize = u.am.pReg->n; u.am.zRec = u.am.pReg->z; u.am.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; assert( u.am.payloadSize==0 || u.am.zRec!=0 ); }else{ /* Consider the row to be NULL */ u.am.payloadSize = 0; | > | 62137 62138 62139 62140 62141 62142 62143 62144 62145 62146 62147 62148 62149 62150 62151 | assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) ); rc = sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize); assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } }else if( u.am.pC->pseudoTableReg>0 ){ u.am.pReg = &aMem[u.am.pC->pseudoTableReg]; assert( u.am.pReg->flags & MEM_Blob ); assert( memIsValid(u.am.pReg) ); u.am.payloadSize = u.am.pReg->n; u.am.zRec = u.am.pReg->z; u.am.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; assert( u.am.payloadSize==0 || u.am.zRec!=0 ); }else{ /* Consider the row to be NULL */ u.am.payloadSize = 0; |
︙ | ︙ | |||
61129 61130 61131 61132 61133 61134 61135 61136 61137 61138 61139 61140 61141 61142 61143 61144 | u.an.zAffinity = pOp->p4.z; assert( u.an.zAffinity!=0 ); assert( u.an.zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[p->nMem] ); ExpandBlob(pIn1); applyAffinity(pIn1, u.an.cAff, encoding); pIn1++; } break; } /* Opcode: MakeRecord P1 P2 P3 P4 * ** | > | | < | < < | 62362 62363 62364 62365 62366 62367 62368 62369 62370 62371 62372 62373 62374 62375 62376 62377 62378 62379 62380 62381 62382 62383 62384 62385 62386 62387 62388 | u.an.zAffinity = pOp->p4.z; assert( u.an.zAffinity!=0 ); assert( u.an.zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[p->nMem] ); assert( memIsValid(pIn1) ); ExpandBlob(pIn1); applyAffinity(pIn1, u.an.cAff, encoding); pIn1++; } break; } /* Opcode: MakeRecord P1 P2 P3 P4 * ** ** Convert P2 registers beginning with P1 into the [record format] ** use as a data record in a database table or as a key ** in an index. The OP_Column opcode can decode the record later. ** ** P4 may be a string that is P2 characters long. The nth character of the ** string indicates the column affinity that should be used for the nth ** field of the index key. ** ** The mapping from character to affinity is given by the SQLITE_AFF_ ** macros defined in sqliteInt.h. |
︙ | ︙ | |||
61199 61200 61201 61202 61203 61204 61205 61206 61207 61208 61209 61210 61211 61212 61213 61214 61215 61216 61217 | u.ao.nField = pOp->p1; u.ao.zAffinity = pOp->p4.z; assert( u.ao.nField>0 && pOp->p2>0 && pOp->p2+u.ao.nField<=p->nMem+1 ); u.ao.pData0 = &aMem[u.ao.nField]; u.ao.nField = pOp->p2; u.ao.pLast = &u.ao.pData0[u.ao.nField-1]; u.ao.file_format = p->minWriteFileFormat; /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ if( u.ao.zAffinity ){ applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding); } if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){ sqlite3VdbeMemExpandBlob(u.ao.pRec); } u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format); | > > > > > > | 62430 62431 62432 62433 62434 62435 62436 62437 62438 62439 62440 62441 62442 62443 62444 62445 62446 62447 62448 62449 62450 62451 62452 62453 62454 | u.ao.nField = pOp->p1; u.ao.zAffinity = pOp->p4.z; assert( u.ao.nField>0 && pOp->p2>0 && pOp->p2+u.ao.nField<=p->nMem+1 ); u.ao.pData0 = &aMem[u.ao.nField]; u.ao.nField = pOp->p2; u.ao.pLast = &u.ao.pData0[u.ao.nField-1]; u.ao.file_format = p->minWriteFileFormat; /* Identify the output register */ assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ assert( memIsValid(u.ao.pRec) ); if( u.ao.zAffinity ){ applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding); } if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){ sqlite3VdbeMemExpandBlob(u.ao.pRec); } u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format); |
︙ | ︙ | |||
61238 61239 61240 61241 61242 61243 61244 | } /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ | < < | 62475 62476 62477 62478 62479 62480 62481 62482 62483 62484 62485 62486 62487 62488 | } /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ if( sqlite3VdbeMemGrow(pOut, (int)u.ao.nByte, 0) ){ goto no_mem; } u.ao.zNewRecord = (u8 *)pOut->z; /* Write the record */ u.ao.i = putVarint32(u.ao.zNewRecord, u.ao.nHdr); |
︙ | ︙ | |||
61412 61413 61414 61415 61416 61417 61418 61419 61420 61421 61422 61423 61424 61425 | if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( u.aq.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); } } /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ while( db->pSavepoint!=u.aq.pSavepoint ){ u.aq.pTmp = db->pSavepoint; | > | 62647 62648 62649 62650 62651 62652 62653 62654 62655 62656 62657 62658 62659 62660 62661 | if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( u.aq.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); db->flags = (db->flags | SQLITE_InternChanges); } } /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ while( db->pSavepoint!=u.aq.pSavepoint ){ u.aq.pTmp = db->pSavepoint; |
︙ | ︙ | |||
61802 61803 61804 61805 61806 61807 61808 61809 61810 61811 61812 61813 61814 61815 | }else{ u.aw.wrFlag = 0; } if( pOp->p5 ){ assert( u.aw.p2>0 ); assert( u.aw.p2<=p->nMem ); pIn2 = &aMem[u.aw.p2]; sqlite3VdbeMemIntegerify(pIn2); u.aw.p2 = (int)pIn2->u.i; /* The u.aw.p2 value always comes from a prior OP_CreateTable opcode and ** that opcode will always set the u.aw.p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ if( NEVER(u.aw.p2<2) ) { | > > | 63038 63039 63040 63041 63042 63043 63044 63045 63046 63047 63048 63049 63050 63051 63052 63053 | }else{ u.aw.wrFlag = 0; } if( pOp->p5 ){ assert( u.aw.p2>0 ); assert( u.aw.p2<=p->nMem ); pIn2 = &aMem[u.aw.p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); u.aw.p2 = (int)pIn2->u.i; /* The u.aw.p2 value always comes from a prior OP_CreateTable opcode and ** that opcode will always set the u.aw.p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ if( NEVER(u.aw.p2<2) ) { |
︙ | ︙ | |||
61824 61825 61826 61827 61828 61829 61830 61831 61832 61833 61834 61835 61836 61837 | }else if( pOp->p4type==P4_INT32 ){ u.aw.nField = pOp->p4.i; } assert( pOp->p1>=0 ); u.aw.pCur = allocateCursor(p, pOp->p1, u.aw.nField, u.aw.iDb, 1); if( u.aw.pCur==0 ) goto no_mem; u.aw.pCur->nullRow = 1; rc = sqlite3BtreeCursor(u.aw.pX, u.aw.p2, u.aw.wrFlag, u.aw.pKeyInfo, u.aw.pCur->pCursor); u.aw.pCur->pKeyInfo = u.aw.pKeyInfo; /* Since it performs no memory allocation or IO, the only values that ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. ** SQLITE_EMPTY is only returned when attempting to open the table ** rooted at page 1 of a zero-byte database. */ | > | 63062 63063 63064 63065 63066 63067 63068 63069 63070 63071 63072 63073 63074 63075 63076 | }else if( pOp->p4type==P4_INT32 ){ u.aw.nField = pOp->p4.i; } assert( pOp->p1>=0 ); u.aw.pCur = allocateCursor(p, pOp->p1, u.aw.nField, u.aw.iDb, 1); if( u.aw.pCur==0 ) goto no_mem; u.aw.pCur->nullRow = 1; u.aw.pCur->isOrdered = 1; rc = sqlite3BtreeCursor(u.aw.pX, u.aw.p2, u.aw.wrFlag, u.aw.pKeyInfo, u.aw.pCur->pCursor); u.aw.pCur->pKeyInfo = u.aw.pKeyInfo; /* Since it performs no memory allocation or IO, the only values that ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. ** SQLITE_EMPTY is only returned when attempting to open the table ** rooted at page 1 of a zero-byte database. */ |
︙ | ︙ | |||
61876 61877 61878 61879 61880 61881 61882 | ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { #if 0 /* local variables moved into u.ax */ VdbeCursor *pCx; #endif /* local variables moved into u.ax */ | | < | > | | | > | 63115 63116 63117 63118 63119 63120 63121 63122 63123 63124 63125 63126 63127 63128 63129 63130 63131 63132 63133 63134 63135 63136 63137 63138 63139 63140 63141 63142 63143 63144 63145 63146 63147 63148 63149 63150 63151 63152 63153 63154 63155 63156 63157 63158 63159 63160 63161 63162 63163 63164 63165 63166 63167 63168 | ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { #if 0 /* local variables moved into u.ax */ VdbeCursor *pCx; #endif /* local variables moved into u.ax */ static const int vfsFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); u.ax.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( u.ax.pCx==0 ) goto no_mem; u.ax.pCx->nullRow = 1; rc = sqlite3BtreeOpen(0, db, &u.ax.pCx->pBt, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ rc = sqlite3BtreeBeginTrans(u.ax.pCx->pBt, 1); } if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( pOp->p4.pKeyInfo ){ int pgno; assert( pOp->p4type==P4_KEYINFO ); rc = sqlite3BtreeCreateTable(u.ax.pCx->pBt, &pgno, BTREE_BLOBKEY); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); rc = sqlite3BtreeCursor(u.ax.pCx->pBt, pgno, 1, (KeyInfo*)pOp->p4.z, u.ax.pCx->pCursor); u.ax.pCx->pKeyInfo = pOp->p4.pKeyInfo; u.ax.pCx->pKeyInfo->enc = ENC(p->db); } u.ax.pCx->isTable = 0; }else{ rc = sqlite3BtreeCursor(u.ax.pCx->pBt, MASTER_ROOT, 1, 0, u.ax.pCx->pCursor); u.ax.pCx->isTable = 1; } } u.ax.pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); u.ax.pCx->isIndex = !u.ax.pCx->isTable; break; } /* Opcode: OpenPseudo P1 P2 P3 * * ** ** Open a new cursor that points to a fake table that contains a single |
︙ | ︙ | |||
62034 62035 62036 62037 62038 62039 62040 62041 62042 62043 62044 62045 62046 62047 | assert( pOp->p2!=0 ); u.az.pC = p->apCsr[pOp->p1]; assert( u.az.pC!=0 ); assert( u.az.pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); if( u.az.pC->pCursor!=0 ){ u.az.oc = pOp->opcode; u.az.pC->nullRow = 0; if( u.az.pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do ** the seek, so covert it. */ | > | 63274 63275 63276 63277 63278 63279 63280 63281 63282 63283 63284 63285 63286 63287 63288 | assert( pOp->p2!=0 ); u.az.pC = p->apCsr[pOp->p1]; assert( u.az.pC!=0 ); assert( u.az.pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); assert( u.az.pC->isOrdered ); if( u.az.pC->pCursor!=0 ){ u.az.oc = pOp->opcode; u.az.pC->nullRow = 0; if( u.az.pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do ** the seek, so covert it. */ |
︙ | ︙ | |||
62116 62117 62118 62119 62120 62121 62122 62123 62124 62125 62126 62127 62128 62129 | u.az.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.az.oc - OP_SeekLt))); assert( u.az.oc!=OP_SeekGt || u.az.r.flags==UNPACKED_INCRKEY ); assert( u.az.oc!=OP_SeekLe || u.az.r.flags==UNPACKED_INCRKEY ); assert( u.az.oc!=OP_SeekGe || u.az.r.flags==0 ); assert( u.az.oc!=OP_SeekLt || u.az.r.flags==0 ); u.az.r.aMem = &aMem[pOp->p3]; ExpandBlob(u.az.r.aMem); rc = sqlite3BtreeMovetoUnpacked(u.az.pC->pCursor, &u.az.r, 0, 0, &u.az.res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } u.az.pC->rowidIsValid = 0; } | > > > | 63357 63358 63359 63360 63361 63362 63363 63364 63365 63366 63367 63368 63369 63370 63371 63372 63373 | u.az.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.az.oc - OP_SeekLt))); assert( u.az.oc!=OP_SeekGt || u.az.r.flags==UNPACKED_INCRKEY ); assert( u.az.oc!=OP_SeekLe || u.az.r.flags==UNPACKED_INCRKEY ); assert( u.az.oc!=OP_SeekGe || u.az.r.flags==0 ); assert( u.az.oc!=OP_SeekLt || u.az.r.flags==0 ); u.az.r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG { int i; for(i=0; i<u.az.r.nField; i++) assert( memIsValid(&u.az.r.aMem[i]) ); } #endif ExpandBlob(u.az.r.aMem); rc = sqlite3BtreeMovetoUnpacked(u.az.pC->pCursor, &u.az.r, 0, 0, &u.az.res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } u.az.pC->rowidIsValid = 0; } |
︙ | ︙ | |||
62244 62245 62246 62247 62248 62249 62250 62251 62252 62253 62254 | if( ALWAYS(u.bb.pC->pCursor!=0) ){ assert( u.bb.pC->isTable==0 ); if( pOp->p4.i>0 ){ u.bb.r.pKeyInfo = u.bb.pC->pKeyInfo; u.bb.r.nField = (u16)pOp->p4.i; u.bb.r.aMem = pIn3; u.bb.r.flags = UNPACKED_PREFIX_MATCH; u.bb.pIdxKey = &u.bb.r; }else{ assert( pIn3->flags & MEM_Blob ); | > > > | | 63488 63489 63490 63491 63492 63493 63494 63495 63496 63497 63498 63499 63500 63501 63502 63503 63504 63505 63506 63507 63508 63509 | if( ALWAYS(u.bb.pC->pCursor!=0) ){ assert( u.bb.pC->isTable==0 ); if( pOp->p4.i>0 ){ u.bb.r.pKeyInfo = u.bb.pC->pKeyInfo; u.bb.r.nField = (u16)pOp->p4.i; u.bb.r.aMem = pIn3; #ifdef SQLITE_DEBUG { int i; for(i=0; i<u.bb.r.nField; i++) assert( memIsValid(&u.bb.r.aMem[i]) ); } #endif u.bb.r.flags = UNPACKED_PREFIX_MATCH; u.bb.pIdxKey = &u.bb.r; }else{ assert( pIn3->flags & MEM_Blob ); assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ u.bb.pIdxKey = sqlite3VdbeRecordUnpack(u.bb.pC->pKeyInfo, pIn3->n, pIn3->z, u.bb.aTempRec, sizeof(u.bb.aTempRec)); if( u.bb.pIdxKey==0 ){ goto no_mem; } u.bb.pIdxKey->flags |= UNPACKED_PREFIX_MATCH; } |
︙ | ︙ | |||
62343 62344 62345 62346 62347 62348 62349 62350 62351 62352 62353 62354 62355 62356 | if( u.bc.pCrsr!=0 ){ /* Populate the index search key. */ u.bc.r.pKeyInfo = u.bc.pCx->pKeyInfo; u.bc.r.nField = u.bc.nField + 1; u.bc.r.flags = UNPACKED_PREFIX_SEARCH; u.bc.r.aMem = u.bc.aMx; /* Extract the value of u.bc.R from register P3. */ sqlite3VdbeMemIntegerify(pIn3); u.bc.R = pIn3->u.i; /* Search the B-Tree index. If no conflicting record is found, jump ** to P2. Otherwise, copy the rowid of the conflicting record to | > > > | 63590 63591 63592 63593 63594 63595 63596 63597 63598 63599 63600 63601 63602 63603 63604 63605 63606 | if( u.bc.pCrsr!=0 ){ /* Populate the index search key. */ u.bc.r.pKeyInfo = u.bc.pCx->pKeyInfo; u.bc.r.nField = u.bc.nField + 1; u.bc.r.flags = UNPACKED_PREFIX_SEARCH; u.bc.r.aMem = u.bc.aMx; #ifdef SQLITE_DEBUG { int i; for(i=0; i<u.bc.r.nField; i++) assert( memIsValid(&u.bc.r.aMem[i]) ); } #endif /* Extract the value of u.bc.R from register P3. */ sqlite3VdbeMemIntegerify(pIn3); u.bc.R = pIn3->u.i; /* Search the B-Tree index. If no conflicting record is found, jump ** to P2. Otherwise, copy the rowid of the conflicting record to |
︙ | ︙ | |||
62365 62366 62367 62368 62369 62370 62371 | break; } /* Opcode: NotExists P1 P2 P3 * * ** ** Use the content of register P3 as a integer key. If a record ** with that key does not exist in table of P1, then jump to P2. | | | 63615 63616 63617 63618 63619 63620 63621 63622 63623 63624 63625 63626 63627 63628 63629 | break; } /* Opcode: NotExists P1 P2 P3 * * ** ** Use the content of register P3 as a integer key. If a record ** with that key does not exist in table of P1, then jump to P2. ** If the record does exist, then fall through. The cursor is left ** pointing to the record if it exists. ** ** The difference between this operation and NotFound is that this ** operation assumes the key is an integer and that P1 is a table whereas ** NotFound assumes key is a blob constructed from MakeRecord and ** P1 is an index. ** |
︙ | ︙ | |||
62523 62524 62525 62526 62527 62528 62529 62530 62531 62532 62533 62534 62535 62536 62537 62538 62539 62540 62541 62542 62543 62544 62545 62546 62547 62548 | /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=u.be.pFrame->nMem ); u.be.pMem = &u.be.pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=p->nMem ); u.be.pMem = &aMem[pOp->p3]; } REGISTER_TRACE(pOp->p3, u.be.pMem); sqlite3VdbeMemIntegerify(u.be.pMem); assert( (u.be.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( u.be.pMem->u.i==MAX_ROWID || u.be.pC->useRandomRowid ){ rc = SQLITE_FULL; /* IMP: R-12275-61338 */ goto abort_due_to_error; } if( u.be.v<u.be.pMem->u.i+1 ){ u.be.v = u.be.pMem->u.i + 1; } u.be.pMem->u.i = u.be.v; } #endif sqlite3BtreeSetCachedRowid(u.be.pC->pCursor, u.be.v<MAX_ROWID ? u.be.v+1 : 0); } if( u.be.pC->useRandomRowid ){ | > > | | | < > > > < > > | | < > | | > > > > < | < > > | 63773 63774 63775 63776 63777 63778 63779 63780 63781 63782 63783 63784 63785 63786 63787 63788 63789 63790 63791 63792 63793 63794 63795 63796 63797 63798 63799 63800 63801 63802 63803 63804 63805 63806 63807 63808 63809 63810 63811 63812 63813 63814 63815 63816 63817 63818 63819 63820 63821 63822 63823 63824 63825 63826 63827 63828 63829 63830 63831 63832 63833 63834 63835 63836 63837 | /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=u.be.pFrame->nMem ); u.be.pMem = &u.be.pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=p->nMem ); u.be.pMem = &aMem[pOp->p3]; memAboutToChange(p, u.be.pMem); } assert( memIsValid(u.be.pMem) ); REGISTER_TRACE(pOp->p3, u.be.pMem); sqlite3VdbeMemIntegerify(u.be.pMem); assert( (u.be.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( u.be.pMem->u.i==MAX_ROWID || u.be.pC->useRandomRowid ){ rc = SQLITE_FULL; /* IMP: R-12275-61338 */ goto abort_due_to_error; } if( u.be.v<u.be.pMem->u.i+1 ){ u.be.v = u.be.pMem->u.i + 1; } u.be.pMem->u.i = u.be.v; } #endif sqlite3BtreeSetCachedRowid(u.be.pC->pCursor, u.be.v<MAX_ROWID ? u.be.v+1 : 0); } if( u.be.pC->useRandomRowid ){ /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the ** largest possible integer (9223372036854775807) then the database ** engine starts picking positive candidate ROWIDs at random until ** it finds one that is not previously used. */ assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is ** an AUTOINCREMENT table. */ /* on the first attempt, simply do one more than previous */ u.be.v = db->lastRowid; u.be.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ u.be.v++; /* ensure non-zero */ u.be.cnt = 0; while( ((rc = sqlite3BtreeMovetoUnpacked(u.be.pC->pCursor, 0, (u64)u.be.v, 0, &u.be.res))==SQLITE_OK) && (u.be.res==0) && (++u.be.cnt<100)){ /* collision - try another random rowid */ sqlite3_randomness(sizeof(u.be.v), &u.be.v); if( u.be.cnt<5 ){ /* try "small" random rowids for the initial attempts */ u.be.v &= 0xffffff; }else{ u.be.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ } u.be.v++; /* ensure non-zero */ } if( rc==SQLITE_OK && u.be.res==0 ){ rc = SQLITE_FULL; /* IMP: R-38219-53002 */ goto abort_due_to_error; } assert( u.be.v>0 ); /* EV: R-40812-03570 */ } u.be.pC->rowidIsValid = 0; u.be.pC->deferredMoveto = 0; u.be.pC->cacheStatus = CACHE_STALE; } pOut->u.i = u.be.v; break; |
︙ | ︙ | |||
62634 62635 62636 62637 62638 62639 62640 62641 62642 62643 62644 62645 62646 62647 62648 62649 62650 62651 62652 62653 62654 62655 62656 62657 | const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ #endif /* local variables moved into u.bf */ u.bf.pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); u.bf.pC = p->apCsr[pOp->p1]; assert( u.bf.pC!=0 ); assert( u.bf.pC->pCursor!=0 ); assert( u.bf.pC->pseudoTableReg==0 ); assert( u.bf.pC->isTable ); REGISTER_TRACE(pOp->p2, u.bf.pData); if( pOp->opcode==OP_Insert ){ u.bf.pKey = &aMem[pOp->p3]; assert( u.bf.pKey->flags & MEM_Int ); REGISTER_TRACE(pOp->p3, u.bf.pKey); u.bf.iKey = u.bf.pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); u.bf.iKey = pOp->p3; } | > > | 63893 63894 63895 63896 63897 63898 63899 63900 63901 63902 63903 63904 63905 63906 63907 63908 63909 63910 63911 63912 63913 63914 63915 63916 63917 63918 | const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ #endif /* local variables moved into u.bf */ u.bf.pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( memIsValid(u.bf.pData) ); u.bf.pC = p->apCsr[pOp->p1]; assert( u.bf.pC!=0 ); assert( u.bf.pC->pCursor!=0 ); assert( u.bf.pC->pseudoTableReg==0 ); assert( u.bf.pC->isTable ); REGISTER_TRACE(pOp->p2, u.bf.pData); if( pOp->opcode==OP_Insert ){ u.bf.pKey = &aMem[pOp->p3]; assert( u.bf.pKey->flags & MEM_Int ); assert( memIsValid(u.bf.pKey) ); REGISTER_TRACE(pOp->p3, u.bf.pKey); u.bf.iKey = u.bf.pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); u.bf.iKey = pOp->p3; } |
︙ | ︙ | |||
62795 62796 62797 62798 62799 62800 62801 62802 62803 62804 62805 62806 62807 62808 | VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; #endif /* local variables moved into u.bh */ pOut = &aMem[pOp->p2]; /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); u.bh.pC = p->apCsr[pOp->p1]; assert( u.bh.pC->isTable || pOp->opcode==OP_RowKey ); assert( u.bh.pC->isIndex || pOp->opcode==OP_RowData ); assert( u.bh.pC!=0 ); | > | 64056 64057 64058 64059 64060 64061 64062 64063 64064 64065 64066 64067 64068 64069 64070 | VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; #endif /* local variables moved into u.bh */ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); u.bh.pC = p->apCsr[pOp->p1]; assert( u.bh.pC->isTable || pOp->opcode==OP_RowKey ); assert( u.bh.pC->isIndex || pOp->opcode==OP_RowData ); assert( u.bh.pC!=0 ); |
︙ | ︙ | |||
63137 63138 63139 63140 63141 63142 63143 63144 63145 63146 63147 63148 63149 63150 | assert( u.bo.pC!=0 ); u.bo.pCrsr = u.bo.pC->pCursor; if( ALWAYS(u.bo.pCrsr!=0) ){ u.bo.r.pKeyInfo = u.bo.pC->pKeyInfo; u.bo.r.nField = (u16)pOp->p3; u.bo.r.flags = 0; u.bo.r.aMem = &aMem[pOp->p2]; rc = sqlite3BtreeMovetoUnpacked(u.bo.pCrsr, &u.bo.r, 0, 0, &u.bo.res); if( rc==SQLITE_OK && u.bo.res==0 ){ rc = sqlite3BtreeDelete(u.bo.pCrsr); } assert( u.bo.pC->deferredMoveto==0 ); u.bo.pC->cacheStatus = CACHE_STALE; } | > > > | 64399 64400 64401 64402 64403 64404 64405 64406 64407 64408 64409 64410 64411 64412 64413 64414 64415 | assert( u.bo.pC!=0 ); u.bo.pCrsr = u.bo.pC->pCursor; if( ALWAYS(u.bo.pCrsr!=0) ){ u.bo.r.pKeyInfo = u.bo.pC->pKeyInfo; u.bo.r.nField = (u16)pOp->p3; u.bo.r.flags = 0; u.bo.r.aMem = &aMem[pOp->p2]; #ifdef SQLITE_DEBUG { int i; for(i=0; i<u.bo.r.nField; i++) assert( memIsValid(&u.bo.r.aMem[i]) ); } #endif rc = sqlite3BtreeMovetoUnpacked(u.bo.pCrsr, &u.bo.r, 0, 0, &u.bo.res); if( rc==SQLITE_OK && u.bo.res==0 ){ rc = sqlite3BtreeDelete(u.bo.pCrsr); } assert( u.bo.pC->deferredMoveto==0 ); u.bo.pC->cacheStatus = CACHE_STALE; } |
︙ | ︙ | |||
63198 63199 63200 63201 63202 63203 63204 | ** then jump to P2. Otherwise fall through to the next instruction. ** ** If P5 is non-zero then the key value is increased by an epsilon ** prior to the comparison. This make the opcode work like IdxGT except ** that if the key from register P3 is a prefix of the key in the cursor, ** the result is false whereas it would be true with IdxGT. */ | | | 64463 64464 64465 64466 64467 64468 64469 64470 64471 64472 64473 64474 64475 64476 64477 | ** then jump to P2. Otherwise fall through to the next instruction. ** ** If P5 is non-zero then the key value is increased by an epsilon ** prior to the comparison. This make the opcode work like IdxGT except ** that if the key from register P3 is a prefix of the key in the cursor, ** the result is false whereas it would be true with IdxGT. */ /* Opcode: IdxLT P1 P2 P3 P4 P5 ** ** The P4 register values beginning with P3 form an unpacked index ** key that omits the ROWID. Compare this key value against the index ** that P1 is currently pointing to, ignoring the ROWID on the P1 index. ** ** If the P1 index entry is less than the key value then jump to P2. ** Otherwise fall through to the next instruction. |
︙ | ︙ | |||
63221 63222 63223 63224 63225 63226 63227 63228 63229 63230 63231 63232 63233 63234 63235 63236 63237 63238 63239 63240 63241 63242 63243 63244 63245 63246 | int res; UnpackedRecord r; #endif /* local variables moved into u.bq */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); u.bq.pC = p->apCsr[pOp->p1]; assert( u.bq.pC!=0 ); if( ALWAYS(u.bq.pC->pCursor!=0) ){ assert( u.bq.pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); u.bq.r.pKeyInfo = u.bq.pC->pKeyInfo; u.bq.r.nField = (u16)pOp->p4.i; if( pOp->p5 ){ u.bq.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; }else{ u.bq.r.flags = UNPACKED_IGNORE_ROWID; } u.bq.r.aMem = &aMem[pOp->p3]; rc = sqlite3VdbeIdxKeyCompare(u.bq.pC, &u.bq.r, &u.bq.res); if( pOp->opcode==OP_IdxLT ){ u.bq.res = -u.bq.res; }else{ assert( pOp->opcode==OP_IdxGE ); u.bq.res++; } | > > > > | 64486 64487 64488 64489 64490 64491 64492 64493 64494 64495 64496 64497 64498 64499 64500 64501 64502 64503 64504 64505 64506 64507 64508 64509 64510 64511 64512 64513 64514 64515 | int res; UnpackedRecord r; #endif /* local variables moved into u.bq */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); u.bq.pC = p->apCsr[pOp->p1]; assert( u.bq.pC!=0 ); assert( u.bq.pC->isOrdered ); if( ALWAYS(u.bq.pC->pCursor!=0) ){ assert( u.bq.pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); u.bq.r.pKeyInfo = u.bq.pC->pKeyInfo; u.bq.r.nField = (u16)pOp->p4.i; if( pOp->p5 ){ u.bq.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; }else{ u.bq.r.flags = UNPACKED_IGNORE_ROWID; } u.bq.r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG { int i; for(i=0; i<u.bq.r.nField; i++) assert( memIsValid(&u.bq.r.aMem[i]) ); } #endif rc = sqlite3VdbeIdxKeyCompare(u.bq.pC, &u.bq.r, &u.bq.res); if( pOp->opcode==OP_IdxLT ){ u.bq.res = -u.bq.res; }else{ assert( pOp->opcode==OP_IdxGE ); u.bq.res++; } |
︙ | ︙ | |||
63336 63337 63338 63339 63340 63341 63342 63343 63344 63345 63346 63347 63348 63349 | assert( (p->btreeMask & (1<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bs.nChange : 0) ); if( pOp->p3 ){ p->nChange += u.bs.nChange; if( pOp->p3>0 ){ aMem[pOp->p3].u.i += u.bs.nChange; } } break; } /* Opcode: CreateTable P1 P2 * * * | > > | 64605 64606 64607 64608 64609 64610 64611 64612 64613 64614 64615 64616 64617 64618 64619 64620 | assert( (p->btreeMask & (1<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bs.nChange : 0) ); if( pOp->p3 ){ p->nChange += u.bs.nChange; if( pOp->p3>0 ){ assert( memIsValid(&aMem[pOp->p3]) ); memAboutToChange(p, &aMem[pOp->p3]); aMem[pOp->p3].u.i += u.bs.nChange; } } break; } /* Opcode: CreateTable P1 P2 * * * |
︙ | ︙ | |||
63379 63380 63381 63382 63383 63384 63385 | u.bt.pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (1<<pOp->p1))!=0 ); u.bt.pDb = &db->aDb[pOp->p1]; assert( u.bt.pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ /* u.bt.flags = BTREE_INTKEY; */ | | | | 64650 64651 64652 64653 64654 64655 64656 64657 64658 64659 64660 64661 64662 64663 64664 64665 64666 | u.bt.pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (1<<pOp->p1))!=0 ); u.bt.pDb = &db->aDb[pOp->p1]; assert( u.bt.pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ /* u.bt.flags = BTREE_INTKEY; */ u.bt.flags = BTREE_INTKEY; }else{ u.bt.flags = BTREE_BLOBKEY; } rc = sqlite3BtreeCreateTable(u.bt.pDb->pBt, &u.bt.pgno, u.bt.flags); pOut->u.i = u.bt.pgno; break; } /* Opcode: ParseSchema P1 P2 * P4 * |
︙ | ︙ | |||
63710 63711 63712 63713 63714 63715 63716 63717 63718 63719 63720 63721 63722 63723 | VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ #endif /* local variables moved into u.by */ u.by.pProgram = pOp->p4.pProgram; u.by.pRt = &aMem[pOp->p3]; assert( u.by.pProgram->nOp>0 ); /* If the p5 flag is clear, then recursive invocation of triggers is ** disabled for backwards compatibility (p5 is set if this sub-program ** is really a trigger, not a foreign key action, and the flag set ** and cleared by the "PRAGMA recursive_triggers" command is clear). ** | > | 64981 64982 64983 64984 64985 64986 64987 64988 64989 64990 64991 64992 64993 64994 64995 | VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ #endif /* local variables moved into u.by */ u.by.pProgram = pOp->p4.pProgram; u.by.pRt = &aMem[pOp->p3]; assert( memIsValid(u.by.pRt) ); assert( u.by.pProgram->nOp>0 ); /* If the p5 flag is clear, then recursive invocation of triggers is ** disabled for backwards compatibility (p5 is set if this sub-program ** is really a trigger, not a foreign key action, and the flag set ** and cleared by the "PRAGMA recursive_triggers" command is clear). ** |
︙ | ︙ | |||
63883 63884 63885 63886 63887 63888 63889 63890 63891 63892 63893 63894 63895 63896 | #endif /* local variables moved into u.ca */ if( p->pFrame ){ for(u.ca.pFrame=p->pFrame; u.ca.pFrame->pParent; u.ca.pFrame=u.ca.pFrame->pParent); u.ca.pIn1 = &u.ca.pFrame->aMem[pOp->p1]; }else{ u.ca.pIn1 = &aMem[pOp->p1]; } sqlite3VdbeMemIntegerify(u.ca.pIn1); pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); if( u.ca.pIn1->u.i<pIn2->u.i){ u.ca.pIn1->u.i = pIn2->u.i; } break; | > | 65155 65156 65157 65158 65159 65160 65161 65162 65163 65164 65165 65166 65167 65168 65169 | #endif /* local variables moved into u.ca */ if( p->pFrame ){ for(u.ca.pFrame=p->pFrame; u.ca.pFrame->pParent; u.ca.pFrame=u.ca.pFrame->pParent); u.ca.pIn1 = &u.ca.pFrame->aMem[pOp->p1]; }else{ u.ca.pIn1 = &aMem[pOp->p1]; } assert( memIsValid(u.ca.pIn1) ); sqlite3VdbeMemIntegerify(u.ca.pIn1); pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); if( u.ca.pIn1->u.i<pIn2->u.i){ u.ca.pIn1->u.i = pIn2->u.i; } break; |
︙ | ︙ | |||
63969 63970 63971 63972 63973 63974 63975 63976 63977 63978 63979 63980 63981 63982 63983 63984 63985 63986 63987 63988 63989 63990 63991 63992 63993 63994 63995 | u.cb.n = pOp->p5; assert( u.cb.n>=0 ); u.cb.pRec = &aMem[pOp->p2]; u.cb.apVal = p->apArg; assert( u.cb.apVal || u.cb.n==0 ); for(u.cb.i=0; u.cb.i<u.cb.n; u.cb.i++, u.cb.pRec++){ u.cb.apVal[u.cb.i] = u.cb.pRec; sqlite3VdbeMemStoreType(u.cb.pRec); } u.cb.ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); u.cb.ctx.pMem = u.cb.pMem = &aMem[pOp->p3]; u.cb.pMem->n++; u.cb.ctx.s.flags = MEM_Null; u.cb.ctx.s.z = 0; u.cb.ctx.s.zMalloc = 0; u.cb.ctx.s.xDel = 0; u.cb.ctx.s.db = db; u.cb.ctx.isError = 0; u.cb.ctx.pColl = 0; if( u.cb.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>p->aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); u.cb.ctx.pColl = pOp[-1].p4.pColl; } | > > | | 65242 65243 65244 65245 65246 65247 65248 65249 65250 65251 65252 65253 65254 65255 65256 65257 65258 65259 65260 65261 65262 65263 65264 65265 65266 65267 65268 65269 65270 65271 65272 65273 65274 65275 65276 65277 65278 | u.cb.n = pOp->p5; assert( u.cb.n>=0 ); u.cb.pRec = &aMem[pOp->p2]; u.cb.apVal = p->apArg; assert( u.cb.apVal || u.cb.n==0 ); for(u.cb.i=0; u.cb.i<u.cb.n; u.cb.i++, u.cb.pRec++){ assert( memIsValid(u.cb.pRec) ); u.cb.apVal[u.cb.i] = u.cb.pRec; memAboutToChange(p, u.cb.pRec); sqlite3VdbeMemStoreType(u.cb.pRec); } u.cb.ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); u.cb.ctx.pMem = u.cb.pMem = &aMem[pOp->p3]; u.cb.pMem->n++; u.cb.ctx.s.flags = MEM_Null; u.cb.ctx.s.z = 0; u.cb.ctx.s.zMalloc = 0; u.cb.ctx.s.xDel = 0; u.cb.ctx.s.db = db; u.cb.ctx.isError = 0; u.cb.ctx.pColl = 0; if( u.cb.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>p->aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); u.cb.ctx.pColl = pOp[-1].p4.pColl; } (u.cb.ctx.pFunc->xStep)(&u.cb.ctx, u.cb.n, u.cb.apVal); /* IMP: R-24505-23230 */ if( u.cb.ctx.isError ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cb.ctx.s)); rc = u.cb.ctx.isError; } sqlite3VdbeMemRelease(&u.cb.ctx.s); break; } |
︙ | ︙ | |||
64376 64377 64378 64379 64380 64381 64382 64383 64384 64385 64386 64387 64388 64389 | int i; Mem **apArg; #endif /* local variables moved into u.ch */ u.ch.pQuery = &aMem[pOp->p3]; u.ch.pArgc = &u.ch.pQuery[1]; u.ch.pCur = p->apCsr[pOp->p1]; REGISTER_TRACE(pOp->p3, u.ch.pQuery); assert( u.ch.pCur->pVtabCursor ); u.ch.pVtabCursor = u.ch.pCur->pVtabCursor; u.ch.pVtab = u.ch.pVtabCursor->pVtab; u.ch.pModule = u.ch.pVtab->pModule; /* Grab the index number and argc parameters */ | > | 65651 65652 65653 65654 65655 65656 65657 65658 65659 65660 65661 65662 65663 65664 65665 | int i; Mem **apArg; #endif /* local variables moved into u.ch */ u.ch.pQuery = &aMem[pOp->p3]; u.ch.pArgc = &u.ch.pQuery[1]; u.ch.pCur = p->apCsr[pOp->p1]; assert( memIsValid(u.ch.pQuery) ); REGISTER_TRACE(pOp->p3, u.ch.pQuery); assert( u.ch.pCur->pVtabCursor ); u.ch.pVtabCursor = u.ch.pCur->pVtabCursor; u.ch.pVtab = u.ch.pVtabCursor->pVtab; u.ch.pModule = u.ch.pVtab->pModule; /* Grab the index number and argc parameters */ |
︙ | ︙ | |||
64433 64434 64435 64436 64437 64438 64439 64440 64441 64442 64443 64444 64445 64446 | sqlite3_context sContext; #endif /* local variables moved into u.ci */ VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); u.ci.pDest = &aMem[pOp->p3]; if( pCur->nullRow ){ sqlite3VdbeMemSetNull(u.ci.pDest); break; } u.ci.pVtab = pCur->pVtabCursor->pVtab; u.ci.pModule = u.ci.pVtab->pModule; assert( u.ci.pModule->xColumn ); | > | 65709 65710 65711 65712 65713 65714 65715 65716 65717 65718 65719 65720 65721 65722 65723 | sqlite3_context sContext; #endif /* local variables moved into u.ci */ VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); u.ci.pDest = &aMem[pOp->p3]; memAboutToChange(p, u.ci.pDest); if( pCur->nullRow ){ sqlite3VdbeMemSetNull(u.ci.pDest); break; } u.ci.pVtab = pCur->pVtabCursor->pVtab; u.ci.pModule = u.ci.pVtab->pModule; assert( u.ci.pModule->xColumn ); |
︙ | ︙ | |||
64535 64536 64537 64538 64539 64540 64541 64542 64543 64544 64545 64546 64547 64548 64549 64550 64551 64552 | sqlite3_vtab *pVtab; Mem *pName; #endif /* local variables moved into u.ck */ u.ck.pVtab = pOp->p4.pVtab->pVtab; u.ck.pName = &aMem[pOp->p1]; assert( u.ck.pVtab->pModule->xRename ); REGISTER_TRACE(pOp->p1, u.ck.pName); assert( u.ck.pName->flags & MEM_Str ); rc = u.ck.pVtab->pModule->xRename(u.ck.pVtab, u.ck.pName->z); importVtabErrMsg(p, u.ck.pVtab); break; } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VUpdate P1 P2 P3 P4 * | > > | 65812 65813 65814 65815 65816 65817 65818 65819 65820 65821 65822 65823 65824 65825 65826 65827 65828 65829 65830 65831 | sqlite3_vtab *pVtab; Mem *pName; #endif /* local variables moved into u.ck */ u.ck.pVtab = pOp->p4.pVtab->pVtab; u.ck.pName = &aMem[pOp->p1]; assert( u.ck.pVtab->pModule->xRename ); assert( memIsValid(u.ck.pName) ); REGISTER_TRACE(pOp->p1, u.ck.pName); assert( u.ck.pName->flags & MEM_Str ); rc = u.ck.pVtab->pModule->xRename(u.ck.pVtab, u.ck.pName->z); importVtabErrMsg(p, u.ck.pVtab); p->expired = 0; break; } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VUpdate P1 P2 P3 P4 * |
︙ | ︙ | |||
64587 64588 64589 64590 64591 64592 64593 64594 64595 64596 64597 64598 64599 64600 | u.cl.pModule = (sqlite3_module *)u.cl.pVtab->pModule; u.cl.nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); if( ALWAYS(u.cl.pModule->xUpdate) ){ u.cl.apArg = p->apArg; u.cl.pX = &aMem[pOp->p3]; for(u.cl.i=0; u.cl.i<u.cl.nArg; u.cl.i++){ sqlite3VdbeMemStoreType(u.cl.pX); u.cl.apArg[u.cl.i] = u.cl.pX; u.cl.pX++; } rc = u.cl.pModule->xUpdate(u.cl.pVtab, u.cl.nArg, u.cl.apArg, &u.cl.rowid); importVtabErrMsg(p, u.cl.pVtab); if( rc==SQLITE_OK && pOp->p1 ){ | > > | 65866 65867 65868 65869 65870 65871 65872 65873 65874 65875 65876 65877 65878 65879 65880 65881 | u.cl.pModule = (sqlite3_module *)u.cl.pVtab->pModule; u.cl.nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); if( ALWAYS(u.cl.pModule->xUpdate) ){ u.cl.apArg = p->apArg; u.cl.pX = &aMem[pOp->p3]; for(u.cl.i=0; u.cl.i<u.cl.nArg; u.cl.i++){ assert( memIsValid(u.cl.pX) ); memAboutToChange(p, u.cl.pX); sqlite3VdbeMemStoreType(u.cl.pX); u.cl.apArg[u.cl.i] = u.cl.pX; u.cl.pX++; } rc = u.cl.pModule->xUpdate(u.cl.pVtab, u.cl.nArg, u.cl.apArg, &u.cl.rowid); importVtabErrMsg(p, u.cl.pVtab); if( rc==SQLITE_OK && pOp->p1 ){ |
︙ | ︙ | |||
65641 65642 65643 65644 65645 65646 65647 | ** an in-memory journal */ SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *pJfd){ return pJfd->pMethods==&MemJournalMethods; } /* | | < | 66922 66923 66924 66925 66926 66927 66928 66929 66930 66931 66932 66933 66934 66935 66936 | ** an in-memory journal */ SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *pJfd){ return pJfd->pMethods==&MemJournalMethods; } /* ** Return the number of bytes required to store a MemJournal file descriptor. */ SQLITE_PRIVATE int sqlite3MemJournalSize(void){ return sizeof(MemJournal); } /************** End of memjournal.c ******************************************/ /************** Begin file walker.c ******************************************/ |
︙ | ︙ | |||
67545 67546 67547 67548 67549 67550 67551 | if( z[1]==0 ){ /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); pExpr->iColumn = (ynVar)(++pParse->nVar); }else if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ | | > | | | 68825 68826 68827 68828 68829 68830 68831 68832 68833 68834 68835 68836 68837 68838 68839 68840 68841 68842 68843 68844 68845 68846 68847 68848 68849 68850 68851 | if( z[1]==0 ){ /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); pExpr->iColumn = (ynVar)(++pParse->nVar); }else if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ i64 i; int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8); pExpr->iColumn = (ynVar)i; testcase( i==0 ); testcase( i==1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); } if( i>pParse->nVar ){ pParse->nVar = (int)i; } }else{ /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable ** number as the prior appearance of the same name, or if the name ** has never appeared before, reuse the same variable number */ int i; |
︙ | ︙ | |||
68525 68526 68527 68528 68529 68530 68531 | pX->iTable = iTab; } return eType; } #endif /* | | | | 69806 69807 69808 69809 69810 69811 69812 69813 69814 69815 69816 69817 69818 69819 69820 69821 | pX->iTable = iTab; } return eType; } #endif /* ** Generate code for scalar subqueries used as a subquery expression, EXISTS, ** or IN operators. Examples: ** ** (SELECT a FROM b) -- subquery ** EXISTS (SELECT a FROM b) -- EXISTS subquery ** x IN (4,5,11) -- IN operator with list on right-hand side ** x IN (SELECT a FROM b) -- IN operator with subquery on the right ** ** The pExpr parameter describes the expression that contains the IN |
︙ | ︙ | |||
68589 68590 68591 68592 68593 68594 68595 | sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); assert( testAddr>0 || pParse->db->mallocFailed ); } switch( pExpr->op ){ case TK_IN: { | | | | | | 69870 69871 69872 69873 69874 69875 69876 69877 69878 69879 69880 69881 69882 69883 69884 69885 69886 69887 | sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); assert( testAddr>0 || pParse->db->mallocFailed ); } switch( pExpr->op ){ case TK_IN: { char affinity; /* Affinity of the LHS of the IN */ KeyInfo keyInfo; /* Keyinfo for the generated table */ int addr; /* Address of OP_OpenEphemeral instruction */ Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ if( rMayHaveNull ){ sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull); } affinity = sqlite3ExprAffinity(pLeft); |
︙ | ︙ | |||
68615 68616 68617 68618 68619 68620 68621 68622 68623 68624 68625 68626 68627 68628 | ** SELECT... statement are columns, then numeric affinity is used ** if either column has NUMERIC or INTEGER affinity. If neither ** 'x' nor the SELECT... statement are columns, then numeric affinity ** is used. */ pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); memset(&keyInfo, 0, sizeof(keyInfo)); keyInfo.nField = 1; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into the temporary | > | 69896 69897 69898 69899 69900 69901 69902 69903 69904 69905 69906 69907 69908 69909 69910 | ** SELECT... statement are columns, then numeric affinity is used ** if either column has NUMERIC or INTEGER affinity. If neither ** 'x' nor the SELECT... statement are columns, then numeric affinity ** is used. */ pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED); memset(&keyInfo, 0, sizeof(keyInfo)); keyInfo.nField = 1; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into the temporary |
︙ | ︙ | |||
68907 68908 68909 68910 68911 68912 68913 | ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ double value; char *zV; | | | < < > > > | < < | | 70189 70190 70191 70192 70193 70194 70195 70196 70197 70198 70199 70200 70201 70202 70203 70204 70205 70206 70207 70208 70209 70210 70211 70212 70213 70214 70215 70216 70217 70218 70219 70220 70221 70222 70223 70224 70225 70226 70227 70228 70229 70230 70231 70232 70233 | ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ double value; char *zV; sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ if( negateFlag ) value = -value; zV = dup8bytes(v, (char*)&value); sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); } } #endif /* ** Generate an instruction that will put the integer describe by ** text z[0..n-1] into register iMem. ** ** Expr.u.zToken is always UTF8 and zero-terminated. */ 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; if( negFlag ) i = -i; sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); }else{ int c; i64 value; const char *z = pExpr->u.zToken; assert( z!=0 ); c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); if( c==0 || (c==2 && negFlag) ){ char *zV; if( negFlag ){ value = -value; } zV = dup8bytes(v, (char*)&value); sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); }else{ #ifdef SQLITE_OMIT_FLOATING_POINT sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); #else codeReal(v, z, negFlag, iMem); |
︙ | ︙ | |||
69223 69224 69225 69226 69227 69228 69229 | int r = p->iReg; if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/ } return 0; } #endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 70504 70505 70506 70507 70508 70509 70510 70511 70512 70513 70514 70515 70516 70517 | int r = p->iReg; if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/ } return 0; } #endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ /* ** Generate code into the current Vdbe to evaluate the given ** expression. Attempt to store the results in register "target". ** Return the register where results are stored. ** ** With this routine, there is no guarantee that results will ** be stored in target. The result might be stored in some other |
︙ | ︙ | |||
69398 69399 69400 69401 69402 69403 69404 | break; } case TK_REGISTER: { inReg = pExpr->iTable; break; } case TK_AS: { | | | 70612 70613 70614 70615 70616 70617 70618 70619 70620 70621 70622 70623 70624 70625 70626 | break; } case TK_REGISTER: { inReg = pExpr->iTable; break; } case TK_AS: { inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); break; } #ifndef SQLITE_OMIT_CAST case TK_CAST: { /* Expressions of the form: CAST(pLeft AS token) */ int aff, to_op; inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); |
︙ | ︙ | |||
69830 69831 69832 69833 69834 69835 69836 69837 69838 69839 69840 69841 69842 69843 | testcase( pX->op==TK_REGISTER ); cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, ®Free1); testcase( regFree1==0 ); cacheX.op = TK_REGISTER; opCompare.op = TK_EQ; opCompare.pLeft = &cacheX; pTest = &opCompare; } for(i=0; i<nExpr; i=i+2){ sqlite3ExprCachePush(pParse); if( pX ){ assert( pTest!=0 ); opCompare.pRight = aListelem[i].pExpr; }else{ | > > > > > | 71044 71045 71046 71047 71048 71049 71050 71051 71052 71053 71054 71055 71056 71057 71058 71059 71060 71061 71062 | testcase( pX->op==TK_REGISTER ); cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, ®Free1); testcase( regFree1==0 ); cacheX.op = TK_REGISTER; opCompare.op = TK_EQ; opCompare.pLeft = &cacheX; pTest = &opCompare; /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001: ** The value in regFree1 might get SCopy-ed into the file result. ** So make sure that the regFree1 register is not reused for other ** purposes and possibly overwritten. */ regFree1 = 0; } for(i=0; i<nExpr; i=i+2){ sqlite3ExprCachePush(pParse); if( pX ){ assert( pTest!=0 ); opCompare.pRight = aListelem[i].pExpr; }else{ |
︙ | ︙ | |||
69923 69924 69925 69926 69927 69928 69929 | ** results in register target. The results are guaranteed to appear ** in register target. */ SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ int inReg; assert( target>0 && target<=pParse->nMem ); | > > > | | | | > | 71142 71143 71144 71145 71146 71147 71148 71149 71150 71151 71152 71153 71154 71155 71156 71157 71158 71159 71160 71161 71162 71163 | ** results in register target. The results are guaranteed to appear ** in register target. */ SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ int inReg; assert( target>0 && target<=pParse->nMem ); if( pExpr && pExpr->op==TK_REGISTER ){ sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target); }else{ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); assert( pParse->pVdbe || pParse->db->mallocFailed ); if( inReg!=target && pParse->pVdbe ){ sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target); } } return target; } /* ** Generate code that evalutes the given expression and puts the result ** in register target. |
︙ | ︙ | |||
70099 70100 70101 70102 70103 70104 70105 70106 70107 | int target, /* Where to write results */ int doHardCopy /* Make a hard copy of every element */ ){ struct ExprList_item *pItem; int i, n; assert( pList!=0 ); assert( target>0 ); n = pList->nExpr; for(pItem=pList->a, i=0; i<n; i++, pItem++){ | > | < | | | < < | < < < | 71322 71323 71324 71325 71326 71327 71328 71329 71330 71331 71332 71333 71334 71335 71336 71337 71338 71339 71340 71341 71342 71343 | int target, /* Where to write results */ int doHardCopy /* Make a hard copy of every element */ ){ struct ExprList_item *pItem; int i, n; assert( pList!=0 ); assert( target>0 ); assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ n = pList->nExpr; for(pItem=pList->a, i=0; i<n; i++, pItem++){ Expr *pExpr = pItem->pExpr; int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); if( inReg!=target+i ){ sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy, inReg, target+i); } } return n; } /* ** Generate code for a BETWEEN operator. |
︙ | ︙ | |||
71093 71094 71095 71096 71097 71098 71099 71100 71101 71102 71103 71104 71105 71106 | sqlite3 *db = pParse->db; for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ zWhere = whereOrName(db, zWhere, pTrig->zName); } } } return zWhere; } /* ** Generate code to drop and reload the internal representation of table ** pTab from the database, including triggers and temporary triggers. ** Argument zName is the name of the table in the database schema at | > > > > > | 72311 72312 72313 72314 72315 72316 72317 72318 72319 72320 72321 72322 72323 72324 72325 72326 72327 72328 72329 | sqlite3 *db = pParse->db; for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ zWhere = whereOrName(db, zWhere, pTrig->zName); } } } if( zWhere ){ char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere); sqlite3DbFree(pParse->db, zWhere); zWhere = zNew; } return zWhere; } /* ** Generate code to drop and reload the internal representation of table ** pTab from the database, including triggers and temporary triggers. ** Argument zName is the name of the table in the database schema at |
︙ | ︙ | |||
71700 71701 71702 71703 71704 71705 71706 | sqlite3 *db = pParse->db; /* Database handle */ Index *pIdx; /* An index to being analyzed */ int iIdxCur; /* Cursor open on index being analyzed */ Vdbe *v; /* The virtual machine being built up */ int i; /* Loop counter */ int topOfLoop; /* The top of the loop */ int endOfLoop; /* The end of the loop */ | | > | > > > > > > > | > | < < < | 72923 72924 72925 72926 72927 72928 72929 72930 72931 72932 72933 72934 72935 72936 72937 72938 72939 72940 72941 72942 72943 72944 72945 72946 72947 72948 72949 72950 72951 72952 72953 72954 72955 72956 72957 72958 72959 72960 72961 72962 72963 72964 72965 72966 72967 72968 72969 72970 72971 72972 72973 72974 72975 72976 72977 72978 72979 72980 72981 72982 72983 72984 72985 72986 72987 72988 72989 72990 72991 72992 72993 72994 72995 72996 72997 | sqlite3 *db = pParse->db; /* Database handle */ Index *pIdx; /* An index to being analyzed */ int iIdxCur; /* Cursor open on index being analyzed */ Vdbe *v; /* The virtual machine being built up */ 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 iDb; /* Index of database containing pTab */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ int regSampleno = iMem++; /* Register containing next sample number */ int regCol = iMem++; /* Content of a column analyzed table */ int regRec = iMem++; /* Register holding completed record */ int regTemp = iMem++; /* Temporary use register */ int regRowid = iMem++; /* Rowid for the inserted record */ #ifdef SQLITE_ENABLE_STAT2 int regTemp2 = iMem++; /* Temporary use register */ int regSamplerecno = iMem++; /* Index of next sample to record */ int regRecno = iMem++; /* Current sample index */ int regLast = iMem++; /* Index of last sample to record */ int regFirst = iMem++; /* Index of first sample to record */ #endif v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) ){ return; } if( pTab->tnum==0 ){ /* Do not gather statistics on views or virtual tables */ return; } if( memcmp(pTab->zName, "sqlite_", 7)==0 ){ /* Do not gather statistics on system tables */ return; } assert( sqlite3BtreeHoldsAllMutexes(db) ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ return; } #endif /* Establish a read-lock on the table at the shared-cache level. */ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); 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); if( iMem+1+(nCol*2)>pParse->nMem ){ pParse->nMem = iMem+1+(nCol*2); } /* Open a cursor to the index to be analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, (char *)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); /* Populate the register containing the index name. */ sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); #ifdef SQLITE_ENABLE_STAT2 /* If this iteration of the loop is generating code to analyze the ** first index in the pTab->pIndex list, then register regLast has ** not been populated. In this case populate it now. */ |
︙ | ︙ | |||
71889 71890 71891 71892 71893 71894 71895 | ** ** I = (K+D-1)/D ** ** If K==0 then no entry is made into the sqlite_stat1 table. ** If K>0 then it is always the case the D>0 so division by zero ** is never possible. */ | < > > > > > > > > > > > > > > > > > > > > > > > > > | | 73118 73119 73120 73121 73122 73123 73124 73125 73126 73127 73128 73129 73130 73131 73132 73133 73134 73135 73136 73137 73138 73139 73140 73141 73142 73143 73144 73145 73146 73147 73148 73149 73150 73151 73152 73153 73154 73155 73156 73157 73158 73159 73160 73161 73162 73163 73164 73165 73166 73167 73168 73169 73170 73171 73172 73173 73174 73175 73176 73177 | ** ** I = (K+D-1)/D ** ** If K==0 then no entry is made into the sqlite_stat1 table. ** If K>0 then it is always the case the D>0 so division by zero ** is never possible. */ sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); if( jZeroRows==0 ){ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); } for(i=0; i<nCol; i++){ sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0); sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno); sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp); sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1); sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp); sqlite3VdbeAddOp1(v, OP_ToInt, regTemp); sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno); } sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); } /* If the table has no indices, create a single sqlite_stat1 entry ** containing NULL as the index name and the row count as the content. */ if( pTab->pIndex==0 ){ sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb); VdbeComment((v, "%s", pTab->zName)); sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno); sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); }else{ assert( jZeroRows>0 ); addr = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, jZeroRows); } sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); if( pParse->nMem<regRec ) pParse->nMem = regRec; if( jZeroRows ){ sqlite3VdbeJumpHere(v, addr); } } /* ** Generate code that will cause the most recent index analysis to ** be loaded into internal hash tables where is can be used. */ static void loadAnalysis(Parse *pParse, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); if( v ){ sqlite3VdbeAddOp1(v, OP_LoadAnalysis, iDb); } } |
︙ | ︙ | |||
72040 72041 72042 72043 72044 72045 72046 | const char *zDatabase; }; /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. ** | | > | > > > > | | | | | > > > > > > | | > > | 73293 73294 73295 73296 73297 73298 73299 73300 73301 73302 73303 73304 73305 73306 73307 73308 73309 73310 73311 73312 73313 73314 73315 73316 73317 73318 73319 73320 73321 73322 73323 73324 73325 73326 73327 73328 73329 73330 73331 73332 73333 73334 73335 73336 73337 73338 73339 73340 73341 73342 73343 73344 73345 73346 | const char *zDatabase; }; /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. ** ** argv[0] = name of the table ** argv[1] = name of the index (might be NULL) ** argv[2] = results of analysis - on integer for each column ** ** Entries for which argv[1]==NULL simply record the number of rows in ** the table. */ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ analysisInfo *pInfo = (analysisInfo*)pData; Index *pIndex; Table *pTable; int i, c, n; unsigned int v; const char *z; assert( argc==3 ); UNUSED_PARAMETER2(NotUsed, argc); if( argv==0 || argv[0]==0 || argv[2]==0 ){ return 0; } pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase); if( pTable==0 ){ return 0; } if( argv[1] ){ pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); }else{ pIndex = 0; } n = pIndex ? pIndex->nColumn : 0; z = argv[2]; for(i=0; *z && i<=n; i++){ v = 0; while( (c=z[0])>='0' && c<='9' ){ v = v*10 + c - '0'; z++; } if( i==0 ) pTable->nRowEst = v; if( pIndex==0 ) break; pIndex->aiRowEst[i] = v; if( *z==' ' ) z++; } return 0; } /* |
︙ | ︙ | |||
72090 72091 72092 72093 72094 72095 72096 72097 72098 72099 72100 72101 72102 72103 | if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ sqlite3DbFree(db, p->u.z); } } sqlite3DbFree(db, pIdx->aSample); } #else UNUSED_PARAMETER(pIdx); #endif } /* ** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The ** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] | > | 73356 73357 73358 73359 73360 73361 73362 73363 73364 73365 73366 73367 73368 73369 73370 | if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ sqlite3DbFree(db, p->u.z); } } sqlite3DbFree(db, pIdx->aSample); } #else UNUSED_PARAMETER(db); UNUSED_PARAMETER(pIdx); #endif } /* ** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The ** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] |
︙ | ︙ | |||
72141 72142 72143 72144 72145 72146 72147 | sInfo.zDatabase = db->aDb[iDb].zName; if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ return SQLITE_ERROR; } /* Load new statistics out of the sqlite_stat1 table */ zSql = sqlite3MPrintf(db, | | | 73408 73409 73410 73411 73412 73413 73414 73415 73416 73417 73418 73419 73420 73421 73422 | sInfo.zDatabase = db->aDb[iDb].zName; if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ return SQLITE_ERROR; } /* Load new statistics out of the sqlite_stat1 table */ zSql = sqlite3MPrintf(db, "SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); sqlite3DbFree(db, zSql); } |
︙ | ︙ | |||
72358 72359 72360 72361 72362 72363 72364 | aNew = &db->aDb[db->nDb]; memset(aNew, 0, sizeof(*aNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ | | | < | 73625 73626 73627 73628 73629 73630 73631 73632 73633 73634 73635 73636 73637 73638 73639 73640 | aNew = &db->aDb[db->nDb]; memset(aNew, 0, sizeof(*aNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0, db->openFlags | SQLITE_OPEN_MAIN_DB); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite3MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ Pager *pPager; aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt); |
︙ | ︙ | |||
72601 72602 72603 72604 72605 72606 72607 | 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ detachFunc, /* xFunc */ 0, /* xStep */ 0, /* xFinalize */ "sqlite_detach", /* zName */ | > | > | | 73867 73868 73869 73870 73871 73872 73873 73874 73875 73876 73877 73878 73879 73880 73881 73882 73883 73884 73885 73886 73887 73888 73889 73890 73891 73892 73893 73894 73895 73896 73897 73898 73899 73900 73901 73902 73903 73904 | 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ detachFunc, /* xFunc */ 0, /* xStep */ 0, /* xFinalize */ "sqlite_detach", /* zName */ 0, /* pHash */ 0 /* pDestructor */ }; codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname); } /* ** Called by the parser to compile an ATTACH statement. ** ** ATTACH p AS pDbname KEY pKey */ SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ static const FuncDef attach_func = { 3, /* nArg */ SQLITE_UTF8, /* iPrefEnc */ 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ attachFunc, /* xFunc */ 0, /* xStep */ 0, /* xFinalize */ "sqlite_attach", /* zName */ 0, /* pHash */ 0 /* pDestructor */ }; codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey); } #endif /* SQLITE_OMIT_ATTACH */ /* ** Initialize a DbFixer structure. This routine must be called prior |
︙ | ︙ | |||
73751 73752 73753 73754 73755 73756 73757 | ** The call below sets the pName pointer to point at the token (pName1 or ** pName2) that stores the unqualified table name. The variable iDb is ** set to the index of the database that the table or view is to be ** created in. */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) return; | | | > | 75019 75020 75021 75022 75023 75024 75025 75026 75027 75028 75029 75030 75031 75032 75033 75034 75035 | ** The call below sets the pName pointer to point at the token (pName1 or ** pName2) that stores the unqualified table name. The variable iDb is ** set to the index of the database that the table or view is to be ** created in. */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) return; if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){ /* If creating a temp table, the name may not be qualified. Unless ** the database name is "temp" anyway. */ sqlite3ErrorMsg(pParse, "temporary table name must be unqualified"); return; } if( !OMIT_TEMPDB && isTemp ) iDb = 1; pParse->sNameToken = *pName; zName = sqlite3NameFromToken(db, pName); |
︙ | ︙ | |||
73800 73801 73802 73803 73804 73805 73806 73807 73808 73809 | ** index or table name in the same database. Issue an error message if ** it does. The exception is if the statement being parsed was passed ** to an sqlite3_declare_vtab() call. In that case only the column names ** and types will be used, so there is no need to test for namespace ** collisions. */ if( !IN_DECLARE_VTAB ){ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto begin_table_error; } | > | | > | 75069 75070 75071 75072 75073 75074 75075 75076 75077 75078 75079 75080 75081 75082 75083 75084 75085 75086 75087 75088 75089 75090 75091 75092 75093 75094 75095 75096 75097 75098 75099 75100 75101 75102 75103 75104 75105 75106 75107 75108 75109 75110 75111 | ** index or table name in the same database. Issue an error message if ** it does. The exception is if the statement being parsed was passed ** to an sqlite3_declare_vtab() call. In that case only the column names ** and types will be used, so there is no need to test for namespace ** collisions. */ if( !IN_DECLARE_VTAB ){ char *zDb = db->aDb[iDb].zName; if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto begin_table_error; } pTable = sqlite3FindTable(db, zName, zDb); if( pTable ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "table %T already exists", pName); } goto begin_table_error; } if( sqlite3FindIndex(db, zName, zDb)!=0 ){ sqlite3ErrorMsg(pParse, "there is already an index named %s", zName); goto begin_table_error; } } pTable = sqlite3DbMallocZero(db, sizeof(Table)); if( pTable==0 ){ db->mallocFailed = 1; pParse->rc = SQLITE_NOMEM; pParse->nErr++; goto begin_table_error; } pTable->zName = zName; pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; pTable->nRef = 1; pTable->nRowEst = 1000000; assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; /* If this is the magic sqlite_sequence table used by autoincrement, ** then record a pointer to this table in the main database structure ** so that INSERT can find the table easily. */ |
︙ | ︙ | |||
74673 74674 74675 74676 74677 74678 74679 | if( pParse->nVar>0 ){ sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); sqlite3SelectDelete(db, pSelect); return; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; | | < < | 75944 75945 75946 75947 75948 75949 75950 75951 75952 75953 75954 75955 75956 75957 75958 75959 75960 75961 | if( pParse->nVar>0 ){ sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); sqlite3SelectDelete(db, pSelect); return; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; if( p==0 || pParse->nErr ){ sqlite3SelectDelete(db, pSelect); return; } sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlite3SchemaToIndex(db, p->pSchema); if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName) && sqlite3FixSelect(&sFix, pSelect) ){ sqlite3SelectDelete(db, pSelect); return; |
︙ | ︙ | |||
75796 75797 75798 75799 75800 75801 75802 | /* Fill the index with data and reparse the schema. Code an OP_Expire ** to invalidate all pre-compiled statements. */ if( pTblName ){ sqlite3RefillIndex(pParse, pIndex, iMem); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, | | > | 77065 77066 77067 77068 77069 77070 77071 77072 77073 77074 77075 77076 77077 77078 77079 77080 | /* Fill the index with data and reparse the schema. Code an OP_Expire ** to invalidate all pre-compiled statements. */ if( pTblName ){ sqlite3RefillIndex(pParse, pIndex, iMem); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), P4_DYNAMIC); sqlite3VdbeAddOp1(v, OP_Expire, 0); } } /* When adding an index to the list of indices for a table, make ** sure all indices labeled OE_Replace come after all those labeled ** OE_Ignore. This is necessary for the correct constraint check |
︙ | ︙ | |||
75857 75858 75859 75860 75861 75862 75863 75864 | ** Apart from that, we have little to go on besides intuition as to ** how aiRowEst[] should be initialized. The numbers generated here ** are based on typical values found in actual indices. */ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ unsigned *a = pIdx->aiRowEst; int i; assert( a!=0 ); | > > | > | < < < | | | 77127 77128 77129 77130 77131 77132 77133 77134 77135 77136 77137 77138 77139 77140 77141 77142 77143 77144 77145 77146 77147 77148 | ** Apart from that, we have little to go on besides intuition as to ** how aiRowEst[] should be initialized. The numbers generated here ** are based on typical values found in actual indices. */ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ unsigned *a = pIdx->aiRowEst; int i; unsigned n; assert( a!=0 ); a[0] = pIdx->pTable->nRowEst; if( a[0]<10 ) a[0] = 10; n = 10; for(i=1; i<=pIdx->nColumn; i++){ a[i] = n; if( n>5 ) n--; } if( pIdx->onError!=OE_None ){ a[pIdx->nColumn] = 1; } } /* |
︙ | ︙ | |||
75924 75925 75926 75927 75928 75929 75930 | #endif /* Generate code to remove the index and from the master table */ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, | | | 77194 77195 77196 77197 77198 77199 77200 77201 77202 77203 77204 77205 77206 77207 77208 | #endif /* Generate code to remove the index and from the master table */ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE name=%Q AND type='index'", db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName ); if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q", db->aDb[iDb].zName, pIndex->zName |
︙ | ︙ | |||
76416 76417 76418 76419 76420 76421 76422 | static const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; | | | 77686 77687 77688 77689 77690 77691 77692 77693 77694 77695 77696 77697 77698 77699 77700 | static const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags); if( rc!=SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to open a temporary database " "file for storing temporary tables"); pParse->rc = rc; return 1; } db->aDb[1].pBt = pBt; |
︙ | ︙ | |||
77073 77074 77075 77076 77077 77078 77079 | /* If no match is found, search the built-in functions. ** ** If the SQLITE_PreferBuiltin flag is set, then search the built-in ** functions even if a prior app-defined function was found. And give ** priority to built-in functions. ** ** Except, if createFlag is true, that means that we are trying to | | | 78343 78344 78345 78346 78347 78348 78349 78350 78351 78352 78353 78354 78355 78356 78357 | /* If no match is found, search the built-in functions. ** ** If the SQLITE_PreferBuiltin flag is set, then search the built-in ** functions even if a prior app-defined function was found. And give ** priority to built-in functions. ** ** Except, if createFlag is true, that means that we are trying to ** install a new function. Whatever FuncDef structure is returned it will ** have fields overwritten with new information appropriate for the ** new function. But the FuncDefs for built-in functions are read-only. ** So we must not search for built-ins when creating a new function. */ if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); bestScore = 0; |
︙ | ︙ | |||
78089 78090 78091 78092 78093 78094 78095 | r = -(double)((sqlite_int64)((-r)+0.5)); }else{ zBuf = sqlite3_mprintf("%.*f",n,r); if( zBuf==0 ){ sqlite3_result_error_nomem(context); return; } | | | 79359 79360 79361 79362 79363 79364 79365 79366 79367 79368 79369 79370 79371 79372 79373 | r = -(double)((sqlite_int64)((-r)+0.5)); }else{ zBuf = sqlite3_mprintf("%.*f",n,r); if( zBuf==0 ){ sqlite3_result_error_nomem(context); return; } sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8); sqlite3_free(zBuf); } sqlite3_result_double(context, r); } #endif /* |
︙ | ︙ | |||
79254 79255 79256 79257 79258 79259 79260 | SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ struct compareInfo *pInfo; if( caseSensitive ){ pInfo = (struct compareInfo*)&likeInfoAlt; }else{ pInfo = (struct compareInfo*)&likeInfoNorm; } | | | | | 80524 80525 80526 80527 80528 80529 80530 80531 80532 80533 80534 80535 80536 80537 80538 80539 80540 80541 | SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ struct compareInfo *pInfo; if( caseSensitive ){ pInfo = (struct compareInfo*)&likeInfoAlt; }else{ pInfo = (struct compareInfo*)&likeInfoNorm; } sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0); sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0); sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY, (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0); setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); setLikeOptFlag(db, "like", caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE); } /* ** pExpr points to an expression which implements a function. If |
︙ | ︙ | |||
79341 79342 79343 79344 79345 79346 79347 | FUNCTION(round, 2, 0, 0, roundFunc ), #endif FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), /* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */ | | | | 80611 80612 80613 80614 80615 80616 80617 80618 80619 80620 80621 80622 80623 80624 80625 80626 80627 80628 | FUNCTION(round, 2, 0, 0, roundFunc ), #endif FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), /* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */ {-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0}, FUNCTION(hex, 1, 0, 0, hexFunc ), /* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */ {2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0}, FUNCTION(random, 0, 0, 0, randomFunc ), FUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), |
︙ | ︙ | |||
79371 79372 79373 79374 79375 79376 79377 | FUNCTION(load_extension, 1, 0, 0, loadExt ), FUNCTION(load_extension, 2, 0, 0, loadExt ), #endif AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */ | | | 80641 80642 80643 80644 80645 80646 80647 80648 80649 80650 80651 80652 80653 80654 80655 | FUNCTION(load_extension, 1, 0, 0, loadExt ), FUNCTION(load_extension, 2, 0, 0, loadExt ), #endif AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */ {0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0}, AGGREGATE(count, 1, 0, 0, countStep, countFinalize ), AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize), AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize), LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), #ifdef SQLITE_CASE_SENSITIVE_LIKE LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), |
︙ | ︙ | |||
79782 79783 79784 79785 79786 79787 79788 | int regTemp = sqlite3GetTempRange(pParse, nCol); int regRec = sqlite3GetTempReg(pParse); KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); for(i=0; i<nCol; i++){ | | | 81052 81053 81054 81055 81056 81057 81058 81059 81060 81061 81062 81063 81064 81065 81066 | int regTemp = sqlite3GetTempRange(pParse, nCol); int regRec = sqlite3GetTempReg(pParse); KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); for(i=0; i<nCol; i++){ sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i); } /* If the parent table is the same as the child table, and we are about ** to increment the constraint-counter (i.e. this is an INSERT operation), ** then check if the row being inserted matches itself. If so, do not ** increment the constraint-counter. */ if( pTab==pFKey->pFrom && nIncr==1 ){ |
︙ | ︙ | |||
84147 84148 84149 84150 84151 84152 84153 | ** ** Get or set the size limit on rollback journal files. */ if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ | | | 85417 85418 85419 85420 85421 85422 85423 85424 85425 85426 85427 85428 85429 85430 85431 | ** ** Get or set the size limit on rollback journal files. */ if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8); if( iLimit<-1 ) iLimit = -1; } iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); returnSingleInt(pParse, "journal_size_limit", iLimit); }else #endif /* SQLITE_OMIT_PAGER_PRAGMAS */ |
︙ | ︙ | |||
86380 86381 86382 86383 86384 86385 86386 | addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); addr2 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); sqlite3VdbeJumpHere(v, addr2); | < | 87650 87651 87652 87653 87654 87655 87656 87657 87658 87659 87660 87661 87662 87663 | addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); addr2 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); sqlite3VdbeJumpHere(v, addr2); } } /* ** Add code to implement the OFFSET */ static void codeOffset( |
︙ | ︙ | |||
86429 86430 86431 86432 86433 86434 86435 86436 86437 86438 | r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1); sqlite3ReleaseTempReg(pParse, r1); } /* ** Generate an error message when a SELECT is used within a subexpression ** (example: "a IN (SELECT * FROM table)") but it has more than 1 result | > | | > > | 87698 87699 87700 87701 87702 87703 87704 87705 87706 87707 87708 87709 87710 87711 87712 87713 87714 87715 87716 87717 87718 87719 87720 87721 87722 87723 87724 87725 87726 87727 87728 87729 87730 87731 87732 87733 87734 | r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1); sqlite3ReleaseTempReg(pParse, r1); } #ifndef SQLITE_OMIT_SUBQUERY /* ** Generate an error message when a SELECT is used within a subexpression ** (example: "a IN (SELECT * FROM table)") but it has more than 1 result ** column. We do this in a subroutine because the error used to occur ** in multiple places. (The error only occurs in one place now, but we ** retain the subroutine to minimize code disruption.) */ static int checkForMultiColumnSelectError( Parse *pParse, /* Parse context. */ SelectDest *pDest, /* Destination of SELECT results */ int nExpr /* Number of result columns returned by SELECT */ ){ int eDest = pDest->eDest; if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){ sqlite3ErrorMsg(pParse, "only a single result allowed for " "a SELECT that is part of an expression"); return 1; }else{ return 0; } } #endif /* ** This routine generates the code for the inside of the inner loop ** of a SELECT. ** ** If srcTab and nColumn are both zero, then the pEList expressions ** are evaluated in order to get the data for this row. If nColumn>0 |
︙ | ︙ | |||
86528 86529 86530 86531 86532 86533 86534 | assert( pEList->nExpr==nColumn ); codeDistinct(pParse, distinct, iContinue, nColumn, regResult); if( pOrderBy==0 ){ codeOffset(v, p, iContinue); } } | < < < < | 87800 87801 87802 87803 87804 87805 87806 87807 87808 87809 87810 87811 87812 87813 | assert( pEList->nExpr==nColumn ); codeDistinct(pParse, distinct, iContinue, nColumn, regResult); if( pOrderBy==0 ){ codeOffset(v, p, iContinue); } } switch( eDest ){ /* In this mode, write each query result to the key of the temporary ** table iParm. */ #ifndef SQLITE_OMIT_COMPOUND_SELECT case SRT_Union: { int r1; |
︙ | ︙ | |||
86660 86661 86662 86663 86664 86665 86666 | default: { assert( eDest==SRT_Discard ); break; } #endif } | | > > | < < | 87928 87929 87930 87931 87932 87933 87934 87935 87936 87937 87938 87939 87940 87941 87942 87943 87944 87945 87946 | default: { assert( eDest==SRT_Discard ); break; } #endif } /* Jump to the end of the loop if the LIMIT is reached. Except, if ** there is a sorter, in which case the sorter has already limited ** the output for us. */ if( pOrderBy==0 && p->iLimit ){ sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); } } /* ** Given an expression list, generate a KeyInfo structure that records ** the collating sequence for each expression in that expression list. |
︙ | ︙ | |||
86799 86800 86801 86802 86803 86804 86805 | } break; } } sqlite3ReleaseTempReg(pParse, regRow); sqlite3ReleaseTempReg(pParse, regRowid); | < < < < | 88067 88068 88069 88070 88071 88072 88073 88074 88075 88076 88077 88078 88079 88080 | } break; } } sqlite3ReleaseTempReg(pParse, regRow); sqlite3ReleaseTempReg(pParse, regRowid); /* The bottom of the loop */ sqlite3VdbeResolveLabel(v, addrContinue); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); sqlite3VdbeResolveLabel(v, addrBreak); if( eDest==SRT_Output || eDest==SRT_Coroutine ){ sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0); |
︙ | ︙ | |||
87441 87442 87443 87444 87445 87446 87447 87448 87449 87450 87451 87452 87453 87454 | assert( v!=0 ); /* The VDBE already created by calling function */ /* Create the destination temporary table if necessary */ if( dest.eDest==SRT_EphemTab ){ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr); dest.eDest = SRT_Table; } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); | > | 88705 88706 88707 88708 88709 88710 88711 88712 88713 88714 88715 88716 88717 88718 88719 | assert( v!=0 ); /* The VDBE already created by calling function */ /* Create the destination temporary table if necessary */ if( dest.eDest==SRT_EphemTab ){ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); dest.eDest = SRT_Table; } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); |
︙ | ︙ | |||
87736 87737 87738 87739 87740 87741 87742 | ** The data to be output is contained in pIn->iMem. There are ** pIn->nMem columns to be output. pDest is where the output should ** be sent. ** ** regReturn is the number of the register holding the subroutine ** return address. ** | | | 89001 89002 89003 89004 89005 89006 89007 89008 89009 89010 89011 89012 89013 89014 89015 | ** The data to be output is contained in pIn->iMem. There are ** pIn->nMem columns to be output. pDest is where the output should ** be sent. ** ** regReturn is the number of the register holding the subroutine ** return address. ** ** If regPrev>0 then it is the first register in a vector that ** records the previous output. mem[regPrev] is a flag that is false ** if there has been no previous output. If regPrev>0 then code is ** generated to suppress duplicates. pKeyInfo is used for comparing ** keys. ** ** If the LIMIT found in p->iLimit is reached, jump immediately to ** iBreak. |
︙ | ︙ | |||
88119 88120 88121 88122 88123 88124 88125 | } } } /* Separate the left and the right query from one another */ p->pPrior = 0; | < | 89384 89385 89386 89387 89388 89389 89390 89391 89392 89393 89394 89395 89396 89397 | } } } /* Separate the left and the right query from one another */ p->pPrior = 0; sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); if( pPrior->pPrior==0 ){ sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); } /* Compute the limit registers */ computeLimitRegisters(pParse, p, labelEnd); |
︙ | ︙ | |||
88433 88434 88435 88436 88437 88438 88439 | ** Flattening is only attempted if all of the following are true: ** ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or the outer query is not a join. ** ** (3) The subquery is not the right operand of a left outer join | | | | > | | | | 89697 89698 89699 89700 89701 89702 89703 89704 89705 89706 89707 89708 89709 89710 89711 89712 89713 89714 89715 89716 89717 89718 89719 89720 89721 89722 89723 89724 89725 89726 89727 89728 89729 89730 89731 89732 89733 89734 89735 89736 89737 89738 89739 | ** Flattening is only attempted if all of the following are true: ** ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or the outer query is not a join. ** ** (3) The subquery is not the right operand of a left outer join ** (Originally ticket #306. Strengthened by ticket #3300) ** ** (4) The subquery is not DISTINCT. ** ** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT ** sub-queries that were excluded from this optimization. Restriction ** (4) has since been expanded to exclude all DISTINCT subqueries. ** ** (6) The subquery does not use aggregates or the outer query is not ** DISTINCT. ** ** (7) The subquery has a FROM clause. ** ** (8) The subquery does not use LIMIT or the outer query is not a join. ** ** (9) The subquery does not use LIMIT or the outer query does not use ** aggregates. ** ** (10) The subquery does not use aggregates or the outer query does not ** use LIMIT. ** ** (11) The subquery and the outer query do not both have ORDER BY clauses. ** ** (**) Not implemented. Subsumed into restriction (3). Was previously ** a separate restriction deriving from ticket #350. ** ** (13) The subquery and outer query do not both use LIMIT. ** ** (14) The subquery does not use OFFSET. ** ** (15) The outer query is not part of a compound select or the ** subquery does not have a LIMIT clause. ** (See ticket #2339 and ticket [02a8e81d44]). ** ** (16) The outer query is not an aggregate or the subquery does ** not contain ORDER BY. (Ticket #2942) This used to not matter |
︙ | ︙ | |||
88551 88552 88553 88554 88555 88556 88557 | ** and (14). */ if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */ if( pSub->pOffset ) return 0; /* Restriction (14) */ if( p->pRightmost && pSub->pLimit ){ return 0; /* Restriction (15) */ } if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */ | | | | | 89816 89817 89818 89819 89820 89821 89822 89823 89824 89825 89826 89827 89828 89829 89830 89831 89832 | ** and (14). */ if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */ if( pSub->pOffset ) return 0; /* Restriction (14) */ if( p->pRightmost && pSub->pLimit ){ return 0; /* Restriction (15) */ } if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */ if( pSub->selFlags & SF_Distinct ) return 0; /* Restriction (5) */ if( pSub->pLimit && (pSrc->nSrc>1 || isAgg) ){ return 0; /* Restrictions (8)(9) */ } if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){ return 0; /* Restriction (6) */ } if( p->pOrderBy && pSub->pOrderBy ){ return 0; /* Restriction (11) */ } |
︙ | ︙ | |||
89402 89403 89404 89405 89406 89407 89408 | int addrNext = 0; int regAgg; ExprList *pList = pF->pExpr->x.pList; assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); | | | 90667 90668 90669 90670 90671 90672 90673 90674 90675 90676 90677 90678 90679 90680 90681 | int addrNext = 0; int regAgg; ExprList *pList = pF->pExpr->x.pList; assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); sqlite3ExprCodeExprList(pParse, pList, regAgg, 1); }else{ nArg = 0; regAgg = 0; } if( pF->iDistinct>=0 ){ addrNext = sqlite3VdbeMakeLabel(v); assert( nArg==1 ); |
︙ | ︙ | |||
89561 89562 89563 89564 89565 89566 89567 89568 89569 89570 89571 89572 89573 89574 | isAgg = (p->selFlags & SF_Aggregate)!=0; assert( pEList!=0 ); /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto select_end; /* Generate code for all sub-queries in the FROM clause */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; SelectDest dest; | > > > > > > > > > | 90826 90827 90828 90829 90830 90831 90832 90833 90834 90835 90836 90837 90838 90839 90840 90841 90842 90843 90844 90845 90846 90847 90848 | isAgg = (p->selFlags & SF_Aggregate)!=0; assert( pEList!=0 ); /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto select_end; /* If writing to memory or generating a set ** only a single column may be output. */ #ifndef SQLITE_OMIT_SUBQUERY if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){ goto select_end; } #endif /* Generate code for all sub-queries in the FROM clause */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; SelectDest dest; |
︙ | ︙ | |||
89635 89636 89637 89638 89639 89640 89641 | return 1; } } return multiSelect(pParse, p, pDest); } #endif | < < < < < < < < < | 90909 90910 90911 90912 90913 90914 90915 90916 90917 90918 90919 90920 90921 90922 | return 1; } } return multiSelect(pParse, p, pDest); } #endif /* If possible, rewrite the query to use GROUP BY instead of DISTINCT. ** GROUP BY might use an index, DISTINCT never does. */ assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 ); if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){ p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); pGroupBy = p->pGroupBy; |
︙ | ︙ | |||
89706 89707 89708 89709 89710 89711 89712 89713 89714 89715 89716 89717 89718 89719 | if( isDistinct ){ KeyInfo *pKeyInfo; assert( isAgg || pGroupBy ); distinct = pParse->nTab++; pKeyInfo = keyInfoFromExprList(pParse, p->pEList); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); }else{ distinct = -1; } /* Aggregate and non-aggregate queries are handled differently */ if( !isAgg && pGroupBy==0 ){ /* This case is for non-aggregate queries | > | 90971 90972 90973 90974 90975 90976 90977 90978 90979 90980 90981 90982 90983 90984 90985 | if( isDistinct ){ KeyInfo *pKeyInfo; assert( isAgg || pGroupBy ); distinct = pParse->nTab++; pKeyInfo = keyInfoFromExprList(pParse, p->pEList); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); }else{ distinct = -1; } /* Aggregate and non-aggregate queries are handled differently */ if( !isAgg && pGroupBy==0 ){ /* This case is for non-aggregate queries |
︙ | ︙ | |||
92181 92182 92183 92184 92185 92186 92187 92188 92189 92190 92191 92192 92193 92194 | /* Create the ephemeral table into which the update results will ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); /* fill the ephemeral table */ sqlite3SelectDestInit(&dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, &dest); /* Generate code to scan the ephemeral table and call VUpdate. */ | > | 93447 93448 93449 93450 93451 93452 93453 93454 93455 93456 93457 93458 93459 93460 93461 | /* Create the ephemeral table into which the update results will ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); /* fill the ephemeral table */ sqlite3SelectDestInit(&dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, &dest); /* Generate code to scan the ephemeral table and call VUpdate. */ |
︙ | ︙ | |||
92319 92320 92321 92322 92323 92324 92325 92326 92327 92328 92329 92330 92331 92332 | int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; | > > > > | 93586 93587 93588 93589 93590 93591 93592 93593 93594 93595 93596 93597 93598 93599 93600 93601 93602 93603 | int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } if( db->activeVdbeCnt>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; |
︙ | ︙ | |||
92921 92922 92923 92924 92925 92926 92927 | pParse->regRowid ); sqlite3DbFree(db, zStmt); v = sqlite3GetVdbe(pParse); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); | | | 94192 94193 94194 94195 94196 94197 94198 94199 94200 94201 94202 94203 94204 94205 94206 | pParse->regRowid ); sqlite3DbFree(db, zStmt); v = sqlite3GetVdbe(pParse); sqlite3ChangeCookie(pParse, iDb); 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_VCreate, iDb, 0, 0, pTab->zName, sqlite3Strlen30(pTab->zName) + 1); } /* If we are rereading the sqlite_master table create the in-memory ** record of the table. The xConnect() method is not called until |
︙ | ︙ | |||
93223 93224 93225 93226 93227 93228 93229 | pTab->nCol = pParse->pNewTable->nCol; pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } db->pVTab = 0; }else{ sqlite3Error(db, SQLITE_ERROR, zErr); | | | 94494 94495 94496 94497 94498 94499 94500 94501 94502 94503 94504 94505 94506 94507 94508 | pTab->nCol = pParse->pNewTable->nCol; pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } db->pVTab = 0; }else{ sqlite3Error(db, SQLITE_ERROR, zErr); sqlite3DbFree(db, zErr); rc = SQLITE_ERROR; } pParse->declareVtab = 0; if( pParse->pVdbe ){ sqlite3VdbeFinalize(pParse->pVdbe); } |
︙ | ︙ | |||
94161 94162 94163 94164 94165 94166 94167 | pRight = pList->a[0].pExpr; op = pRight->op; if( op==TK_REGISTER ){ op = pRight->op2; } if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; | > | | | | 95432 95433 95434 95435 95436 95437 95438 95439 95440 95441 95442 95443 95444 95445 95446 95447 95448 95449 95450 95451 95452 95453 95454 95455 95456 95457 95458 95459 95460 95461 95462 95463 95464 95465 95466 95467 95468 95469 | pRight = pList->a[0].pExpr; op = pRight->op; if( op==TK_REGISTER ){ op = pRight->op2; } if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; int iCol = pRight->iColumn; pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ z = (char *)sqlite3_value_text(pVal); } sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */ assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ z = pRight->u.zToken; } if( z ){ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; } if( cnt!=0 && 255!=(u8)z[cnt-1] ){ Expr *pPrefix; *pisComplete = c==wc[0] && z[cnt+1]==0; pPrefix = sqlite3Expr(db, TK_STRING, z); if( pPrefix ) pPrefix->u.zToken[cnt] = 0; *ppPrefix = pPrefix; if( op==TK_VARIABLE ){ Vdbe *v = pParse->pVdbe; sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */ if( *pisComplete && pRight->u.zToken[1] ){ /* If the rhs of the LIKE expression is a variable, and the current ** value of the variable means there is no need to invoke the LIKE ** function, then no OP_Variable will be added to the program. ** This causes problems for the sqlite3_bind_parameter_name() ** API. To workaround them, add a dummy OP_Variable here. */ |
︙ | ︙ | |||
95047 95048 95049 95050 95051 95052 95053 | #define TRACE_IDX_OUTPUTS(A) #endif /* ** Required because bestIndex() is called by bestOrClauseIndex() */ static void bestIndex( | | > | > | 96319 96320 96321 96322 96323 96324 96325 96326 96327 96328 96329 96330 96331 96332 96333 96334 96335 96336 96337 96338 96339 96340 96341 96342 96343 96344 96345 96346 96347 96348 | #define TRACE_IDX_OUTPUTS(A) #endif /* ** Required because bestIndex() is called by bestOrClauseIndex() */ static void bestIndex( Parse*, WhereClause*, struct SrcList_item*, Bitmask, Bitmask, ExprList*, WhereCost*); /* ** This routine attempts to find an scanning strategy that can be used ** to optimize an 'OR' expression that is part of a WHERE clause. ** ** The table associated with FROM clause term pSrc may be either a ** regular B-Tree table or a virtual table. */ static void bestOrClauseIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for indexing */ Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_OR_OPTIMIZATION const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ |
︙ | ︙ | |||
95096 95097 95098 95099 95100 95101 95102 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ WhereCost sTermCost; WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", (pOrTerm - pOrWC->a), (pTerm - pWC->a) )); if( pOrTerm->eOperator==WO_AND ){ WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; | | | | 96370 96371 96372 96373 96374 96375 96376 96377 96378 96379 96380 96381 96382 96383 96384 96385 96386 96387 96388 96389 96390 96391 96392 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ WhereCost sTermCost; WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", (pOrTerm - pOrWC->a), (pTerm - pWC->a) )); if( pOrTerm->eOperator==WO_AND ){ WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost); }else if( pOrTerm->leftCursor==iCur ){ WhereClause tempWC; tempWC.pParse = pWC->pParse; tempWC.pMaskSet = pWC->pMaskSet; tempWC.op = TK_AND; tempWC.a = pOrTerm; tempWC.nTerm = 1; bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); }else{ continue; } rTotal += sTermCost.rCost; nRow += sTermCost.nRow; used |= sTermCost.used; if( rTotal>=pCost->rCost ) break; |
︙ | ︙ | |||
95197 95198 95199 95200 95201 95202 95203 | if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; } assert( pParse->nQueryLoop >= (double)1 ); pTable = pSrc->pTab; | | | 96471 96472 96473 96474 96475 96476 96477 96478 96479 96480 96481 96482 96483 96484 96485 | if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; } assert( pParse->nQueryLoop >= (double)1 ); pTable = pSrc->pTab; nTableRow = pTable->nRowEst; logN = estLog(nTableRow); costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1); if( costTempIdx>=pCost->rCost ){ /* The cost of creating the transient table would be greater than ** doing the full table scan */ return; } |
︙ | ︙ | |||
95551 95552 95553 95554 95555 95556 95557 | ** routine takes care of freeing the sqlite3_index_info structure after ** everybody has finished with it. */ static void bestVirtualIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ | | > | 96825 96826 96827 96828 96829 96830 96831 96832 96833 96834 96835 96836 96837 96838 96839 96840 | ** routine takes care of freeing the sqlite3_index_info structure after ** everybody has finished with it. */ static void bestVirtualIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for index */ Bitmask notValid, /* Cursors not valid for any purpose */ ExprList *pOrderBy, /* The order by clause */ WhereCost *pCost, /* Lowest cost query plan */ sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */ ){ Table *pTab = pSrc->pTab; sqlite3_index_info *pIdxInfo; struct sqlite3_index_constraint *pIdxCons; |
︙ | ︙ | |||
95681 95682 95683 95684 95685 95686 95687 | } pCost->plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; /* Try to find a more efficient access pattern by using multiple indexes ** to optimize an OR expression within the WHERE clause. */ | | | 96956 96957 96958 96959 96960 96961 96962 96963 96964 96965 96966 96967 96968 96969 96970 | } pCost->plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; /* Try to find a more efficient access pattern by using multiple indexes ** to optimize an OR expression within the WHERE clause. */ bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** 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 |
︙ | ︙ | |||
95807 95808 95809 95810 95811 95812 95813 | sqlite3_value **pp ){ /* The evalConstExpr() function will have already converted any TK_VARIABLE ** expression involved in an comparison into a TK_REGISTER. */ assert( pExpr->op!=TK_VARIABLE ); if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ int iVar = pExpr->iColumn; | | | 97082 97083 97084 97085 97086 97087 97088 97089 97090 97091 97092 97093 97094 97095 97096 | sqlite3_value **pp ){ /* The evalConstExpr() function will have already converted any TK_VARIABLE ** expression involved in an comparison into a TK_REGISTER. */ assert( pExpr->op!=TK_VARIABLE ); if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ int iVar = pExpr->iColumn; sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */ *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); return SQLITE_OK; } return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); } #endif |
︙ | ︙ | |||
95962 95963 95964 95965 95966 95967 95968 | ** selected plan may still take advantage of the tables built-in rowid ** index. */ static void bestBtreeIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ | | > | 97237 97238 97239 97240 97241 97242 97243 97244 97245 97246 97247 97248 97249 97250 97251 97252 | ** selected plan may still take advantage of the tables built-in rowid ** index. */ static void bestBtreeIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for indexing */ Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ Index *pProbe; /* An index we are evaluating */ Index *pIdx; /* Copy of pProbe, or zero for IPK index */ int eqTermMask; /* Current mask of valid equality operators */ |
︙ | ︙ | |||
96004 96005 96006 96007 96008 96009 96010 | /* There is no INDEXED BY clause. Create a fake Index object to ** represent the primary key */ Index *pFirst; /* Any other index on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowEst = aiRowEstPk; | < > > < < < < < < < < < < | 97280 97281 97282 97283 97284 97285 97286 97287 97288 97289 97290 97291 97292 97293 97294 97295 97296 97297 97298 97299 97300 97301 | /* There is no INDEXED BY clause. Create a fake Index object to ** represent the primary key */ Index *pFirst; /* Any other index on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowEst = aiRowEstPk; sPk.onError = OE_Replace; sPk.pTable = pSrc->pTab; aiRowEstPk[0] = pSrc->pTab->nRowEst; aiRowEstPk[1] = 1; pFirst = pSrc->pTab->pIndex; if( pSrc->notIndexed==0 ){ sPk.pNext = pFirst; } pProbe = &sPk; wsFlagMask = ~( WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE ); eqTermMask = WO_EQ|WO_IN; pIdx = 0; } |
︙ | ︙ | |||
96233 96234 96235 96236 96237 96238 96239 | /* If there are additional constraints on this table that cannot ** be used with the current index, but which might lower the number ** of output rows, adjust the nRow value accordingly. This only ** matters if the current index is the least costly, so do not bother ** with this step if we already know this index will not be chosen. ** Also, never reduce the output row count below 2 using this step. ** | | < < | < > | > > | | | | | 97500 97501 97502 97503 97504 97505 97506 97507 97508 97509 97510 97511 97512 97513 97514 97515 97516 97517 97518 97519 97520 97521 97522 97523 97524 97525 97526 97527 97528 97529 97530 97531 97532 | /* If there are additional constraints on this table that cannot ** be used with the current index, but which might lower the number ** of output rows, adjust the nRow value accordingly. This only ** matters if the current index is the least costly, so do not bother ** with this step if we already know this index will not be chosen. ** Also, never reduce the output row count below 2 using this step. ** ** It is critical that the notValid mask be used here instead of ** the notReady mask. When computing an "optimal" index, the notReady ** mask will only have one bit set - the bit for the current table. ** The notValid mask, on the other hand, always has all bits set for ** tables that are not in outer loops. If notReady is used here instead ** of notValid, then a optimal index that depends on inner joins loops ** might be selected even when there exists an optimal index that has ** no such dependency. */ if( nRow>2 && cost<=pCost->rCost ){ int k; /* Loop counter */ int nSkipEq = nEq; /* Number of == constraints to skip */ int nSkipRange = nBound; /* Number of < constraints to skip */ Bitmask thisTab; /* Bitmap for pSrc */ thisTab = getMask(pWC->pMaskSet, iCur); for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ if( pTerm->wtFlags & TERM_VIRTUAL ) continue; if( (pTerm->prereqAll & notValid)!=thisTab ) continue; if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ if( nSkipEq ){ /* Ignore the first nEq equality matches since the index ** has already accounted for these */ nSkipEq--; }else{ /* Assume each additional equality match reduces the result |
︙ | ︙ | |||
96333 96334 96335 96336 96337 96338 96339 | ); WHERETRACE(("best index is: %s\n", ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") )); | | | > | | | 97600 97601 97602 97603 97604 97605 97606 97607 97608 97609 97610 97611 97612 97613 97614 97615 97616 97617 97618 97619 97620 97621 97622 97623 97624 97625 97626 97627 97628 97629 97630 97631 97632 97633 97634 97635 97636 97637 97638 97639 97640 97641 97642 97643 97644 97645 | ); WHERETRACE(("best index is: %s\n", ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") )); bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); pCost->plan.wsFlags |= eqTermMask; } /* ** Find the query plan for accessing table pSrc->pTab. Write the ** best query plan and its cost into the WhereCost object supplied ** as the last parameter. This function may calculate the cost of ** both real and virtual table scans. */ static void bestIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for indexing */ Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pSrc->pTab) ){ sqlite3_index_info *p = 0; bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p); if( p->needToFreeIdxStr ){ sqlite3_free(p->idxStr); } sqlite3DbFree(pParse->db, p); }else #endif { bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); } } /* ** Disable a term in the WHERE clause. Except, do not disable the term ** if it controls a LEFT OUTER JOIN and it did not originate in the ON ** or USING clause of that join. |
︙ | ︙ | |||
96857 96858 96859 96860 96861 96862 96863 | ** If there are no inequality constraints, then N is at ** least one. ** ** This case is also used when there are no WHERE clause ** constraints but an index is selected anyway, in order ** to force the output order to conform to an ORDER BY. */ | | | | | | | | | 98125 98126 98127 98128 98129 98130 98131 98132 98133 98134 98135 98136 98137 98138 98139 98140 98141 98142 98143 98144 98145 98146 98147 98148 98149 98150 98151 98152 98153 98154 98155 98156 98157 98158 98159 98160 98161 98162 98163 98164 98165 98166 98167 | ** If there are no inequality constraints, then N is at ** least one. ** ** This case is also used when there are no WHERE clause ** constraints but an index is selected anyway, in order ** to force the output order to conform to an ORDER BY. */ static const u8 aStartOp[] = { 0, 0, OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */ OP_Last, /* 3: (!start_constraints && startEq && bRev) */ OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */ OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */ OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */ OP_SeekLe /* 7: (start_constraints && startEq && bRev) */ }; static const u8 aEndOp[] = { OP_Noop, /* 0: (!end_constraints) */ OP_IdxGE, /* 1: (end_constraints && !bRev) */ OP_IdxLT /* 2: (end_constraints && bRev) */ }; int nEq = pLevel->plan.nEq; /* Number of == or IN terms */ int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ int regBase; /* Base register holding constraint values */ int r1; /* Temp register */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */ int startEq; /* True if range start uses ==, >= or <= */ int endEq; /* True if range end uses ==, >= or <= */ int start_constraints; /* Start of range is constrained */ int nConstraint; /* Number of constraint terms */ Index *pIdx; /* The index we will be using */ int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ char *zStartAff; /* Affinity for start of range constraint */ char *zEndAff; /* Affinity for end of range constraint */ pIdx = pLevel->plan.u.pIdx; iIdxCur = pLevel->iIdxCur; k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */ |
︙ | ︙ | |||
97578 97579 97580 97581 97582 97583 97584 97585 97586 | ** were used as the innermost nested loop. In other words, a table ** is chosen such that the cost of running that table cannot be reduced ** by waiting for other tables to run first. This "optimal" test works ** by first assuming that the FROM clause is on the inner loop and finding ** its query plan, then checking to see if that query plan uses any ** other FROM clause terms that are notReady. If no notReady terms are ** used then the "optimal" query plan works. ** ** The second loop iteration is only performed if no optimal scan | > > > > > > | | | | | 98846 98847 98848 98849 98850 98851 98852 98853 98854 98855 98856 98857 98858 98859 98860 98861 98862 98863 98864 98865 98866 98867 98868 98869 98870 98871 98872 98873 98874 98875 98876 98877 98878 98879 98880 98881 98882 98883 98884 98885 98886 98887 98888 98889 | ** were used as the innermost nested loop. In other words, a table ** is chosen such that the cost of running that table cannot be reduced ** by waiting for other tables to run first. This "optimal" test works ** by first assuming that the FROM clause is on the inner loop and finding ** its query plan, then checking to see if that query plan uses any ** other FROM clause terms that are notReady. If no notReady terms are ** used then the "optimal" query plan works. ** ** Note that the WhereCost.nRow parameter for an optimal scan might ** not be as small as it would be if the table really were the innermost ** join. The nRow value can be reduced by WHERE clause constraints ** that do not use indices. But this nRow reduction only happens if the ** table really is the innermost join. ** ** The second loop iteration is only performed if no optimal scan ** strategies were found by the first iteration. This second iteration ** is used to search for the lowest cost scan overall. ** ** Previous versions of SQLite performed only the second iteration - ** the next outermost loop was always that with the lowest overall ** cost. However, this meant that SQLite could select the wrong plan ** for scripts such as the following: ** ** CREATE TABLE t1(a, b); ** CREATE TABLE t2(c, d); ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; ** ** The best strategy is to iterate through table t1 first. However it ** is not possible to determine this with a simple greedy algorithm. ** Since the cost of a linear scan through table t2 is the same ** as the cost of a linear scan through table t1, a simple greedy ** algorithm may choose to use t2 for the outer loop, which is a much ** costlier approach. */ nUnconstrained = 0; notIndexed = 0; for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ Bitmask mask; /* Mask of tables not yet ready */ for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){ int doNotReorder; /* True if this table should not be reordered */ WhereCost sCost; /* Cost information from best[Virtual]Index() */ ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0; |
︙ | ︙ | |||
97623 97624 97625 97626 97627 97628 97629 | pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); if( pTabItem->pIndex==0 ) nUnconstrained++; assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; | | > | > | 98897 98898 98899 98900 98901 98902 98903 98904 98905 98906 98907 98908 98909 98910 98911 98912 98913 98914 98915 98916 98917 | pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); if( pTabItem->pIndex==0 ) nUnconstrained++; assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost, pp); }else #endif { bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost); } assert( isOptimal || (sCost.used¬Ready)==0 ); /* If an INDEXED BY clause is present, then the plan must use that ** index if it uses any index at all */ assert( pTabItem->pIndex==0 || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 |
︙ | ︙ | |||
102663 102664 102665 102666 102667 102668 102669 | #endif /* __cplusplus */ /************** End of sqliteicu.h *******************************************/ /************** Continuing where we left off in main.c ***********************/ #endif | < > | > < > > > > > > > > > > > > > > > > > > | 103939 103940 103941 103942 103943 103944 103945 103946 103947 103948 103949 103950 103951 103952 103953 103954 103955 103956 103957 103958 103959 103960 103961 103962 103963 103964 103965 103966 103967 103968 103969 103970 103971 103972 103973 103974 103975 103976 103977 103978 103979 | #endif /* __cplusplus */ /************** End of sqliteicu.h *******************************************/ /************** Continuing where we left off in main.c ***********************/ #endif #ifndef SQLITE_AMALGAMATION /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant ** contains the text of SQLITE_VERSION macro. */ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; #endif /* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns ** a pointer to the to the sqlite3_version[] string constant. */ SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; } /* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a ** pointer to a string constant whose value is the same as the ** SQLITE_SOURCE_ID C preprocessor macro. */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function ** returns an integer equal to SQLITE_VERSION_NUMBER. */ SQLITE_API int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } /* IMPLEMENTATION-OF: R-54823-41343 The sqlite3_threadsafe() function returns ** zero if and only if SQLite was compiled mutexing code omitted due to ** the SQLITE_THREADSAFE compile-time option being set to 0. */ SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } #if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) /* ** If the following function pointer is not NULL and if ** SQLITE_ENABLE_IOTRACE is enabled, then messages describing ** I/O active are written using this function. These messages |
︙ | ︙ | |||
102792 102793 102794 102795 102796 102797 102798 102799 102800 102801 102802 102803 102804 102805 | } /* Do the rest of the initialization under the recursive mutex so ** that we will be able to handle recursive calls into ** sqlite3_initialize(). The recursive calls normally come through ** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other ** recursive calls might also be possible. */ sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex); if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); sqlite3GlobalConfig.inProgress = 1; memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); sqlite3RegisterGlobalFunctions(); | > > > > > > > | 104086 104087 104088 104089 104090 104091 104092 104093 104094 104095 104096 104097 104098 104099 104100 104101 104102 104103 104104 104105 104106 | } /* Do the rest of the initialization under the recursive mutex so ** that we will be able to handle recursive calls into ** sqlite3_initialize(). The recursive calls normally come through ** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other ** recursive calls might also be possible. ** ** IMPLEMENTATION-OF: R-00140-37445 SQLite automatically serializes calls ** to the xInit method, so the xInit method need not be threadsafe. ** ** The following mutex is what serializes access to the appdef pcache xInit ** methods. The sqlite3_pcache_methods.xInit() all is embedded in the ** call to sqlite3PcacheInitialize(). */ sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex); if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); sqlite3GlobalConfig.inProgress = 1; memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); sqlite3RegisterGlobalFunctions(); |
︙ | ︙ | |||
103072 103073 103074 103075 103076 103077 103078 | */ if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; if( cnt<0 ) cnt = 0; if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; }else if( pBuf==0 ){ | | | | | 104373 104374 104375 104376 104377 104378 104379 104380 104381 104382 104383 104384 104385 104386 104387 104388 104389 104390 104391 104392 | */ if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; if( cnt<0 ) cnt = 0; if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; }else if( pBuf==0 ){ sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ sqlite3BeginBenignMalloc(); pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); }else{ sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ pStart = pBuf; } db->lookaside.pStart = pStart; db->lookaside.pFree = 0; db->lookaside.sz = (u16)sz; if( pStart ){ int i; |
︙ | ︙ | |||
103120 103121 103122 103123 103124 103125 103126 | */ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_LOOKASIDE: { | | | | | | 104421 104422 104423 104424 104425 104426 104427 104428 104429 104430 104431 104432 104433 104434 104435 104436 104437 104438 104439 104440 104441 104442 | */ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_LOOKASIDE: { void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */ 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: { rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ break; } } va_end(ap); return rc; } |
︙ | ︙ | |||
103231 103232 103233 103234 103235 103236 103237 103238 103239 103240 103241 103242 | db->pSavepoint = pTmp->pNext; sqlite3DbFree(db, pTmp); } db->nSavepoint = 0; db->nStatement = 0; db->isTransactionSavepoint = 0; } /* ** Close an existing SQLite database */ SQLITE_API int sqlite3_close(sqlite3 *db){ | > > > > > > > > > > > > > > > > > | | 104532 104533 104534 104535 104536 104537 104538 104539 104540 104541 104542 104543 104544 104545 104546 104547 104548 104549 104550 104551 104552 104553 104554 104555 104556 104557 104558 104559 104560 104561 104562 104563 104564 104565 104566 104567 104568 | db->pSavepoint = pTmp->pNext; sqlite3DbFree(db, pTmp); } db->nSavepoint = 0; db->nStatement = 0; db->isTransactionSavepoint = 0; } /* ** Invoke the destructor function associated with FuncDef p, if any. Except, ** if this is not the last copy of the function, do not invoke it. Multiple ** copies of a single function are created when create_function() is called ** with SQLITE_ANY as the encoding. */ static void functionDestroy(sqlite3 *db, FuncDef *p){ FuncDestructor *pDestructor = p->pDestructor; if( pDestructor ){ pDestructor->nRef--; if( pDestructor->nRef==0 ){ pDestructor->xDestroy(pDestructor->pUserData); sqlite3DbFree(db, pDestructor); } } } /* ** Close an existing SQLite database */ SQLITE_API int sqlite3_close(sqlite3 *db){ HashElem *i; /* Hash table iterator */ int j; if( !db ){ return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; |
︙ | ︙ | |||
103304 103305 103306 103307 103308 103309 103310 103311 103312 103313 103314 103315 103316 103317 | assert( db->nDb<=2 ); assert( db->aDb==db->aDbStatic ); for(j=0; j<ArraySize(db->aFunc.a); j++){ FuncDef *pNext, *pHash, *p; for(p=db->aFunc.a[j]; p; p=pHash){ pHash = p->pHash; while( p ){ pNext = p->pNext; sqlite3DbFree(db, p); p = pNext; } } } for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ | > | 104622 104623 104624 104625 104626 104627 104628 104629 104630 104631 104632 104633 104634 104635 104636 | assert( db->nDb<=2 ); assert( db->aDb==db->aDbStatic ); for(j=0; j<ArraySize(db->aFunc.a); j++){ FuncDef *pNext, *pHash, *p; for(p=db->aFunc.a[j]; p; p=pHash){ pHash = p->pHash; while( p ){ functionDestroy(db, p); pNext = p->pNext; sqlite3DbFree(db, p); p = pNext; } } } for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ |
︙ | ︙ | |||
103578 103579 103580 103581 103582 103583 103584 | sqlite3 *db, const char *zFunctionName, int nArg, int enc, void *pUserData, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), | | > | 104897 104898 104899 104900 104901 104902 104903 104904 104905 104906 104907 104908 104909 104910 104911 104912 | sqlite3 *db, const char *zFunctionName, int nArg, int enc, void *pUserData, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), void (*xFinal)(sqlite3_context*), FuncDestructor *pDestructor ){ FuncDef *p; int nName; assert( sqlite3_mutex_held(db->mutex) ); if( zFunctionName==0 || (xFunc && (xFinal || xStep)) || |
︙ | ︙ | |||
103606 103607 103608 103609 103610 103611 103612 | ** to the hash table. */ if( enc==SQLITE_UTF16 ){ enc = SQLITE_UTF16NATIVE; }else if( enc==SQLITE_ANY ){ int rc; rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8, | | | | 104926 104927 104928 104929 104930 104931 104932 104933 104934 104935 104936 104937 104938 104939 104940 104941 104942 104943 | ** to the hash table. */ if( enc==SQLITE_UTF16 ){ enc = SQLITE_UTF16NATIVE; }else if( enc==SQLITE_ANY ){ int rc; rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8, pUserData, xFunc, xStep, xFinal, pDestructor); if( rc==SQLITE_OK ){ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE, pUserData, xFunc, xStep, xFinal, pDestructor); } if( rc!=SQLITE_OK ){ return rc; } enc = SQLITE_UTF16BE; } #else |
︙ | ︙ | |||
103642 103643 103644 103645 103646 103647 103648 103649 103650 103651 103652 103653 103654 103655 103656 103657 103658 103659 103660 103661 103662 | } p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1); assert(p || db->mallocFailed); if( !p ){ return SQLITE_NOMEM; } p->flags = 0; p->xFunc = xFunc; p->xStep = xStep; p->xFinalize = xFinal; p->pUserData = pUserData; p->nArg = (u16)nArg; return SQLITE_OK; } /* ** Create new user functions. */ SQLITE_API int sqlite3_create_function( sqlite3 *db, | > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > > > | 104962 104963 104964 104965 104966 104967 104968 104969 104970 104971 104972 104973 104974 104975 104976 104977 104978 104979 104980 104981 104982 104983 104984 104985 104986 104987 104988 104989 104990 104991 104992 104993 104994 104995 104996 104997 104998 104999 105000 105001 105002 105003 105004 105005 105006 105007 105008 105009 105010 105011 105012 105013 105014 105015 105016 105017 105018 105019 105020 105021 105022 105023 105024 105025 105026 105027 105028 105029 105030 105031 105032 105033 105034 105035 105036 105037 105038 105039 105040 105041 | } p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1); assert(p || db->mallocFailed); if( !p ){ return SQLITE_NOMEM; } /* If an older version of the function with a configured destructor is ** being replaced invoke the destructor function here. */ functionDestroy(db, p); if( pDestructor ){ pDestructor->nRef++; } p->pDestructor = pDestructor; p->flags = 0; p->xFunc = xFunc; p->xStep = xStep; p->xFinalize = xFinal; p->pUserData = pUserData; p->nArg = (u16)nArg; return SQLITE_OK; } /* ** Create new user functions. */ SQLITE_API int sqlite3_create_function( sqlite3 *db, const char *zFunc, int nArg, int enc, void *p, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), void (*xFinal)(sqlite3_context*) ){ return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, 0); } SQLITE_API int sqlite3_create_function_v2( sqlite3 *db, const char *zFunc, int nArg, int enc, void *p, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), void (*xFinal)(sqlite3_context*), void (*xDestroy)(void *) ){ int rc = SQLITE_ERROR; FuncDestructor *pArg = 0; sqlite3_mutex_enter(db->mutex); if( xDestroy ){ pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor)); if( !pArg ){ xDestroy(p); goto out; } pArg->xDestroy = xDestroy; pArg->pUserData = p; } rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg); if( pArg && pArg->nRef==0 ){ assert( rc!=SQLITE_OK ); xDestroy(p); sqlite3DbFree(db, pArg); } out: rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } #ifndef SQLITE_OMIT_UTF16 SQLITE_API int sqlite3_create_function16( |
︙ | ︙ | |||
103688 103689 103690 103691 103692 103693 103694 | void (*xFinal)(sqlite3_context*) ){ int rc; char *zFunc8; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE); | | | 105049 105050 105051 105052 105053 105054 105055 105056 105057 105058 105059 105060 105061 105062 105063 | void (*xFinal)(sqlite3_context*) ){ int rc; char *zFunc8; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE); rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0); sqlite3DbFree(db, zFunc8); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } #endif |
︙ | ︙ | |||
103719 103720 103721 103722 103723 103724 103725 | int nArg ){ int nName = sqlite3Strlen30(zName); int rc; sqlite3_mutex_enter(db->mutex); if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, | | | 105080 105081 105082 105083 105084 105085 105086 105087 105088 105089 105090 105091 105092 105093 105094 | int nArg ){ int nName = sqlite3Strlen30(zName); int rc; sqlite3_mutex_enter(db->mutex); if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, 0, sqlite3InvalidFunction, 0, 0, 0); } rc = sqlite3ApiExit(db, SQLITE_OK); sqlite3_mutex_leave(db->mutex); return rc; } #ifndef SQLITE_OMIT_TRACE |
︙ | ︙ | |||
103857 103858 103859 103860 103861 103862 103863 | ** ** The callback registered by this function replaces any existing callback ** registered using sqlite3_wal_hook(). Likewise, registering a callback ** using sqlite3_wal_hook() disables the automatic checkpoint mechanism ** configured by this function. */ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ | | > > > | 105218 105219 105220 105221 105222 105223 105224 105225 105226 105227 105228 105229 105230 105231 105232 105233 105234 105235 | ** ** The callback registered by this function replaces any existing callback ** registered using sqlite3_wal_hook(). Likewise, registering a callback ** using sqlite3_wal_hook() disables the automatic checkpoint mechanism ** configured by this function. */ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ #ifdef SQLITE_OMIT_WAL UNUSED_PARAMETER(db); UNUSED_PARAMETER(nFrame); #else if( nFrame>0 ){ sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame)); }else{ sqlite3_wal_hook(db, 0, 0); } #endif return SQLITE_OK; |
︙ | ︙ | |||
103987 103988 103989 103990 103991 103992 103993 | return 1; #endif #if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3 return 0; #endif } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 105351 105352 105353 105354 105355 105356 105357 105358 105359 105360 105361 105362 105363 105364 | return 1; #endif #if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3 return 0; #endif } /* ** Return UTF-8 encoded English language explanation of the most recent ** error. */ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ const char *z; if( !db ){ |
︙ | ︙ | |||
104283 104284 104285 104286 104287 104288 104289 104290 104291 104292 104293 | ** ** A new lower limit does not shrink existing constructs. ** It merely prevents new constructs that exceed the limit ** from forming. */ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ int oldLimit; if( limitId<0 || limitId>=SQLITE_N_LIMIT ){ return -1; } oldLimit = db->aLimit[limitId]; | > > > > > > > > > > > > > > > > > > > > > > | | | | 105593 105594 105595 105596 105597 105598 105599 105600 105601 105602 105603 105604 105605 105606 105607 105608 105609 105610 105611 105612 105613 105614 105615 105616 105617 105618 105619 105620 105621 105622 105623 105624 105625 105626 105627 105628 105629 105630 105631 105632 105633 105634 105635 105636 105637 105638 105639 | ** ** A new lower limit does not shrink existing constructs. ** It merely prevents new constructs that exceed the limit ** from forming. */ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ int oldLimit; /* EVIDENCE-OF: R-30189-54097 For each limit category SQLITE_LIMIT_NAME ** there is a hard upper bound set at compile-time by a C preprocessor ** macro called SQLITE_MAX_NAME. (The "_LIMIT_" in the name is changed to ** "_MAX_".) */ assert( aHardLimit[SQLITE_LIMIT_LENGTH]==SQLITE_MAX_LENGTH ); assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH ); assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN ); assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH ); assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT); assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP ); assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG ); assert( aHardLimit[SQLITE_LIMIT_ATTACHED]==SQLITE_MAX_ATTACHED ); assert( aHardLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]== SQLITE_MAX_LIKE_PATTERN_LENGTH ); assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER); assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH ); assert( SQLITE_LIMIT_TRIGGER_DEPTH==(SQLITE_N_LIMIT-1) ); if( limitId<0 || limitId>=SQLITE_N_LIMIT ){ return -1; } oldLimit = db->aLimit[limitId]; if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ } db->aLimit[limitId] = newLimit; } return oldLimit; /* IMP: R-53341-35419 */ } /* ** This routine does the work of opening a database on behalf of ** sqlite3_open() and sqlite3_open16(). The database filename "zFilename" ** is UTF-8 encoded. */ |
︙ | ︙ | |||
104316 104317 104318 104319 104320 104321 104322 104323 104324 104325 104326 104327 104328 104329 | int isThreadsafe; *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; | > > > > > > > > > > > > > > > > > > | 105648 105649 105650 105651 105652 105653 105654 105655 105656 105657 105658 105659 105660 105661 105662 105663 105664 105665 105666 105667 105668 105669 105670 105671 105672 105673 105674 105675 105676 105677 105678 105679 | int isThreadsafe; *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif /* Only allow sensible combinations of bits in the flags argument. ** Throw an error if any non-sense combination is used. If we ** do not block illegal combinations here, it could trigger ** assert() statements in deeper layers. Sensible combinations ** are: ** ** 1: SQLITE_OPEN_READONLY ** 2: SQLITE_OPEN_READWRITE ** 6: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE */ assert( SQLITE_OPEN_READONLY == 0x01 ); assert( SQLITE_OPEN_READWRITE == 0x02 ); assert( SQLITE_OPEN_CREATE == 0x04 ); testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE; if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; |
︙ | ︙ | |||
104350 104351 104352 104353 104354 104355 104356 | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB | SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL | SQLITE_OPEN_NOMUTEX | | | > | 105700 105701 105702 105703 105704 105705 105706 105707 105708 105709 105710 105711 105712 105713 105714 105715 | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB | SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_WAL ); /* Allocate the sqlite data structure */ db = sqlite3MallocZero( sizeof(sqlite3) ); if( db==0 ) goto opendb_out; if( isThreadsafe ){ db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); |
︙ | ︙ | |||
104422 104423 104424 104425 104426 104427 104428 | /* Also add a UTF-8 case-insensitive collation sequence. */ createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, nocaseCollatingFunc, 0); /* Open the backend database driver */ db->openFlags = flags; | | | < | 105773 105774 105775 105776 105777 105778 105779 105780 105781 105782 105783 105784 105785 105786 105787 105788 | /* Also add a UTF-8 case-insensitive collation sequence. */ createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, nocaseCollatingFunc, 0); /* Open the backend database driver */ db->openFlags = flags; rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0, flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } sqlite3Error(db, rc, 0); goto opendb_out; } |
︙ | ︙ | |||
105130 105131 105132 105133 105134 105135 105136 105137 105138 105139 105140 105141 105142 105143 | ** ** Return the size of a pcache header in bytes. */ case SQLITE_TESTCTRL_PGHDRSZ: { rc = sizeof(PgHdr); break; } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } | > > > > > > > > > > > > > > > > | 106480 106481 106482 106483 106484 106485 106486 106487 106488 106489 106490 106491 106492 106493 106494 106495 106496 106497 106498 106499 106500 106501 106502 106503 106504 106505 106506 106507 106508 106509 | ** ** Return the size of a pcache header in bytes. */ case SQLITE_TESTCTRL_PGHDRSZ: { rc = sizeof(PgHdr); break; } /* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree); ** ** Pass pFree into sqlite3ScratchFree(). ** If sz>0 then allocate a scratch buffer into pNew. */ case SQLITE_TESTCTRL_SCRATCHMALLOC: { void *pFree, **ppNew; int sz; sz = va_arg(ap, int); ppNew = va_arg(ap, void**); pFree = va_arg(ap, void*); if( sz ) *ppNew = sqlite3ScratchMalloc(sz); sqlite3ScratchFree(pFree); break; } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } |
︙ | ︙ | |||
106319 106320 106321 106322 106323 106324 106325 106326 106327 106328 106329 106330 106331 106332 | Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, int (*)(Fts3Table *, void *, char *, int, char *, int), void * ); SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char const**, int*); SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); SQLITE_PRIVATE int sqlite3Fts3MatchinfoDocsizeLocal(Fts3Cursor*, u32*); SQLITE_PRIVATE int sqlite3Fts3MatchinfoDocsizeGlobal(Fts3Cursor*, u32*); /* 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 | > | 107685 107686 107687 107688 107689 107690 107691 107692 107693 107694 107695 107696 107697 107698 107699 | Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, int (*)(Fts3Table *, void *, char *, int, char *, int), void * ); SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char const**, int*); SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); SQLITE_PRIVATE int sqlite3Fts3MatchinfoDocsizeLocal(Fts3Cursor*, u32*); SQLITE_PRIVATE int sqlite3Fts3MatchinfoDocsizeGlobal(Fts3Cursor*, u32*); SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *); /* 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 |
︙ | ︙ | |||
108268 108269 108270 108271 108272 108273 108274 108275 108276 108277 108278 108279 108280 108281 | if( rc==SQLITE_ERROR ){ p->base.zErrMsg = sqlite3_mprintf("malformed MATCH expression: [%s]", zQuery); } return rc; } rc = evalFts3Expr(p, pCsr->pExpr, &pCsr->aDoclist, &pCsr->nDoclist, 0); pCsr->pNextId = pCsr->aDoclist; pCsr->iPrevId = 0; } if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); | > > > | 109635 109636 109637 109638 109639 109640 109641 109642 109643 109644 109645 109646 109647 109648 109649 109650 109651 | if( rc==SQLITE_ERROR ){ p->base.zErrMsg = sqlite3_mprintf("malformed MATCH expression: [%s]", zQuery); } return rc; } rc = sqlite3Fts3ReadLock(p); if( rc!=SQLITE_OK ) return rc; rc = evalFts3Expr(p, pCsr->pExpr, &pCsr->aDoclist, &pCsr->nDoclist, 0); pCsr->pNextId = pCsr->aDoclist; pCsr->iPrevId = 0; } if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); |
︙ | ︙ | |||
108646 108647 108648 108649 108650 108651 108652 | ** Implementation of FTS3 xRename method. Rename an fts3 table. */ static int fts3RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ Fts3Table *p = (Fts3Table *)pVtab; | | | | | > > > | 110016 110017 110018 110019 110020 110021 110022 110023 110024 110025 110026 110027 110028 110029 110030 110031 110032 110033 110034 110035 110036 110037 | ** Implementation of FTS3 xRename method. Rename an fts3 table. */ static int fts3RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ Fts3Table *p = (Fts3Table *)pVtab; sqlite3 *db = p->db; /* Database connection */ int rc; /* Return Code */ rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ){ return rc; } fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", p->zDb, p->zName, zName ); if( rc==SQLITE_ERROR ) rc = SQLITE_OK; if( p->bHasDocsize ){ fts3DbExec(&rc, db, |
︙ | ︙ | |||
108718 108719 108720 108721 108722 108723 108724 | /* ** The fts3 built-in tokenizers - "simple" and "porter" - are implemented ** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following ** two forward declarations are for functions declared in these files ** used to retrieve the respective implementations. ** ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed | | | 110091 110092 110093 110094 110095 110096 110097 110098 110099 110100 110101 110102 110103 110104 110105 | /* ** The fts3 built-in tokenizers - "simple" and "porter" - are implemented ** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following ** two forward declarations are for functions declared in these files ** used to retrieve the respective implementations. ** ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed ** to by the argument to point to the "simple" tokenizer implementation. ** Function ...PorterTokenizerModule() sets *pModule to point to the ** porter tokenizer/stemmer implementation. */ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); |
︙ | ︙ | |||
108920 108921 108922 108923 108924 108925 108926 | ** an integer that falls outside of the range of the unsigned char type ** is undefined (and sometimes, "undefined" means segfault). This wrapper ** is defined to accept an argument of type char, and always returns 0 for ** any values that fall outside of the range of the unsigned char type (i.e. ** negative values). */ static int fts3isspace(char c){ | | | 110293 110294 110295 110296 110297 110298 110299 110300 110301 110302 110303 110304 110305 110306 110307 | ** an integer that falls outside of the range of the unsigned char type ** is undefined (and sometimes, "undefined" means segfault). This wrapper ** is defined to accept an argument of type char, and always returns 0 for ** any values that fall outside of the range of the unsigned char type (i.e. ** negative values). */ static int fts3isspace(char c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } /* ** Extract the next token from buffer z (length n) using the tokenizer ** and other information (column names etc.) in pParse. Create an Fts3Expr ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this ** single token and set *ppExpr to point to it. If the end of the buffer is |
︙ | ︙ | |||
111307 111308 111309 111310 111311 111312 111313 111314 111315 111316 111317 111318 111319 111320 | int nTokenAllocated; /* space allocated to zToken buffer */ } simple_tokenizer_cursor; static int simpleDelim(simple_tokenizer *t, unsigned char c){ return c<0x80 && t->delim[c]; } /* ** Create a new tokenizer instance. */ static int simpleCreate( int argc, const char * const *argv, sqlite3_tokenizer **ppTokenizer | > > > | 112680 112681 112682 112683 112684 112685 112686 112687 112688 112689 112690 112691 112692 112693 112694 112695 112696 | int nTokenAllocated; /* space allocated to zToken buffer */ } simple_tokenizer_cursor; static int simpleDelim(simple_tokenizer *t, unsigned char c){ return c<0x80 && t->delim[c]; } static int fts3_isalnum(int x){ return (x>='0' && x<='9') || (x>='A' && x<='Z') || (x>='a' && x<='z'); } /* ** Create a new tokenizer instance. */ static int simpleCreate( int argc, const char * const *argv, sqlite3_tokenizer **ppTokenizer |
︙ | ︙ | |||
111341 111342 111343 111344 111345 111346 111347 | } t->delim[ch] = 1; } } else { /* Mark non-alphanumeric ASCII characters as delimiters */ int i; for(i=1; i<0x80; i++){ | | | 112717 112718 112719 112720 112721 112722 112723 112724 112725 112726 112727 112728 112729 112730 112731 | } t->delim[ch] = 1; } } else { /* Mark non-alphanumeric ASCII characters as delimiters */ int i; for(i=1; i<0x80; i++){ t->delim[i] = !fts3_isalnum(i) ? -1 : 0; } } *ppTokenizer = &t->base; return SQLITE_OK; } |
︙ | ︙ | |||
111447 111448 111449 111450 111451 111452 111453 | c->pToken = pNew; } for(i=0; i<n; i++){ /* TODO(shess) This needs expansion to handle UTF-8 ** case-insensitivity. */ unsigned char ch = p[iStartOffset+i]; | | | 112823 112824 112825 112826 112827 112828 112829 112830 112831 112832 112833 112834 112835 112836 112837 | c->pToken = pNew; } for(i=0; i<n; i++){ /* TODO(shess) This needs expansion to handle UTF-8 ** case-insensitivity. */ unsigned char ch = p[iStartOffset+i]; c->pToken[i] = (char)((ch>='A' && ch<='Z') ? ch-'A'+'a' : ch); } *ppToken = c->pToken; *pnBytes = n; *piStartOffset = iStartOffset; *piEndOffset = c->iOffset; *piPosition = c->iToken++; |
︙ | ︙ | |||
111806 111807 111808 111809 111810 111811 111812 111813 111814 111815 111816 111817 111818 111819 | *pzBlock = (char *)sqlite3_column_blob(pStmt, 0); if( sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ return SQLITE_CORRUPT; } } return SQLITE_OK; } /* ** Set *ppStmt to a statement handle that may be used to iterate through ** all rows in the %_segdir table, from oldest to newest. If successful, ** return SQLITE_OK. If an error occurs while preparing the statement, ** return an SQLite error code. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 113182 113183 113184 113185 113186 113187 113188 113189 113190 113191 113192 113193 113194 113195 113196 113197 113198 113199 113200 113201 113202 113203 113204 113205 113206 113207 113208 113209 113210 113211 113212 113213 113214 113215 113216 113217 113218 113219 113220 113221 113222 113223 113224 113225 | *pzBlock = (char *)sqlite3_column_blob(pStmt, 0); if( sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ return SQLITE_CORRUPT; } } return SQLITE_OK; } /* ** This function ensures that the caller has obtained a shared-cache ** table-lock on the %_content table. This is required before reading ** data from the fts3 table. If this lock is not acquired first, then ** the caller may end up holding read-locks on the %_segments and %_segdir ** tables, but no read-lock on the %_content table. If this happens ** a second connection will be able to write to the fts3 table, but ** attempting to commit those writes might return SQLITE_LOCKED or ** SQLITE_LOCKED_SHAREDCACHE (because the commit attempts to obtain ** write-locks on the %_segments and %_segdir ** tables). ** ** We try to avoid this because if FTS3 returns any error when committing ** a transaction, the whole transaction will be rolled back. And this is ** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can ** still happen if the user reads data directly from the %_segments or ** %_segdir tables instead of going through FTS3 though. */ SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){ int rc; /* Return code */ sqlite3_stmt *pStmt; /* Statement used to obtain lock */ rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_bind_null(pStmt, 1); sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); } return rc; } /* ** Set *ppStmt to a statement handle that may be used to iterate through ** all rows in the %_segdir table, from oldest to newest. If successful, ** return SQLITE_OK. If an error occurs while preparing the statement, ** return an SQLite error code. ** |
︙ | ︙ | |||
115297 115298 115299 115300 115301 115302 115303 115304 115305 115306 115307 115308 115309 115310 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code for implementations of the r-tree and r*-tree ** algorithms packaged as an SQLite virtual table module. */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) /* ** This file contains an implementation of a couple of different variants ** of the r-tree algorithm. See the README file for further details. The ** same data-structure is used for all, but the algorithms for insert and | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 116703 116704 116705 116706 116707 116708 116709 116710 116711 116712 116713 116714 116715 116716 116717 116718 116719 116720 116721 116722 116723 116724 116725 116726 116727 116728 116729 116730 116731 116732 116733 116734 116735 116736 116737 116738 116739 116740 116741 116742 116743 116744 116745 116746 116747 116748 116749 116750 116751 116752 116753 116754 116755 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code for implementations of the r-tree and r*-tree ** algorithms packaged as an SQLite virtual table module. */ /* ** Database Format of R-Tree Tables ** -------------------------------- ** ** The data structure for a single virtual r-tree table is stored in three ** native SQLite tables declared as follows. In each case, the '%' character ** in the table name is replaced with the user-supplied name of the r-tree ** table. ** ** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB) ** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER) ** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER) ** ** The data for each node of the r-tree structure is stored in the %_node ** table. For each node that is not the root node of the r-tree, there is ** an entry in the %_parent table associating the node with its parent. ** And for each row of data in the table, there is an entry in the %_rowid ** table that maps from the entries rowid to the id of the node that it ** is stored on. ** ** The root node of an r-tree always exists, even if the r-tree table is ** empty. The nodeno of the root node is always 1. All other nodes in the ** table must be the same size as the root node. The content of each node ** is formatted as follows: ** ** 1. If the node is the root node (node 1), then the first 2 bytes ** of the node contain the tree depth as a big-endian integer. ** For non-root nodes, the first 2 bytes are left unused. ** ** 2. The next 2 bytes contain the number of entries currently ** stored in the node. ** ** 3. The remainder of the node contains the node entries. Each entry ** consists of a single 8-byte integer followed by an even number ** of 4-byte coordinates. For leaf nodes the integer is the rowid ** of a record. For internal nodes it is the node number of a ** child page. */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) /* ** This file contains an implementation of a couple of different variants ** of the r-tree algorithm. See the README file for further details. The ** same data-structure is used for all, but the algorithms for insert and |
︙ | ︙ | |||
115338 115339 115340 115341 115342 115343 115344 115345 115346 115347 115348 115349 115350 115351 115352 115353 115354 115355 115356 115357 115358 115359 115360 115361 115362 115363 115364 115365 115366 115367 115368 115369 | #define PickSeeds LinearPickSeeds #define AssignCells splitNodeGuttman #endif #if VARIANT_RSTARTREE_SPLIT #define AssignCells splitNodeStartree #endif #ifndef SQLITE_CORE SQLITE_EXTENSION_INIT1 #else #endif #ifndef SQLITE_AMALGAMATION typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned int u32; #endif typedef struct Rtree Rtree; typedef struct RtreeCursor RtreeCursor; typedef struct RtreeNode RtreeNode; typedef struct RtreeCell RtreeCell; typedef struct RtreeConstraint RtreeConstraint; typedef union RtreeCoord RtreeCoord; /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is | > > > > > > | 116783 116784 116785 116786 116787 116788 116789 116790 116791 116792 116793 116794 116795 116796 116797 116798 116799 116800 116801 116802 116803 116804 116805 116806 116807 116808 116809 116810 116811 116812 116813 116814 116815 116816 116817 116818 116819 116820 | #define PickSeeds LinearPickSeeds #define AssignCells splitNodeGuttman #endif #if VARIANT_RSTARTREE_SPLIT #define AssignCells splitNodeStartree #endif #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif #ifndef SQLITE_CORE SQLITE_EXTENSION_INIT1 #else #endif #ifndef SQLITE_AMALGAMATION #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned int u32; #endif typedef struct Rtree Rtree; typedef struct RtreeCursor RtreeCursor; typedef struct RtreeNode RtreeNode; typedef struct RtreeCell RtreeCell; typedef struct RtreeConstraint RtreeConstraint; typedef struct RtreeMatchArg RtreeMatchArg; typedef struct RtreeGeomCallback RtreeGeomCallback; typedef union RtreeCoord RtreeCoord; /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is |
︙ | ︙ | |||
115425 115426 115427 115428 115429 115430 115431 115432 115433 115434 115435 115436 115437 115438 | ** If an R*-tree "Reinsert" operation is required, the same number of ** cells are removed from the overfull node and reinserted into the tree. */ #define RTREE_MINCELLS(p) ((((p)->iNodeSize-4)/(p)->nBytesPerCell)/3) #define RTREE_REINSERT(p) RTREE_MINCELLS(p) #define RTREE_MAXCELLS 51 /* ** An rtree cursor object. */ struct RtreeCursor { sqlite3_vtab_cursor base; RtreeNode *pNode; /* Node cursor is currently pointing at */ int iCell; /* Index of current cell in pNode */ | > > > > > > > > > | 116876 116877 116878 116879 116880 116881 116882 116883 116884 116885 116886 116887 116888 116889 116890 116891 116892 116893 116894 116895 116896 116897 116898 | ** If an R*-tree "Reinsert" operation is required, the same number of ** cells are removed from the overfull node and reinserted into the tree. */ #define RTREE_MINCELLS(p) ((((p)->iNodeSize-4)/(p)->nBytesPerCell)/3) #define RTREE_REINSERT(p) RTREE_MINCELLS(p) #define RTREE_MAXCELLS 51 /* ** The smallest possible node-size is (512-64)==448 bytes. And the largest ** supported cell size is 48 bytes (8 byte rowid + ten 4 byte coordinates). ** Therefore all non-root nodes must contain at least 3 entries. Since ** 2^40 is greater than 2^64, an r-tree structure always has a depth of ** 40 or less. */ #define RTREE_MAX_DEPTH 40 /* ** An rtree cursor object. */ struct RtreeCursor { sqlite3_vtab_cursor base; RtreeNode *pNode; /* Node cursor is currently pointing at */ int iCell; /* Index of current cell in pNode */ |
︙ | ︙ | |||
115457 115458 115459 115460 115461 115462 115463 | ((double)coord.i) \ ) /* ** A search constraint. */ struct RtreeConstraint { | | | | > > | | | | | > < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 116917 116918 116919 116920 116921 116922 116923 116924 116925 116926 116927 116928 116929 116930 116931 116932 116933 116934 116935 116936 116937 116938 116939 116940 116941 116942 116943 116944 116945 116946 116947 116948 116949 116950 116951 116952 116953 116954 116955 116956 116957 116958 116959 116960 116961 116962 116963 116964 116965 116966 116967 116968 116969 116970 116971 116972 116973 116974 116975 116976 116977 116978 116979 116980 116981 116982 116983 116984 116985 116986 116987 116988 116989 116990 116991 116992 116993 116994 116995 116996 116997 116998 116999 117000 | ((double)coord.i) \ ) /* ** A search constraint. */ struct RtreeConstraint { int iCoord; /* Index of constrained coordinate */ int op; /* Constraining operation */ double rValue; /* Constraint value. */ int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */ }; /* Possible values for RtreeConstraint.op */ #define RTREE_EQ 0x41 #define RTREE_LE 0x42 #define RTREE_LT 0x43 #define RTREE_GE 0x44 #define RTREE_GT 0x45 #define RTREE_MATCH 0x46 /* ** An rtree structure node. */ struct RtreeNode { RtreeNode *pParent; /* Parent node */ i64 iNode; int nRef; int isDirty; u8 *zData; RtreeNode *pNext; /* Next node in this hash chain */ }; #define NCELL(pNode) readInt16(&(pNode)->zData[2]) /* ** Structure to store a deserialized rtree record. */ struct RtreeCell { i64 iRowid; RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; }; /* ** Value for the first field of every RtreeMatchArg object. The MATCH ** operator tests that the first field of a blob operand matches this ** value to avoid operating on invalid blobs (which could cause a segfault). */ #define RTREE_GEOMETRY_MAGIC 0x891245AB /* ** An instance of this structure must be supplied as a blob argument to ** the right-hand-side of an SQL MATCH operator used to constrain an ** r-tree query. */ struct RtreeMatchArg { u32 magic; /* Always RTREE_GEOMETRY_MAGIC */ int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); void *pContext; int nParam; double aParam[1]; }; /* ** When a geometry callback is created (see sqlite3_rtree_geometry_callback), ** a single instance of the following structure is allocated. It is used ** as the context for the user-function created by by s_r_g_c(). The object ** is eventually deleted by the destructor mechanism provided by ** sqlite3_create_function_v2() (which is called by s_r_g_c() to create ** the geometry callback function). */ struct RtreeGeomCallback { int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); void *pContext; }; #ifndef MAX # define MAX(x,y) ((x) < (y) ? (y) : (x)) #endif #ifndef MIN # define MIN(x,y) ((x) > (y) ? (y) : (x)) #endif |
︙ | ︙ | |||
115587 115588 115589 115590 115591 115592 115593 | } } /* ** Clear the content of node p (set all bytes to 0x00). */ static void nodeZero(Rtree *pRtree, RtreeNode *p){ | < | | < < < | | | | | < | 117069 117070 117071 117072 117073 117074 117075 117076 117077 117078 117079 117080 117081 117082 117083 117084 117085 117086 117087 117088 117089 117090 117091 117092 117093 117094 117095 117096 117097 117098 117099 117100 117101 117102 117103 117104 117105 117106 117107 117108 117109 117110 117111 117112 117113 117114 117115 117116 | } } /* ** Clear the content of node p (set all bytes to 0x00). */ static void nodeZero(Rtree *pRtree, RtreeNode *p){ memset(&p->zData[2], 0, pRtree->iNodeSize-2); p->isDirty = 1; } /* ** Given a node number iNode, return the corresponding key to use ** in the Rtree.aHash table. */ static int nodeHash(i64 iNode){ return ( (iNode>>56) ^ (iNode>>48) ^ (iNode>>40) ^ (iNode>>32) ^ (iNode>>24) ^ (iNode>>16) ^ (iNode>> 8) ^ (iNode>> 0) ) % HASHSIZE; } /* ** Search the node hash table for node iNode. If found, return a pointer ** to it. Otherwise, return 0. */ static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){ RtreeNode *p; for(p=pRtree->aHash[nodeHash(iNode)]; p && p->iNode!=iNode; p=p->pNext); return p; } /* ** Add node pNode to the node hash table. */ static void nodeHashInsert(Rtree *pRtree, RtreeNode *pNode){ int iHash; assert( pNode->pNext==0 ); iHash = nodeHash(pNode->iNode); pNode->pNext = pRtree->aHash[iHash]; pRtree->aHash[iHash] = pNode; } /* ** Remove node pNode from the node hash table. */ static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){ RtreeNode **pp; |
︙ | ︙ | |||
115647 115648 115649 115650 115651 115652 115653 | /* ** Allocate and return new r-tree node. Initially, (RtreeNode.iNode==0), ** indicating that node has not yet been assigned a node number. It is ** assigned a node number when nodeWrite() is called to write the ** node contents out to the database. */ | | | > > > > > > | | < | | | | | | | | > > | < > > | | | > > > > > > > > > > > > | > > > > > | > > > | > > > > > > > | < < < < < < < < < < | 117124 117125 117126 117127 117128 117129 117130 117131 117132 117133 117134 117135 117136 117137 117138 117139 117140 117141 117142 117143 117144 117145 117146 117147 117148 117149 117150 117151 117152 117153 117154 117155 117156 117157 117158 117159 117160 117161 117162 117163 117164 117165 117166 117167 117168 117169 117170 117171 117172 117173 117174 117175 117176 117177 117178 117179 117180 117181 117182 117183 117184 117185 117186 117187 117188 117189 117190 117191 117192 117193 117194 117195 117196 117197 117198 117199 117200 117201 117202 117203 117204 117205 117206 117207 117208 117209 117210 117211 117212 117213 117214 117215 117216 117217 117218 117219 117220 117221 117222 117223 117224 117225 117226 117227 117228 117229 117230 117231 117232 117233 117234 117235 117236 | /* ** Allocate and return new r-tree node. Initially, (RtreeNode.iNode==0), ** indicating that node has not yet been assigned a node number. It is ** assigned a node number when nodeWrite() is called to write the ** node contents out to the database. */ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ RtreeNode *pNode; pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize); if( pNode ){ memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize); pNode->zData = (u8 *)&pNode[1]; pNode->nRef = 1; pNode->pParent = pParent; pNode->isDirty = 1; nodeReference(pParent); } return pNode; } /* ** Obtain a reference to an r-tree node. */ static int nodeAcquire( Rtree *pRtree, /* R-tree structure */ i64 iNode, /* Node number to load */ RtreeNode *pParent, /* Either the parent node or NULL */ RtreeNode **ppNode /* OUT: Acquired node */ ){ int rc; int rc2 = SQLITE_OK; RtreeNode *pNode; /* Check if the requested node is already in the hash table. If so, ** increase its reference count and return it. */ if( (pNode = nodeHashLookup(pRtree, iNode)) ){ assert( !pParent || !pNode->pParent || pNode->pParent==pParent ); if( pParent && !pNode->pParent ){ nodeReference(pParent); pNode->pParent = pParent; } pNode->nRef++; *ppNode = pNode; return SQLITE_OK; } sqlite3_bind_int64(pRtree->pReadNode, 1, iNode); rc = sqlite3_step(pRtree->pReadNode); if( rc==SQLITE_ROW ){ const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0); if( pRtree->iNodeSize==sqlite3_column_bytes(pRtree->pReadNode, 0) ){ pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize); if( !pNode ){ rc2 = SQLITE_NOMEM; }else{ pNode->pParent = pParent; pNode->zData = (u8 *)&pNode[1]; pNode->nRef = 1; pNode->iNode = iNode; pNode->isDirty = 0; pNode->pNext = 0; memcpy(pNode->zData, zBlob, pRtree->iNodeSize); nodeReference(pParent); } } } rc = sqlite3_reset(pRtree->pReadNode); if( rc==SQLITE_OK ) rc = rc2; /* If the root node was just loaded, set pRtree->iDepth to the height ** of the r-tree structure. A height of zero means all data is stored on ** the root node. A height of one means the children of the root node ** are the leaves, and so on. If the depth as specified on the root node ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. */ if( pNode && iNode==1 ){ pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT; } } /* If no error has occurred so far, check if the "number of entries" ** field on the node is too large. If so, set the return code to ** SQLITE_CORRUPT. */ if( pNode && rc==SQLITE_OK ){ if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){ rc = SQLITE_CORRUPT; } } if( rc==SQLITE_OK ){ if( pNode!=0 ){ nodeHashInsert(pRtree, pNode); }else{ rc = SQLITE_CORRUPT; } *ppNode = pNode; }else{ sqlite3_free(pNode); *ppNode = 0; } return rc; } /* ** Overwrite cell iCell of node pNode with the contents of pCell. */ |
︙ | ︙ | |||
115773 115774 115775 115776 115777 115778 115779 | ){ int nCell; /* Current number of cells in pNode */ int nMaxCell; /* Maximum number of cells for pNode */ nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell; nCell = NCELL(pNode); | | < | 117275 117276 117277 117278 117279 117280 117281 117282 117283 117284 117285 117286 117287 117288 117289 | ){ int nCell; /* Current number of cells in pNode */ int nMaxCell; /* Maximum number of cells for pNode */ nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell; nCell = NCELL(pNode); assert( nCell<=nMaxCell ); if( nCell<nMaxCell ){ nodeOverwriteCell(pRtree, pNode, pCell, nCell); writeInt16(&pNode->zData[2], nCell+1); pNode->isDirty = 1; } return (nCell==nMaxCell); |
︙ | ︙ | |||
115994 115995 115996 115997 115998 115999 116000 116001 116002 116003 116004 116005 116006 116007 | rc = SQLITE_OK; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return rc; } /* ** Rtree virtual table module xClose method. */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int rc; RtreeCursor *pCsr = (RtreeCursor *)cur; | > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | > > > | > > > | > > > > > > > > > > > > | | | > > > > > | > | > > > > > > | > | > > | > > | | > | | 117495 117496 117497 117498 117499 117500 117501 117502 117503 117504 117505 117506 117507 117508 117509 117510 117511 117512 117513 117514 117515 117516 117517 117518 117519 117520 117521 117522 117523 117524 117525 117526 117527 117528 117529 117530 117531 117532 117533 117534 117535 117536 117537 117538 117539 117540 117541 117542 117543 117544 117545 117546 117547 117548 117549 117550 117551 117552 117553 117554 117555 117556 117557 117558 117559 117560 117561 117562 117563 117564 117565 117566 117567 117568 117569 117570 117571 117572 117573 117574 117575 117576 117577 117578 117579 117580 117581 117582 117583 117584 117585 117586 117587 117588 117589 117590 117591 117592 117593 117594 117595 117596 117597 117598 117599 117600 117601 117602 117603 117604 117605 117606 117607 117608 117609 117610 117611 117612 117613 117614 117615 117616 117617 117618 117619 117620 117621 117622 117623 117624 117625 117626 117627 117628 117629 117630 117631 117632 117633 117634 117635 117636 117637 117638 117639 117640 117641 117642 117643 117644 117645 117646 117647 117648 117649 117650 117651 117652 117653 117654 117655 117656 117657 117658 117659 117660 117661 117662 117663 117664 117665 117666 117667 117668 117669 117670 117671 117672 117673 117674 117675 117676 117677 | rc = SQLITE_OK; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return rc; } /* ** Free the RtreeCursor.aConstraint[] array and its contents. */ static void freeCursorConstraints(RtreeCursor *pCsr){ if( pCsr->aConstraint ){ int i; /* Used to iterate through constraint array */ for(i=0; i<pCsr->nConstraint; i++){ sqlite3_rtree_geometry *pGeom = pCsr->aConstraint[i].pGeom; if( pGeom ){ if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser); sqlite3_free(pGeom); } } sqlite3_free(pCsr->aConstraint); pCsr->aConstraint = 0; } } /* ** Rtree virtual table module xClose method. */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int rc; RtreeCursor *pCsr = (RtreeCursor *)cur; freeCursorConstraints(pCsr); rc = nodeRelease(pRtree, pCsr->pNode); sqlite3_free(pCsr); return rc; } /* ** Rtree virtual table module xEof method. ** ** Return non-zero if the cursor does not currently point to a valid ** record (i.e if the scan has finished), or zero otherwise. */ static int rtreeEof(sqlite3_vtab_cursor *cur){ RtreeCursor *pCsr = (RtreeCursor *)cur; return (pCsr->pNode==0); } /* ** The r-tree constraint passed as the second argument to this function is ** guaranteed to be a MATCH constraint. */ static int testRtreeGeom( Rtree *pRtree, /* R-Tree object */ RtreeConstraint *pConstraint, /* MATCH constraint to test */ RtreeCell *pCell, /* Cell to test */ int *pbRes /* OUT: Test result */ ){ int i; double aCoord[RTREE_MAX_DIMENSIONS*2]; int nCoord = pRtree->nDim*2; assert( pConstraint->op==RTREE_MATCH ); assert( pConstraint->pGeom ); for(i=0; i<nCoord; i++){ aCoord[i] = DCOORD(pCell->aCoord[i]); } return pConstraint->xGeom(pConstraint->pGeom, nCoord, aCoord, pbRes); } /* ** Cursor pCursor currently points to a cell in a non-leaf page. ** Set *pbEof to true if the sub-tree headed by the cell is filtered ** (excluded) by the constraints in the pCursor->aConstraint[] ** array, or false otherwise. ** ** Return SQLITE_OK if successful or an SQLite error code if an error ** occurs within a geometry callback. */ static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){ RtreeCell cell; int ii; int bRes = 0; nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){ RtreeConstraint *p = &pCursor->aConstraint[ii]; double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]); double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]); assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH ); switch( p->op ){ case RTREE_LE: case RTREE_LT: bRes = p->rValue<cell_min; break; case RTREE_GE: case RTREE_GT: bRes = p->rValue>cell_max; break; case RTREE_EQ: bRes = (p->rValue>cell_max || p->rValue<cell_min); break; default: { int rc; assert( p->op==RTREE_MATCH ); rc = testRtreeGeom(pRtree, p, &cell, &bRes); if( rc!=SQLITE_OK ){ return rc; } bRes = !bRes; break; } } } *pbEof = bRes; return SQLITE_OK; } /* ** Test if the cell that cursor pCursor currently points to ** would be filtered (excluded) by the constraints in the ** pCursor->aConstraint[] array. If so, set *pbEof to true before ** returning. If the cell is not filtered (excluded) by the constraints, ** set pbEof to zero. ** ** Return SQLITE_OK if successful or an SQLite error code if an error ** occurs within a geometry callback. ** ** This function assumes that the cell is part of a leaf node. */ static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){ RtreeCell cell; int ii; *pbEof = 0; nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); for(ii=0; ii<pCursor->nConstraint; ii++){ RtreeConstraint *p = &pCursor->aConstraint[ii]; double coord = DCOORD(cell.aCoord[p->iCoord]); int res; assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH ); switch( p->op ){ case RTREE_LE: res = (coord<=p->rValue); break; case RTREE_LT: res = (coord<p->rValue); break; case RTREE_GE: res = (coord>=p->rValue); break; case RTREE_GT: res = (coord>p->rValue); break; case RTREE_EQ: res = (coord==p->rValue); break; default: { int rc; assert( p->op==RTREE_MATCH ); rc = testRtreeGeom(pRtree, p, &cell, &res); if( rc!=SQLITE_OK ){ return rc; } break; } } if( !res ){ *pbEof = 1; return SQLITE_OK; } } return SQLITE_OK; } /* ** Cursor pCursor currently points at a node that heads a sub-tree of ** height iHeight (if iHeight==0, then the node is a leaf). Descend ** to point to the left-most cell of the sub-tree that matches the ** configured constraints. |
︙ | ︙ | |||
116108 116109 116110 116111 116112 116113 116114 | RtreeNode *pSavedNode = pCursor->pNode; int iSavedCell = pCursor->iCell; assert( iHeight>=0 ); if( iHeight==0 ){ | | | | | | 117690 117691 117692 117693 117694 117695 117696 117697 117698 117699 117700 117701 117702 117703 117704 117705 117706 117707 117708 117709 117710 | RtreeNode *pSavedNode = pCursor->pNode; int iSavedCell = pCursor->iCell; assert( iHeight>=0 ); if( iHeight==0 ){ rc = testRtreeEntry(pRtree, pCursor, &isEof); }else{ rc = testRtreeCell(pRtree, pCursor, &isEof); } if( rc!=SQLITE_OK || isEof || iHeight==0 ){ *pEof = isEof; return rc; } iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell); rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild); if( rc!=SQLITE_OK ){ return rc; } |
︙ | ︙ | |||
116150 116151 116152 116153 116154 116155 116156 | return SQLITE_OK; } /* ** One of the cells in node pNode is guaranteed to have a 64-bit ** integer value equal to iRowid. Return the index of this cell. */ | | > > > > > > > | | > | > | | | > | > > > > > > | < < | > > > | 117732 117733 117734 117735 117736 117737 117738 117739 117740 117741 117742 117743 117744 117745 117746 117747 117748 117749 117750 117751 117752 117753 117754 117755 117756 117757 117758 117759 117760 117761 117762 117763 117764 117765 117766 117767 117768 117769 117770 117771 117772 117773 117774 117775 117776 117777 117778 117779 117780 117781 117782 117783 117784 117785 117786 117787 117788 117789 117790 117791 117792 117793 117794 117795 117796 117797 117798 117799 117800 117801 117802 117803 117804 117805 117806 117807 117808 117809 117810 117811 | return SQLITE_OK; } /* ** One of the cells in node pNode is guaranteed to have a 64-bit ** integer value equal to iRowid. Return the index of this cell. */ static int nodeRowidIndex( Rtree *pRtree, RtreeNode *pNode, i64 iRowid, int *piIndex ){ int ii; int nCell = NCELL(pNode); for(ii=0; ii<nCell; ii++){ if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){ *piIndex = ii; return SQLITE_OK; } } return SQLITE_CORRUPT; } /* ** Return the index of the cell containing a pointer to node pNode ** in its parent. If pNode is the root node, return -1. */ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ RtreeNode *pParent = pNode->pParent; if( pParent ){ return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); } *piIndex = -1; return SQLITE_OK; } /* ** Rtree virtual table module xNext method. */ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab); RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; int rc = SQLITE_OK; /* RtreeCursor.pNode must not be NULL. If is is NULL, then this cursor is ** already at EOF. It is against the rules to call the xNext() method of ** a cursor that has already reached EOF. */ assert( pCsr->pNode ); if( pCsr->iStrategy==1 ){ /* This "scan" is a direct lookup by rowid. There is no next entry. */ nodeRelease(pRtree, pCsr->pNode); pCsr->pNode = 0; }else{ /* Move to the next entry that matches the configured constraints. */ int iHeight = 0; while( pCsr->pNode ){ RtreeNode *pNode = pCsr->pNode; int nCell = NCELL(pNode); for(pCsr->iCell++; pCsr->iCell<nCell; pCsr->iCell++){ int isEof; rc = descendToCell(pRtree, pCsr, iHeight, &isEof); if( rc!=SQLITE_OK || !isEof ){ return rc; } } pCsr->pNode = pNode->pParent; rc = nodeParentIndex(pRtree, pNode, &pCsr->iCell); if( rc!=SQLITE_OK ){ return rc; } nodeReference(pCsr->pNode); nodeRelease(pRtree, pNode); iHeight++; } } return rc; |
︙ | ︙ | |||
116266 116267 116268 116269 116270 116271 116272 116273 116274 116275 116276 116277 116278 116279 116280 116281 116282 116283 116284 116285 116286 116287 116288 116289 116290 | sqlite3_reset(pRtree->pReadRowid); }else{ rc = sqlite3_reset(pRtree->pReadRowid); } return rc; } /* ** Rtree virtual table module xFilter method. */ static int rtreeFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeNode *pRoot = 0; int ii; int rc = SQLITE_OK; rtreeReference(pRtree); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | > | > > > > > > > > > > > | > | 117865 117866 117867 117868 117869 117870 117871 117872 117873 117874 117875 117876 117877 117878 117879 117880 117881 117882 117883 117884 117885 117886 117887 117888 117889 117890 117891 117892 117893 117894 117895 117896 117897 117898 117899 117900 117901 117902 117903 117904 117905 117906 117907 117908 117909 117910 117911 117912 117913 117914 117915 117916 117917 117918 117919 117920 117921 117922 117923 117924 117925 117926 117927 117928 117929 117930 117931 117932 117933 117934 117935 117936 117937 117938 117939 117940 117941 117942 117943 117944 117945 117946 117947 117948 117949 117950 117951 117952 117953 117954 117955 117956 117957 117958 117959 117960 117961 117962 117963 117964 117965 117966 117967 117968 117969 117970 117971 117972 117973 117974 117975 117976 117977 117978 117979 117980 117981 117982 | sqlite3_reset(pRtree->pReadRowid); }else{ rc = sqlite3_reset(pRtree->pReadRowid); } return rc; } /* ** This function is called to configure the RtreeConstraint object passed ** as the second argument for a MATCH constraint. The value passed as the ** first argument to this function is the right-hand operand to the MATCH ** operator. */ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ RtreeMatchArg *p; sqlite3_rtree_geometry *pGeom; int nBlob; /* Check that value is actually a blob. */ if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_ERROR; /* Check that the blob is roughly the right size. */ nBlob = sqlite3_value_bytes(pValue); if( nBlob<sizeof(RtreeMatchArg) || ((nBlob-sizeof(RtreeMatchArg))%sizeof(double))!=0 ){ return SQLITE_ERROR; } pGeom = (sqlite3_rtree_geometry *)sqlite3_malloc( sizeof(sqlite3_rtree_geometry) + nBlob ); if( !pGeom ) return SQLITE_NOMEM; memset(pGeom, 0, sizeof(sqlite3_rtree_geometry)); p = (RtreeMatchArg *)&pGeom[1]; memcpy(p, sqlite3_value_blob(pValue), nBlob); if( p->magic!=RTREE_GEOMETRY_MAGIC || nBlob!=(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(double)) ){ sqlite3_free(pGeom); return SQLITE_ERROR; } pGeom->pContext = p->pContext; pGeom->nParam = p->nParam; pGeom->aParam = p->aParam; pCons->xGeom = p->xGeom; pCons->pGeom = pGeom; return SQLITE_OK; } /* ** Rtree virtual table module xFilter method. */ static int rtreeFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeNode *pRoot = 0; int ii; int rc = SQLITE_OK; rtreeReference(pRtree); freeCursorConstraints(pCsr); pCsr->iStrategy = idxNum; if( idxNum==1 ){ /* Special case - lookup by rowid. */ RtreeNode *pLeaf; /* Leaf on which the required cell resides */ i64 iRowid = sqlite3_value_int64(argv[0]); rc = findLeafNode(pRtree, iRowid, &pLeaf); pCsr->pNode = pLeaf; if( pLeaf ){ assert( rc==SQLITE_OK ); rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &pCsr->iCell); } }else{ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array ** with the configured constraints. */ if( argc>0 ){ pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc); pCsr->nConstraint = argc; if( !pCsr->aConstraint ){ rc = SQLITE_NOMEM; }else{ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); assert( (idxStr==0 && argc==0) || strlen(idxStr)==argc*2 ); for(ii=0; ii<argc; ii++){ RtreeConstraint *p = &pCsr->aConstraint[ii]; p->op = idxStr[ii*2]; p->iCoord = idxStr[ii*2+1]-'a'; if( p->op==RTREE_MATCH ){ /* A MATCH operator. The right-hand-side must be a blob that ** can be cast into an RtreeMatchArg object. One created using ** an sqlite3_rtree_geometry_callback() SQL user function. */ rc = deserializeGeometry(argv[ii], p); if( rc!=SQLITE_OK ){ break; } }else{ p->rValue = sqlite3_value_double(argv[ii]); } } } } if( rc==SQLITE_OK ){ pCsr->pNode = 0; rc = nodeAcquire(pRtree, 1, 0, &pRoot); |
︙ | ︙ | |||
116353 116354 116355 116356 116357 116358 116359 | ** Rtree virtual table module xBestIndex method. There are three ** table scan strategies to choose from (in order from most to ** least desirable): ** ** idxNum idxStr Strategy ** ------------------------------------------------ ** 1 Unused Direct lookup by rowid. | | < | > | 118009 118010 118011 118012 118013 118014 118015 118016 118017 118018 118019 118020 118021 118022 118023 118024 118025 118026 118027 118028 118029 118030 118031 118032 118033 118034 118035 118036 118037 118038 118039 118040 118041 118042 | ** Rtree virtual table module xBestIndex method. There are three ** table scan strategies to choose from (in order from most to ** least desirable): ** ** idxNum idxStr Strategy ** ------------------------------------------------ ** 1 Unused Direct lookup by rowid. ** 2 See below R-tree query or full-table scan. ** ------------------------------------------------ ** ** If strategy 1 is used, then idxStr is not meaningful. If strategy ** 2 is used, idxStr is formatted to contain 2 bytes for each ** constraint used. The first two bytes of idxStr correspond to ** the constraint in sqlite3_index_info.aConstraintUsage[] with ** (argvIndex==1) etc. ** ** The first of each pair of bytes in idxStr identifies the constraint ** operator as follows: ** ** Operator Byte Value ** ---------------------- ** = 0x41 ('A') ** <= 0x42 ('B') ** < 0x43 ('C') ** >= 0x44 ('D') ** > 0x45 ('E') ** MATCH 0x46 ('F') ** ---------------------- ** ** The second of each pair of bytes identifies the coordinate column ** to which the constraint applies. The leftmost coordinate column ** is 'a', the second from the left 'b' etc. */ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
︙ | ︙ | |||
116411 116412 116413 116414 116415 116416 116417 | ** considered almost as quick as a direct rowid lookup (for which ** sqlite uses an internal cost of 0.0). */ pIdxInfo->estimatedCost = 10.0; return SQLITE_OK; } | | > > > > > > | > | | | | | | | | | < < | | | | | | | | | | | < | 118067 118068 118069 118070 118071 118072 118073 118074 118075 118076 118077 118078 118079 118080 118081 118082 118083 118084 118085 118086 118087 118088 118089 118090 118091 118092 118093 118094 118095 118096 118097 118098 118099 118100 118101 118102 118103 118104 118105 118106 118107 118108 118109 118110 118111 118112 118113 118114 118115 118116 118117 | ** considered almost as quick as a direct rowid lookup (for which ** sqlite uses an internal cost of 0.0). */ pIdxInfo->estimatedCost = 10.0; return SQLITE_OK; } 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; switch( p->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; default: assert( p->op==SQLITE_INDEX_CONSTRAINT_MATCH ); 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; j<iIdx; j+=2){ if( zIdxStr[j+1]==cCol && (compatible[zIdxStr[j] & 7] & opmsk)!=0 ){ op = 0; break; } } if( op ){ assert( iIdx<sizeof(zIdxStr)-1 ); zIdxStr[iIdx++] = op; zIdxStr[iIdx++] = cCol; pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); |
︙ | ︙ | |||
116551 116552 116553 116554 116555 116556 116557 | RtreeCell *aCell, int nCell, int iExclude ){ int ii; float overlap = 0.0; for(ii=0; ii<nCell; ii++){ | > | > > > > | 118211 118212 118213 118214 118215 118216 118217 118218 118219 118220 118221 118222 118223 118224 118225 118226 118227 118228 118229 118230 | RtreeCell *aCell, int nCell, int iExclude ){ int ii; float overlap = 0.0; for(ii=0; ii<nCell; ii++){ #if VARIANT_RSTARTREE_CHOOSESUBTREE if( ii!=iExclude ) #else assert( iExclude==-1 ); #endif { int jj; float o = 1.0; for(jj=0; jj<(pRtree->nDim*2); jj+=2){ double x1; double x2; x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); |
︙ | ︙ | |||
116644 116645 116646 116647 116648 116649 116650 116651 116652 116653 116654 116655 116656 116657 116658 116659 116660 | #endif /* Select the child node which will be enlarged the least if pCell ** is inserted into it. Resolve ties by choosing the entry with ** the smallest area. */ for(iCell=0; iCell<nCell; iCell++){ float growth; float area; float overlap = 0.0; nodeGetCell(pRtree, pNode, iCell, &cell); growth = cellGrowth(pRtree, &cell, pCell); area = cellArea(pRtree, &cell); #if VARIANT_RSTARTREE_CHOOSESUBTREE if( ii==(pRtree->iDepth-1) ){ overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell); } | > > < > > > > > > > > | 118309 118310 118311 118312 118313 118314 118315 118316 118317 118318 118319 118320 118321 118322 118323 118324 118325 118326 118327 118328 118329 118330 118331 118332 118333 118334 118335 118336 118337 118338 118339 118340 118341 118342 118343 118344 118345 118346 118347 | #endif /* Select the child node which will be enlarged the least if pCell ** is inserted into it. Resolve ties by choosing the entry with ** the smallest area. */ for(iCell=0; iCell<nCell; iCell++){ int bBest = 0; float growth; float area; float overlap = 0.0; nodeGetCell(pRtree, pNode, iCell, &cell); growth = cellGrowth(pRtree, &cell, pCell); area = cellArea(pRtree, &cell); #if VARIANT_RSTARTREE_CHOOSESUBTREE if( ii==(pRtree->iDepth-1) ){ overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell); } if( (iCell==0) || (overlap<fMinOverlap) || (overlap==fMinOverlap && growth<fMinGrowth) || (overlap==fMinOverlap && growth==fMinGrowth && area<fMinArea) ){ bBest = 1; } #else if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){ bBest = 1; } #endif if( bBest ){ fMinOverlap = overlap; fMinGrowth = growth; fMinArea = area; iBest = cell.iRowid; } } |
︙ | ︙ | |||
116682 116683 116684 116685 116686 116687 116688 | } /* ** A cell with the same content as pCell has just been inserted into ** the node pNode. This function updates the bounding box cells in ** all ancestor elements. */ | | > > | | > > > | 118356 118357 118358 118359 118360 118361 118362 118363 118364 118365 118366 118367 118368 118369 118370 118371 118372 118373 118374 118375 118376 118377 118378 118379 118380 118381 118382 118383 118384 118385 118386 118387 118388 118389 118390 118391 118392 118393 | } /* ** A cell with the same content as pCell has just been inserted into ** the node pNode. This function updates the bounding box cells in ** all ancestor elements. */ static int AdjustTree( Rtree *pRtree, /* Rtree table */ RtreeNode *pNode, /* Adjust ancestry of this node. */ RtreeCell *pCell /* This cell was just inserted */ ){ RtreeNode *p = pNode; while( p->pParent ){ RtreeNode *pParent = p->pParent; RtreeCell cell; int iCell; if( nodeParentIndex(pRtree, p, &iCell) ){ return SQLITE_CORRUPT; } nodeGetCell(pRtree, pParent, iCell, &cell); if( !cellContains(pRtree, &cell, pCell) ){ cellUnion(pRtree, &cell, pCell); nodeOverwriteCell(pRtree, pParent, &cell, iCell); } p = pParent; } return SQLITE_OK; } /* ** Write mapping (iRowid->iNode) to the <rtree>_rowid table. */ static int rowidWrite(Rtree *pRtree, sqlite3_int64 iRowid, sqlite3_int64 iNode){ sqlite3_bind_int64(pRtree->pWriteRowid, 1, iRowid); |
︙ | ︙ | |||
117229 117230 117231 117232 117233 117234 117235 | nodeGetCell(pRtree, pNode, i, &aCell[i]); } nodeZero(pRtree, pNode); memcpy(&aCell[nCell], pCell, sizeof(RtreeCell)); nCell++; if( pNode->iNode==1 ){ | | | | | > > > > | > | > | | > > > > | 118908 118909 118910 118911 118912 118913 118914 118915 118916 118917 118918 118919 118920 118921 118922 118923 118924 118925 118926 118927 118928 118929 118930 118931 118932 118933 118934 118935 118936 118937 118938 118939 118940 118941 118942 118943 118944 118945 118946 118947 118948 118949 118950 118951 118952 118953 118954 118955 118956 118957 118958 118959 118960 118961 118962 118963 118964 118965 118966 118967 118968 118969 118970 118971 118972 118973 118974 118975 | nodeGetCell(pRtree, pNode, i, &aCell[i]); } nodeZero(pRtree, pNode); memcpy(&aCell[nCell], pCell, sizeof(RtreeCell)); nCell++; if( pNode->iNode==1 ){ pRight = nodeNew(pRtree, pNode); pLeft = nodeNew(pRtree, pNode); pRtree->iDepth++; pNode->isDirty = 1; writeInt16(pNode->zData, pRtree->iDepth); }else{ pLeft = pNode; pRight = nodeNew(pRtree, pLeft->pParent); nodeReference(pLeft); } if( !pLeft || !pRight ){ rc = SQLITE_NOMEM; goto splitnode_out; } memset(pLeft->zData, 0, pRtree->iNodeSize); memset(pRight->zData, 0, pRtree->iNodeSize); rc = AssignCells(pRtree, aCell, nCell, pLeft, pRight, &leftbbox, &rightbbox); if( rc!=SQLITE_OK ){ goto splitnode_out; } /* Ensure both child nodes have node numbers assigned to them by calling ** nodeWrite(). Node pRight always needs a node number, as it was created ** by nodeNew() above. But node pLeft sometimes already has a node number. ** In this case avoid the all to nodeWrite(). */ if( SQLITE_OK!=(rc = nodeWrite(pRtree, pRight)) || (0==pLeft->iNode && SQLITE_OK!=(rc = nodeWrite(pRtree, pLeft))) ){ goto splitnode_out; } rightbbox.iRowid = pRight->iNode; leftbbox.iRowid = pLeft->iNode; if( pNode->iNode==1 ){ rc = rtreeInsertCell(pRtree, pLeft->pParent, &leftbbox, iHeight+1); if( rc!=SQLITE_OK ){ goto splitnode_out; } }else{ RtreeNode *pParent = pLeft->pParent; int iCell; rc = nodeParentIndex(pRtree, pLeft, &iCell); if( rc==SQLITE_OK ){ nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); rc = AdjustTree(pRtree, pParent, &leftbbox); } if( rc!=SQLITE_OK ){ goto splitnode_out; } } if( (rc = rtreeInsertCell(pRtree, pRight->pParent, &rightbbox, iHeight+1)) ){ goto splitnode_out; } for(i=0; i<NCELL(pRight); i++){ i64 iRowid = nodeGetRowid(pRtree, pRight, i); |
︙ | ︙ | |||
117316 117317 117318 117319 117320 117321 117322 117323 117324 | splitnode_out: nodeRelease(pRtree, pRight); nodeRelease(pRtree, pLeft); sqlite3_free(aCell); return rc; } static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ int rc = SQLITE_OK; | > > > > > > > > > > > > | > | | > > > > > > > > > | > > | < < | > | | > | < > | > | | | > | | > > > | 119005 119006 119007 119008 119009 119010 119011 119012 119013 119014 119015 119016 119017 119018 119019 119020 119021 119022 119023 119024 119025 119026 119027 119028 119029 119030 119031 119032 119033 119034 119035 119036 119037 119038 119039 119040 119041 119042 119043 119044 119045 119046 119047 119048 119049 119050 119051 119052 119053 119054 119055 119056 119057 119058 119059 119060 119061 119062 119063 119064 119065 119066 119067 119068 119069 119070 119071 119072 119073 119074 119075 119076 119077 119078 119079 119080 119081 | splitnode_out: nodeRelease(pRtree, pRight); nodeRelease(pRtree, pLeft); sqlite3_free(aCell); return rc; } /* ** If node pLeaf is not the root of the r-tree and its pParent pointer is ** still NULL, load all ancestor nodes of pLeaf into memory and populate ** the pLeaf->pParent chain all the way up to the root node. ** ** This operation is required when a row is deleted (or updated - an update ** is implemented as a delete followed by an insert). SQLite provides the ** rowid of the row to delete, which can be used to find the leaf on which ** the entry resides (argument pLeaf). Once the leaf is located, this ** function is called to determine its ancestry. */ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ int rc = SQLITE_OK; RtreeNode *pChild = pLeaf; while( rc==SQLITE_OK && pChild->iNode!=1 && pChild->pParent==0 ){ int rc2 = SQLITE_OK; /* sqlite3_reset() return code */ sqlite3_bind_int64(pRtree->pReadParent, 1, pChild->iNode); rc = sqlite3_step(pRtree->pReadParent); if( rc==SQLITE_ROW ){ RtreeNode *pTest; /* Used to test for reference loops */ i64 iNode; /* Node number of parent node */ /* Before setting pChild->pParent, test that we are not creating a ** loop of references (as we would if, say, pChild==pParent). We don't ** want to do this as it leads to a memory leak when trying to delete ** the referenced counted node structures. */ iNode = sqlite3_column_int64(pRtree->pReadParent, 0); for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); if( !pTest ){ rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); } } rc = sqlite3_reset(pRtree->pReadParent); if( rc==SQLITE_OK ) rc = rc2; if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT; pChild = pChild->pParent; } return rc; } static int deleteCell(Rtree *, RtreeNode *, int, int); static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ int rc; int rc2; RtreeNode *pParent; int iCell; assert( pNode->nRef==1 ); /* Remove the entry in the parent cell. */ rc = nodeParentIndex(pRtree, pNode, &iCell); if( rc==SQLITE_OK ){ pParent = pNode->pParent; pNode->pParent = 0; rc = deleteCell(pRtree, pParent, iCell, iHeight+1); } rc2 = nodeRelease(pRtree, pParent); if( rc==SQLITE_OK ){ rc = rc2; } if( rc!=SQLITE_OK ){ return rc; } /* Remove the xxx_node entry. */ sqlite3_bind_int64(pRtree->pDeleteNode, 1, pNode->iNode); sqlite3_step(pRtree->pDeleteNode); if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteNode)) ){ |
︙ | ︙ | |||
117379 117380 117381 117382 117383 117384 117385 | pNode->pNext = pRtree->pDeleted; pNode->nRef++; pRtree->pDeleted = pNode; return SQLITE_OK; } | | > | > | | | > > > < | > | | < | | 119097 119098 119099 119100 119101 119102 119103 119104 119105 119106 119107 119108 119109 119110 119111 119112 119113 119114 119115 119116 119117 119118 119119 119120 119121 119122 119123 119124 119125 119126 119127 119128 119129 119130 119131 119132 119133 119134 119135 119136 119137 119138 119139 119140 119141 119142 119143 119144 119145 119146 119147 119148 119149 119150 119151 119152 119153 119154 119155 119156 119157 119158 119159 119160 119161 119162 | pNode->pNext = pRtree->pDeleted; pNode->nRef++; pRtree->pDeleted = pNode; return SQLITE_OK; } static int fixBoundingBox(Rtree *pRtree, RtreeNode *pNode){ RtreeNode *pParent = pNode->pParent; int rc = SQLITE_OK; if( pParent ){ int ii; int nCell = NCELL(pNode); RtreeCell box; /* Bounding box for pNode */ nodeGetCell(pRtree, pNode, 0, &box); for(ii=1; ii<nCell; ii++){ RtreeCell cell; nodeGetCell(pRtree, pNode, ii, &cell); cellUnion(pRtree, &box, &cell); } box.iRowid = pNode->iNode; rc = nodeParentIndex(pRtree, pNode, &ii); if( rc==SQLITE_OK ){ nodeOverwriteCell(pRtree, pParent, &box, ii); rc = fixBoundingBox(pRtree, pParent); } } return rc; } /* ** Delete the cell at index iCell of node pNode. After removing the ** cell, adjust the r-tree data structure if required. */ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){ RtreeNode *pParent; int rc; if( SQLITE_OK!=(rc = fixLeafParent(pRtree, pNode)) ){ return rc; } /* Remove the cell from the node. This call just moves bytes around ** the in-memory node image, so it cannot fail. */ nodeDeleteCell(pRtree, pNode, iCell); /* If the node is not the tree root and now has less than the minimum ** number of cells, remove it from the tree. Otherwise, update the ** cell in the parent node so that it tightly contains the updated ** node. */ pParent = pNode->pParent; assert( pParent || pNode->iNode==1 ); if( pParent ){ if( NCELL(pNode)<RTREE_MINCELLS(pRtree) ){ rc = removeNode(pRtree, pNode, iHeight); }else{ rc = fixBoundingBox(pRtree, pNode); } } return rc; } static int Reinsert( |
︙ | ︙ | |||
117509 117510 117511 117512 117513 117514 117515 | rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); }else{ rc = parentWrite(pRtree, p->iRowid, pNode->iNode); } } } if( rc==SQLITE_OK ){ | | | 119231 119232 119233 119234 119235 119236 119237 119238 119239 119240 119241 119242 119243 119244 119245 | rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); }else{ rc = parentWrite(pRtree, p->iRowid, pNode->iNode); } } } if( rc==SQLITE_OK ){ rc = fixBoundingBox(pRtree, pNode); } for(; rc==SQLITE_OK && ii<nCell; ii++){ /* Find a node to store this cell in. pNode->iNode currently contains ** the height of the sub-tree headed by the cell. */ RtreeNode *pInsert; RtreeCell *p = &aCell[aOrder[ii]]; |
︙ | ︙ | |||
117563 117564 117565 117566 117567 117568 117569 | pRtree->iReinsertHeight = iHeight; rc = Reinsert(pRtree, pNode, pCell, iHeight); } #else rc = SplitNode(pRtree, pNode, pCell, iHeight); #endif }else{ | | > | | | | > | 119285 119286 119287 119288 119289 119290 119291 119292 119293 119294 119295 119296 119297 119298 119299 119300 119301 119302 119303 119304 119305 | pRtree->iReinsertHeight = iHeight; rc = Reinsert(pRtree, pNode, pCell, iHeight); } #else rc = SplitNode(pRtree, pNode, pCell, iHeight); #endif }else{ rc = AdjustTree(pRtree, pNode, pCell); if( rc==SQLITE_OK ){ if( iHeight==0 ){ rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); }else{ rc = parentWrite(pRtree, pCell->iRowid, pNode->iNode); } } } return rc; } static int reinsertNodeContent(Rtree *pRtree, RtreeNode *pNode){ int ii; |
︙ | ︙ | |||
117637 117638 117639 117640 117641 117642 117643 | ){ Rtree *pRtree = (Rtree *)pVtab; int rc = SQLITE_OK; rtreeReference(pRtree); assert(nData>=1); | < | 119361 119362 119363 119364 119365 119366 119367 119368 119369 119370 119371 119372 119373 119374 | ){ Rtree *pRtree = (Rtree *)pVtab; int rc = SQLITE_OK; rtreeReference(pRtree); assert(nData>=1); /* If azData[0] is not an SQL NULL value, it is the rowid of a ** record to delete from the r-tree table. The following block does ** just that. */ if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){ i64 iDelete; /* The rowid to delete */ |
︙ | ︙ | |||
117663 117664 117665 117666 117667 117668 117669 | iDelete = sqlite3_value_int64(azData[0]); rc = findLeafNode(pRtree, iDelete, &pLeaf); } /* Delete the cell in question from the leaf node. */ if( rc==SQLITE_OK ){ int rc2; | | > | > | 119386 119387 119388 119389 119390 119391 119392 119393 119394 119395 119396 119397 119398 119399 119400 119401 119402 119403 | iDelete = sqlite3_value_int64(azData[0]); rc = findLeafNode(pRtree, iDelete, &pLeaf); } /* Delete the cell in question from the leaf node. */ if( rc==SQLITE_OK ){ int rc2; rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); if( rc==SQLITE_OK ){ rc = deleteCell(pRtree, pLeaf, iCell, 0); } rc2 = nodeRelease(pRtree, pLeaf); if( rc==SQLITE_OK ){ rc = rc2; } } /* Delete the corresponding entry in the <rtree>_rowid table. */ |
︙ | ︙ | |||
117686 117687 117688 117689 117690 117691 117692 | ** it, schedule the contents of the child for reinsertion and ** reduce the tree height by one. ** ** This is equivalent to copying the contents of the child into ** the root node (the operation that Gutman's paper says to perform ** in this scenario). */ | | | | | | | | | > > | | | | < | 119411 119412 119413 119414 119415 119416 119417 119418 119419 119420 119421 119422 119423 119424 119425 119426 119427 119428 119429 119430 119431 119432 119433 119434 119435 119436 119437 119438 | ** it, schedule the contents of the child for reinsertion and ** reduce the tree height by one. ** ** This is equivalent to copying the contents of the child into ** the root node (the operation that Gutman's paper says to perform ** in this scenario). */ if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){ int rc2; RtreeNode *pChild; i64 iChild = nodeGetRowid(pRtree, pRoot, 0); rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); if( rc==SQLITE_OK ){ rc = removeNode(pRtree, pChild, pRtree->iDepth-1); } rc2 = nodeRelease(pRtree, pChild); if( rc==SQLITE_OK ) rc = rc2; if( rc==SQLITE_OK ){ pRtree->iDepth--; writeInt16(pRoot->zData, pRtree->iDepth); pRoot->isDirty = 1; } } /* Re-insert the contents of any underfull nodes removed from the tree. */ for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){ if( rc==SQLITE_OK ){ rc = reinsertNodeContent(pRtree, pLeaf); |
︙ | ︙ | |||
117988 117989 117990 117991 117992 117993 117994 | char **pzErr, /* OUT: Error message, if any */ int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; Rtree *pRtree; int nDb; /* Length of string argv[1] */ int nName; /* Length of string argv[2] */ | | | 119714 119715 119716 119717 119718 119719 119720 119721 119722 119723 119724 119725 119726 119727 119728 | char **pzErr, /* OUT: Error message, if any */ int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; Rtree *pRtree; int nDb; /* Length of string argv[1] */ int nName; /* Length of string argv[2] */ int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32); const char *aErrMsg[] = { 0, /* 0 */ "Wrong number of columns for an rtree table", /* 1 */ "Too few columns for an rtree table", /* 2 */ "Too many columns for an rtree table" /* 3 */ }; |
︙ | ︙ | |||
118134 118135 118136 118137 118138 118139 118140 | /* ** Register the r-tree module with database handle db. This creates the ** virtual table module "rtree" and the debugging/analysis scalar ** function "rtreenode". */ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db){ | > | < < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 119860 119861 119862 119863 119864 119865 119866 119867 119868 119869 119870 119871 119872 119873 119874 119875 119876 119877 119878 119879 119880 119881 119882 119883 119884 119885 119886 119887 119888 119889 119890 119891 119892 119893 119894 119895 119896 119897 119898 119899 119900 119901 119902 119903 119904 119905 119906 119907 119908 119909 119910 119911 119912 119913 119914 119915 119916 119917 119918 119919 119920 119921 119922 119923 119924 119925 119926 119927 119928 119929 119930 119931 119932 119933 119934 119935 119936 119937 119938 119939 119940 119941 119942 119943 119944 119945 119946 119947 119948 119949 119950 119951 119952 119953 119954 119955 119956 | /* ** Register the r-tree module with database handle db. This creates the ** virtual table module "rtree" and the debugging/analysis scalar ** function "rtreenode". */ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db){ const int utf8 = SQLITE_UTF8; int rc; rc = sqlite3_create_function(db, "rtreenode", 2, utf8, 0, rtreenode, 0, 0); if( rc==SQLITE_OK ){ int utf8 = SQLITE_UTF8; rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0); } if( rc==SQLITE_OK ){ void *c = (void *)RTREE_COORD_REAL32; rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0); } if( rc==SQLITE_OK ){ void *c = (void *)RTREE_COORD_INT32; rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0); } return rc; } /* ** A version of sqlite3_free() that can be used as a callback. This is used ** in two places - as the destructor for the blob value returned by the ** invocation of a geometry function, and as the destructor for the geometry ** functions themselves. */ static void doSqlite3Free(void *p){ sqlite3_free(p); } /* ** Each call to sqlite3_rtree_geometry_callback() creates an ordinary SQLite ** scalar user function. This C function is the callback used for all such ** registered SQL functions. ** ** The scalar user functions return a blob that is interpreted by r-tree ** table MATCH operators. */ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); RtreeMatchArg *pBlob; int nBlob; nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(double); pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob); if( !pBlob ){ sqlite3_result_error_nomem(ctx); }else{ int i; pBlob->magic = RTREE_GEOMETRY_MAGIC; pBlob->xGeom = pGeomCtx->xGeom; pBlob->pContext = pGeomCtx->pContext; pBlob->nParam = nArg; for(i=0; i<nArg; i++){ pBlob->aParam[i] = sqlite3_value_double(aArg[i]); } sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free); } } /* ** Register a new geometry function for use with the r-tree MATCH operator. */ SQLITE_API int sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *), void *pContext ){ RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */ /* Allocate and populate the context object. */ pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); if( !pGeomCtx ) return SQLITE_NOMEM; pGeomCtx->xGeom = xGeom; pGeomCtx->pContext = pContext; /* Create the new user-function. Register a destructor function to delete ** the context object when it is no longer required. */ return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY, (void *)pGeomCtx, geomCallback, 0, 0, doSqlite3Free ); } #if !SQLITE_CORE SQLITE_API int sqlite3_extension_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ |
︙ | ︙ |
Changes to src/sqlite3.h.
︙ | ︙ | |||
93 94 95 96 97 98 99 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management | | | | | | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 #define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
︙ | ︙ | |||
150 151 152 153 154 155 156 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** | | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** ** ^The sqlite3_compileoption_get() function allows iterating ** over the list of options that were defined at compile time by ** returning the N-th compile time option string. ^If N is out of range, ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ ** prefix is omitted from any strings returned by ** sqlite3_compileoption_get(). ** ** ^Support for the diagnostic functions sqlite3_compileoption_used() ** and sqlite3_compileoption_get() may be omitted by specifying the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); |
︙ | ︙ | |||
264 265 266 267 268 269 270 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is | | | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is ** successfully destroyed and all associated resources are deallocated. ** ** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with ** the [sqlite3] object prior to attempting to close the object. ^If ** sqlite3_close() is called on a [database connection] that still has ** outstanding [prepared statements] or [BLOB handles], then it returns ** SQLITE_BUSY. |
︙ | ︙ | |||
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only | > > > > > > > > > | 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. ** ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified ** by the user. The fourth argument to [sqlite3_file_control()] should ** point to an integer (type int) containing the new chunk-size to use ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and ** improve performance on some systems. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
︙ | ︙ | |||
744 745 746 747 748 749 750 | ** or modify this field while holding a particular static mutex. ** The application should never modify anything within the sqlite3_vfs ** object once the object has been registered. ** ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** | | > > > > | | | | > | | | > | | | | | | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 | ** or modify this field while holding a particular static mutex. ** The application should never modify anything within the sqlite3_vfs ** object once the object has been registered. ** ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** ** ^SQLite guarantees that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained ** from xFullPathname() with an optional suffix added. ** ^If a suffix is added to the zFilename parameter, it will ** consist of a single "-" character followed by no more than ** 10 alphanumeric and/or "-" characters. ** ^SQLite further guarantees that ** the string will be valid and unchanged until xClose() is ** called. Because of the previous sentence, ** the [sqlite3_file] can safely store a pointer to the ** filename if it needs to remember the filename for some reason. ** If the zFilename parameter to xOpen is a NULL pointer then xOpen ** must invent its own temporary name for the file. ^Whenever the ** xFilename parameter is NULL it will also be the case that the ** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. ** ** The flags argument to xOpen() includes all bits set in ** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] ** or [sqlite3_open16()] is used, then flags includes at least ** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. ** If xOpen() opens a file read-only then it sets *pOutFlags to ** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. ** ** ^(SQLite will also add one of the following flags to the xOpen() ** call, depending on the object being opened: ** ** <ul> ** <li> [SQLITE_OPEN_MAIN_DB] ** <li> [SQLITE_OPEN_MAIN_JOURNAL] ** <li> [SQLITE_OPEN_TEMP_DB] ** <li> [SQLITE_OPEN_TEMP_JOURNAL] ** <li> [SQLITE_OPEN_TRANSIENT_DB] ** <li> [SQLITE_OPEN_SUBJOURNAL] ** <li> [SQLITE_OPEN_MASTER_JOURNAL] ** <li> [SQLITE_OPEN_WAL] ** </ul>)^ ** ** The file I/O implementation can use the object type flags to ** change the way it deals with files. For example, an application ** that does not care about crash recovery or rollback might make ** the open of a journal file a no-op. Writes to this journal would ** also be no-ops, and any attempt to read the journal would return ** SQLITE_IOERR. Or the implementation might recognize that a database ** file will be doing page-aligned sector reads and writes in a random ** order and set up its I/O subsystem accordingly. ** ** SQLite might also add one of the following flags to the xOpen method: ** ** <ul> ** <li> [SQLITE_OPEN_DELETEONCLOSE] ** <li> [SQLITE_OPEN_EXCLUSIVE] ** </ul> ** ** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be ** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] ** will be set for TEMP databases and their journals, transient ** databases, and subjournals. ** ** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction ** with the [SQLITE_OPEN_CREATE] flag, which are both directly ** analogous to the O_EXCL and O_CREAT flags of the POSIX open() ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the ** SQLITE_OPEN_CREATE, is used to indicate that file should always ** be created, and that it is an error if it already exists. ** It is <i>not</i> used to indicate the file should be opened ** for exclusive access. ** ** ^At least szOsFile bytes of memory are allocated by SQLite ** to hold the [sqlite3_file] structure passed as the third ** argument to xOpen. The xOpen method does not have to ** allocate the structure; it should just fill it in. Note that ** the xOpen method must set the sqlite3_file.pMethods to either ** a valid [sqlite3_io_methods] object or to NULL. xOpen must do ** this even if the open fails. SQLite expects that the sqlite3_file.pMethods ** element will be valid after xOpen returns regardless of the success ** or failure of the xOpen call. ** ** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] ** to test whether a file is at least readable. The file can be a ** directory. ** ** ^SQLite will always allocate at least mxPathname+1 bytes for the ** output buffer xFullPathname. The exact size of the output buffer ** is also passed as a parameter to both methods. If the output buffer ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is ** handled as a fatal error by SQLite, vfs implementations should endeavor ** to prevent this by setting mxPathname to a sufficiently large value. ** ** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() ** interfaces are not strictly a part of the filesystem, but they are ** included in the VFS structure for completeness. ** The xRandomness() function attempts to return nBytes bytes ** of good-quality randomness into zOut. The return value is ** the actual number of bytes of randomness obtained. ** The xSleep() method causes the calling thread to sleep for at ** least the number of microseconds given. ^The xCurrentTime() ** method returns a Julian Day Number for the current date and time as ** a floating point value. ** ^The xCurrentTimeInt64() method returns, as an integer, the Julian ** Day Number multipled by 86400000 (the number of milliseconds in ** a 24-hour day). ** ^SQLite will use the xCurrentTimeInt64() method to get the current ** date and time if that method is available (if iVersion is 2 or ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. */ |
︙ | ︙ | |||
1235 1236 1237 1238 1239 1240 1241 | ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the ** following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] | | | < | | | | | < | | 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 | ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the ** following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] ** <li> [sqlite3_status()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory ** allocation statistics are disabled by default. ** </dd> ** ** <dt>SQLITE_CONFIG_SCRATCH</dt> ** <dd> ^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 ** 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. ** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. ** ^SQLite will use no more than two scratch buffers per thread. So ** N should be set to twice the expected maximum number of threads. ** ^SQLite will never require a scratch buffer that is more than 6 ** times the database page size. ^If SQLite needs needs additional ** scratch memory beyond what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.</dd> ** ** <dt>SQLITE_CONFIG_PAGECACHE</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implemenation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. ** There are three arguments to this option: A pointer to 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each ** page header. ^The page header size is 20 to 40 bytes depending on ** the host architecture. ^It is harmless, apart from the wasted memory, ** to make sz a little too large. The first ** argument should point to an allocation of at least sz*N bytes of memory. ** ^SQLite will use the memory provided by the first argument to satisfy its ** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space. ** The pointer in the first argument must ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.</dd> ** ** <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. |
︙ | ︙ | |||
1407 1408 1409 1410 1411 1412 1413 | ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally | | > > > | > > > | 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 | ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally ** rounded down to the next smaller multiple of 8. ^(The lookaside memory ** configuration for a database connection can only be changed when that ** connection is not currently using lookaside memory, or in other words ** when the "current value" returned by ** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^</dd> ** ** </dl> */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ /* |
︙ | ︙ | |||
1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** Definition: A <b>result table</b> is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These | > > > | 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. ** ** Definition: A <b>result table</b> is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These |
︙ | ︙ | |||
1733 1734 1735 1736 1737 1738 1739 | ** in NULL pointers. All other values are in their UTF-8 zero-terminated ** string representation as returned by [sqlite3_column_text()]. ** ** A result table might consist of one or more memory allocations. ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** | | | 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 | ** in NULL pointers. All other values are in their UTF-8 zero-terminated ** string representation as returned by [sqlite3_column_text()]. ** ** A result table might consist of one or more memory allocations. ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** ** ^(As an example of the result table format, suppose a query result ** is as follows: ** ** <blockquote><pre> ** Name | Age ** ----------------------- ** Alice | 43 ** Bob | 28 |
︙ | ︙ | |||
1757 1758 1759 1760 1761 1762 1763 | ** azResult[1] = "Age"; ** azResult[2] = "Alice"; ** azResult[3] = "43"; ** azResult[4] = "Bob"; ** azResult[5] = "28"; ** azResult[6] = "Cindy"; ** azResult[7] = "21"; | | | | | | 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 | ** azResult[1] = "Age"; ** azResult[2] = "Alice"; ** azResult[3] = "43"; ** azResult[4] = "Bob"; ** azResult[5] = "28"; ** azResult[6] = "Cindy"; ** azResult[7] = "21"; ** </pre></blockquote>)^ ** ** ^The sqlite3_get_table() function evaluates one or more ** semicolon-separated SQL statements in the zero-terminated UTF-8 ** string of its 2nd parameter and returns a result table to the ** pointer given in its 3rd parameter. ** ** After the application has finished with the result from sqlite3_get_table(), ** it must pass the result table pointer to sqlite3_free_table() in order to ** release the memory that was malloced. Because of the way the ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling ** function must not try to call [sqlite3_free()] directly. Only ** [sqlite3_free_table()] is able to release the memory properly and safely. ** ** The sqlite3_get_table() interface is implemented as a wrapper around ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ SQLITE_API int sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ int *pnRow, /* Number of result rows written here */ int *pnColumn, /* Number of result columns written here */ |
︙ | ︙ | |||
1929 1930 1931 1932 1933 1934 1935 | ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc() and the prior allocation is freed. ** ^If sqlite3_realloc() returns NULL, then the prior allocation ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() | | > > | 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 | ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc() and the prior allocation is freed. ** ^If sqlite3_realloc() returns NULL, then the prior allocation ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() ** is always aligned to at least an 8 byte boundary, or to a ** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time ** option is used. ** ** In SQLite version 3.5.0 and 3.5.1, it was possible to define ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in ** implementation of these routines to be omitted. That capability ** is no longer provided. Only built-in memory allocators can be used. ** ** The Windows OS interface layer calls |
︙ | ︙ | |||
2187 2188 2189 2190 2191 2192 2193 | SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** | | | | | > > > > > > > > > > > | | 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 | SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to ** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for ** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** ** ^The parameter P is passed through as the only parameter to the ** callback function X. ^The parameter N is the number of ** [virtual machine instructions] that are evaluated between successive ** invocations of the callback X. ** ** ^Only a single progress handler may be defined at one time per ** [database connection]; setting a new progress handler cancels the ** old one. ^Setting parameter X to NULL disables the progress handler. ** ^The progress handler is also disabled by setting N to a value less ** than 1. ** ** ^If the progress callback returns non-zero, the operation is ** interrupted. This feature can be used to implement a ** "Cancel" button on a GUI progress dialog box. ** ** The progress handler callback must not do anything that will modify ** the database connection that invoked the progress handler. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** */ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); |
︙ | ︙ | |||
2256 2257 2258 2259 2260 2261 2262 | ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined ** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], | | | 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 | ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined ** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], ** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_PRIVATECACHE] flags, ** then the behavior is undefined. ** ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection ** opens in the multi-thread [threading mode] as long as the single-thread ** mode has not been set at compile-time or start-time. ^If the ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens ** in the serialized [threading mode] unless single-thread was |
︙ | ︙ | |||
2381 2382 2383 2384 2385 2386 2387 | ** CAPI3REF: Run-time Limits ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the | | | | | > > > > > | 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 | ** CAPI3REF: Run-time Limits ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the ** new limit for that construct.)^ ** ** ^If the new limit is a negative number, the limit is unchanged. ** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a ** [limits | hard upper bound] ** set at compile-time by a C preprocessor macro called ** [limits | SQLITE_MAX_<i>NAME</i>]. ** (The "_LIMIT_" in the name is changed to "_MAX_".))^ ** ^Attempts to increase a limit above its hard upper bound are ** silently truncated to the hard upper bound. ** ** ^Regardless of whether or not the limit was changed, the ** [sqlite3_limit()] interface returns the prior value of the limit. ** ^Hence, to find the current value of a limit without changing it, ** simply invoke this interface with the third parameter set to -1. ** ** Run-time limits are intended for use in applications that manage ** both their own internal database and also databases that are controlled ** by untrusted external sources. An example application might be a ** web browser that has its own databases for storing history and ** separate databases controlled by JavaScript applications downloaded ** off the Internet. The internal databases can be given the ** large, default limits. Databases managed by external sources can |
︙ | ︙ | |||
2420 2421 2422 2423 2424 2425 2426 | ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> ** ^(<dt>SQLITE_LIMIT_LENGTH</dt> | | | > > | < | 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 | ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> ** ^(<dt>SQLITE_LIMIT_LENGTH</dt> ** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^ ** ** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt> ** <dd>The maximum length of an SQL statement, in bytes.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_COLUMN</dt> ** <dd>The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index ** or in an ORDER BY or GROUP BY clause.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt> ** <dd>The maximum depth of the parse tree on any expression.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> ** <dd>The maximum number of instructions in a virtual machine program ** used to implement an SQL statement. This limit is not currently ** enforced, though that might be added in some future release of ** SQLite.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> ** <dd>The maximum number of arguments on a function.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_ATTACHED</dt> ** <dd>The maximum number of [ATTACH | attached databases].)^</dd> ** ** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt> ** <dd>The maximum length of the pattern argument to the [LIKE] or ** [GLOB] operators.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt> ** <dd>The maximum index number of any [parameter] in an SQL statement.)^ ** ** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> ** <dd>The maximum depth of recursion for triggers.</dd>)^ ** </dl> */ #define SQLITE_LIMIT_LENGTH 0 #define SQLITE_LIMIT_SQL_LENGTH 1 |
︙ | ︙ | |||
2524 2525 2526 2527 2528 2529 2530 | ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** ** <ol> ** <li> ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL | | < < < < < | | | | | > > > > > | 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 | ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** ** <ol> ** <li> ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL ** statement and try to run it again. ** </li> ** ** <li> ** ^When an error occurs, [sqlite3_step()] will return one of the detailed ** [error codes] or [extended error codes]. ^The legacy behavior was that ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code ** and the application would have to make a second call to [sqlite3_reset()] ** in order to find the underlying cause of the problem. With the "v2" prepare ** interfaces, the underlying reason for the error is returned immediately. ** </li> ** ** <li> ** ^If the specific value bound to [parameter | host parameter] in the ** WHERE clause might influence the choice of query plan for a statement, ** then the statement will be automatically recompiled, as if there had been ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled. ** the ** </li> ** </ol> */ SQLITE_API int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ |
︙ | ︙ | |||
2613 2614 2615 2616 2617 2618 2619 | ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications | | | 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 | ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications ** still make the distinction between protected and unprotected ** sqlite3_value objects even when not strictly required. ** ** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. ** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used with |
︙ | ︙ | |||
2659 2660 2661 2662 2663 2664 2665 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, | | | 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, ** and VVV represents an alphanumeric identifier.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** ** ^The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from ** [sqlite3_prepare_v2()] or its variants. ** |
︙ | ︙ | |||
2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 | /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column | > > | 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 | /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). ** ** See also: [sqlite3_data_count()] */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column |
︙ | ︙ | |||
2998 2999 3000 3001 3002 3003 3004 | ** by sqlite3_step(). The use of the "v2" interface is recommended. */ SQLITE_API int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** | | | > > > > > > | 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 | ** by sqlite3_step(). The use of the "v2" interface is recommended. */ SQLITE_API int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. ** ^If prepared statement P does not have results ready to return ** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of ** interfaces) then sqlite3_data_count(P) returns 0. ** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. ** ** See also: [sqlite3_column_count()] */ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes ** KEYWORDS: SQLITE_TEXT ** |
︙ | ︙ | |||
3079 3080 3081 3082 3083 3084 3085 | ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. | > > > > > > > > > > > > | | > | < < < < < | 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 | ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. ** ^If the result is NULL, then sqlite3_column_bytes() returns zero. ** ** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts ** the string to UTF-16 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes16() uses ** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns ** the number of bytes in that string. ** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. ** ** ^The values returned by [sqlite3_column_bytes()] and ** [sqlite3_column_bytes16()] do not include the zero terminators at the end ** of the string. ^For clarity: the values returned by ** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of ** bytes in the string, not the number of characters. ** ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), ** even empty strings, are always zero terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** ** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by ** [sqlite3_column_value()] is used in any other way, including calls ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], |
︙ | ︙ | |||
3135 3136 3137 3138 3139 3140 3141 | ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are ** used in the table for brevity and because they are familiar to most ** C programmers. ** | | | | | | | | 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 | ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are ** used in the table for brevity and because they are familiar to most ** C programmers. ** ** Note that when type conversions occur, pointers returned by prior ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or ** sqlite3_column_text16() may be invalidated. ** Type conversions and pointer invalidations might occur ** in the following cases: ** ** <ul> ** <li> The initial content is a BLOB and sqlite3_column_text() or ** sqlite3_column_text16() is called. A zero-terminator might ** need to be added to the string.</li> ** <li> The initial content is UTF-8 text and sqlite3_column_bytes16() or ** sqlite3_column_text16() is called. The content must be converted ** to UTF-16.</li> ** <li> The initial content is UTF-16 text and sqlite3_column_bytes() or ** sqlite3_column_text() is called. The content must be converted ** to UTF-8.</li> ** </ul> ** ** ^Conversions between UTF-16be and UTF-16le are always done in place and do ** not invalidate a prior pointer, though of course the content of the buffer ** that the prior pointer references will have been modified. Other kinds ** of conversion are done in place when it is possible, but sometimes they ** are not possible and in those cases prior pointers are invalidated. ** ** The safest and easiest to remember policy is to invoke these routines ** in one of the following ways: ** ** <ul> ** <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li> ** <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li> ** <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li> ** </ul> ** ** In other words, you should call sqlite3_column_text(), ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result ** into the desired format, then invoke sqlite3_column_bytes() or ** sqlite3_column_bytes16() to find the size of the result. Do not mix calls ** to sqlite3_column_text() or sqlite3_column_blob() with calls to ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() |
︙ | ︙ | |||
3204 3205 3206 3207 3208 3209 3210 | SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. | > | | > | | | > > > | > | < < < > > > > > > | 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 | SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors or ** or if the statement is never been evaluated, then sqlite3_finalize() returns ** SQLITE_OK. ^If the most recent evaluation of statement S failed, then ** sqlite3_finalize(S) returns the appropriate [error code] or ** [extended error code]. ** ** ^The sqlite3_finalize(S) routine can be called at any point during ** the life cycle of [prepared statement] S: ** before statement S is ever evaluated, after ** one or more calls to [sqlite3_reset()], or after any call ** to [sqlite3_step()] regardless of whether or not the statement has ** completed execution. ** ** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. ** ** The application must finalize every [prepared statement] in order to avoid ** resource leaks. It is a grievous error for the application to try to use ** a prepared statement after it has been finalized. Any use of a prepared ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object ** ** The sqlite3_reset() function is called to reset a [prepared statement] |
︙ | ︙ | |||
3250 3251 3252 3253 3254 3255 3256 | /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** | | | > | < | > | | | > | | | | | | | | | > > > > > > > > < < < < < | 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 | /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** ** ^These functions (collectively known as "function creation routines") ** 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) ** and the presence or absence of a destructor callback for ** the application data pointer. ** ** ^The first parameter is the [database connection] to which the SQL ** function is to be added. ^If an application uses more than one database ** connection then application-defined SQL functions must be added ** to each database connection separately. ** ** ^The second parameter is the name of the SQL function to be created or ** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 ** representation, exclusive of the zero-terminator. ^Note that the name ** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. ** ^Any attempt to create a function with a longer name ** will result in [SQLITE_MISUSE] being returned. ** ** ^The third parameter (nArg) ** is the number of arguments that the SQL function or ** aggregate takes. ^If this parameter is -1, then the SQL function or ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is ** undefined. ** ** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. Every SQL function implementation must be able to work ** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be ** more efficient with one encoding than another. ^An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. ** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** If there is only a single implementation which does not care what text ** encoding is used, then the fourth argument should be [SQLITE_ANY]. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** ^The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** 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 ** callbacks. ** ** ^If the tenth parameter to sqlite3_create_function_v2() is not NULL, ** then it is invoked when the function is deleted, either by being ** overloaded or when the database connection closes. ** ^When the destructure callback of the tenth parameter is invoked, it ** is passed a single argument which is a copy of the pointer which was ** the fifth parameter to sqlite3_create_function_v2(). ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. ^SQLite will use ** the implementation that most closely matches the way in which the ** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with ** a negative nArg. ^A function where the preferred text encoding ** matches the database encoding is a better ** match than a function where the encoding is different. ** ^A function where the encoding difference is between UTF16le and UTF16be ** is a closer match than a function where the encoding difference is ** between UTF8 and UTF16. ** ** ^Built-in functions may be overloaded by new application-defined functions. ** ** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ SQLITE_API int sqlite3_create_function( |
︙ | ︙ | |||
3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 | const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. | > > > > > > > > > > > | 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 | const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); SQLITE_API int sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. |
︙ | ︙ | |||
3438 3439 3440 3441 3442 3443 3444 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** | | | 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. ** ** ^The first time the sqlite3_aggregate_context(C,N) routine is called ** for a particular aggregate function, SQLite ** allocates N of memory, zeroes out that memory, and returns a pointer ** to the new memory. ^On second and subsequent calls to ** sqlite3_aggregate_context() for the same aggregate function instance, |
︙ | ︙ | |||
3690 3691 3692 3693 3694 3695 3696 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** | | | | | | > | > > | < < > | < | < > > > > | > > > > > | > > > | < | < | < < | | > | | > > > > > > > > > > > > > > > > > | | < < | | | | | | | 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. ** ** ^The name of the collation is a UTF-8 string ** for sqlite3_create_collation() and sqlite3_create_collation_v2() ** and a UTF-16 string in native byte order for sqlite3_create_collation16(). ** ^Collation names that compare equal according to [sqlite3_strnicmp()] are ** considered to be the same name. ** ** ^(The third argument (eTextRep) must be one of the constants: ** <ul> ** <li> [SQLITE_UTF8], ** <li> [SQLITE_UTF16LE], ** <li> [SQLITE_UTF16BE], ** <li> [SQLITE_UTF16], or ** <li> [SQLITE_UTF16_ALIGNED]. ** </ul>)^ ** ^The eTextRep argument determines the encoding of strings passed ** to the collating function callback, xCallback. ** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep ** force strings to be UTF16 with native byte order. ** ^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 ** through as the first argument to the collating function callback. ** ** ^The fifth argument, xCallback, is a pointer to the collating function. ** ^Multiple collating functions can be registered using the same name but ** with different eTextRep parameters and SQLite will use whichever ** function requires the least amount of data transformation. ** ^If the xCallback argument is NULL then the collating function is ** deleted. ^When all collating functions having the same name are deleted, ** that collation is no longer usable. ** ** ^The collating function callback is invoked with a copy of the pArg ** application data pointer and with two strings in the encoding specified ** 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 ** 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. ** The collating function must obey the following properties for all ** strings A, B, and C: ** ** <ol> ** <li> If A==B then B==A. ** <li> If A==B and B==C then A==C. ** <li> If A<B THEN B>A. ** <li> If A<B and B<C then A<C. ** </ol> ** ** If a collating function fails any of the above constraints and that ** collating function is registered and used, then the behavior of SQLite ** is undefined. ** ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** with the addition that the xDestroy callback is invoked on pArg when ** the collating function is deleted. ** ^Collating functions are deleted when they are overridden by later ** calls to the collation creation functions or when the ** [database connection] is closed using [sqlite3_close()]. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ SQLITE_API int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); SQLITE_API int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); SQLITE_API int sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); /* ** CAPI3REF: Collation Needed Callbacks ** ** ^To avoid having to register all collation sequences before a database |
︙ | ︙ | |||
3841 3842 3843 3844 3845 3846 3847 | const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** | | | | | > > > | 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 | const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** ** The sqlite3_sleep() function causes the current thread to suspend execution ** for at least a number of milliseconds specified in its parameter. ** ** If the operating system does not support sleep requests with ** millisecond time resolution, then the time will be rounded up to ** the nearest second. The number of milliseconds of sleep actually ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ SQLITE_API int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is |
︙ | ︙ | |||
4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 | ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. */ SQLITE_API int sqlite3_release_memory(int); /* ** CAPI3REF: Impose A Limit On Heap Size ** | > > | | > > > > | | < > | < | > > > > > | < > > > | > > > > > > > > > > > > | | > > | > > > > > > | > > > | | < < | < < > | > | 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 | ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. ** ^The sqlite3_release_memory() routine is a no-op returning zero ** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. */ SQLITE_API int sqlite3_release_memory(int); /* ** CAPI3REF: Impose A Limit On Heap Size ** ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the ** soft limit on the amount of heap memory that may be allocated by SQLite. ** ^SQLite strives to keep heap memory utilization below the soft heap ** limit by reducing the number of pages held in the page cache ** as heap memory usages approaches the limit. ** ^The soft heap limit is "soft" because even though SQLite strives to stay ** below the limit, it will exceed the limit rather than generate ** an [SQLITE_NOMEM] error. In other words, the soft heap limit ** is advisory only. ** ** ^The return value from sqlite3_soft_heap_limit64() is the size of ** the soft heap limit prior to the call. ^If the argument N is negative ** then no change is made to the soft heap limit. Hence, the current ** size of the soft heap limit can be determined by invoking ** sqlite3_soft_heap_limit64() with a negative argument. ** ** ^If the argument N is zero then the soft heap limit is disabled. ** ** ^(The soft heap limit is not enforced in the current implementation ** if one or more of following conditions are true: ** ** <ul> ** <li> The soft heap limit is set to zero. ** <li> 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. ** <li> An alternative page cache implementation is specifed using ** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). ** <li> The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. ** </ul>)^ ** ** Beginning with SQLite version 3.7.3, the soft heap limit is enforced ** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] ** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], ** the soft heap limit is enforced on every memory allocation. Without ** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced ** when memory is allocated by the page cache. Testing suggests that because ** the page cache is the predominate memory user in SQLite, most ** applications will achieve adequate soft heap limit enforcement without ** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. ** ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface ** DEPRECATED ** ** This is a deprecated version of the [sqlite3_soft_heap_limit64()] ** interface. This routine is provided for historical compatibility ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table ** ** ^This routine returns metadata about a specific column of a specific ** database table accessible using the [database connection] handle ** passed as the first function argument. |
︙ | ︙ | |||
4229 4230 4231 4232 4233 4234 4235 | ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* | | | < | > > > > > | > > > | > | > > > | < > > > | | > | < | > > > | < | < < | 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 | ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions ** ** ^This interface causes the xEntryPoint() function to be invoked for ** each new [database connection] that is created. The idea here is that ** xEntryPoint() is the entry point for a statically linked SQLite extension ** that is to be automatically loaded into all new database connections. ** ** ^(Even though the function prototype shows that xEntryPoint() takes ** no arguments and returns void, SQLite invokes xEntryPoint() with three ** arguments and expects and integer result as if the signature of the ** entry point where as follows: ** ** <blockquote><pre> ** int xEntryPoint( ** sqlite3 *db, ** const char **pzErrMsg, ** const struct sqlite3_api_routines *pThunk ** ); ** </pre></blockquote>)^ ** ** If the xEntryPoint routine encounters an error, it should make *pzErrMsg ** point to an appropriate error message (obtained from [sqlite3_mprintf()]) ** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg ** is NULL before calling the xEntryPoint(). ^SQLite will invoke ** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any ** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], ** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. ** ** ^Calling sqlite3_auto_extension(X) with an entry point X that is already ** on the list of automatic extensions is a harmless no-op. ^No entry point ** will be called more than once for each database connection that is opened. ** ** See also: [sqlite3_reset_auto_extension()]. */ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading ** ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ SQLITE_API void sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. |
︙ | ︙ | |||
4895 4896 4897 4898 4899 4900 4901 | ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. | | | 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 | ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. ** ^The xMutexInit routine is called by SQLite exactly once for each ** effective call to [sqlite3_initialize()]. ** ** ^The xMutexEnd method defined by this structure is invoked as ** part of system shutdown by the sqlite3_shutdown() function. The ** implementation of this method is expected to release all outstanding ** resources obtained by the mutex methods implementation, especially ** those obtained by the xMutexInit method. ^The xMutexEnd() |
︙ | ︙ | |||
4928 4929 4930 4931 4932 4933 4934 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to | | | 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to ** invoke xMutexInit() multiple times within the same process and without ** intervening calls to xMutexEnd(). Second and subsequent calls to ** xMutexInit() must be no-ops. ** ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory ** allocation for a static mutex. ^However xMutexAlloc() may use SQLite ** memory allocation for a fast or recursive mutex. |
︙ | ︙ | |||
5092 5093 5094 5095 5096 5097 5098 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 | > | | | | 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_SCRATCHMALLOC 18 #define SQLITE_TESTCTRL_LAST 18 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after ** *pHighwater is written. ^(Some parameters do not record the highest ** value. For those parameters ** nothing is written into *pHighwater and the resetFlag is ignored.)^ ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** ** ^The sqlite3_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** This routine is threadsafe but is not atomic. This routine can be ** called while other threads are running the same or different SQLite ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time ** and it is possible that another thread might change the parameter |
︙ | ︙ | |||
5161 5162 5163 5164 5165 5166 5167 | ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache | | | 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 | ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** where too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> |
︙ | ︙ | |||
5184 5185 5186 5187 5188 5189 5190 | ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory | | | 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 | ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory ** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values ** returned include overflows because the requested allocation was too ** larger (that is, because the requested allocation was larger than the ** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer ** slots were available. ** </dd>)^ ** |
︙ | ︙ | |||
5224 5225 5226 5227 5228 5229 5230 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that | | > > > | 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that ** determines the parameter to interrogate. The set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections |
︙ | ︙ | |||
5359 5360 5361 5362 5363 5364 5365 | /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an | | > | | | | > > > > | > | > > > | | > | | | | | | > > | | | | | > | | | | | | | | | > | | < < | | < | > | | | | | | | | | 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 | /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an ** instance of the sqlite3_pcache_methods structure.)^ ** In many applications, most of the heap memory allocated by ** SQLite is used for the page cache. ** By implementing a ** custom page cache using this API, an application can better control ** the amount of memory consumed by SQLite, the way in which ** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** ** The alternative page cache mechanism is an ** extreme measure that is only needed by the most demanding applications. ** The built-in page cache is recommended for most uses. ** ** ^(The contents of the sqlite3_pcache_methods structure are copied to an ** internal buffer by SQLite within the call to [sqlite3_config]. Hence ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** ** ^(The xInit() method is called once for each effective ** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() ** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ ** The intent of the xInit() method is to set up global data structures ** required by the custom page cache implementation. ** ^(If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache.)^ ** ** ^The xShutdown() method is called by [sqlite3_shutdown()]. ** It can be used to clean up ** any outstanding resources before process shutdown, if required. ** ^The xShutdown() method may be NULL. ** ** ^SQLite automatically serializes calls to the xInit method, ** so the xInit method need not be threadsafe. ^The ** xShutdown method is only called from [sqlite3_shutdown()] so it does ** not need to be threadsafe either. All other methods must be threadsafe ** in multithreaded applications. ** ** ^SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). ** ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must ** be allocated by the cache. ^szPage will not be a power of two. ^szPage ** will the page size of the database file that is to be cached plus an ** increment (here called "R") of about 100 or 200. SQLite will use the ** extra R bytes on each page to store metadata about the underlying ** database page on disk. The value of R depends ** on the SQLite version, the target platform, and how SQLite was compiled. ** ^R is constant for a particular build of SQLite. ^The second argument to ** xCreate(), bPurgeable, is true if the cache being created will ** be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation ** does not have to do anything special based with the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to ** false will always have the "discard" flag set to true. ** ^Hence, a cache created with bPurgeable false will ** never contain any unpinned pages. ** ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** ** The xPagecount() method must return the number of pages currently ** stored in the cache, both pinned and unpinned. ** ** The xFetch() method locates a page in the cache and returns a pointer to ** the page, or a NULL pointer. ** A "page", in this context, means a buffer of szPage bytes aligned at an ** 8-byte boundary. The page to be fetched is determined by the key. ^The ** mimimum key value is 1. After it has been retrieved using xFetch, the page ** is considered to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the ** behavior of the cache implementation should use the value of the createFlag ** parameter to help it determined what action to take: ** ** <table border=1 width=85% align=center> ** <tr><th> createFlag <th> Behaviour when page is not already in cache ** <tr><td> 0 <td> Do not allocate a new page. Return NULL. ** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so. ** Otherwise return NULL. ** <tr><td> 2 <td> Make every effort to allocate a new page. Only return ** NULL if allocating a new page is effectively impossible. ** </table> ** ** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite ** will only use a createFlag of 2 after a prior call with a createFlag of 1 ** failed.)^ In between the to xFetch() calls, SQLite may ** attempt to unpin one or more cache pages by spilling the content of ** pinned pages to disk and synching the operating system disk cache. ** ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. If the third parameter, discard, is non-zero, ** then the page must be evicted from the cache. ** ^If the discard parameter is ** zero, then the page may be discarded or retained at the discretion of ** page cache implementation. ^The page cache implementation ** may choose to evict unpinned pages at any time. ** ** The cache must not perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch(). ** ** The xRekey() method is used to change the key value associated with the ** page passed as the second argument. If the cache ** previously contains an entry associated with newKey, it must be ** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** ** When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal ** to the value of the iLimit parameter passed to xTruncate(). If any ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] ** handle invalid, and will not use it with any other sqlite3_pcache_methods |
︙ | ︙ | |||
5646 5647 5648 5649 5650 5651 5652 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed | | | 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed ** up and the total number of pages in the source database file. ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces ** retrieve these two values, respectively. ** ** ^The values returned by these functions are only updated by ** sqlite3_backup_step(). ^If the source database is modified during a backup ** operation, then the values are not updated to account for any extra ** pages that need to be updated or the size of the source database file |
︙ | ︙ | |||
5742 5743 5744 5745 5746 5747 5748 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing | | | 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing ** unlock-notify callback is canceled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** |
︙ | ︙ | |||
5824 5825 5826 5827 5828 5829 5830 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a | | | 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a ** case-independent fashion, using the same definition of case independence ** that SQLite uses internally when comparing identifiers. */ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: Error Logging Interface ** |
︙ | ︙ | |||
5948 5949 5950 5951 5952 5953 5954 | #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 | #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif /* ** 2010 August 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. ** ************************************************************************* */ #ifndef _SQLITE3RTREE_H_ #define _SQLITE3RTREE_H_ #ifdef __cplusplus extern "C" { #endif typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; /* ** Register a geometry callback named zGeom that can be used as part of an ** R-Tree geometry query as follows: ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...) */ SQLITE_API int sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes), void *pContext ); /* ** A pointer to a structure of the following type is passed as the first ** argument to callbacks registered using rtree_geometry_callback(). */ struct sqlite3_rtree_geometry { void *pContext; /* Copy of pContext passed to s_r_g_c() */ int nParam; /* Size of array aParam[] */ double *aParam; /* Parameters passed to SQL geom function */ void *pUser; /* Callback implementation user data */ void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ }; #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* ifndef _SQLITE3RTREE_H_ */ |
Changes to src/stat.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | /* ** WEBPAGE: stat ** ** Show statistics and global information about the repository. */ void stat_page(void){ | | | > > | > | > > | > > > > > | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | /* ** WEBPAGE: stat ** ** Show statistics and global information about the repository. */ void stat_page(void){ i64 t, fsize; int n, m; int szMax, szAvg; char zBuf[100]; login_check_credentials(); if( !g.okRead ){ login_needed(); return; } style_header("Repository Statistics"); @ <table class="label-value"> @ <tr><th>Repository Size:</th><td> fsize = file_size(g.zRepositoryName); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", fsize); @ %s(zBuf) bytes @ </td></tr> @ <tr><th>Number Of Artifacts:</th><td> n = db_int(0, "SELECT count(*) FROM blob"); m = db_int(0, "SELECT count(*) FROM delta"); @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs) @ </td></tr> if( n>0 ){ int a, b; Stmt q; @ <tr><th>Uncompressed Artifact Size:</th><td> db_prepare(&q, "SELECT total(size), avg(size), max(size)" " FROM blob WHERE size>0"); db_step(&q); t = db_column_int64(&q, 0); szAvg = db_column_int(&q, 1); szMax = db_column_int(&q, 2); db_finalize(&q); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t); @ %d(szAvg) bytes average, %d(szMax) bytes max, %s(zBuf) bytes total @ </td></tr> @ <tr><th>Compression Ratio:</th><td> if( t/fsize < 5 ){ b = 10; fsize /= 10; }else{ b = 1; |
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | " WHERE +tagname GLOB 'tkt-*'"); @ %d(n) @ </td></tr> @ <tr><th>Duration Of Project:</th><td> n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" " + 0.99"); @ %d(n) days @ </td></tr> @ <tr><th>Project ID:</th><td> @ %h(db_get("project-code","")) @ </td></tr> @ <tr><th>Server ID:</th><td> @ %h(db_get("server-code","")) @ </td></tr> @ <tr><th>Fossil Version:</th><td> | > > | | | > | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | " WHERE +tagname GLOB 'tkt-*'"); @ %d(n) @ </td></tr> @ <tr><th>Duration Of Project:</th><td> n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" " + 0.99"); @ %d(n) days sqlite3_snprintf(sizeof(zBuf), zBuf, "%.2f", n/365.24); @ or approximately %s(zBuf) years @ </td></tr> @ <tr><th>Project ID:</th><td> @ %h(db_get("project-code","")) @ </td></tr> @ <tr><th>Server ID:</th><td> @ %h(db_get("server-code","")) @ </td></tr> @ <tr><th>Fossil Version:</th><td> @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) (%h(COMPILER_NAME)) @ </td></tr> @ <tr><th>SQLite Version:</th><td> sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)", SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION); @ %s(zBuf) @ </td></tr> @ <tr><th>Database Stats:</th><td> @ %d(db_int(0, "PRAGMA %s.page_count", g.zRepoDb)) pages, @ %d(db_int(0, "PRAGMA %s.page_size", g.zRepoDb)) bytes/page, @ %d(db_int(0, "PRAGMA %s.freelist_count", g.zRepoDb)) free pages, @ %s(db_text(0, "PRAGMA %s.encoding", g.zRepoDb)), @ %s(db_text(0, "PRAGMA %s.journal_mode", g.zRepoDb)) mode @ </td></tr> @ </table> style_footer(); } |
Changes to src/style.c.
︙ | ︙ | |||
38 39 40 41 42 43 44 45 46 47 48 49 50 51 | /* ** Remember that the header has been generated. The footer is omitted ** if an error occurs before the header. */ static int headerHasBeenGenerated = 0; /* ** Add a new element to the submenu */ void style_submenu_element( const char *zLabel, const char *zTitle, const char *zLink, | > > > > > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /* ** Remember that the header has been generated. The footer is omitted ** if an error occurs before the header. */ static int headerHasBeenGenerated = 0; /* ** remember, if a sidebox was used */ static int sideboxUsed = 0; /* ** Add a new element to the submenu */ void style_submenu_element( const char *zLabel, const char *zTitle, const char *zLink, |
︙ | ︙ | |||
81 82 83 84 85 86 87 | va_start(ap, zTitleFormat); zTitle = vmprintf(zTitleFormat, ap); va_end(ap); cgi_destination(CGI_HEADER); cgi_printf("%s", | | > > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | va_start(ap, zTitleFormat); zTitle = vmprintf(zTitleFormat, ap); va_end(ap); cgi_destination(CGI_HEADER); cgi_printf("%s", "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" " \"http://www.x3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1); /* Generate the header up through the main menu */ Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); Th_Store("title", zTitle); Th_Store("baseurl", g.zBaseURL); Th_Store("index_page", db_get("index-page","/home")); Th_Store("current_page", g.zPath); Th_Store("manifest_version", MANIFEST_VERSION); Th_Store("manifest_date", MANIFEST_DATE); Th_Store("compiler_name", COMPILER_NAME); if( g.zLogin ){ Th_Store("login", g.zLogin); } if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); Th_Render(zHeader); if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1); Th_Unstore("title"); /* Avoid collisions with ticket field names */ cgi_destination(CGI_BODY); g.cgiOutput = 1; headerHasBeenGenerated = 1; sideboxUsed = 0; } /* ** Draw the footer at the bottom of the page. */ void style_footer(void){ const char *zFooter; |
︙ | ︙ | |||
136 137 138 139 140 141 142 | } } @ </div> } @ <div class="content"> cgi_destination(CGI_BODY); | > | > > | > > | | | > | < < | | | | | | | | | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | } } @ </div> } @ <div class="content"> cgi_destination(CGI_BODY); if (sideboxUsed) { /* Put the footer at the bottom of the page. ** the additional clear/both is needed to extend the content ** part to the end of an optional sidebox. */ @ <div class="endContent"></div> } @ </div> zFooter = db_get("footer", (char*)zDefaultFooter); if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); Th_Render(zFooter); if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); /* Render trace log if TH1 tracing is enabled. */ if( g.thTrace ){ cgi_append_content("<span class=\"thTrace\"><hr />\n", -1); cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog)); cgi_append_content("</span>\n", -1); } } /* ** Begin a side-box on the right-hand side of a page. The title and ** the width of the box are given as arguments. The width is usually ** a percentage of total screen width. */ void style_sidebox_begin(const char *zTitle, const char *zWidth){ sideboxUsed = 1; @ <div class="sidebox" style="width:%s(zWidth)"> @ <div class="sideboxTitle">%h(zTitle)</div> } /* End the side-box */ void style_sidebox_end(void){ @ </div> } /* @-comment: // */ /* ** The default page header. */ const char zDefaultHeader[] = @ <html> @ <head> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" @ href="$baseurl/timeline.rss" /> @ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css" @ media="screen" /> @ </head> @ <body> @ <div class="header"> @ <div class="logo"> @ <img src="$baseurl/logo" alt="logo" /> @ </div> @ <div class="title"><small>$<project_name></small><br />$<title></div> @ <div class="status"><th1> @ if {[info exists login]} { @ puts "Logged in as $login" @ } else { @ puts "Not logged in" @ } @ </th1></div> @ </div> @ <div class="mainmenu"><th1> @ html "<a href='$baseurl$index_page'>Home</a> " @ if {[anycap jor]} { @ html "<a href='$baseurl/timeline'>Timeline</a> " @ } @ if {[hascap oh]} { |
︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | @ Fossil version $manifest_version $manifest_date @ </div> @ </body></html> ; /* ** The default Cascading Style Sheet. */ const char zDefaultCSS[] = @ /* General settings for the entire page */ @ body { @ margin: 0ex 1ex; @ padding: 0px; @ background-color: white; | > > > > | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | @ Fossil version $manifest_version $manifest_date @ </div> @ </body></html> ; /* ** The default Cascading Style Sheet. ** It's assembled by different strings for each class. ** The default css conatains all definitions. ** The style sheet, send to the client only contains the ones, ** not defined in the user defined css. */ const char zDefaultCSS[] = @ /* General settings for the entire page */ @ body { @ margin: 0ex 1ex; @ padding: 0px; @ background-color: white; |
︙ | ︙ | |||
270 271 272 273 274 275 276 | @ display: table-cell; @ font-size: 2em; @ font-weight: bold; @ text-align: center; @ padding: 0 0 0 1em; @ color: #558195; @ vertical-align: bottom; | | > | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | @ display: table-cell; @ font-size: 2em; @ font-weight: bold; @ text-align: center; @ padding: 0 0 0 1em; @ color: #558195; @ vertical-align: bottom; @ width: 100% ; @ } @ @ /* The login status message in the top right-hand corner */ @ div.status { @ display: table-cell; @ text-align: right; @ vertical-align: bottom; @ color: #558195; @ font-size: 0.8em; @ font-weight: bold; @ min-width: 200px; @ white-space: nowrap; @ } @ @ /* The header across the top of the page */ @ div.header { @ display: table; @ width: 100% ; @ } @ @ /* The main menu bar that appears at the top of the page beneath @ ** the header */ @ div.mainmenu { @ padding: 5px 10px 5px 10px; @ font-size: 0.9em; |
︙ | ︙ | |||
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | @ margin-bottom: 0px; @ margin-top: 1em; @ padding: 1px 1px 1px 1px; @ font-size: 1.2em; @ font-weight: bold; @ background-color: #558195; @ color: white; @ } @ @ /* The "Date" that occurs on the left hand side of timelines */ @ div.divider { @ background: #a1c4d4; @ border: 2px #558195 solid; @ font-size: 1em; font-weight: normal; @ padding: .25em; @ margin: .2em 0 .2em 0; @ float: left; @ clear: left; @ } @ @ /* The footer at the very bottom of the page */ @ div.footer { @ font-size: 0.8em; @ margin-top: 12px; @ padding: 5px 10px 5px 10px; @ text-align: right; @ background-color: #558195; @ color: white; @ } @ @ /* Hyperlink colors in the footer */ @ div.footer a { color: white; } @ div.footer a:link { color: white; } @ div.footer a:visited { color: white; } @ div.footer a:hover { background-color: white; color: #558195; } @ | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | | | | < | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 | @ margin-bottom: 0px; @ margin-top: 1em; @ padding: 1px 1px 1px 1px; @ font-size: 1.2em; @ font-weight: bold; @ background-color: #558195; @ color: white; @ white-space: nowrap; @ } @ @ /* The "Date" that occurs on the left hand side of timelines */ @ div.divider { @ background: #a1c4d4; @ border: 2px #558195 solid; @ font-size: 1em; font-weight: normal; @ padding: .25em; @ margin: .2em 0 .2em 0; @ float: left; @ clear: left; @ white-space: nowrap; @ } @ @ /* The footer at the very bottom of the page */ @ div.footer { @ clear: both; @ font-size: 0.8em; @ margin-top: 12px; @ padding: 5px 10px 5px 10px; @ text-align: right; @ background-color: #558195; @ color: white; @ } @ @ /* Hyperlink colors in the footer */ @ div.footer a { color: white; } @ div.footer a:link { color: white; } @ div.footer a:visited { color: white; } @ div.footer a:hover { background-color: white; color: #558195; } @ @ /* verbatim blocks */ @ pre.verbatim { @ background-color: #f5f5f5; @ padding: 0.5em; @} @ @ /* The label/value pairs on (for example) the ci page */ @ table.label-value th { @ vertical-align: top; @ text-align: right; @ padding: 0.2ex 2ex; @ } @ ; /* The following table contains bits of default CSS that must ** be included if they are not found in the application-defined ** CSS. */ const struct strctCssDefaults { char const * const elementClass; /* Name of element needed */ char const * const comment; /* Comment text */ char const * const value; /* CSS text */ } cssDefaultList[] = { { "", "", zDefaultCSS }, { "div.sidebox", "The nomenclature sidebox for branches,..", @ float: right; @ background-color: white; @ border-width: medium; @ border-style: double; @ margin: 10; }, { "div.sideboxTitle", "The nomenclature title in sideboxes for branches,..", @ display: inline; @ font-weight: bold; }, { "div.sideboxDescribed", "The defined element in sideboxes for branches,..", @ display: inline; @ font-weight: bold; }, { "span.disabled", "The defined element in sideboxes for branches,..", @ color: red; }, { "span.timelineDisabled", "The suppressed duplicates lines in timeline, ..", @ font-style: italic; @ font-size: small; }, { "table.timelineTable", "the format for the timeline data table", @ cellspacing: 0; @ border: 0; @ cellpadding: 0 }, { "td.timelineTableCell", "the format for the timeline data cells", @ valign: top; @ align: left; }, { "span.timelineLeaf", "the format for the timeline leaf marks", @ font-weight: bold; }, { "a.timelineHistLink", "the format for the timeline version links", @ }, { "span.timelineHistDsp", "the format for the timeline version display(no history permission!)", @ font-weight: bold; }, { "td.timelineTime", "the format for the timeline time display", @ vertical-align: top; @ text-align: right; }, { "td.timelineGraph", "the format for the grap placeholder cells in timelines", @ width: 20; @ text-align: left; @ vertical-align: top; }, { "a.tagLink", "the format for the tag links", @ }, { "span.tagDsp", "the format for the tag display(no history permission!)", @ font-weight: bold; }, { "span.wikiError", "the format for wiki errors", @ font-weight: bold; @ color: red; }, { "span.infoTagCancelled", "the format for fixed/canceled tags,..", @ font-weight: bold; @ text-decoration: line-through; }, { "span.infoTag", "the format for tags,..", @ font-weight: bold; }, { "span.wikiTagCancelled", "the format for fixed/cancelled tags,.. on wiki pages", @ text-decoration: line-through; }, { "table.browser", "format for the file display table", @ /* the format for wiki errors */ @ width: 100% ; @ border: 0; }, { "td.browser", "format for cells in the file browser", @ width: 24% ; @ vertical-align: top; }, { "ul.browser", "format for the list in the file browser", @ margin-left: 0.5em; @ padding-left: 0.5em; }, { "table.login_out", "table format for login/out label/input table", @ text-align: left; @ margin-right: 10px; @ margin-left: 10px; @ margin-top: 10px; }, { "div.captcha", "captcha display options", @ text-align: center; }, { "table.captcha", "format for the layout table, used for the captcha display", @ margin: auto; @ padding: 10px; @ border-width: 4px; @ border-style: double; @ border-color: black; }, { "td.login_out_label", "format for the label cells in the login/out table", @ text-align: center; }, { "span.loginError", "format for login error messages", @ color: red; }, { "span.note", "format for leading text for notes", @ font-weight: bold; }, { "span.textareaLabel", "format for textare labels", @ font-weight: bold; }, { "table.usetupLayoutTable", "format for the user setup layout table", @ outline-style: none; @ padding: 0; @ margin: 25px; }, { "td.usetupColumnLayout", "format of the columns on the user setup list page", @ vertical-align: top }, { "table.usetupUserList", "format for the user list table on the user setup page", @ outline-style: double; @ outline-width: 1; @ padding: 10px; }, { "th.usetupListUser", "format for table header user in user list on user setup page", @ text-align: right; @ padding-right: 20px; }, { "th.usetupListCap", "format for table header capabilities in user list on user setup page", @ text-align: center; @ padding-right: 15px; }, { "th.usetupListCon", "format for table header contact info in user list on user setup page", @ text-align: left; }, { "td.usetupListUser", "format for table cell user in user list on user setup page", @ text-align: right; @ padding-right: 20px; @ white-space:nowrap; }, { "td.usetupListCap", "format for table cell capabilities in user list on user setup page", @ text-align: center; @ padding-right: 15px; }, { "td.usetupListCon", "format for table cell contact info in user list on user setup page", @ text-align: left }, { "div.ueditCapBox", "layout definition for the capabilities box on the user edit detail page", @ float: left; @ margin-right: 20px; @ margin-bottom: 20px; }, { "td.usetupEditLabel", "format of the label cells in the detailed user edit page", @ text-align: right; @ vertical-align: top; @ white-space: nowrap; }, { "span.ueditInheritNobody", "color for capabilities, inherited by nobody", @ color: green; }, { "span.ueditInheritDeveloper", "color for capabilities, inherited by developer", @ color: red; }, { "span.ueditInheritReader", "color for capabilities, inherited by reader", @ color: black; }, { "span.ueditInheritAnonymous", "color for capabilities, inherited by anonymous", @ color: blue; }, { "span.capability", "format for capabilites, mentioned on the user edit page", @ font-weight: bold; }, { "span.usertype", "format for different user types, mentioned on the user edit page", @ font-weight: bold; }, { "span.usertype:before", "leading text for user types, mentioned on the user edit page", @ content:"'"; }, { "span.usertype:after", "trailing text for user types, mentioned on the user edit page", @ content:"'"; }, { "p.missingPriv", "format for missing priviliges note on user setup page", @ color: blue; }, { "span.wikiruleHead", "format for leading text in wikirules definitions", @ font-weight: bold; }, { "td.tktDspLabel", "format for labels on ticket display page", @ text-align: right; }, { "td.tktDspValue", "format for values on ticket display page", @ text-align: left; @ vertical-align: top; @ background-color: #d0d0d0; }, { "span.tktError", "format for ticket error messages", @ color: red; @ font-weight: bold; }, { "table.rpteditex", "format for example tables on the report edit page", @ float: right; @ margin: 0; @ padding: 0; @ width: 125px; @ text-align: center; @ border-collapse: collapse; @ border-spacing: 0; }, { "td.rpteditex", "format for example table cells on the report edit page", @ border-width: thin; @ border-color: #000000; @ border-style: solid; }, { "input.checkinUserColor", "format for user color input on checkin edit page", @ # no special definitions, class defined, to enable color pickers, f.e.: @ # add the color picker found at http:jscolor.com as java script include @ # to the header and configure the java script file with @ # 1. use as bindClass :checkinUserColor @ # 2. change the default hash adding behaviour to ON @ # or change the class defition of element identified by id="clrcust" @ # to a standard jscolor definition with java script in the footer. }, { "div.endContent", "format for end of content area, to be used to clear page flow(sidebox on branch,..", @ clear: both; }, { "p.generalError", "format for general errors", @ color: red; }, { "p.tktsetupError", "format for tktsetup errors", @ color: red; @ font-weight: bold; }, { "p.thmainError", "format for th script errors", @ color: red; @ font-weight: bold; }, { "span.thTrace", "format for th script trace messages", @ color: red; }, { "p:reportError", "format for report configuration errors", @ color: red; @ font-weight: bold; }, { "blockquote.reportError", "format for report configuration errors", @ color: red; @ font-weight: bold; }, { "p.noMoreShun", "format for artifact lines, no longer shunned", @ color: blue; }, { "p.shunned", "format for artifact lines beeing shunned", @ color: blue; }, { 0, 0, 0 } }; /* ** Append all of the default CSS to the CGI output. */ void cgi_append_default_css(void) { int i; for (i=0;cssDefaultList[i].elementClass;i++){ if (cssDefaultList[i].elementClass[0]){ cgi_printf("/* %s */\n%s {\n%s\n}\n\n", cssDefaultList[i].comment, cssDefaultList[i].elementClass, cssDefaultList[i].value ); }else{ cgi_printf("%s", cssDefaultList[i].value ); } } } /* ** WEBPAGE: style.css */ void page_style_css(void){ const char *zCSS = 0; int i; cgi_set_content_type("text/css"); zCSS = db_get("css",(char*)zDefaultCSS); /* append user defined css */ cgi_append_content(zCSS, -1); /* add special missing definitions */ for (i=1;cssDefaultList[i].elementClass;i++) if (!strstr(zCSS,cssDefaultList[i].elementClass)) { cgi_append_content("/* ", -1); cgi_append_content(cssDefaultList[i].comment, -1); cgi_append_content(" */\n", -1); cgi_append_content(cssDefaultList[i].elementClass, -1); cgi_append_content(" {\n", -1); cgi_append_content(cssDefaultList[i].value, -1); cgi_append_content("}\n\n", -1); } g.isConst = 1; } /* ** WEBPAGE: test_env */ void page_test_env(void){ style_header("Environment Test"); #if !defined(_WIN32) @ uid=%d(getuid()), gid=%d(getgid())<br /> #endif @ g.zBaseURL = %h(g.zBaseURL)<br /> @ g.zTop = %h(g.zTop)<br /> cgi_print_all(); style_footer(); } |
Changes to src/sync.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 | }else{ /* Autosync defaults on. To make it default off, "return" here. */ } zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ return; /* No default server */ } | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | }else{ /* Autosync defaults on. To make it default off, "return" here. */ } zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ return; /* No default server */ } zPw = unobscure(db_get("last-sync-pw", 0)); url_parse(zUrl); if( g.urlUser!=0 && g.urlPasswd==0 ){ g.urlPasswd = mprintf("%s", zPw); } if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ /* When doing an automatic pull, also automatically pull shuns from ** the server if pull_shuns is enabled. |
︙ | ︙ | |||
91 92 93 94 95 96 97 | int urlOptional = find_option("autourl",0,0)!=0; g.dontKeepUrl = find_option("once",0,0)!=0; url_proxy_options(); db_find_and_open_repository(1); db_open_config(0); if( g.argc==2 ){ zUrl = db_get("last-sync-url", 0); | | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | int urlOptional = find_option("autourl",0,0)!=0; g.dontKeepUrl = find_option("once",0,0)!=0; url_proxy_options(); db_find_and_open_repository(1); db_open_config(0); if( g.argc==2 ){ zUrl = db_get("last-sync-url", 0); zPw = unobscure(db_get("last-sync-pw", 0)); if( db_get_boolean("auto-sync",1) ) configSync = CONFIGSET_SHUN; }else if( g.argc==3 ){ zUrl = g.argv[2]; } if( zUrl==0 ){ if( urlOptional ) fossil_exit(0); usage("URL"); } url_parse(zUrl); if( !g.dontKeepUrl ){ db_set("last-sync-url", g.urlCanonical, 0); if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0); } if( g.urlUser!=0 && g.urlPasswd==0 ){ if( zPw==0 ){ url_prompt_for_password(); }else{ g.urlPasswd = mprintf("%s", zPw); } |
︙ | ︙ | |||
230 231 232 233 234 235 236 | }else{ url_parse(g.argv[2]); if( g.urlUser && g.urlPasswd==0 ){ url_prompt_for_password(); } db_set("last-sync-url", g.urlCanonical, 0); if( g.urlPasswd ){ | | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | }else{ url_parse(g.argv[2]); if( g.urlUser && g.urlPasswd==0 ){ url_prompt_for_password(); } db_set("last-sync-url", g.urlCanonical, 0); if( g.urlPasswd ){ db_set("last-sync-pw", obscure(g.urlPasswd), 0); }else{ db_unset("last-sync-pw", 0); } } } zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ |
︙ | ︙ |
Changes to src/tag.c.
︙ | ︙ | |||
256 257 258 259 260 261 262 | ** or cancels a tag. */ void tag_add_artifact( const char *zPrefix, /* Prefix to prepend to tag name */ const char *zTagname, /* The tag to add or cancel */ const char *zObjName, /* Name of object attached to */ const char *zValue, /* Value for the tag. Might be NULL */ | | > > | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | ** or cancels a tag. */ void tag_add_artifact( const char *zPrefix, /* Prefix to prepend to tag name */ const char *zTagname, /* The tag to add or cancel */ const char *zObjName, /* Name of object attached to */ const char *zValue, /* Value for the tag. Might be NULL */ int tagtype, /* 0:cancel 1:singleton 2:propagated */ const char *zDateOvrd, /* Override date string */ const char *zUserOvrd /* Override user name */ ){ int rid; int nrid; char *zDate; Blob uuid; Blob ctrl; Blob cksum; |
︙ | ︙ | |||
287 288 289 290 291 292 293 | fossil_fatal( "invalid tag name \"%s\" - might be confused with" " a hexadecimal artifact ID", zTagname ); } #endif | | | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | fossil_fatal( "invalid tag name \"%s\" - might be confused with" " a hexadecimal artifact ID", zTagname ); } #endif zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); zDate[10] = 'T'; blob_appendf(&ctrl, "D %s\n", zDate); blob_appendf(&ctrl, "T %c%s%F %b", zTagtype[tagtype], zPrefix, zTagname, &uuid); if( tagtype>0 && zValue && zValue[0] ){ blob_appendf(&ctrl, " %F\n", zValue); }else{ blob_appendf(&ctrl, "\n"); } blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); md5sum_blob(&ctrl, &cksum); blob_appendf(&ctrl, "Z %b\n", &cksum); nrid = content_put(&ctrl, 0, 0); manifest_crosslink(nrid, &ctrl); } /* |
︙ | ︙ | |||
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | ** will be taken as an artifact or baseline ID and fossil will ** probably complain that no such revision was found. However ** ** fossil update tag:decaf ** ** will assume that "decaf" is a tag/branch name. ** */ void tag_cmd(void){ int n; int fRaw = find_option("raw","",0)!=0; int fPropagate = find_option("propagate","",0)!=0; const char *zPrefix = fRaw ? "" : "sym-"; db_find_and_open_repository(1); if( g.argc<3 ){ goto tag_cmd_usage; } n = strlen(g.argv[2]); if( n==0 ){ goto tag_cmd_usage; } if( strncmp(g.argv[2],"add",n)==0 ){ char *zValue; if( g.argc!=5 && g.argc!=6 ){ usage("add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?"); } zValue = g.argc==6 ? g.argv[5] : 0; db_begin_transaction(); | > > > > > > | > | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | ** will be taken as an artifact or baseline ID and fossil will ** probably complain that no such revision was found. However ** ** fossil update tag:decaf ** ** will assume that "decaf" is a tag/branch name. ** ** only allow --date-override and --user-override in ** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \\ ** --user-override user ** in order to import history from other scm systems */ void tag_cmd(void){ int n; int fRaw = find_option("raw","",0)!=0; int fPropagate = find_option("propagate","",0)!=0; const char *zPrefix = fRaw ? "" : "sym-"; db_find_and_open_repository(1); if( g.argc<3 ){ goto tag_cmd_usage; } n = strlen(g.argv[2]); if( n==0 ){ goto tag_cmd_usage; } if( strncmp(g.argv[2],"add",n)==0 ){ char *zValue; const char *zDateOvrd = find_option("date-override",0,1); const char *zUserOvrd = find_option("user-override",0,1); if( g.argc!=5 && g.argc!=6 ){ usage("add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?"); } zValue = g.argc==6 ? g.argv[5] : 0; db_begin_transaction(); tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue, 1+fPropagate,zDateOvrd,zUserOvrd); db_end_transaction(0); }else if( strncmp(g.argv[2],"branch",n)==0 ){ fossil_fatal("the \"fossil tag branch\" command is discontinued\n" "Use the \"fossil branch new\" command instead."); }else if( strncmp(g.argv[2],"cancel",n)==0 ){ if( g.argc!=5 ){ usage("cancel ?--raw? TAGNAME CHECK-IN"); } db_begin_transaction(); tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, 0, 0, 0); db_end_transaction(0); }else if( strncmp(g.argv[2],"find",n)==0 ){ Stmt q; if( g.argc!=4 ){ usage("find ?--raw? TAGNAME"); |
︙ | ︙ | |||
512 513 514 515 516 517 518 | " AND tagname GLOB 'sym-*'" " ORDER BY tagname" ); @ <ul> while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); if( g.okHistory ){ | | > | | 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | " AND tagname GLOB 'sym-*'" " ORDER BY tagname" ); @ <ul> while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); if( g.okHistory ){ @ <li><a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zName)"> @ %h(zName)</a></li> }else{ @ <li><span class="tagDsp">%h(zName)</span></li> } } @ </ul> db_finalize(&q); style_footer(); } |
︙ | ︙ | |||
540 541 542 543 544 545 546 | " AND tagxref.tagtype>0 AND tagxref.srcid>0" " AND tag.tagname GLOB 'sym-*'", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTagName = db_column_text(&q, 0); if( g.okHistory ){ | | > | | 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 | " AND tagxref.tagtype>0 AND tagxref.srcid>0" " AND tag.tagname GLOB 'sym-*'", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTagName = db_column_text(&q, 0); if( g.okHistory ){ @ <a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zTagName)"> @ [%h(zTagName)]</a> }else{ @ <span class="tagDsp">[%h(zTagName)]</span> } } db_finalize(&q); } /* ** WEBPAGE: /tagtimeline |
︙ | ︙ | |||
571 572 573 574 575 576 577 | " AND tagid IN (SELECT tagid FROM tag " " WHERE tagname GLOB 'sym-*'))" " ORDER BY event.mtime DESC", timeline_query_for_www() ); www_print_timeline(&q, 0, tagtimeline_extra); db_finalize(&q); | | | | 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | " AND tagid IN (SELECT tagid FROM tag " " WHERE tagname GLOB 'sym-*'))" " ORDER BY event.mtime DESC", timeline_query_for_www() ); www_print_timeline(&q, 0, tagtimeline_extra); db_finalize(&q); @ <br /> @ <script type="text/JavaScript"> @ function xin(id){ @ } @ function xout(id){ @ } @ </script> style_footer(); } |
Changes to src/th.c.
︙ | ︙ | |||
844 845 846 847 848 849 850 | continue; } /* Gobble up input a word at a time until the end of the command ** (a semi-colon or end of line). */ while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){ | | | 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 | continue; } /* Gobble up input a word at a time until the end of the command ** (a semi-colon or end of line). */ while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){ int nWord=0; thNextSpace(interp, zInput, nInput, &nSpace); rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1); zInput += (nSpace+nWord); nInput -= (nSpace+nWord); } if( rc!=TH_OK ) continue; |
︙ | ︙ |
Changes to src/th_lang.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* ** This file contains the implementation of all of the TH language ** built-in commands. ** ** All built-in commands are implemented using the public interface ** declared in th.h, so this file serves as both a part of the language ** implementation and an example of how to extend the language with ** new commands. */ #include "th.h" #include <string.h> #include <assert.h> int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1); return TH_ERROR; | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** This file contains the implementation of all of the TH language ** built-in commands. ** ** All built-in commands are implemented using the public interface ** declared in th.h, so this file serves as both a part of the language ** implementation and an example of how to extend the language with ** new commands. */ #include "config.h" #include "th.h" #include <string.h> #include <assert.h> int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1); return TH_ERROR; |
︙ | ︙ | |||
569 570 571 572 573 574 575 | ){ if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "return ?value?"); } if( argc==2 ){ Th_SetResult(interp, argv[1], argl[1]); } | | | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 | ){ if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "return ?value?"); } if( argc==2 ){ Th_SetResult(interp, argv[1], argl[1]); } return FOSSIL_PTR_TO_INT(ctx); } /* ** TH Syntax: ** ** return ?-code code? ?value? */ |
︙ | ︙ |
Changes to src/th_main.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 | */ static int nOutstandingMalloc = 0; /* ** Implementations of malloc() and free() to pass to the interpreter. */ static void *xMalloc(unsigned int n){ | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | */ static int nOutstandingMalloc = 0; /* ** Implementations of malloc() and free() to pass to the interpreter. */ static void *xMalloc(unsigned int n){ void *p = fossil_malloc(n); if( p ){ nOutstandingMalloc++; } return p; } static void xFree(void *p){ if( p ){ |
︙ | ︙ | |||
276 277 278 279 280 281 282 | sendText(z, -1, 0); free(z); blob_reset(&name); for(i=0; i<nElem; i++){ zH = htmlize((char*)azElem[i], aszElem[i]); if( zValue && aszElem[i]==nValue && memcmp(zValue, azElem[i], nValue)==0 ){ | | > | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | sendText(z, -1, 0); free(z); blob_reset(&name); for(i=0; i<nElem; i++){ zH = htmlize((char*)azElem[i], aszElem[i]); if( zValue && aszElem[i]==nValue && memcmp(zValue, azElem[i], nValue)==0 ){ z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>", zH, zH); }else{ z = mprintf("<option value=\"%s\">%s</option>", zH, zH); } free(zH); sendText(z, -1, 0); free(z); } |
︙ | ︙ | |||
432 433 434 435 436 437 438 | static int validVarName(const char *z){ int i = 0; int inBracket = 0; if( z[0]=='<' ){ inBracket = 1; z++; } | | | | | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | static int validVarName(const char *z){ int i = 0; int inBracket = 0; if( z[0]=='<' ){ inBracket = 1; z++; } if( z[0]==':' && z[1]==':' && fossil_isalpha(z[2]) ){ z += 3; i += 3; }else if( fossil_isalpha(z[0]) ){ z ++; i += 1; }else{ return 0; } while( fossil_isalnum(z[0]) || z[0]=='_' ){ z++; i++; } if( inBracket ){ if( z[0]!='>' ) return 0; i += 2; } |
︙ | ︙ | |||
501 502 503 504 505 506 507 | if( z[0] ){ z += 6; } i = 0; }else{ i++; } } if( rc==TH_ERROR ){ | | | | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | if( z[0] ){ z += 6; } i = 0; }else{ i++; } } if( rc==TH_ERROR ){ sendText("<hr><p class=\"thmainError\">ERROR: ", -1, 0); zResult = (char*)Th_GetResult(g.interp, &n); sendText((char*)zResult, n, 1); sendText("</p>", -1, 0); }else{ sendText(z, i, 0); } return rc; } /* |
︙ | ︙ |
Changes to src/timeline.c.
︙ | ︙ | |||
46 47 48 49 50 51 52 | /* ** Generate a hyperlink to a version. */ void hyperlink_to_uuid(const char *zUuid){ char zShortUuid[UUID_SIZE+1]; shorten_uuid(zShortUuid, zUuid); if( g.okHistory ){ | | > | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | /* ** Generate a hyperlink to a version. */ void hyperlink_to_uuid(const char *zUuid){ char zShortUuid[UUID_SIZE+1]; shorten_uuid(zShortUuid, zUuid); if( g.okHistory ){ @ <a class="timelineHistLink" href="%s(g.zBaseURL)/info/%s(zShortUuid)"> @ [%s(zShortUuid)]</a> }else{ @ <span class="timelineHistDsp">[%s(zShortUuid)]</span> } } /* ** Generate a hyperlink that invokes javascript to highlight ** a version on mouseover. */ |
︙ | ︙ | |||
81 82 83 84 85 86 87 | ** Generate a hyperlink to a diff between two versions. */ void hyperlink_to_diff(const char *zV1, const char *zV2){ if( g.okHistory ){ if( zV2==0 ){ @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a> }else{ | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | ** Generate a hyperlink to a diff between two versions. */ void hyperlink_to_diff(const char *zV1, const char *zV2){ if( g.okHistory ){ if( zV2==0 ){ @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a> }else{ @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a> } } } /* ** Generate a hyperlink to a date & time. */ |
︙ | ︙ | |||
107 108 109 110 111 112 113 | ** events by that user. If the date+time is specified, then the timeline ** is centered on that date+time. */ void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){ if( zSuf==0 ) zSuf = ""; if( g.okHistory ){ if( zD && zD[0] ){ | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | ** events by that user. If the date+time is specified, then the timeline ** is centered on that date+time. */ void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){ if( zSuf==0 ) zSuf = ""; if( g.okHistory ){ if( zD && zD[0] ){ @ <a href="%s(g.zTop)/timeline?c=%T(zD)&u=%T(zU)">%h(zU)</a>%s(zSuf) }else{ @ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf) } }else{ @ %s(zU) } } |
︙ | ︙ | |||
162 163 164 165 166 167 168 | ** 2. Date/Time ** 3. Comment string ** 4. User ** 5. True if is a leaf ** 6. background color ** 7. type ("ci", "w", "t") ** 8. list of symbolic tags. | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | ** 2. Date/Time ** 3. Comment string ** 4. User ** 5. True if is a leaf ** 6. background color ** 7. type ("ci", "w", "t") ** 8. list of symbolic tags. ** 9. tagid for ticket or wiki or event ** 10. Short comment to user for repeated tickets and wiki */ void www_print_timeline( Stmt *pQuery, /* Query to implement the timeline */ int tmFlags, /* Flags controlling display behavior */ void (*xExtra)(int) /* Routine to call on each line of display */ ){ |
︙ | ︙ | |||
187 188 189 190 191 192 193 194 195 196 | if( db_get_boolean("timeline-block-markup", 0) ){ wikiFlags = WIKI_INLINE; }else{ wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; } if( tmFlags & TIMELINE_GRAPH ){ pGraph = graph_init(); @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> } | > > > | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | if( db_get_boolean("timeline-block-markup", 0) ){ wikiFlags = WIKI_INLINE; }else{ wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; } if( tmFlags & TIMELINE_GRAPH ){ pGraph = graph_init(); /* style is not moved to css, because this is ** a technical div for the timeline graph */ @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> } @ <table class="timelineTable"> blob_zero(&comment); while( db_step(pQuery)==SQLITE_ROW ){ int rid = db_column_int(pQuery, 0); const char *zUuid = db_column_text(pQuery, 1); int isLeaf = db_column_int(pQuery, 5); const char *zBgClr = db_column_text(pQuery, 6); const char *zDate = db_column_text(pQuery, 2); |
︙ | ︙ | |||
216 217 218 219 220 221 222 | }else{ commentColumn = 10; } } } prevTagid = tagid; if( suppressCnt ){ | | | | | | | | | 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | }else{ commentColumn = 10; } } } prevTagid = tagid; if( suppressCnt ){ @ <tr><td /><td /><td> @ <span class="timelineDisabled">... %d(suppressCnt) similar @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr> suppressCnt = 0; } if( strcmp(zType,"div")==0 ){ @ <tr><td colspan="3"><hr /></td></tr> continue; } if( memcmp(zDate, zPrevDate, 10) ){ sprintf(zPrevDate, "%.10s", zDate); @ <tr><td> @ <div class="divider">%s(zPrevDate)</div> @ </td></tr> } memcpy(zTime, &zDate[11], 5); zTime[5] = 0; @ <tr> @ <td class="timelineTime">%s(zTime)</td> @ <td class="timelineGraph"> if( pGraph && zType[0]=='c' ){ int nParent = 0; int aParent[32]; const char *zBr; int gidx; static Stmt qparent; static Stmt qbranch; |
︙ | ︙ | |||
265 266 267 268 269 270 271 272 | }else{ zBr = "trunk"; } gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr); db_reset(&qbranch); @ <div id="m%d(gidx)"></div> } if( zBgClr && zBgClr[0] ){ | > | | | | > > | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | }else{ zBr = "trunk"; } gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr); db_reset(&qbranch); @ <div id="m%d(gidx)"></div> } @</td> if( zBgClr && zBgClr[0] ){ @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> }else{ @ <td class="timelineTableCell"> } if( zType[0]=='c' ){ hyperlink_to_uuid(zUuid); if( isLeaf ){ if( db_exists("SELECT 1 FROM tagxref" " WHERE rid=%d AND tagid=%d AND tagtype>0", rid, TAG_CLOSED) ){ @ <span class="timelineLeaf">Closed-Leaf:</span> }else{ @ <span class="timelineLeaf">Leaf:</span> } } }else if( zType[0]=='e' && tagid ){ hyperlink_to_event_tagid(tagid); }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ hyperlink_to_uuid(zUuid); } db_column_blob(pQuery, commentColumn, &comment); if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ Blob truncated; blob_zero(&truncated); |
︙ | ︙ | |||
307 308 309 310 311 312 313 | } if( xExtra ){ xExtra(rid); } @ </td></tr> } if( suppressCnt ){ | | | | > > > > | > | > | | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | } if( xExtra ){ xExtra(rid); } @ </td></tr> } if( suppressCnt ){ @ <tr><td /><td /><td> @ <span class="timelineDisabled">... %d(suppressCnt) similar @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr> suppressCnt = 0; } if( pGraph ){ graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0); if( pGraph->nErr ){ graph_free(pGraph); pGraph = 0; }else{ /* style is not moved to css, because this is ** a technical div for the timeline graph */ @ <tr><td /><td> @ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div> @ </td></tr> } } @ </table> timeline_output_graph_javascript(pGraph); } /* ** Generate all of the necessary javascript to generate a timeline ** graph. */ void timeline_output_graph_javascript(GraphContext *pGraph){ if( pGraph && pGraph->nErr==0 ){ GraphRow *pRow; int i; char cSep; @ <script type="text/JavaScript"> @ /* <![CDATA[ */ cgi_printf("var rowinfo = [\n"); for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:", pRow->idx, pRow->zBgClr, pRow->iRail, pRow->bDescender, pRow->mergeOut, pRow->mergeUpto, pRow->aiRaiser[pRow->iRail] ); cSep = '['; for(i=0; i<GR_MAX_RAIL; i++){ if( i==pRow->iRail ) continue; if( pRow->aiRaiser[i]>0 ){ cgi_printf("%c%d,%d", cSep, i, pRow->aiRaiser[i]); cSep = ','; } } if( cSep=='[' ) cgi_printf("["); cgi_printf("],mi:"); cSep = '['; for(i=0; i<GR_MAX_RAIL; i++){ if( pRow->mergeIn & (1<<i) ){ cgi_printf("%c%d", cSep, i); cSep = ','; } } if( cSep=='[' ) cgi_printf("["); cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n"); } cgi_printf("var nrail = %d\n", pGraph->mxRail+1); |
︙ | ︙ | |||
480 481 482 483 484 485 486 | @ var canvasY = absoluteY("canvas"); @ var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15; @ var width = nrail*20; @ for(var i in rowinfo){ @ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY; @ rowinfo[i].x = left + rowinfo[i].r*20; @ } | | | | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | @ var canvasY = absoluteY("canvas"); @ var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15; @ var width = nrail*20; @ for(var i in rowinfo){ @ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY; @ rowinfo[i].x = left + rowinfo[i].r*20; @ } @ var btm = absoluteY("grbtm") + 10 - canvasY; @ if( btm<32768 ){ @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+ @ 'style="position:absolute;left:'+(left-5)+'px;"' + @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>'; @ realCanvas = document.getElementById('timeline-canvas'); @ }else{ @ realCanvas = 0; @ } @ var context; @ if( realCanvas && realCanvas.getContext @ && (context = realCanvas.getContext('2d'))) { |
︙ | ︙ | |||
516 517 518 519 520 521 522 523 524 525 526 527 528 529 | @ if( h!=lastY ){ @ renderGraph(); @ lastY = h; @ } @ setTimeout("checkHeight();", 1000); @ } @ checkHeight(); @ </script> } } /* ** Create a temporary table suitable for storing timeline data. */ | > | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 | @ if( h!=lastY ){ @ renderGraph(); @ lastY = h; @ } @ setTimeout("checkHeight();", 1000); @ } @ checkHeight(); @ /* ]]> */ @ </script> } } /* ** Create a temporary table suitable for storing timeline data. */ |
︙ | ︙ | |||
625 626 627 628 629 630 631 | ** c=TIMESTAMP "circa" this date. ** n=COUNT number of events in output ** p=RID artifact RID and up to COUNT parents and ancestors ** d=RID artifact RID and up to COUNT descendants ** t=TAGID show only check-ins with the given tagid ** r=TAGID show check-ins related to tagid ** u=USER only if belonging to this user | | | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | ** c=TIMESTAMP "circa" this date. ** n=COUNT number of events in output ** p=RID artifact RID and up to COUNT parents and ancestors ** d=RID artifact RID and up to COUNT descendants ** t=TAGID show only check-ins with the given tagid ** r=TAGID show check-ins related to tagid ** u=USER only if belonging to this user ** y=TYPE 'ci', 'w', 't', 'e' ** s=TEXT string search (comment and brief) ** ng Suppress the graph if present ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If a= and b= appear, only a= is used. If neither appear, the most |
︙ | ︙ | |||
735 736 737 738 739 740 741 | blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>", g.zBaseURL, zUuid, zUuid); }else{ blob_appendf(&desc, " of check-in [%.10s]", zUuid); } }else{ int n; | | | | > | > > | | | | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 | blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>", g.zBaseURL, zUuid, zUuid); }else{ blob_appendf(&desc, " of check-in [%.10s]", zUuid); } }else{ int n; const char *zEType = "timeline item"; char *zDate; char *zNEntry = mprintf("%d", nEntry); url_initialize(&url, "timeline"); url_add_parameter(&url, "n", zNEntry); if( tagid>0 ){ if( zType[0]!='e' ) zType = "ci"; blob_appendf(&sql, "AND (EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid); if( zBrName && zType[0]=='c' ){ /* The next two blob_appendf() calls add SQL that causes checkins that ** are not part of the branch which are parents or childen of the branch ** to be included in the report. This related check-ins are useful ** in helping to visualize what has happened on a quiescent branch ** that is infrequently merged with a much more activate branch. */ url_add_parameter(&url, "r", zBrName); blob_appendf(&sql, " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid" " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)", tagid); blob_appendf(&sql, " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid" " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)", tagid); }else{ url_add_parameter(&url, "t", zTagName); } blob_appendf(&sql, ")"); } if( (zType[0]=='w' && !g.okRdWiki) || (zType[0]=='t' && !g.okRdTkt) || (zType[0]=='e' && !g.okRdWiki) || (zType[0]=='c' && !g.okRead) ){ zType = "all"; } if( zType[0]=='a' ){ if( !g.okRead || !g.okRdWiki || !g.okRdTkt ){ char cSep = '('; blob_appendf(&sql, " AND event.type IN "); if( g.okRead ){ blob_appendf(&sql, "%c'ci'", cSep); cSep = ','; } if( g.okRdWiki ){ blob_appendf(&sql, "%c'w','e'", cSep); cSep = ','; } if( g.okRdTkt ){ blob_appendf(&sql, "%c't'", cSep); cSep = ','; } blob_appendf(&sql, ")"); } }else{ /* zType!="all" */ blob_appendf(&sql, " AND event.type=%Q", zType); url_add_parameter(&url, "y", zType); if( zType[0]=='c' ){ zEType = "checkin"; }else if( zType[0]=='w' ){ zEType = "wiki edit"; }else if( zType[0]=='t' ){ zEType = "ticket change"; }else if( zType[0]=='e' ){ zEType = "event"; } } if( zUser ){ blob_appendf(&sql, " AND event.user=%Q", zUser); url_add_parameter(&url, "u", zUser); } if ( zSearch ){ blob_appendf(&sql, " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", zSearch, zSearch); url_add_parameter(&url, "s", zSearch); } if( zAfter ){ while( fossil_isspace(zAfter[0]) ){ zAfter++; } if( zAfter[0] ){ blob_appendf(&sql, " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" " ORDER BY event.mtime ASC", zAfter); url_add_parameter(&url, "a", zAfter); zBefore = 0; }else{ zAfter = 0; } }else if( zBefore ){ while( fossil_isspace(zBefore[0]) ){ zBefore++; } if( zBefore[0] ){ blob_appendf(&sql, " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" " ORDER BY event.mtime DESC", zBefore); url_add_parameter(&url, "b", zBefore); }else{ zBefore = 0; } }else if( zCirca ){ while( fossil_isspace(zCirca[0]) ){ zCirca++; } if( zCirca[0] ){ double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca); Blob sql2; blob_init(&sql2, blob_str(&sql), -1); blob_appendf(&sql2, " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d", rCirca, (nEntry+1)/2 |
︙ | ︙ | |||
880 881 882 883 884 885 886 | blob_appendf(&desc, " tagged with \"%h\"", zTagName); tmFlags |= TIMELINE_DISJOINT; }else if( zBrName ){ blob_appendf(&desc, " related to \"%h\"", zBrName); tmFlags |= TIMELINE_DISJOINT; } if( zAfter ){ | | | | | 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 | blob_appendf(&desc, " tagged with \"%h\"", zTagName); tmFlags |= TIMELINE_DISJOINT; }else if( zBrName ){ blob_appendf(&desc, " related to \"%h\"", zBrName); tmFlags |= TIMELINE_DISJOINT; } if( zAfter ){ blob_appendf(&desc, " occurring on or after %h.<br />", zAfter); }else if( zBefore ){ blob_appendf(&desc, " occurring on or before %h.<br />", zBefore); }else if( zCirca ){ blob_appendf(&desc, " occurring around %h.<br />", zCirca); } if( zSearch ){ blob_appendf(&desc, " matching \"%h\"", zSearch); } if( g.okHistory ){ if( zAfter || n==nEntry ){ zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
︙ | ︙ | |||
912 913 914 915 916 917 918 | } if( zType[0]!='c' && g.okRead ){ timeline_submenu(&url, "Checkins Only", "y", "ci", 0); } if( zType[0]!='t' && g.okRdTkt ){ timeline_submenu(&url, "Tickets Only", "y", "t", 0); } | > > | > | | > > > | 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | } if( zType[0]!='c' && g.okRead ){ timeline_submenu(&url, "Checkins Only", "y", "ci", 0); } if( zType[0]!='t' && g.okRdTkt ){ timeline_submenu(&url, "Tickets Only", "y", "t", 0); } if( zType[0]!='e' && g.okRdWiki ){ timeline_submenu(&url, "Events Only", "y", "e", 0); } } if( nEntry>20 ){ timeline_submenu(&url, "20 Entries", "n", "20", 0); } if( nEntry<200 ){ timeline_submenu(&url, "200 Entries", "n", "200", 0); } } } if( P("showsql") ){ @ <blockquote>%h(blob_str(&sql))</blockquote> } blob_zero(&sql); db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); @ <h2>%b(&desc)</h2> blob_reset(&desc); www_print_timeline(&q, tmFlags, 0); db_finalize(&q); style_footer(); |
︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 | ** Return true if the input string is a date in the ISO 8601 format: ** YYYY-MM-DD. */ static int isIsoDate(const char *z){ return strlen(z)==10 && z[4]=='-' && z[7]=='-' | | | | 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 | ** Return true if the input string is a date in the ISO 8601 format: ** YYYY-MM-DD. */ static int isIsoDate(const char *z){ return strlen(z)==10 && z[4]=='-' && z[7]=='-' && fossil_isdigit(z[0]) && fossil_isdigit(z[5]); } /* ** COMMAND: timeline ** ** Usage: %fossil timeline ?WHEN? ?BASELINE|DATETIME? ?-n N? ?-t TYPE? ** |
︙ | ︙ |
Changes to src/tkt.c.
︙ | ︙ | |||
51 52 53 54 55 56 57 | int i; if( nField>0 ) return; db_prepare(&q, "PRAGMA table_info(ticket)"); while( db_step(&q)==SQLITE_ROW ){ const char *zField = db_column_text(&q, 1); if( strncmp(zField,"tkt_",4)==0 ) continue; if( nField%10==0 ){ | | < < < | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | int i; if( nField>0 ) return; db_prepare(&q, "PRAGMA table_info(ticket)"); while( db_step(&q)==SQLITE_ROW ){ const char *zField = db_column_text(&q, 1); if( strncmp(zField,"tkt_",4)==0 ) continue; if( nField%10==0 ){ azField = fossil_realloc(azField, sizeof(azField)*3*(nField+10) ); } azField[nField] = mprintf("%s", zField); nField++; } db_finalize(&q); qsort(azField, nField, sizeof(azField[0]), nameCmpr); azAppend = &azField[nField]; |
︙ | ︙ | |||
215 216 217 218 219 220 221 | /* ** Rebuild an entire entry in the TICKET table */ void ticket_rebuild_entry(const char *zTktUuid){ char *zTag = mprintf("tkt-%s", zTktUuid); int tagid = tag_findid(zTag, 1); Stmt q; | | < | | | | | > | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | /* ** Rebuild an entire entry in the TICKET table */ void ticket_rebuild_entry(const char *zTktUuid){ char *zTag = mprintf("tkt-%s", zTktUuid); int tagid = tag_findid(zTag, 1); Stmt q; Manifest *pTicket; int createFlag = 1; db_multi_exec( "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid ); db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); pTicket = manifest_get(rid, CFTYPE_TICKET); if( pTicket ){ ticket_insert(pTicket, createFlag, rid); manifest_ticket_event(rid, pTicket, createFlag, tagid); manifest_destroy(pTicket); } createFlag = 0; } db_finalize(&q); } /* ** Create the subscript interpreter and load the "common" code. |
︙ | ︙ | |||
311 312 313 314 315 316 317 | } if( g.okNewTkt ){ style_submenu_element("New Ticket", "Create a new ticket", "%s/tktnew", g.zTop); } if( g.okApndTkt && g.okAttach ){ style_submenu_element("Attach", "Add An Attachment", | | | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | } if( g.okNewTkt ){ style_submenu_element("New Ticket", "Create a new ticket", "%s/tktnew", g.zTop); } if( g.okApndTkt && g.okAttach ){ style_submenu_element("Attach", "Add An Attachment", "%s/attachadd?tkt=%T&from=%s/tktview/%t", g.zTop, zUuid, g.zTop, zUuid); } style_header("View Ticket"); if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); ticket_init(); initializeVariablesFromDb(); zScript = ticket_viewpage_code(); |
︙ | ︙ | |||
340 341 342 343 344 345 346 | " ORDER BY mtime DESC", zFullName); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zFile = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); if( cnt==0 ){ | | > > | | > > > > | > | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | " ORDER BY mtime DESC", zFullName); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zFile = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); if( cnt==0 ){ @ <hr /><h2>Attachments:</h2> @ <ul> } cnt++; @ <li> if( g.okRead && g.okHistory ){ @ <a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)"> @ %h(zFile)</a> }else{ @ %h(zFile) } @ added by %h(zUser) on hyperlink_to_date(zDate, "."); if( g.okWrTkt && g.okAttach ){ @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>] } @ </li> } if( cnt ){ @ </ul> } db_finalize(&q); } |
︙ | ︙ | |||
436 437 438 439 440 441 442 | int nValue; if( azAppend[i] ){ blob_appendf(&tktchng, "J +%s %z\n", azField[i], fossilize(azAppend[i], -1)); }else{ zValue = Th_Fetch(azField[i], &nValue); if( zValue ){ | | | 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | int nValue; if( azAppend[i] ){ blob_appendf(&tktchng, "J +%s %z\n", azField[i], fossilize(azAppend[i], -1)); }else{ zValue = Th_Fetch(azField[i], &nValue); if( zValue ){ while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){ if( strncmp(azField[i], "private_", 8)==0 ){ zValue = db_conceal(zValue, nValue); blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); }else{ blob_appendf(&tktchng, "J %s %#F\n", azField[i], nValue, zValue); } |
︙ | ︙ | |||
460 461 462 463 464 465 466 | zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } *(const char**)pUuid = zUuid; blob_appendf(&tktchng, "K %s\n", zUuid); blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : ""); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); | > > > > > > > | | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } *(const char**)pUuid = zUuid; blob_appendf(&tktchng, "K %s\n", zUuid); blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : ""); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); if( g.zPath[0]=='d' ){ /* If called from /debug_tktnew or /debug_tktedit... */ @ <font color="blue"> @ <p>Ticket artifact that would have been submitted:</p> @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> @ <hr /></font> return TH_OK; }else if( g.thTrace ){ Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" "}<br />\n", blob_str(&tktchng)); }else{ rid = content_put(&tktchng, 0, 0); if( rid==0 ){ fossil_panic("trouble committing ticket: %s", g.zErrMsg); |
︙ | ︙ | |||
504 505 506 507 508 509 510 | } style_header("New Ticket"); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); ticket_init(); getAllTicketFields(); initializeVariablesFromDb(); initializeVariablesFromCGI(); | | > | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | } style_header("New Ticket"); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); ticket_init(); getAllTicketFields(); initializeVariablesFromDb(); initializeVariablesFromCGI(); @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p> login_insert_csrf_secret(); @ </p> zScript = ticket_newpage_code(); Th_Store("login", g.zLogin); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zNewUuid, 0); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
︙ | ︙ | |||
547 548 549 550 551 552 553 | zName = P("name"); if( P("cancel") ){ cgi_redirectf("tktview?name=%T", zName); } style_header("Edit Ticket"); if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE || !validate16(zName,nName) ){ | | | | > | | > | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 | zName = P("name"); if( P("cancel") ){ cgi_redirectf("tktview?name=%T", zName); } style_header("Edit Ticket"); if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE || !validate16(zName,nName) ){ @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span> style_footer(); return; } nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'", zName); if( nRec==0 ){ @ <span class="tktError">No such ticket: \"%h(zName)\"</span> style_footer(); return; } if( nRec>1 ){ @ <span class="tktError">%d(nRec) tickets begin with: @ \"%h(zName)\"</span> style_footer(); return; } if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); ticket_init(); getAllTicketFields(); initializeVariablesFromCGI(); initializeVariablesFromDb(); @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p> @ <input type="hidden" name="name" value="%s(zName)" /> login_insert_csrf_secret(); @ </p> zScript = ticket_editpage_code(); Th_Store("login", g.zLogin); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
︙ | ︙ | |||
617 618 619 620 621 622 623 | } } return 0; } /* ** WEBPAGE: tkttimeline | | | | 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | } } return 0; } /* ** WEBPAGE: tkttimeline ** URL: /tkttimeline?name=TICKETUUID&y=TYPE ** ** Show the change history for a single ticket in timeline format. */ void tkttimeline_page(void){ Stmt q; char *zTitle; char *zSQL; const char *zUuid; char *zFullUuid; int tagid; char zGlobPattern[50]; const char *zType; login_check_credentials(); if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; } zUuid = PD("name",""); zType = PD("y","a"); if( zType[0]!='c' ){ style_submenu_element("Check-ins", "Check-ins", "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid); }else{ style_submenu_element("Timeline", "Timeline", "%s/tkttimeline?name=%T", g.zTop, zUuid); } style_submenu_element("History", "History", "%s/tkthistory/%s", g.zTop, zUuid); style_submenu_element("Status", "Status", |
︙ | ︙ | |||
737 738 739 740 741 742 743 | " FROM attachment, blob" " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)" " AND blob.rid=attachid" " ORDER BY 1 DESC", tagid, tagid ); while( db_step(&q)==SQLITE_ROW ){ | < | | 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 | " FROM attachment, blob" " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)" " AND blob.rid=attachid" " ORDER BY 1 DESC", tagid, tagid ); while( db_step(&q)==SQLITE_ROW ){ Manifest *pTicket; char zShort[12]; const char *zDate = db_column_text(&q, 0); int rid = db_column_int(&q, 1); const char *zChngUuid = db_column_text(&q, 2); const char *zFile = db_column_text(&q, 4); memcpy(zShort, zChngUuid, 10); zShort[10] = 0; |
︙ | ︙ | |||
761 762 763 764 765 766 767 | @ <p>Add attachment "%h(zFile)" } @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] @ (rid %d(rid)) by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate, ".</p>"); }else{ | | | | < > | | 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 | @ <p>Add attachment "%h(zFile)" } @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] @ (rid %d(rid)) by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate, ".</p>"); }else{ pTicket = manifest_get(rid, CFTYPE_TICKET); if( pTicket ){ @ @ <p>Ticket change @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] @ (rid %d(rid)) by hyperlink_to_user(pTicket->zUser,zDate," on"); hyperlink_to_date(zDate, ":"); @ </p> ticket_output_change_artifact(pTicket); } manifest_destroy(pTicket); } } db_finalize(&q); style_footer(); } /* |
︙ | ︙ | |||
818 819 820 821 822 823 824 | }else{ @ <li>Change %h(z) to "%h(blob_str(&val))"</li> } blob_reset(&val); } @ </ol> } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | }else{ @ <li>Change %h(z) to "%h(blob_str(&val))"</li> } blob_reset(&val); } @ </ol> } /* ** COMMAND: ticket ** Usage: %fossil ticket SUBCOMMAND ... ** ** Run various subcommands to control tickets ** ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? ** ** options can be: ** ?-l|--limit LIMITCHAR? ** ?-q|--quote? ** ?-R|--repository FILE? ** ** Run the ticket report, identified by the report format title ** used in the gui. The data is written as flat file on stdout, ** using "," as separator. The seperator "," can be changed using ** the -l or --limit option. ** If TICKETFILTER is given on the commandline, the query is ** limited with a new WHERE-condition. ** example: Report lists a column # with the uuid ** TICKETFILTER may be [#]='uuuuuuuuu' ** example: Report only lists rows with status not open ** TICKETFILTER: status != 'open' ** If the option -q|--quote is used, the tickets are encoded by ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n, ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\). ** Otherwise, the simplified encoding as on the show report raw ** page in the gui is used. ** ** Instead of the report title its possible to use the report ** number. Using the special report number 0 list all columns, ** defined in the ticket table. ** ** %fossil ticket list fields ** ** list all fields, defined for ticket in the fossil repository ** ** %fossil ticket list reports ** ** list all ticket reports, defined in the fossil repository ** ** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** ** change ticket identified by TICKETUUID and set the value of ** field FIELD to VALUE. Valid field descriptions are: ** status, type, severity, priority, resolution, ** foundin, private_contact, resolution, title or comment ** Field names given above are the ones, defined in a standard ** fossil environment. If you have added, deleted columns, you ** change the all your configured columns. ** You can use more than one field/value pair on the commandline. ** Using -q|--quote enables the special character decoding as ** in "ticket show". So it's possible, to set multiline text or ** text with special characters. ** ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** ** like set, but create a new ticket with the given values. ** ** The values in set|add are not validated against the definitions ** given in "Ticket Common Script". */ void ticket_cmd(void){ int n; /* do some ints, we want to be inside a checkout */ db_find_and_open_repository(1); user_select(); /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } if( g.argc<3 ){ usage("add|fieldlist|set|show"); }else{ n = strlen(g.argv[2]); if( n==1 && g.argv[2][0]=='s' ){ /* set/show cannot be distinguished, so show the usage */ usage("add|fieldlist|set|show"); }else if( strncmp(g.argv[2],"list",n)==0 ){ if( g.argc==3 ){ usage("list fields|reports"); }else{ n = strlen(g.argv[3]); if( !strncmp(g.argv[3],"fields",n) ){ /* simply show all field names */ int i; /* read all available ticket fields */ getAllTicketFields(); for(i=0; i<nField; i++){ printf("%s\n",azField[i]); } }else if( !strncmp(g.argv[3],"reports",n) ){ rpt_list_reports(); }else{ fossil_fatal("unknown ticket list option '%s'!",g.argv[3]); } } }else{ /* add a new ticket or set fields on existing tickets */ tTktShowEncoding tktEncoding; tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; if( strncmp(g.argv[2],"show",n)==0 ){ if( g.argc==3 ){ usage("show REPORTNR"); }else{ const char *zRep = 0; const char *zSep = 0; const char *zFilterUuid = 0; zSep = find_option("limit","l",1); zRep = g.argv[3]; if( !strcmp(zRep,"0") ){ zRep = 0; } if( g.argc>4 ){ zFilterUuid = g.argv[4]; } rptshow( zRep, zSep, zFilterUuid, tktEncoding ); } }else{ /* add a new ticket or update an existing ticket */ enum { set,add,err } eCmd = err; int i = 0; int rid; const char *zTktUuid = 0; Blob tktchng, cksum; /* get command type (set/add) and get uuid, if needed for set */ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){ eCmd = set; if( g.argc==3 ){ usage("set TICKETUUID"); } zTktUuid = db_text(0, "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] ); if( !zTktUuid ){ fossil_fatal("unknown ticket: '%s'!",g.argv[3]); } i=4; }else if( strncmp(g.argv[2],"add",n)==0 ){ eCmd = add; i = 3; zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } /* none of set/add, so show the usage! */ if( eCmd==err ){ usage("add|fieldlist|set|show"); } /* read all given ticket field/value pairs from command line */ if( i==g.argc ){ fossil_fatal("empty %s command aborted!",g.argv[2]); } getAllTicketFields(); /* read commandline and assign fields in the azValue array */ while( i<g.argc ){ char *zFName; char *zFValue; int j; zFName = g.argv[i++]; if( i==g.argc ){ fossil_fatal("missing value for '%s'!",zFName); } zFValue = g.argv[i++]; j = fieldId(zFName); if( tktEncoding == tktFossilize ){ zFValue=mprintf("%s",zFValue); defossilize(zFValue); } if( j == -1 ){ fossil_fatal("unknown field name '%s'!",zFName); }else{ azValue[j] = zFValue; } } /* now add the needed artifacts to the repository */ blob_zero(&tktchng); { /* add the time to the ticket manifest */ char *zDate; zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&tktchng, "D %s\n", zDate); free(zDate); } /* append defined elements */ for(i=0; i<nField; i++){ char *zValue; zValue = azValue[i]; if( azValue[i] && azValue[i][0] ){ if( strncmp(azField[i], "private_", 8)==0 ){ zValue = db_conceal(zValue, strlen(zValue)); blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); }else{ blob_appendf(&tktchng, "J %s %#F\n", azField[i], strlen(zValue), zValue); } if( tktEncoding == tktFossilize ){ free(azValue[i]); } } } blob_appendf(&tktchng, "K %s\n", zTktUuid); blob_appendf(&tktchng, "U %F\n", g.zLogin); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); rid = content_put(&tktchng, 0, 0); if( rid==0 ){ fossil_panic("trouble committing ticket: %s", g.zErrMsg); } manifest_crosslink_begin(); manifest_crosslink(rid, &tktchng); manifest_crosslink_end(); printf("ticket %s succeeded for UID %s\n", (eCmd==set?"set":"add"),zTktUuid); } } } } |
Changes to src/tktsetup.c.
︙ | ︙ | |||
121 122 123 124 125 126 127 | db_unset(zDbField, 0); if( xRebuild ) xRebuild(); z = zDfltValue; }else if( isSubmit ){ char *zErr = 0; login_verify_csrf_secret(); if( xText && (zErr = xText(z))!=0 ){ | | | | | | | | | | | | | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | db_unset(zDbField, 0); if( xRebuild ) xRebuild(); z = zDfltValue; }else if( isSubmit ){ char *zErr = 0; login_verify_csrf_secret(); if( xText && (zErr = xText(z))!=0 ){ @ <p class="tktsetupError">ERROR: %h(zErr)</p> }else{ db_set(zDbField, z, 0); if( xRebuild ) xRebuild(); cgi_redirect("tktsetup"); } } @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="post"><div> login_insert_csrf_secret(); @ <p>%s(zDesc)</p> @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> @ <blockquote><p> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ <input type="submit" name="setup" value="Cancel" /> @ </p></blockquote> @ </div></form> @ <hr /> @ <h2>Default %s(zTitle)</h2> @ <blockquote><pre> @ %h(zDfltValue) @ </pre></blockquote> style_footer(); } /* ** WEBPAGE: tktsetup_tab */ void tktsetup_tab_page(void){ static const char zDesc[] = @ Enter a valid CREATE TABLE statement for the "ticket" table. The @ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime" @ with an unique index on "tkt_uuid" and "tkt_mtime". ; tktsetup_generic( "Ticket Table Schema", "ticket-table", zDefaultTicketTable, zDesc, ticket_schema_check, |
︙ | ︙ | |||
227 228 229 230 231 232 233 | } /* ** WEBPAGE: tktsetup_com */ void tktsetup_com_page(void){ static const char zDesc[] = | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | } /* ** WEBPAGE: tktsetup_com */ void tktsetup_com_page(void){ static const char zDesc[] = @ Enter TH1 script that initializes variables prior to generating @ any of the ticket view, edit, or creation pages. ; tktsetup_generic( "Ticket Common Script", "ticket-common", zDefaultTicketCommon, zDesc, 0, 0, 30 ); } static const char zDefaultNew[] = @ <th1> @ if {[info exists submit]} { @ set status Open @ submit_ticket @ } @ </th1> @ <h1 style="text-align: center;">Enter A New Ticket</h1> @ <table cellpadding="5"> @ <tr> @ <td colspan="2"> @ Enter a one-line summary of the ticket:<br /> @ <input type="text" name="title" size="60" value="$<title>" /> @ </td> @ </tr> @ @ <tr> @ <td style="text-align: center;">Type: @ <th1>combobox type $type_choices 1</th1> @ </td> @ <td>What type of ticket is this?</td> @ </tr> @ @ <tr> @ <td style="text-align: center;">Version: @ <input type="text" name="foundin" size="20" value="$<foundin>" /> @ </td> @ <td>In what version or build number do you observe the problem?</td> @ </tr> @ @ <tr> @ <td style="text-align: center;">Severity: @ <th1>combobox severity $severity_choices 1</th1> @ </td> @ <td>How debilitating is the problem? How badly does the problem @ affect the operation of the product?</td> @ </tr> @ @ <tr> @ <td style="text-align: center;">EMail: @ <input type="text" name="private_contact" value="$<private_contact>" size="30" /> @ </td> @ <td><span style="text-decoration: underline;">Not publicly visible</span>. @ Used by developers to contact you with questions.</td> @ </tr> @ @ <tr> @ <td colspan="2"> @ Enter a detailed description of the problem. @ For code defects, be sure to provide details on exactly how @ the problem can be reproduced. Provide as much detail as @ possible. @ <br /> @ <th1>set nline [linecount $comment 50 10]</th1> @ <textarea name="comment" cols="80" rows="$nline" @ wrap="virtual" class="wikiedit">$<comment></textarea><br /> @ <input type="submit" name="preview" value="Preview" /></td> @ </tr> @ @ <th1>enable_output [info exists preview]</th1> @ <tr><td colspan="2"> @ Description Preview:<br /><hr /> @ <th1>wiki $comment</th1> @ <hr /> @ </td></tr> @ <th1>enable_output 1</th1> @ @ <tr> @ <td style="text-align: center;"> @ <input type="submit" name="submit" value="Submit" /> @ </td> @ <td>After filling in the information above, press this button to create @ the new ticket</td> @ </tr> @ <tr> @ <td style="text-align: center;"> @ <input type="submit" name="cancel" value="Cancel" /> @ </td> @ <td>Abandon and forget this ticket</td> @ </tr> @ </table> ; /* ** Return the code used to generate the new ticket page */ const char *ticket_newpage_code(void){ return db_get("ticket-newpage", (char*)zDefaultNew); } /* ** WEBPAGE: tktsetup_newpage */ void tktsetup_newpage_page(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "new ticket" @ page ; tktsetup_generic( "HTML For New Tickets", "ticket-newpage", zDefaultNew, zDesc, 0, 0, 40 ); } static const char zDefaultView[] = @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Ticket UUID:</td> @ <td class="tktDspValue" colspan="3">$<tkt_uuid></td></tr> @ <tr><td class="tktDspLabel">Title:</td> @ <td class="tktDspValue" colspan="3"> @ <th1>wiki $title</th1> @ </td></tr> @ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue"> @ $<status> @ </td> @ <td class="tktDspLabel">Type:</td><td class="tktDspValue"> @ $<type> @ </td></tr> @ <tr><td class="tktDspLabel">Severity:</td><td class="tktDspValue"> @ $<severity> @ </td> @ <td class="tktDspLabel">Priority:</td><td class="tktDspValue"> @ $<priority> @ </td></tr> @ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue"> @ $<subsystem> @ </td> @ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue"> @ $<resolution> @ </td></tr> @ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue"> @ $<tkt_datetime> @ </td> @ <th1>enable_output [hascap e]</th1> @ <td class="tktDspLabel">Contact:</td><td class="tktDspValue"> @ $<private_contact> @ </td> @ <th1>enable_output 1</th1> @ </tr> @ <tr><td class="tktDspLabel">Version Found In:</td> @ <td colspan="3" valign="top" class="tktDspValue"> @ $<foundin> @ </td></tr> @ <tr><td>Description & Comments:</td></tr> @ <tr><td colspan="4" class="tktDspValue"> @ <th1>wiki $comment</th1> @ </td></tr> @ </table> ; /* ** Return the code used to generate the view ticket page */ const char *ticket_viewpage_code(void){ return db_get("ticket-viewpage", (char*)zDefaultView); } /* ** WEBPAGE: tktsetup_viewpage */ void tktsetup_viewpage_page(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "view ticket" page ; tktsetup_generic( "HTML For Viewing Tickets", "ticket-viewpage", zDefaultView, zDesc, 0, 0, 40 ); } static const char zDefaultEdit[] = @ <th1> @ if {![info exists username]} {set username $login} @ if {[info exists submit]} { @ if {[info exists cmappnd]} { @ if {[string length $cmappnd]>0} { @ set ctxt "\n\n<hr /><i>[htmlize $login]" @ if {$username ne $login} { @ set ctxt "$ctxt claiming to be [htmlize $username]" @ } @ set ctxt "$ctxt added on [date]:</i><br />\n$cmappnd" @ append_field comment $ctxt @ } @ } @ submit_ticket @ } @ </th1> @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Title:</td><td> @ <input type="text" name="title" value="$<title>" size="60" /> @ </td></tr> @ <tr><td class="tktDspLabel">Status:</td><td> @ <th1>combobox status $status_choices 1</th1> @ </td></tr> @ <tr><td class="tktDspLabel">Type:</td><td> @ <th1>combobox type $type_choices 1</th1> @ </td></tr> @ <tr><td class="tktDspLabel">Severity:</td><td> @ <th1>combobox severity $severity_choices 1</th1> @ </td></tr> @ <tr><td class="tktDspLabel">Priority:</td><td> @ <th1>combobox priority $priority_choices 1</th1> @ </td></tr> @ <tr><td class="tktDspLabel">Resolution:</td><td> @ <th1>combobox resolution $resolution_choices 1</th1> @ </td></tr> @ <tr><td class="tktDspLabel">Subsystem:</td><td> @ <th1>combobox subsystem $subsystem_choices 1</th1> @ </td></tr> @ <th1>enable_output [hascap e]</th1> @ <tr><td class="tktDspLabel">Contact:</td><td> @ <input type="text" name="private_contact" size="40" @ value="$<private_contact>" /> @ </td></tr> @ <th1>enable_output 1</th1> @ <tr><td class="tktDspLabel">Version Found In:</td><td> @ <input type="text" name="foundin" size="50" value="$<foundin>" /> @ </td></tr> @ <tr><td colspan="2"> @ <th1> @ if {![info exists eall]} {set eall 0} @ if {[info exists aonlybtn]} {set eall 0} @ if {[info exists eallbtn]} {set eall 1} @ if {![hascap w]} {set eall 0} @ if {![info exists cmappnd]} {set cmappnd {}} @ set nline [linecount $comment 15 10] @ enable_output $eall @ </th1> @ Description And Comments:<br /> @ <textarea name="comment" cols="80" rows="$nline" @ wrap="virtual" class="wikiedit">$<comment></textarea><br /> @ <input type="hidden" name="eall" value="1" /> @ <input type="submit" name="aonlybtn" value="Append Remark" /> @ <input type="submit" name="preview1btn" value="Preview" /> @ <th1>enable_output [expr {!$eall}]</th1> @ Append Remark from @ <input type="text" name="username" value="$<username>" size="30" />:<br /> @ <textarea name="cmappnd" cols="80" rows="15" @ wrap="virtual" class="wikiedit">$<cmappnd></textarea><br /> @ <th1>enable_output [expr {[hascap w] && !$eall}]</th1> @ <input type="submit" name="eallbtn" value="Edit All" /> @ <th1>enable_output [expr {!$eall}]</th1> @ <input type="submit" name="preview2btn" value="Preview" /> @ <th1>enable_output 1</th1> @ </td></tr> @ @ <th1>enable_output [info exists preview1btn]</th1> @ <tr><td colspan="2"> @ Description Preview:<br /><hr /> @ <th1>wiki $comment</th1> @ <hr /> @ </td></tr> @ <th1>enable_output [info exists preview2btn]</th1> @ <tr><td colspan="2"> @ Description Preview:<br /><hr /> @ <th1>wiki $cmappnd</th1> @ <hr /> @ </td></tr> @ <th1>enable_output 1</th1> @ @ <tr><td align="right"></td><td> @ <input type="submit" name="submit" value="Submit Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </td></tr> @ </table> ; /* ** Return the code used to generate the edit ticket page */ const char *ticket_editpage_code(void){ return db_get("ticket-editpage", (char*)zDefaultEdit); } /* ** WEBPAGE: tktsetup_editpage */ void tktsetup_editpage_page(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "edit ticket" page ; tktsetup_generic( "HTML For Editing Tickets", "ticket-editpage", zDefaultEdit, zDesc, 0, |
︙ | ︙ | |||
583 584 585 586 587 588 589 | } /* ** WEBPAGE: tktsetup_reportlist */ void tktsetup_reportlist(void){ static const char zDesc[] = | | < | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | } /* ** WEBPAGE: tktsetup_reportlist */ void tktsetup_reportlist(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "report list" page ; tktsetup_generic( "HTML For Report List", "ticket-reportlist", zDefaultReportList, zDesc, 0, |
︙ | ︙ | |||
631 632 633 634 635 636 637 | } /* ** WEBPAGE: tktsetup_rpttplt */ void tktsetup_rpttplt_page(void){ static const char zDesc[] = | | | | 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | } /* ** WEBPAGE: tktsetup_rpttplt */ void tktsetup_rpttplt_page(void){ static const char zDesc[] = @ Enter the default ticket report format template. This is the @ the template report format that initially appears when creating a @ new ticket summary report. ; tktsetup_generic( "Default Report Template", "ticket-report-template", zDefaultReport, zDesc, 0, |
︙ | ︙ | |||
672 673 674 675 676 677 678 | } /* ** WEBPAGE: tktsetup_keytplt */ void tktsetup_keytplt_page(void){ static const char zDesc[] = | | | | 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | } /* ** WEBPAGE: tktsetup_keytplt */ void tktsetup_keytplt_page(void){ static const char zDesc[] = @ Enter the default ticket report color-key template. This is the @ the color-key that initially appears when creating a @ new ticket summary report. ; tktsetup_generic( "Default Report Color-Key Template", "ticket-key-template", zDefaultKey, zDesc, 0, |
︙ | ︙ | |||
701 702 703 704 705 706 707 | } if( P("setup") ){ cgi_redirect("tktsetup"); } style_header("Ticket Display On Timelines"); db_begin_transaction(); | | | | | | | | | | 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 | } if( P("setup") ){ cgi_redirect("tktsetup"); } style_header("Ticket Display On Timelines"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="post"><div> login_insert_csrf_secret(); @ <hr /> entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title"); @ <p>An SQL expression in a query against the TICKET table that will @ return the title of the ticket for display purposes.</p> @ <hr /> entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status"); @ <p>The name of the column in the TICKET table that contains the ticket @ status in human-readable form. Case sensitive.</p> @ <hr /> entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c", "status='Closed'"); @ <p>An SQL expression that evaluates to true in a TICKET table query if @ the ticket is closed.</p> @ <hr /> @ <p> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="setup" value="Cancel" /> @ </p> @ </div></form> db_end_transaction(0); style_footer(); } |
Changes to src/translate.c.
︙ | ︙ | |||
64 65 66 67 68 69 70 | } } /* ** Translate the input stream into the output stream */ static void trans(FILE *in, FILE *out){ | | | | > | | | > | > | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | } } /* ** Translate the input stream into the output stream */ static void trans(FILE *in, FILE *out){ int i, j, k; /* Loop counters */ char c1, c2; /* Characters used to start a comment */ int lastWasEq = 0; /* True if last non-whitespace character was "=" */ int lastWasComma = 0; /* True if last non-whitespace character was "," */ char zLine[2000]; /* A single line of input */ char zOut[4000]; /* The input line translated into appropriate output */ c1 = c2 = '-'; while( fgets(zLine, sizeof(zLine), in) ){ for(i=0; zLine[i] && isspace(zLine[i]); i++){} if( zLine[i]!='@' ){ if( inPrint || inStr ) end_block(out); fprintf(out,"%s",zLine); /* 0123456789 12345 */ if( strncmp(zLine, "/* @-comment: ", 14)==0 ){ c1 = zLine[14]; c2 = zLine[15]; } i += strlen(&zLine[i]); while( i>0 && isspace(zLine[i-1]) ){ i--; } lastWasEq = i>0 && zLine[i-1]=='='; lastWasComma = i>0 && zLine[i-1]==','; }else if( lastWasEq || lastWasComma){ /* If the last non-whitespace character before the first @ was ** an "="(var init/set) or a ","(const definition in list) then ** generate a string literal. But skip comments ** consisting of all text between c1 and c2 (default "--") ** and end of line. */ int indent, omitline; i++; if( isspace(zLine[i]) ){ i++; } indent = i - 2; |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 | #include "update.h" #include <assert.h> /* ** Return true if artifact rid is a version */ int is_a_version(int rid){ | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include "update.h" #include <assert.h> /* ** Return true if artifact rid is a version */ int is_a_version(int rid){ return db_exists("SELECT 1 FROM event WHERE objid=%d AND type='ci'", rid); } /* ** COMMAND: update ** ** Usage: %fossil update ?VERSION? ?FILES...? ** |
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 | fossil_fatal("Multiple descendants"); } tid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC"); } db_begin_transaction(); vfile_check_signature(vid, 1); if( !nochangeFlag ) undo_begin(); load_vfile_from_rid(tid); /* ** The record.fn field is used to match files against each other. The | > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | fossil_fatal("Multiple descendants"); } tid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC"); } if( tid==vid ) return; /* Nothing to update */ db_begin_transaction(); vfile_check_signature(vid, 1); if( !nochangeFlag ) undo_begin(); load_vfile_from_rid(tid); /* ** The record.fn field is used to match files against each other. The |
︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 286 287 288 289 | */ if( nochangeFlag ){ db_end_transaction(1); /* With --nochange, rollback changes */ }else{ if( g.argc<=3 ){ /* All files updated. Shift the current checkout to the target. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); manifest_to_disk(tid); db_lset_int("checkout", tid); }else{ /* A subset of files have been checked out. Keep the current ** checkout unchanged. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); } | > | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | */ if( nochangeFlag ){ db_end_transaction(1); /* With --nochange, rollback changes */ }else{ if( g.argc<=3 ){ /* All files updated. Shift the current checkout to the target. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); checkout_set_all_exe(vid); manifest_to_disk(tid); db_lset_int("checkout", tid); }else{ /* A subset of files have been checked out. Keep the current ** checkout unchanged. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); } |
︙ | ︙ | |||
299 300 301 302 303 304 305 | */ int historical_version_of_file( const char *revision, /* The checkin containing the file */ const char *file, /* Full treename of the file */ Blob *content, /* Put the content here */ int errCode /* Error code if file not found. Panic if 0. */ ){ | < | > | | | | > | | | | | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | */ int historical_version_of_file( const char *revision, /* The checkin containing the file */ const char *file, /* Full treename of the file */ Blob *content, /* Put the content here */ int errCode /* Error code if file not found. Panic if 0. */ ){ Manifest *pManifest; ManifestFile *pFile; int rid=0; if( revision ){ rid = name_to_rid(revision); }else{ rid = db_lget_int("checkout", 0); } if( !is_a_version(rid) ){ if( errCode>0 ) return errCode; fossil_fatal("no such checkin: %s", revision); } pManifest = manifest_get(rid, CFTYPE_MANIFEST); if( pManifest ){ manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ if( strcmp(pFile->zName, file)==0 ){ rid = uuid_to_rid(pFile->zUuid, 0); manifest_destroy(pManifest); return content_get(rid, content); } } manifest_destroy(pManifest); if( errCode<=0 ){ fossil_fatal("file %s does not exist in checkin: %s", file, revision); } }else if( errCode<=0 ){ fossil_panic("could not parse manifest for checkin: %s", revision); } return errCode; |
︙ | ︙ | |||
383 384 385 386 387 388 389 390 391 392 | blob_reset(&fname); } }else{ int vid; vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); db_multi_exec( "INSERT INTO torevert " "SELECT pathname" " FROM vfile " | > | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | blob_reset(&fname); } }else{ int vid; vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); db_multi_exec( "DELETE FROM vmerge;" "INSERT INTO torevert " "SELECT pathname" " FROM vfile " " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" ); } blob_zero(&record); db_prepare(&q, "SELECT name FROM torevert"); while( db_step(&q)==SQLITE_ROW ){ zFile = db_column_text(&q, 0); if( zRevision!=0 ){ |
︙ | ︙ |
Changes to src/url.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** ******************************************************************************* ** ** This file contains code for parsing URLs that appear on the command-line */ #include "config.h" #include "url.h" /* ** Parse the given URL. Populate variables in the global "g" structure. ** ** g.urlIsFile True if FILE: ** g.urlIsHttps True if HTTPS: ** g.urlProtocol "http" or "https" or "file" | > > > > > > > > > > > | | > > > > | > > > > > > > > > > > > > > > > > < | | > | > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | | | > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | ** ******************************************************************************* ** ** This file contains code for parsing URLs that appear on the command-line */ #include "config.h" #include "url.h" /* ** Convert a string to lower-case. */ static void url_tolower(char *z){ while( *z ){ *z = fossil_tolower(*z); z++; } } /* ** Parse the given URL. Populate variables in the global "g" structure. ** ** g.urlIsFile True if FILE: ** g.urlIsHttps True if HTTPS: ** g.urlIsSsh True if SSH: ** g.urlProtocol "http" or "https" or "file" ** g.urlName Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: ** g.urlPort TCP port number for HTTP or HTTPS. ** g.urlDfltPort Default TCP port number (80 or 443). ** g.urlPath Path name for HTTP or HTTPS. ** g.urlUser Userid. ** g.urlPasswd Password. ** g.urlHostname HOST:PORT or just HOST if port is the default. ** g.urlCanonical The URL in canonical form, omitting the password ** ** HTTP url format is: ** ** http://userid:password@host:port/path ** ** SSH url format is: ** ** ssh://userid:password@host:port/path?fossil=path/to/fossil.exe ** */ void url_parse(const char *zUrl){ int i, j, c; char *zFile = 0; if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 || strncmp(zUrl, "ssh://", 6)==0 ){ int iStart; char *zLogin; char *zExe; g.urlIsFile = 0; if( zUrl[4]=='s' ){ g.urlIsHttps = 1; g.urlProtocol = "https"; g.urlDfltPort = 443; iStart = 8; }else if( zUrl[0]=='s' ){ g.urlIsSsh = 1; g.urlProtocol = "ssh"; g.urlDfltPort = 22; g.urlFossil = "fossil"; iStart = 6; }else{ g.urlIsHttps = 0; g.urlProtocol = "http"; g.urlDfltPort = 80; iStart = 7; } for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} if( c=='@' ){ /* Parse up the user-id and password */ for(j=iStart; j<i && zUrl[j]!=':'; j++){} g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]); dehttpize(g.urlUser); if( j<i ){ g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); dehttpize(g.urlPasswd); } if( g.urlIsSsh && g.urlPasswd ){ zLogin = mprintf("%t:*@", g.urlUser); }else{ zLogin = mprintf("%t@", g.urlUser); } for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); i = j; }else{ for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]); zLogin = mprintf(""); } url_tolower(g.urlName); if( c==':' ){ g.urlPort = 0; i++; while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ g.urlPort = g.urlPort*10 + c - '0'; i++; } g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); }else{ g.urlPort = g.urlDfltPort; g.urlHostname = g.urlName; } dehttpize(g.urlName); g.urlPath = mprintf("%s", &zUrl[i]); for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} if( g.urlPath[i] ){ g.urlPath[i] = 0; i++; } zExe = mprintf(""); while( g.urlPath[i]!=0 ){ char *zName, *zValue; zName = &g.urlPath[i]; zValue = zName; while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } if( g.urlPath[i]=='=' ){ g.urlPath[i] = 0; i++; zValue = &g.urlPath[i]; while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } } if( g.urlPath[i] ){ g.urlPath[i] = 0; i++; } if( strcmp(zName,"fossil")==0 ){ g.urlFossil = zValue; dehttpize(g.urlFossil); zExe = mprintf("?fossil=%T", g.urlFossil); } } dehttpize(g.urlPath); if( g.urlDfltPort==g.urlPort ){ g.urlCanonical = mprintf( "%s://%s%T%T%s", g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe ); }else{ g.urlCanonical = mprintf( "%s://%s%T:%d%T%s", g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe ); } if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++; free(zLogin); }else if( strncmp(zUrl, "file:", 5)==0 ){ g.urlIsFile = 1; if( zUrl[5]=='/' && zUrl[6]=='/' ){ i = 7; }else{ i = 5; |
︙ | ︙ | |||
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | if( g.argc!=3 && g.argc!=4 ){ usage("URL"); } url_parse(g.argv[2]); for(i=0; i<2; i++){ printf("g.urlIsFile = %d\n", g.urlIsFile); printf("g.urlIsHttps = %d\n", g.urlIsHttps); printf("g.urlProtocol = %s\n", g.urlProtocol); printf("g.urlName = %s\n", g.urlName); printf("g.urlPort = %d\n", g.urlPort); printf("g.urlDfltPort = %d\n", g.urlDfltPort); printf("g.urlHostname = %s\n", g.urlHostname); printf("g.urlPath = %s\n", g.urlPath); printf("g.urlUser = %s\n", g.urlUser); printf("g.urlPasswd = %s\n", g.urlPasswd); printf("g.urlCanonical = %s\n", g.urlCanonical); if( i==0 ){ printf("********\n"); url_enable_proxy("Using proxy: "); } } } | > > > | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | if( g.argc!=3 && g.argc!=4 ){ usage("URL"); } url_parse(g.argv[2]); for(i=0; i<2; i++){ printf("g.urlIsFile = %d\n", g.urlIsFile); printf("g.urlIsHttps = %d\n", g.urlIsHttps); printf("g.urlIsSsh = %d\n", g.urlIsSsh); printf("g.urlProtocol = %s\n", g.urlProtocol); printf("g.urlName = %s\n", g.urlName); printf("g.urlPort = %d\n", g.urlPort); printf("g.urlDfltPort = %d\n", g.urlDfltPort); printf("g.urlHostname = %s\n", g.urlHostname); printf("g.urlPath = %s\n", g.urlPath); printf("g.urlUser = %s\n", g.urlUser); printf("g.urlPasswd = %s\n", g.urlPasswd); printf("g.urlCanonical = %s\n", g.urlCanonical); printf("g.urlFossil = %s\n", g.urlFossil); if( g.urlIsFile || g.urlIsSsh ) break; if( i==0 ){ printf("********\n"); url_enable_proxy("Using proxy: "); } } } |
︙ | ︙ | |||
286 287 288 289 290 291 292 | } if( zName2 && strcmp(zName2,p->azName[i])==0 ){ zName2 = 0; z = zValue2; if( z==0 ) continue; } blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z); | | | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | } if( zName2 && strcmp(zName2,p->azName[i])==0 ){ zName2 = 0; z = zValue2; if( z==0 ) continue; } blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z); zSep = "&"; } if( zName1 && zValue1 ){ blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1); } if( zName2 && zValue2 ){ blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2); } |
︙ | ︙ |
Changes to src/user.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | /* ** Strip leading and trailing space from a string and add the string ** onto the end of a blob. */ static void strip_string(Blob *pBlob, char *z){ int i; blob_reset(pBlob); | | | > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | /* ** Strip leading and trailing space from a string and add the string ** onto the end of a blob. */ static void strip_string(Blob *pBlob, char *z){ int i; blob_reset(pBlob); while( fossil_isspace(*z) ){ z++; } for(i=0; z[i]; i++){ if( z[i]=='\r' || z[i]=='\n' ){ while( i>0 && fossil_isspace(z[i-1]) ){ i--; } z[i] = 0; break; } if( z[i]<' ' ) z[i] = ' '; } blob_append(pBlob, z, -1); } #if defined(_WIN32) #ifdef __MINGW32__ #include <conio.h> #endif /* ** getpass for Windows */ static char *getpass(const char *prompt){ static char pwd[64]; size_t i; |
︙ | ︙ |
Changes to src/vfile.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** ** Procedures for managing the VFILE table. */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.h> #include <dirent.h> /* ** Given a UUID, return the corresponding record ID. If the UUID ** does not exist, then return 0. ** ** For this routine, the UUID must be exact. For a match against ** user input with mixed case, use resolve_uuid(). | > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** ** Procedures for managing the VFILE table. */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.h> #if defined(__DMC__) #include "dirent.h" #else #include <dirent.h> #endif /* ** Given a UUID, return the corresponding record ID. If the UUID ** does not exist, then return 0. ** ** For this routine, the UUID must be exact. For a match against ** user input with mixed case, use resolve_uuid(). |
︙ | ︙ | |||
79 80 81 82 83 84 85 | fossil_panic("bad object id: %d", rid); } } } } /* | | < < < < < | < > > | < > > | < < < | < < < < < < < < < < | | < | | | | | < < < > | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | fossil_panic("bad object id: %d", rid); } } } } /* ** Build a catalog of all files in a checkin. */ void vfile_build(int vid){ int rid; Stmt ins; Manifest *p; ManifestFile *pFile; db_begin_transaction(); vfile_verify_not_phantom(vid, 0, 0); p = manifest_get(vid, CFTYPE_MANIFEST); if( p==0 ) return; db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid); db_prepare(&ins, "INSERT INTO vfile(vid,rid,mrid,pathname) " " VALUES(:vid,:id,:id,:name)"); db_bind_int(&ins, ":vid", vid); manifest_file_rewind(p); while( (pFile = manifest_file_next(p,0))!=0 ){ rid = uuid_to_rid(pFile->zUuid, 0); vfile_verify_not_phantom(rid, pFile->zName, pFile->zUuid); db_bind_int(&ins, ":id", rid); db_bind_text(&ins, ":name", pFile->zName); db_step(&ins); db_reset(&ins); } db_finalize(&ins); manifest_destroy(p); db_end_transaction(0); } /* ** Check the file signature of the disk image for every VFILE of vid. ** ** Set the VFILE.CHNGED field on every file that has changed. Also |
︙ | ︙ | |||
363 364 365 366 367 368 369 370 371 372 373 374 375 376 | md5sum_step_text(" 0\n", -1); continue; } fseek(in, 0L, SEEK_END); sprintf(zBuf, " %ld\n", ftell(in)); fseek(in, 0L, SEEK_SET); md5sum_step_text(zBuf, -1); for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; md5sum_step_text(zBuf, n); } fclose(in); | > | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | md5sum_step_text(" 0\n", -1); continue; } fseek(in, 0L, SEEK_END); sprintf(zBuf, " %ld\n", ftell(in)); fseek(in, 0L, SEEK_SET); md5sum_step_text(zBuf, -1); /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; md5sum_step_text(zBuf, n); } fclose(in); |
︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); int rid = db_column_int(&q, 1); md5sum_step_text(zName, -1); content_get(rid, &file); sprintf(zBuf, " %d\n", blob_size(&file)); md5sum_step_text(zBuf, -1); md5sum_step_blob(&file); blob_reset(&file); } db_finalize(&q); md5sum_finish(pOut); } /* ** Compute an aggregate MD5 checksum over the repository image of every ** file in manifest vid. The file names are part of the checksum. ** Return the resulting checksum in blob pOut. ** ** If pManOut is not NULL then fill it with the checksum found in the ** "R" card near the end of the manifest. */ void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){ | > | | | > | | | > | | > | > > | > | | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); int rid = db_column_int(&q, 1); md5sum_step_text(zName, -1); content_get(rid, &file); sprintf(zBuf, " %d\n", blob_size(&file)); md5sum_step_text(zBuf, -1); /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ md5sum_step_blob(&file); blob_reset(&file); } db_finalize(&q); md5sum_finish(pOut); } /* ** Compute an aggregate MD5 checksum over the repository image of every ** file in manifest vid. The file names are part of the checksum. ** Return the resulting checksum in blob pOut. ** ** If pManOut is not NULL then fill it with the checksum found in the ** "R" card near the end of the manifest. */ void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){ int fid; Blob file; Manifest *pManifest; ManifestFile *pFile; char zBuf[100]; blob_zero(pOut); if( pManOut ){ blob_zero(pManOut); } db_must_be_within_tree(); pManifest = manifest_get(vid, CFTYPE_MANIFEST); if( pManifest==0 ){ fossil_panic("manifest file (%d) is malformed", vid); } manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ fid = uuid_to_rid(pFile->zUuid, 0); md5sum_step_text(pFile->zName, -1); content_get(fid, &file); sprintf(zBuf, " %d\n", blob_size(&file)); md5sum_step_text(zBuf, -1); md5sum_step_blob(&file); blob_reset(&file); } if( pManOut ){ if( pManifest->zRepoCksum ){ blob_append(pManOut, pManifest->zRepoCksum, -1); }else{ blob_zero(pManOut); } } manifest_destroy(pManifest); md5sum_finish(pOut); } /* ** COMMAND: test-agg-cksum */ void test_agg_cksum_cmd(void){ |
︙ | ︙ |
Changes to src/wiki.c.
︙ | ︙ | |||
46 47 48 49 50 51 52 | } /* ** Output rules for well-formed wiki pages */ static void well_formed_wiki_name_rules(void){ @ <ul> | | | | | | | > > > > > > > > > | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | } /* ** Output rules for well-formed wiki pages */ static void well_formed_wiki_name_rules(void){ @ <ul> @ <li> Must not begin or end with a space.</li> @ <li> Must not contain any control characters, including tab or @ newline.</li> @ <li> Must not have two or more spaces in a row internally.</li> @ <li> Must be between 3 and 100 characters in length.</li> @ </ul> } /* ** Check a wiki name. If it is not well-formed, then issue an error ** and return true. If it is well-formed, return false. */ static int check_name(const char *z){ if( !wiki_name_is_wellformed((const unsigned char *)z) ){ style_header("Wiki Page Name Error"); @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed. @ Rules for wiki page names: well_formed_wiki_name_rules(); style_footer(); return 1; } return 0; } /* ** WEBPAGE: home ** WEBPAGE: index ** WEBPAGE: not_found */ void home_page(void){ char *zPageName = db_get("project-name",0); char *zIndexPage = db_get("index-page",0); login_check_credentials(); if( !g.okRdWiki ){ cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL); } if( zIndexPage ){ const char *zPathInfo = P("PATH_INFO"); while( zIndexPage[0]=='/' ) zIndexPage++; if( strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0; } if( zIndexPage ){ cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage); } if( zPageName ){ login_check_credentials(); g.zExtra = zPageName; cgi_set_parameter_nocopy("name", g.zExtra); g.isHome = 1; wiki_page(); return; } style_header("Home"); @ <p>This is a stub home-page for the project. @ To fill in this page, first go to @ <a href="%s(g.zBaseURL)/setup_config">setup/config</a> @ and establish a "Project Name". Then create a @ wiki page with that name. The content of that wiki page @ will be displayed in place of this message.</p> style_footer(); } /* ** Return true if the given pagename is the name of the sandbox */ static int is_sandbox(const char *zPagename){ return strcasecmp(zPagename,"sandbox")==0 || strcasecmp(zPagename,"sand box")==0; } /* ** WEBPAGE: wiki ** URL: /wiki?name=PAGENAME */ void wiki_page(void){ char *zTag; int rid = 0; int isSandbox; Blob wiki; Manifest *pWiki = 0; const char *zPageName; char *zBody = mprintf("%s","<i>Empty Page</i>"); Stmt q; int cnt = 0; login_check_credentials(); if( !g.okRdWiki ){ login_needed(); return; } |
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 | @ pages. </li> @ <li> <a href="%s(g.zBaseURL)/wiki_rules">Formatting rules</a> for @ wiki.</li> @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a> @ to experiment.</li> if( g.okNewWiki ){ @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> } @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> @ available on this server.</li> | > > > | | | | < < < < | < | | | < | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | @ pages. </li> @ <li> <a href="%s(g.zBaseURL)/wiki_rules">Formatting rules</a> for @ wiki.</li> @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a> @ to experiment.</li> if( g.okNewWiki ){ @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> if( g.okWrite ){ @ <li> Create a <a href="%s(g.zTop)/eventedit">new event</a>.</li> } } @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> @ available on this server.</li> @ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div> @ Search wiki titles: <input type="text" name="title"/> @ <input type="submit" /></div></form> @ </li> @ </ul> style_footer(); return; } if( check_name(zPageName) ) return; isSandbox = is_sandbox(zPageName); if( isSandbox ){ zBody = db_get("sandbox",zBody); }else{ zTag = mprintf("wiki-%s", zPageName); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " ORDER BY mtime DESC", zTag ); free(zTag); pWiki = manifest_get(rid, CFTYPE_WIKI); if( pWiki ){ while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++; if( pWiki->zWiki[0] ) zBody = pWiki->zWiki; } } if( !g.isHome ){ if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){ style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T", g.zTop, zPageName); } if( rid && g.okApndWiki && g.okAttach ){ style_submenu_element("Attach", "Add An Attachment", "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T", g.zTop, zPageName, g.zTop, zPageName); } if( rid && g.okApndWiki ){ style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T", g.zTop, zPageName); } if( g.okHistory ){ |
︙ | ︙ | |||
212 213 214 215 216 217 218 | " ORDER BY mtime DESC", zPageName); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zFile = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); if( cnt==0 ){ | | > | | > | | | > < | < | | 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | " ORDER BY mtime DESC", zPageName); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zFile = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); if( cnt==0 ){ @ <hr /><h2>Attachments:</h2> @ <ul> } cnt++; @ <li> if( g.okHistory && g.okRead ){ @ <a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)"> @ %h(zFile)</a> }else{ @ <li>%h(zFile) } @ added by %h(zUser) on hyperlink_to_date(zDate, "."); if( g.okWrWiki && g.okAttach ){ @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>] } @ </li> } if( cnt ){ @ </ul> } db_finalize(&q); manifest_destroy(pWiki); style_footer(); } /* ** WEBPAGE: wikiedit ** URL: /wikiedit?name=PAGENAME */ void wikiedit_page(void){ char *zTag; int rid = 0; int isSandbox; Blob wiki; Manifest *pWiki = 0; const char *zPageName; char *zHtmlPageName; int n; const char *z; char *zBody = (char*)P("w"); if( zBody ){ |
︙ | ︙ | |||
281 282 283 284 285 286 287 | " ORDER BY mtime DESC", zTag ); free(zTag); if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){ login_needed(); return; } | < < < < | < < | < | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | " ORDER BY mtime DESC", zTag ); free(zTag); if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){ login_needed(); return; } if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ zBody = pWiki->zWiki; } } if( P("submit")!=0 && zBody!=0 ){ char *zDate; Blob cksum; int nrid; blob_zero(&wiki); |
︙ | ︙ | |||
340 341 342 343 344 345 346 | zBody = mprintf("<i>Empty Page</i>"); } zHtmlPageName = mprintf("Edit: %s", zPageName); style_header(zHtmlPageName); if( P("preview")!=0 ){ blob_zero(&wiki); blob_append(&wiki, zBody, -1); | | | | | | | | | | < | < | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | zBody = mprintf("<i>Empty Page</i>"); } zHtmlPageName = mprintf("Edit: %s", zPageName); style_header(zHtmlPageName); if( P("preview")!=0 ){ blob_zero(&wiki); blob_append(&wiki, zBody, -1); @ Preview:<hr /> wiki_convert(&wiki, 0, 0); @ <hr /> blob_reset(&wiki); } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } if( n<20 ) n = 20; if( n>40 ) n = 40; @ <form method="post" action="%s(g.zBaseURL)/wikiedit"><div> login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zPageName)" /> @ <textarea name="w" class="wikiedit" cols="80" @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> @ <br /> @ <input type="submit" name="preview" value="Preview Your Changes" /> @ <input type="submit" name="submit" value="Apply These Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </div></form> manifest_destroy(pWiki); style_footer(); } /* ** WEBPAGE: wikinew ** URL /wikinew ** |
︙ | ︙ | |||
385 386 387 388 389 390 391 | return; } zName = PD("name",""); if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){ cgi_redirectf("wikiedit?name=%T", zName); } style_header("Create A New Wiki Page"); | | < | | | | | | 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | return; } zName = PD("name",""); if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){ cgi_redirectf("wikiedit?name=%T", zName); } style_header("Create A New Wiki Page"); @ <p>Rules for wiki page names:</p> well_formed_wiki_name_rules(); @ <form method="post" action="%s(g.zBaseURL)/wikinew"> @ <p>Name of new wiki page: @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /> @ <input type="submit" value="Create" /> @ </p></form> if( zName[0] ){ @ <p><span class="wikiError"> @ "%h(zName)" is not a valid wiki page name!</span></p> } style_footer(); } /* ** Append the wiki text for an remark to the end of the given BLOB. |
︙ | ︙ | |||
461 462 463 464 465 466 467 | return; } if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){ char *zDate; Blob cksum; int nrid; Blob body; | < | | < | | > < | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | return; } if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){ char *zDate; Blob cksum; int nrid; Blob body; Blob wiki; Manifest *pWiki = 0; blob_zero(&body); if( isSandbox ){ blob_appendf(&body, db_get("sandbox","")); appendRemark(&body); db_set("sandbox", blob_str(&body), 0); }else{ login_verify_csrf_secret(); pWiki = manifest_get(rid, CFTYPE_WIKI); if( pWiki ){ blob_append(&body, pWiki->zWiki, -1); manifest_destroy(pWiki); } blob_zero(&wiki); db_begin_transaction(); zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&wiki, "D %s\n", zDate); blob_appendf(&wiki, "L %F\n", zPageName); if( rid ){ |
︙ | ︙ | |||
522 523 524 525 526 527 528 | appendRemark(&preview); @ Preview:<hr> wiki_convert(&preview, 0, 0); @ <hr> blob_reset(&preview); } zUser = PD("u", g.zLogin); | | | | | | | | | | | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | appendRemark(&preview); @ Preview:<hr> wiki_convert(&preview, 0, 0); @ <hr> blob_reset(&preview); } zUser = PD("u", g.zLogin); @ <form method="post" action="%s(g.zBaseURL)/wikiappend"> login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zPageName)" /> @ Your Name: @ <input type="text" name="u" size="20" value="%h(zUser)" /><br /> @ Comment to append:<br /> @ <textarea name="r" class="wikiedit" cols="80" @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> @ <br /> @ <input type="submit" name="preview" value="Preview Your Comment" /> @ <input type="submit" name="submit" value="Append Your Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </form> style_footer(); } /* ** Name of the wiki history page being generated */ static const char *zWikiPageName; /* ** Function called to output extra text at the end of each line in ** a wiki history listing. */ static void wiki_history_extra(int rid){ if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){ @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a> } } /* ** WEBPAGE: whistory ** URL: /whistory?name=PAGENAME ** |
︙ | ︙ | |||
596 597 598 599 600 601 602 | ** ** Show the difference between two wiki pages. */ void wdiff_page(void){ char *zTitle; int rid1, rid2; const char *zPageName; | < | | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | ** ** Show the difference between two wiki pages. */ void wdiff_page(void){ char *zTitle; int rid1, rid2; const char *zPageName; Manifest *pW1, *pW2 = 0; Blob w1, w2, d; login_check_credentials(); rid1 = atoi(PD("a","0")); if( !g.okHistory ){ login_needed(); return; } if( rid1==0 ) fossil_redirect_home(); rid2 = atoi(PD("b","0")); |
︙ | ︙ | |||
619 620 621 622 623 624 625 | "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid=" "(SELECT tagid FROM tag WHERE tagname='wiki-%q')" " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)" " ORDER BY event.mtime DESC LIMIT 1", zPageName, rid1 ); } | | < | | | < < < | | < | > > | 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid=" "(SELECT tagid FROM tag WHERE tagname='wiki-%q')" " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)" " ORDER BY event.mtime DESC LIMIT 1", zPageName, rid1 ); } pW1 = manifest_get(rid1, CFTYPE_WIKI); if( pW1==0 ) fossil_redirect_home(); blob_init(&w1, pW1->zWiki, -1); blob_zero(&w2); if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){ blob_init(&w2, pW2->zWiki, -1); } blob_zero(&d); text_diff(&w2, &w1, &d, 5, 1); @ <pre> @ %h(blob_str(&d)) @ </pre> manifest_destroy(pW1); manifest_destroy(pW2); style_footer(); } /* ** WEBPAGE: wcontent ** ** all=1 Show deleted pages |
︙ | ︙ | |||
729 730 731 732 733 734 735 | @ <li>Most ordinary HTML works.</li> @ <li><verbatim> and <nowiki>.</li> @ </ol> @ <p>We call the first five rules above "wiki" formatting rules. The @ last two rules are the HTML formatting rule.</p> @ <h2>Formatting Rule Details</h2> @ <ol> | | | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < | | | | 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 | @ <li>Most ordinary HTML works.</li> @ <li><verbatim> and <nowiki>.</li> @ </ol> @ <p>We call the first five rules above "wiki" formatting rules. The @ last two rules are the HTML formatting rule.</p> @ <h2>Formatting Rule Details</h2> @ <ol> @ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms @ a paragraph break. Centered or right-justified paragraphs are not @ supported by wiki markup, but you can do these things if you need them @ using HTML.</p></li> @ <li> <p><span class="wikiruleHead">Bullet Lists</span>. @ A bullet list item is a line that begins with a single "*" character @ surrounded on @ both sides by two or more spaces or by a tab. Only a single level @ of bullet list is supported by wiki. For nested lists, use HTML.</p></li> @ <li> <p><span class="wikiruleHead">Enumeration Lists</span>. @ An enumeration list item is a line that begins with a single "#" character @ surrounded on both sides by two or more spaces or by a tab. Only a single @ level of enumeration list is supported by wiki. For nested lists or for @ enumerations that count using letters or roman numerials, use HTML.</p></li> @ <li> <p><span class="wikiruleHead">Indented Paragraphs</span>. @ Any paragraph that begins with two or more spaces or a tab and @ which is not a bullet or enumeration list item is rendered @ indented. Only a single level of indentation is supported by wiki; use @ HTML for deeper indentation.</p></li> @ <li> <p><span class="wikiruleHead">Hyperlinks</span>. @ Text within square brackets ("[...]") becomes a hyperlink. The @ target can be a wiki page name, the artifact ID of a check-in or ticket, @ the name of an image, or a URL. By default, the target is displayed @ as the text of the hyperlink. But you can specify alternative text @ after the target name separated by a "|" character.</p> @ <p>You can also link to internal anchor names using [#anchor-name], providing @ you have added the necessary "<a name="anchor-name"></a>" @ tag to your wiki page.</p></li> @ <li> <p><span class="wikiruleHead">HTML</span>. @ The following standard HTML elements may be used: show_allowed_wiki_markup(); @ . There are two non-standard elements available: @ <verbatim> and <nowiki>. @ No other elements are allowed. All attributes are checked and @ only a few benign attributes are allowed on each element. @ In particular, any attributes that specify javascript or CSS @ are elided.</p></li> @ <li><p><span class="wikiruleHead">Special Markup.</span> @ The <nowiki> tag disables all wiki formatting rules @ through the matching </nowiki> element. @ The <verbatim> tag works like <pre> with the addition @ that it also disables all wiki and HTML markup @ through the matching </verbatim>.</p></li> @ </ol> style_footer(); } /* ** Add a new wiki page to the respository. The page name is ** given by the zPageName parameter. isNew must be true to create |
︙ | ︙ | |||
943 944 945 946 947 948 949 | if( n==0 ){ goto wiki_cmd_usage; } if( strncmp(g.argv[2],"export",n)==0 ){ char const *zPageName; /* Name of the wiki page to export */ char const *zFile; /* Name of the output file (0=stdout) */ | | | | | > < < | < < | < | | > | 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 | if( n==0 ){ goto wiki_cmd_usage; } if( strncmp(g.argv[2],"export",n)==0 ){ char const *zPageName; /* Name of the wiki page to export */ char const *zFile; /* Name of the output file (0=stdout) */ int rid; /* Artifact ID of the wiki page */ int i; /* Loop counter */ char *zBody = 0; /* Wiki page content */ Manifest *pWiki = 0; /* Parsed wiki page content */ if( (g.argc!=4) && (g.argc!=5) ){ usage("export PAGENAME ?FILE?"); } zPageName = g.argv[3]; rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" " ORDER BY x.mtime DESC LIMIT 1", zPageName ); if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ zBody = pWiki->zWiki; } if( zBody==0 ){ fossil_fatal("wiki page [%s] not found",zPageName); } for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){} zFile = (g.argc==4) ? 0 : g.argv[4]; if( zFile ){ FILE * zF; short doClose = 0; if( (1 == strlen(zFile)) && ('-'==zFile[0]) ){ zF = stdout; }else{ zF = fopen( zFile, "w" ); doClose = zF ? 1 : 0; } if( ! zF ){ fossil_fatal("wiki export could not open output file for writing."); } fprintf(zF,"%.*s\n", i, zBody); if( doClose ) fclose(zF); }else{ printf("%.*s\n", i, zBody); } manifest_destroy(pWiki); return; }else if( strncmp(g.argv[2],"commit",n)==0 || strncmp(g.argv[2],"create",n)==0 ){ char *zPageName; Blob content; if( g.argc!=4 && g.argc!=5 ){ |
︙ | ︙ |
Changes to src/wikiformat.c.
︙ | ︙ | |||
148 149 150 151 152 153 154 | ** Allowed markup. ** ** Except for MARKUP_INVALID, this must all be in alphabetical order ** and in numerical sequence. The first markup type must be zero. ** The value for MARKUP_XYZ must correspond to the <xyz> entry ** in aAllowedMarkup[]. */ | | | | | | | | | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | > | > | > | | | | | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | ** Allowed markup. ** ** Except for MARKUP_INVALID, this must all be in alphabetical order ** and in numerical sequence. The first markup type must be zero. ** The value for MARKUP_XYZ must correspond to the <xyz> entry ** in aAllowedMarkup[]. */ #define MARKUP_INVALID 0 #define MARKUP_A 1 #define MARKUP_ADDRESS 2 #define MARKUP_B 3 #define MARKUP_BIG 4 #define MARKUP_BLOCKQUOTE 5 #define MARKUP_BR 6 #define MARKUP_CENTER 7 #define MARKUP_CITE 8 #define MARKUP_CODE 9 #define MARKUP_COL 10 #define MARKUP_COLGROUP 11 #define MARKUP_DD 12 #define MARKUP_DFN 13 #define MARKUP_DIV 14 #define MARKUP_DL 15 #define MARKUP_DT 16 #define MARKUP_EM 17 #define MARKUP_FONT 18 #define MARKUP_H1 19 #define MARKUP_H2 20 #define MARKUP_H3 21 #define MARKUP_H4 22 #define MARKUP_H5 23 #define MARKUP_H6 24 #define MARKUP_HR 25 #define MARKUP_I 26 #define MARKUP_IMG 27 #define MARKUP_KBD 28 #define MARKUP_LI 29 #define MARKUP_NOBR 30 #define MARKUP_NOWIKI 31 #define MARKUP_OL 32 #define MARKUP_P 33 #define MARKUP_PRE 34 #define MARKUP_S 35 #define MARKUP_SAMP 36 #define MARKUP_SMALL 37 #define MARKUP_SPAN 38 #define MARKUP_STRIKE 39 #define MARKUP_STRONG 40 #define MARKUP_SUB 41 #define MARKUP_SUP 42 #define MARKUP_TABLE 43 #define MARKUP_TBODY 44 #define MARKUP_TD 45 #define MARKUP_TFOOT 46 #define MARKUP_TH 47 #define MARKUP_THEAD 48 #define MARKUP_TR 49 #define MARKUP_TT 50 #define MARKUP_U 51 #define MARKUP_UL 52 #define MARKUP_VAR 53 #define MARKUP_VERBATIM 54 /* ** The various markup is divided into the following types: */ #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */ #define MUTYPE_BLOCK 0x0002 /* Forms a new paragraph. ex: <p>, <h2> */ #define MUTYPE_FONT 0x0004 /* Font changes. ex: <b>, <font>, <sub> */ |
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 248 249 250 251 252 | { "b", MARKUP_B, MUTYPE_FONT, 0 }, { "big", MARKUP_BIG, MUTYPE_FONT, 0 }, { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 }, { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, { "center", MARKUP_CENTER, MUTYPE_BLOCK, 0 }, { "cite", MARKUP_CITE, MUTYPE_FONT, 0 }, { "code", MARKUP_CODE, MUTYPE_FONT, 0 }, { "dd", MARKUP_DD, MUTYPE_LI, 0 }, { "dfn", MARKUP_DFN, MUTYPE_FONT, 0 }, { "div", MARKUP_DIV, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS }, { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_COMPACT }, { "dt", MARKUP_DT, MUTYPE_LI, 0 }, { "em", MARKUP_EM, MUTYPE_FONT, 0 }, { "font", MARKUP_FONT, MUTYPE_FONT, | > > > > | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | { "b", MARKUP_B, MUTYPE_FONT, 0 }, { "big", MARKUP_BIG, MUTYPE_FONT, 0 }, { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 }, { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, { "center", MARKUP_CENTER, MUTYPE_BLOCK, 0 }, { "cite", MARKUP_CITE, MUTYPE_FONT, 0 }, { "code", MARKUP_CODE, MUTYPE_FONT, 0 }, { "col", MARKUP_COL, MUTYPE_SINGLE, AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH }, { "colgroup", MARKUP_COLGROUP, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH}, { "dd", MARKUP_DD, MUTYPE_LI, 0 }, { "dfn", MARKUP_DFN, MUTYPE_FONT, 0 }, { "div", MARKUP_DIV, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS }, { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_COMPACT }, { "dt", MARKUP_DT, MUTYPE_LI, 0 }, { "em", MARKUP_EM, MUTYPE_FONT, 0 }, { "font", MARKUP_FONT, MUTYPE_FONT, |
︙ | ︙ | |||
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | { "ol", MARKUP_OL, MUTYPE_LIST, AMSK_START|AMSK_TYPE|AMSK_COMPACT }, { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS }, { "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 }, { "s", MARKUP_S, MUTYPE_FONT, 0 }, { "samp", MARKUP_SAMP, MUTYPE_FONT, 0 }, { "small", MARKUP_SMALL, MUTYPE_FONT, 0 }, { "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 }, { "strong", MARKUP_STRONG, MUTYPE_FONT, 0 }, { "sub", MARKUP_SUB, MUTYPE_FONT, 0 }, { "sup", MARKUP_SUP, MUTYPE_FONT, 0 }, { "table", MARKUP_TABLE, MUTYPE_TABLE, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS }, { "td", MARKUP_TD, MUTYPE_TD, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS }, { "th", MARKUP_TH, MUTYPE_TD, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS }, { "tr", MARKUP_TR, MUTYPE_TR, AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN|AMSK_CLASS }, { "tt", MARKUP_TT, MUTYPE_FONT, 0 }, { "u", MARKUP_U, MUTYPE_FONT, 0 }, { "ul", MARKUP_UL, MUTYPE_LIST, AMSK_TYPE|AMSK_COMPACT }, { "var", MARKUP_VAR, MUTYPE_FONT, 0 }, { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID|AMSK_TYPE }, }; /* ** Use binary search to locate a tag in the aMarkup[] table. */ static int findTag(const char *z){ int i, c, first, last; first = 1; | > > > > > > > > > > > > | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | { "ol", MARKUP_OL, MUTYPE_LIST, AMSK_START|AMSK_TYPE|AMSK_COMPACT }, { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS }, { "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 }, { "s", MARKUP_S, MUTYPE_FONT, 0 }, { "samp", MARKUP_SAMP, MUTYPE_FONT, 0 }, { "small", MARKUP_SMALL, MUTYPE_FONT, 0 }, { "span", MARKUP_SPAN, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS }, { "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 }, { "strong", MARKUP_STRONG, MUTYPE_FONT, 0 }, { "sub", MARKUP_SUB, MUTYPE_FONT, 0 }, { "sup", MARKUP_SUP, MUTYPE_FONT, 0 }, { "table", MARKUP_TABLE, MUTYPE_TABLE, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS }, { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS }, { "td", MARKUP_TD, MUTYPE_TD, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS }, { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS }, { "th", MARKUP_TH, MUTYPE_TD, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS }, { "thead", MARKUP_THEAD, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS }, { "tr", MARKUP_TR, MUTYPE_TR, AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN|AMSK_CLASS }, { "tt", MARKUP_TT, MUTYPE_FONT, 0 }, { "u", MARKUP_U, MUTYPE_FONT, 0 }, { "ul", MARKUP_UL, MUTYPE_LIST, AMSK_TYPE|AMSK_COMPACT }, { "var", MARKUP_VAR, MUTYPE_FONT, 0 }, { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID|AMSK_TYPE }, }; void show_allowed_wiki_markup( void ){ int i; /* loop over allowedAttr */ for( i=1 ; i<=sizeof(aMarkup)/sizeof(aMarkup[0]) - 1 ; i++ ){ @ <%s(aMarkup[i].zName)> } } /* ** Use binary search to locate a tag in the aMarkup[] table. */ static int findTag(const char *z){ int i, c, first, last; first = 1; |
︙ | ︙ | |||
319 320 321 322 323 324 325 | } return MARKUP_INVALID; } /* ** Token types */ | | | | | | | | | | | | | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | } return MARKUP_INVALID; } /* ** Token types */ #define TOKEN_MARKUP 1 /* <...> */ #define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */ #define TOKEN_LINK 3 /* [...] */ #define TOKEN_PARAGRAPH 4 /* blank lines */ #define TOKEN_NEWLINE 5 /* A single "\n" */ #define TOKEN_BUL_LI 6 /* " * " */ #define TOKEN_NUM_LI 7 /* " # " */ #define TOKEN_ENUM 8 /* " \(?\d+[.)]? " */ #define TOKEN_INDENT 9 /* " " */ #define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */ #define TOKEN_TEXT 11 /* None of the above */ /* ** State flags */ #define AT_NEWLINE 0x001 /* At start of a line */ #define AT_PARAGRAPH 0x002 /* At start of a paragraph */ #define ALLOW_WIKI 0x004 /* Allow wiki markup */ |
︙ | ︙ | |||
377 378 379 380 381 382 383 | */ static int wikiUsesHtml(void){ static int r = -1; if( r<0 ) r = db_get_boolean("wiki-use-html", 0); return r; } | < | | > > | | | | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 | */ static int wikiUsesHtml(void){ static int r = -1; if( r<0 ) r = db_get_boolean("wiki-use-html", 0); return r; } /* ** z points to a "<" character. Check to see if this is the start of ** a valid markup. If it is, return the total number of characters in ** the markup including the initial "<" and the terminating ">". If ** it is not well-formed markup, return 0. */ static int markupLength(const char *z){ int n = 1; int inparen = 0; int c; if( z[n]=='/' ){ n++; } if( !fossil_isalpha(z[n]) ) return 0; while( fossil_isalnum(z[n]) ){ n++; } c = z[n]; if( c=='/' && z[n+1]=='>' ){ return n+2; } if( c!='>' && !fossil_isspace(c) ) return 0; while( (c = z[n])!=0 && (c!='>' || inparen) ){ if( c==inparen ){ inparen = 0; }else if( inparen==0 && (c=='"' || c=='\'') ){ inparen = c; } n++; } if( z[n]!='>' ) return 0; return n+1; } /* ** z points to a "\n" character. Check to see if this newline is ** followed by one or more blank lines. If it is, return the number ** of characters through the closing "\n". If not, return 0. */ static int paragraphBreakLength(const char *z){ int i, n; int nNewline = 1; for(i=1, n=0; fossil_isspace(z[i]); i++){ if( z[i]=='\n' ){ nNewline++; n = i; } } if( nNewline>=2 ){ return n+1; |
︙ | ︙ | |||
457 458 459 460 461 462 463 | /* ** Return true if z[] begins with an HTML character element. */ static int isElement(const char *z){ int i; assert( z[0]=='&' ); if( z[1]=='#' ){ | | | | 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | /* ** Return true if z[] begins with an HTML character element. */ static int isElement(const char *z){ int i; assert( z[0]=='&' ); if( z[1]=='#' ){ for(i=2; fossil_isdigit(z[i]); i++){} return i>2 && z[i]==';'; }else{ for(i=1; fossil_isalpha(z[i]); i++){} return i>1 && z[i]==';'; } } /* ** Check to see if the z[] string is the beginning of a wiki list item. ** If it is, return the length of the bullet text. Otherwise return 0. |
︙ | ︙ | |||
486 487 488 489 490 491 492 | n++; i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } | | | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | n++; i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 || fossil_isspace(z[n]) ) return 0; return n; } /* ** Check to see if the z[] string is the beginning of a enumeration value. ** If it is, return the length of the bullet text. Otherwise return 0. ** |
︙ | ︙ | |||
511 512 513 514 515 516 517 | i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 ) return 0; | | | | | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 ) return 0; for(i=0; fossil_isdigit(z[n]); i++, n++){} if( i==0 ) return 0; if( z[n]=='.' ){ n++; } i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 || fossil_isspace(z[n]) ) return 0; return n; } /* ** Check to see if the z[] string is the beginning of an indented ** paragraph. If it is, return the length of the indent. Otherwise ** return 0. */ static int indentLength(const char *z){ int i, n; n = 0; i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 || fossil_isspace(z[n]) ) return 0; return n; } /* ** Check to see if the z[] string is a wiki hyperlink. If it is, ** return the length of the hyperlink. Otherwise return 0. */ |
︙ | ︙ | |||
587 588 589 590 591 592 593 | } if( (p->state & ALLOW_WIKI)!=0 ){ if( z[0]=='\n' ){ n = paragraphBreakLength(z); if( n>0 ){ *pTokenType = TOKEN_PARAGRAPH; return n; | | | | | 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 | } if( (p->state & ALLOW_WIKI)!=0 ){ if( z[0]=='\n' ){ n = paragraphBreakLength(z); if( n>0 ){ *pTokenType = TOKEN_PARAGRAPH; return n; }else if( fossil_isspace(z[1]) ){ *pTokenType = TOKEN_NEWLINE; return 1; } } if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){ n = listItemLength(z, '*'); if( n>0 ){ *pTokenType = TOKEN_BUL_LI; return n; } n = listItemLength(z, '#'); if( n>0 ){ *pTokenType = TOKEN_NUM_LI; return n; } n = enumLength(z); if( n>0 ){ *pTokenType = TOKEN_ENUM; return n; } } if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){ n = indentLength(z); if( n>0 ){ *pTokenType = TOKEN_INDENT; return n; } } if( z[0]=='[' && (n = linkLength(z))>0 ){ |
︙ | ︙ | |||
680 681 682 683 684 685 686 | p->endTag = 1; i = 2; }else{ p->endTag = 0; i = 1; } j = 0; | | | | | | | | | | | | 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | p->endTag = 1; i = 2; }else{ p->endTag = 0; i = 1; } j = 0; while( fossil_isalnum(z[i]) ){ if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); i++; } zTag[j] = 0; p->iCode = findTag(zTag); p->iType = aMarkup[p->iCode].iType; p->nAttr = 0; while( fossil_isspace(z[i]) ){ i++; } while( p->nAttr<8 && fossil_isalpha(z[i]) ){ int attrOk; /* True to preserver attribute. False to ignore it */ j = 0; while( fossil_isalnum(z[i]) ){ if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); i++; } zTag[j] = 0; p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0; while( fossil_isspace(z[i]) ){ z++; } if( z[i]!='=' ){ p->aAttr[p->nAttr].zValue = 0; p->aAttr[p->nAttr].cTerm = 0; c = 0; }else{ i++; while( fossil_isspace(z[i]) ){ z++; } if( z[i]=='"' ){ i++; zValue = &z[i]; while( z[i] && z[i]!='"' ){ i++; } }else if( z[i]=='\'' ){ i++; zValue = &z[i]; while( z[i] && z[i]!='\'' ){ i++; } }else{ zValue = &z[i]; while( !fossil_isspace(z[i]) && z[i]!='>' ){ z++; } } if( attrOk ){ p->aAttr[p->nAttr].zValue = zValue; p->aAttr[p->nAttr].cTerm = c = z[i]; z[i] = 0; } i++; } if( attrOk ){ seen |= aAttribute[iACode].iMask; p->nAttr++; } while( fossil_isspace(z[i]) ){ i++; } if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; } } /* ** Render markup on the given blob. */ |
︙ | ︙ | |||
754 755 756 757 758 759 760 761 762 763 764 765 766 767 | const char *zVal = p->aAttr[i].zValue; if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){ blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal); }else{ blob_appendf(pOut, "=\"%s\"", zVal); } } } blob_append(pOut, ">", 1); } } /* ** When the markup was parsed, some "\000" may have been inserted. | > > > | 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 | const char *zVal = p->aAttr[i].zValue; if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){ blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal); }else{ blob_appendf(pOut, "=\"%s\"", zVal); } } } if (p->iType & MUTYPE_SINGLE){ blob_append(pOut, " /", 2); } blob_append(pOut, ">", 1); } } /* ** When the markup was parsed, some "\000" may have been inserted. |
︙ | ︙ | |||
810 811 812 813 814 815 816 | /* ** Push a new markup value onto the stack. Enlarge the stack ** if necessary. */ static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ if( p->nStack>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + 100; | | < < < | 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 | /* ** Push a new markup value onto the stack. Enlarge the stack ** if necessary. */ static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ if( p->nStack>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + 100; p->aStack = fossil_realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); } p->aStack[p->nStack].iCode = elem; p->aStack[p->nStack].zId = zId; p->aStack[p->nStack].allowWiki = w; p->nStack++; } static void pushStack(Renderer *p, int elem){ |
︙ | ︙ | |||
883 884 885 886 887 888 889 | return p->aStack[i-1].iCode; } /* ** Begin a new paragraph if that something that is needed. */ static void startAutoParagraph(Renderer *p){ | > | | 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 | return p->aStack[i-1].iCode; } /* ** Begin a new paragraph if that something that is needed. */ static void startAutoParagraph(Renderer *p){ if( p->wantAutoParagraph==0 ) return; if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return; blob_appendf(p->pOut, "<p>", -1); pushStack(p, MARKUP_P); p->wantAutoParagraph = 0; p->inAutoParagraph = 1; } /* |
︙ | ︙ | |||
1017 1018 1019 1020 1021 1022 1023 | int isClosed = 0; if( is_ticket(zTarget, &isClosed) ){ /* Special display processing for tickets. Display the hyperlink ** as crossed out if the ticket is closed. */ if( isClosed ){ if( g.okHistory ){ | | > | | | | | | 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 | int isClosed = 0; if( is_ticket(zTarget, &isClosed) ){ /* Special display processing for tickets. Display the hyperlink ** as crossed out if the ticket is closed. */ if( isClosed ){ if( g.okHistory ){ blob_appendf(p->pOut, "<a href=\"%s/info/%s\"><span class=\"wikiTagCancelled\">", g.zBaseURL, zTarget ); zTerm = "</span></a>"; }else{ blob_appendf(p->pOut,"<span class=\"wikiTagCancelled\">"); zTerm = "</span>"; } }else{ if( g.okHistory ){ blob_appendf(p->pOut,"<a href=\"%s/info/%s\">", g.zBaseURL, zTarget ); }else{ zTerm = ""; } } }else if( g.okHistory ){ blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget); } }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ blob_appendf(p->pOut, "<a href=\"%s/timeline?c=%T\">", g.zBaseURL, zTarget); }else if( strncmp(zTarget, "wiki:", 5)==0 && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ zTarget += 5; blob_appendf(p->pOut, "<a href=\"%s/wiki?name=%T\">", g.zBaseURL, zTarget); }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
︙ | ︙ | |||
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 | if( inlineOnly ){ blob_append(p->pOut, " • ", -1); }else{ if( p->wikiList!=MARKUP_UL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } pushStack(p, MARKUP_UL); blob_append(p->pOut, "<ul>", 4); p->wikiList = MARKUP_UL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); blob_append(p->pOut, "<li>", 4); } break; } case TOKEN_NUM_LI: { if( inlineOnly ){ blob_append(p->pOut, " # ", -1); }else{ if( p->wikiList!=MARKUP_OL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } pushStack(p, MARKUP_OL); blob_append(p->pOut, "<ol>", 4); p->wikiList = MARKUP_OL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); blob_append(p->pOut, "<li>", 4); } break; } case TOKEN_ENUM: { if( inlineOnly ){ blob_appendf(p->pOut, " (%d) ", atoi(z)); }else{ if( p->wikiList!=MARKUP_OL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } pushStack(p, MARKUP_OL); blob_append(p->pOut, "<ol>", 4); p->wikiList = MARKUP_OL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); | > > > | 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 | if( inlineOnly ){ blob_append(p->pOut, " • ", -1); }else{ if( p->wikiList!=MARKUP_UL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } endAutoParagraph(p); pushStack(p, MARKUP_UL); blob_append(p->pOut, "<ul>", 4); p->wikiList = MARKUP_UL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); blob_append(p->pOut, "<li>", 4); } break; } case TOKEN_NUM_LI: { if( inlineOnly ){ blob_append(p->pOut, " # ", -1); }else{ if( p->wikiList!=MARKUP_OL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } endAutoParagraph(p); pushStack(p, MARKUP_OL); blob_append(p->pOut, "<ol>", 4); p->wikiList = MARKUP_OL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); blob_append(p->pOut, "<li>", 4); } break; } case TOKEN_ENUM: { if( inlineOnly ){ blob_appendf(p->pOut, " (%d) ", atoi(z)); }else{ if( p->wikiList!=MARKUP_OL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } endAutoParagraph(p); pushStack(p, MARKUP_OL); blob_append(p->pOut, "<ol>", 4); p->wikiList = MARKUP_OL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); |
︙ | ︙ | |||
1212 1213 1214 1215 1216 1217 1218 | startAutoParagraph(p); zTarget = &z[1]; for(i=1; z[i] && z[i]!=']'; i++){ if( z[i]=='|' && zDisplay==0 ){ zDisplay = &z[i+1]; z[i] = 0; | | | > > | | 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 | startAutoParagraph(p); zTarget = &z[1]; for(i=1; z[i] && z[i]!=']'; i++){ if( z[i]=='|' && zDisplay==0 ){ zDisplay = &z[i+1]; z[i] = 0; for(j=i-1; j>0 && fossil_isspace(z[j]); j--){ z[j] = 0; } } } z[i] = 0; if( zDisplay==0 ){ zDisplay = zTarget; }else{ while( fossil_isspace(*zDisplay) ) zDisplay++; } openHyperlink(p, zTarget, zClose, sizeof(zClose)); savedState = p->state; p->state &= ~ALLOW_WIKI; p->state |= FONT_MARKUP_ONLY; wiki_render(p, zDisplay); p->state = savedState; blob_append(p->pOut, zClose, -1); break; } case TOKEN_TEXT: { int i; for(i=0; i<n && fossil_isspace(z[i]); i++){} if( i<n ) startAutoParagraph(p); blob_append(p->pOut, z, n); break; } case TOKEN_RAW: { blob_append(p->pOut, z, n); break; } |
︙ | ︙ | |||
1345 1346 1347 1348 1349 1350 1351 | p->zVerbatimId = markup.aAttr[0].zValue; }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){ blob_appendf(p->pOut, "<pre name='code' class='%s'>", markup.aAttr[vAttrIdx].zValue); vAttrDidAppend=1; } } | | > > > | 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 | p->zVerbatimId = markup.aAttr[0].zValue; }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){ blob_appendf(p->pOut, "<pre name='code' class='%s'>", markup.aAttr[vAttrIdx].zValue); vAttrDidAppend=1; } } if( !vAttrDidAppend ) { endAutoParagraph(p); blob_append(p->pOut, "<pre class='verbatim'>",-1); } p->wantAutoParagraph = 0; }else if( markup.iType==MUTYPE_LI ){ if( backupToType(p, MUTYPE_LIST)==0 ){ endAutoParagraph(p); pushStack(p, MARKUP_UL); blob_append(p->pOut, "<ul>", 4); } pushStack(p, MARKUP_LI); renderMarkup(p->pOut, &markup); }else if( markup.iType==MUTYPE_TR ){ |
︙ | ︙ | |||
1382 1383 1384 1385 1386 1387 1388 | startAutoParagraph(p); renderMarkup(p->pOut, &markup); pushStack(p, markup.iCode); }else { if( markup.iType==MUTYPE_FONT ){ startAutoParagraph(p); | | > > > > > > > > > > | 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 | startAutoParagraph(p); renderMarkup(p->pOut, &markup); pushStack(p, markup.iCode); }else { if( markup.iType==MUTYPE_FONT ){ startAutoParagraph(p); }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){ p->wantAutoParagraph = 0; } if( markup.iCode==MARKUP_HR || markup.iCode==MARKUP_H1 || markup.iCode==MARKUP_H2 || markup.iCode==MARKUP_H3 || markup.iCode==MARKUP_H4 || markup.iCode==MARKUP_H5 || markup.iCode==MARKUP_P ){ endAutoParagraph(p); } if( (markup.iType & MUTYPE_STACK )!=0 ){ pushStack(p, markup.iCode); } renderMarkup(p->pOut, &markup); } break; |
︙ | ︙ | |||
1473 1474 1475 1476 1477 1478 1479 | ** title. */ int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ char *z; int i; int iStart; z = skip_bom(blob_str(pIn)); | | | 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 | ** title. */ int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ char *z; int i; int iStart; z = skip_bom(blob_str(pIn)); for(i=0; fossil_isspace(z[i]); i++){} if( z[i]!='<' ) return 0; i++; if( strncmp(&z[i],"title>", 6)!=0 ) return 0; iStart = i+6; for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){} if( z[i]!='<' ) return 0; blob_init(pTitle, &z[iStart], i-iStart); |
︙ | ︙ |
Changes to src/winhttp.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements a very simple (and low-performance) HTTP server ** for windows. */ | < > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements a very simple (and low-performance) HTTP server ** for windows. */ #include "config.h" #ifdef _WIN32 /* This code is for win32 only */ #include "winhttp.h" #include <windows.h> /* ** The HttpRequest structure holds information about each incoming ** HTTP request. */ |
︙ | ︙ | |||
103 104 105 106 107 108 109 | break; } wanted -= got; } fclose(out); out = 0; sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", | | | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | break; } wanted -= got; } fclose(out); out = 0; sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", _pgmptr, g.zRepositoryName, zRequestFName, zReplyFName, inet_ntoa(p->addr.sin_addr), p->zNotFound ); fossil_system(zCmd); in = fopen(zReplyFName, "rb"); if( in ){ while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ send(p->s, zHdr, got, 0); } } |
︙ | ︙ | |||
131 132 133 134 135 136 137 | ** Start a listening socket and process incoming HTTP requests on ** that socket. */ void win32_http_server( int mnPort, int mxPort, /* Range of allowed TCP port numbers */ const char *zBrowser, /* Command to launch browser. (Or NULL) */ const char *zStopper, /* Stop server when this file is exists (Or NULL) */ | | > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | ** Start a listening socket and process incoming HTTP requests on ** that socket. */ void win32_http_server( int mnPort, int mxPort, /* Range of allowed TCP port numbers */ const char *zBrowser, /* Command to launch browser. (Or NULL) */ const char *zStopper, /* Stop server when this file is exists (Or NULL) */ const char *zNotFound, /* The --notfound option, or NULL */ int flags /* One or more HTTP_SERVER_ flags */ ){ WSADATA wd; SOCKET s = INVALID_SOCKET; SOCKADDR_IN addr; int idCnt = 0; int iPort = mnPort; char *zNotFoundOption; |
︙ | ︙ | |||
156 157 158 159 160 161 162 | while( iPort<=mxPort ){ s = socket(AF_INET, SOCK_STREAM, 0); if( s==INVALID_SOCKET ){ fossil_fatal("unable to create a socket"); } addr.sin_family = AF_INET; addr.sin_port = htons(iPort); | > > > | > | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | while( iPort<=mxPort ){ s = socket(AF_INET, SOCK_STREAM, 0); if( s==INVALID_SOCKET ){ fossil_fatal("unable to create a socket"); } addr.sin_family = AF_INET; addr.sin_port = htons(iPort); if( flags & HTTP_SERVER_LOCALHOST ){ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); }else{ addr.sin_addr.s_addr = htonl(INADDR_ANY); } if( bind(s, (struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR ){ closesocket(s); iPort++; continue; } if( listen(s, SOMAXCONN)==SOCKET_ERROR ){ closesocket(s); |
︙ | ︙ | |||
182 183 184 185 186 187 188 | } } zTempPrefix = mprintf("fossil_server_P%d_", iPort); printf("Listening for HTTP requests on TCP port %d\n", iPort); if( zBrowser ){ zBrowser = mprintf(zBrowser, iPort); printf("Launch webbrowser: %s\n", zBrowser); | | | < < < | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | } } zTempPrefix = mprintf("fossil_server_P%d_", iPort); printf("Listening for HTTP requests on TCP port %d\n", iPort); if( zBrowser ){ zBrowser = mprintf(zBrowser, iPort); printf("Launch webbrowser: %s\n", zBrowser); fossil_system(zBrowser); } printf("Type Ctrl-C to stop the HTTP server\n"); for(;;){ SOCKET client; SOCKADDR_IN client_addr; HttpRequest *p; int len = sizeof(client_addr); client = accept(s, (struct sockaddr*)&client_addr, &len); if( zStopper && file_size(zStopper)>=0 ){ break; } if( client==INVALID_SOCKET ){ closesocket(s); fossil_fatal("error from accept()"); } p = fossil_malloc( sizeof(*p) ); p->id = ++idCnt; p->s = client; p->addr = client_addr; p->zNotFound = zNotFoundOption; _beginthread(win32_process_one_http_request, 0, (void*)p); } closesocket(s); WSACleanup(); } #endif /* _WIN32 -- This code is for win32 only */ |
Changes to src/xfer.c.
︙ | ︙ | |||
70 71 72 73 74 75 76 | } /* ** Remember that the other side of the connection already has a copy ** of the file rid. */ static void remote_has(int rid){ | > > | > > > > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | } /* ** Remember that the other side of the connection already has a copy ** of the file rid. */ static void remote_has(int rid){ if( rid ){ static Stmt q; db_static_prepare(&q, "INSERT OR IGNORE INTO onremote VALUES(:r)"); db_bind_int(&q, ":r", rid); db_step(&q); db_reset(&q); } } /* ** The aToken[0..nToken-1] blob array is a parse of a "file" line ** message. This routine finishes parsing that message and does ** a record insert of the file. ** |
︙ | ︙ | |||
93 94 95 96 97 98 99 | ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. ** ** Any artifact successfully received by this routine is considered to ** be public and is therefore removed from the "private" table. */ | | | > | > > > > > > > > > > > > | | > > | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. ** ** Any artifact successfully received by this routine is considered to ** be public and is therefore removed from the "private" table. */ static void xfer_accept_file(Xfer *pXfer, int cloneFlag){ int n; int rid; int srcid = 0; Blob content, hash; if( pXfer->nToken<3 || pXfer->nToken>4 || !blob_is_uuid(&pXfer->aToken[1]) || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n) || n<0 || (pXfer->nToken==4 && !blob_is_uuid(&pXfer->aToken[2])) ){ blob_appendf(&pXfer->err, "malformed file line"); return; } blob_zero(&content); blob_zero(&hash); blob_extract(pXfer->pIn, n, &content); if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ /* Ignore files that have been shunned */ return; } if( cloneFlag ){ if( pXfer->nToken==4 ){ srcid = rid_from_uuid(&pXfer->aToken[2], 1); pXfer->nDeltaRcvd++; }else{ srcid = 0; pXfer->nFileRcvd++; } rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); remote_has(rid); blob_reset(&content); return; } if( pXfer->nToken==4 ){ Blob src, next; srcid = rid_from_uuid(&pXfer->aToken[2], 1); if( content_get(srcid, &src)==0 ){ rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); pXfer->nDanglingFile++; db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); content_make_public(rid); return; } pXfer->nDeltaRcvd++; blob_delta_apply(&src, &content, &next); blob_reset(&src); blob_reset(&content); content = next; }else{ pXfer->nFileRcvd++; } sha1sum_blob(&content, &hash); if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ blob_appendf(&pXfer->err, "content does not match sha1 hash"); } |
︙ | ︙ | |||
463 464 465 466 467 468 469 | /* ** Check to see if the number of unclustered entries is greater than ** 100 and if it is, form a new cluster. Unclustered phantoms do not ** count toward the 100 total. And phantoms are never added to a new ** cluster. */ | | > | | < < | | | | | | | | | > > > > > > > > > | > | > > | | | < | | > > | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 | /* ** Check to see if the number of unclustered entries is greater than ** 100 and if it is, form a new cluster. Unclustered phantoms do not ** count toward the 100 total. And phantoms are never added to a new ** cluster. */ void create_cluster(void){ Blob cluster, cksum; Stmt q; int nUncl; int nRow = 0; /* We should not ever get any private artifacts in the unclustered table. ** But if we do (because of a bug) now is a good time to delete them. */ db_multi_exec( "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" ); nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM phantom" " WHERE rid=unclustered.rid)"); if( nUncl>=100 ){ blob_zero(&cluster); db_prepare(&q, "SELECT uuid FROM unclustered, blob" " WHERE NOT EXISTS(SELECT 1 FROM phantom" " WHERE rid=unclustered.rid)" " AND unclustered.rid=blob.rid" " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " ORDER BY 1"); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0)); nRow++; if( nRow>=800 && nUncl>nRow+100 ){ md5sum_blob(&cluster, &cksum); blob_appendf(&cluster, "Z %b\n", &cksum); blob_reset(&cksum); content_put(&cluster, 0, 0); blob_reset(&cluster); nUncl -= nRow; nRow = 0; } } db_finalize(&q); db_multi_exec("DELETE FROM unclustered"); if( nRow>0 ){ md5sum_blob(&cluster, &cksum); blob_appendf(&cluster, "Z %b\n", &cksum); blob_reset(&cksum); content_put(&cluster, 0, 0); blob_reset(&cluster); } } } /* ** Send an igot message for every entry in unclustered table. ** Return the number of cards sent. */ static int send_unclustered(Xfer *pXfer){ |
︙ | ︙ | |||
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 | int nErr = 0; Xfer xfer; int deltaFlag = 0; int isClone = 0; int nGimme = 0; int size; int recvConfig = 0; if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } memset(&xfer, 0, sizeof(xfer)); blobarray_zero(xfer.aToken, count(xfer.aToken)); cgi_set_content_type(g.zContentType); blob_zero(&xfer.err); xfer.pIn = &g.cgiIn; xfer.pOut = cgi_output_blob(); | > | | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | int nErr = 0; Xfer xfer; int deltaFlag = 0; int isClone = 0; int nGimme = 0; int size; int recvConfig = 0; char *zNow; if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } memset(&xfer, 0, sizeof(xfer)); blobarray_zero(xfer.aToken, count(xfer.aToken)); cgi_set_content_type(g.zContentType); blob_zero(&xfer.err); xfer.pIn = &g.cgiIn; xfer.pOut = cgi_output_blob(); xfer.mxSend = db_get_int("max-download", 20000000); g.xferPanic = 1; db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" ); manifest_crosslink_begin(); |
︙ | ︙ | |||
625 626 627 628 629 630 631 | if( blob_eq(&xfer.aToken[0], "file") ){ if( !isPush ){ cgi_reset_content(); @ error not\sauthorized\sto\swrite nErr++; break; } | | | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | if( blob_eq(&xfer.aToken[0], "file") ){ if( !isPush ){ cgi_reset_content(); @ error not\sauthorized\sto\swrite nErr++; break; } xfer_accept_file(&xfer, 0); if( blob_size(&xfer.err) ){ cgi_reset_content(); @ error %T(blob_str(&xfer.err)) nErr++; break; } }else |
︙ | ︙ | |||
711 712 713 714 715 716 717 | } }else{ isPush = 1; } } }else | | > > > > > > > > > > > > > > > | | | > | 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 | } }else{ isPush = 1; } } }else /* clone ?PROTOCOL-VERSION? ?SEQUENCE-NUMBER? ** ** The client knows nothing. Tell all. */ if( blob_eq(&xfer.aToken[0], "clone") ){ int iVers; login_check_credentials(); if( !g.okClone ){ cgi_reset_content(); @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) @ error not\sauthorized\sto\sclone nErr++; break; } if( xfer.nToken==3 && blob_is_int(&xfer.aToken[1], &iVers) && iVers>=2 ){ int seqno, max; blob_is_int(&xfer.aToken[2], &seqno); max = db_int(0, "SELECT max(rid) FROM blob"); while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ send_file(&xfer, seqno, 0, 1); seqno++; } if( seqno>=max ) seqno = 0; @ clone_seqno %d(seqno) }else{ isClone = 1; isPull = 1; deltaFlag = 1; } @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) }else /* login USER NONCE SIGNATURE ** ** Check for a valid login. This has to happen before anything else. ** The client can send multiple logins. Permissions are cumulative. |
︙ | ︙ | |||
867 868 869 870 871 872 873 874 875 876 877 878 879 880 | create_cluster(); send_unclustered(&xfer); } if( recvConfig ){ configure_finalize_receive(); } manifest_crosslink_end(); db_end_transaction(0); } /* ** COMMAND: test-xfer ** ** This command is used for debugging the server. There is a single | > > > > > > > > | 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 | create_cluster(); send_unclustered(&xfer); } if( recvConfig ){ configure_finalize_receive(); } manifest_crosslink_end(); /* Send the server timestamp last, in case prior processing happened ** to use up a significant fraction of our time window. */ zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); @ # timestamp %s(zNow) free(zNow); db_end_transaction(0); } /* ** COMMAND: test-xfer ** ** This command is used for debugging the server. There is a single |
︙ | ︙ | |||
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | int size; /* Size of a config value */ int nFileSend = 0; int origConfigRcvMask; /* Original value of configRcvMask */ int nFileRecv; /* Number of files received */ int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ const char *zCookie; /* Server cookie */ int nSent, nRcvd; /* Bytes sent and received (after compression) */ Blob send; /* Text we are sending to the server */ Blob recv; /* Reply we got back from the server */ Xfer xfer; /* Transfer data */ const char *zSCode = db_get("server-code", "x"); const char *zPCode = db_get("project-code", 0); if( db_get_boolean("dont-push", 0) ) pushFlag = 0; if( pushFlag + pullFlag + cloneFlag == 0 && configRcvMask==0 && configSendMask==0 ) return; | > > > > | 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | int size; /* Size of a config value */ int nFileSend = 0; int origConfigRcvMask; /* Original value of configRcvMask */ int nFileRecv; /* Number of files received */ int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ const char *zCookie; /* Server cookie */ int nSent, nRcvd; /* Bytes sent and received (after compression) */ int cloneSeqno = 1; /* Sequence number for clones */ Blob send; /* Text we are sending to the server */ Blob recv; /* Reply we got back from the server */ Xfer xfer; /* Transfer data */ int pctDone; /* Percentage done with a message */ int lastPctDone = -1; /* Last displayed pctDone */ double rArrivalTime; /* Time at which a message arrived */ const char *zSCode = db_get("server-code", "x"); const char *zPCode = db_get("project-code", 0); if( db_get_boolean("dont-push", 0) ) pushFlag = 0; if( pushFlag + pullFlag + cloneFlag == 0 && configRcvMask==0 && configSendMask==0 ) return; |
︙ | ︙ | |||
970 971 972 973 974 975 976 | blob_zero(&xfer.line); origConfigRcvMask = 0; /* ** Always begin with a clone, pull, or push message */ if( cloneFlag ){ | | > > | | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 | blob_zero(&xfer.line); origConfigRcvMask = 0; /* ** Always begin with a clone, pull, or push message */ if( cloneFlag ){ blob_appendf(&send, "clone 2 %d\n", cloneSeqno); pushFlag = 0; pullFlag = 0; nCardSent++; /* TBD: Request all transferable configuration values */ content_enable_dephantomize(0); }else if( pullFlag ){ blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); nCardSent++; } if( pushFlag ){ blob_appendf(&send, "push %s %s\n", zSCode, zPCode); nCardSent++; } manifest_crosslink_begin(); transport_global_startup(); fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas"); while( go ){ int newPhantom = 0; char *zRandomness; /* Send make the most recently received cookie. Let the server ** figure out if this is a cookie that it cares about. */ zCookie = db_get("cookie", 0); if( zCookie ){ blob_appendf(&send, "cookie %s\n", zCookie); } /* Generate gimme cards for phantoms and leaf cards ** for all leaves. */ if( pullFlag || (cloneFlag && cloneSeqno==1) ){ request_phantoms(&xfer, mxPhantomReq); } if( pushFlag ){ send_unsent(&xfer); nCardSent += send_unclustered(&xfer); } |
︙ | ︙ | |||
1050 1051 1052 1053 1054 1055 1056 | */ zRandomness = db_text(0, "SELECT hex(randomblob(20))"); blob_appendf(&send, "# %s\n", zRandomness); free(zRandomness); /* Exchange messages with the server */ nFileSend = xfer.nFileSent + xfer.nDeltaSent; | | > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | > | | 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 | */ zRandomness = db_text(0, "SELECT hex(randomblob(20))"); blob_appendf(&send, "# %s\n", zRandomness); free(zRandomness); /* Exchange messages with the server */ nFileSend = xfer.nFileSent + xfer.nDeltaSent; fossil_print(zValueFormat, "Sent:", blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, xfer.nFileSent, xfer.nDeltaSent); nCardSent = 0; nCardRcvd = 0; xfer.nFileSent = 0; xfer.nDeltaSent = 0; xfer.nGimmeSent = 0; xfer.nIGotSent = 0; if( !g.cgiOutput && !g.fQuiet ){ printf("waiting for server..."); } fflush(stdout); http_exchange(&send, &recv, cloneFlag==0 || nCycle>0); lastPctDone = -1; blob_reset(&send); rArrivalTime = db_double(0.0, "SELECT julianday('now')"); /* Begin constructing the next message (which might never be ** sent) by beginning with the pull or push cards */ if( pullFlag ){ blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); nCardSent++; } if( pushFlag ){ blob_appendf(&send, "push %s %s\n", zSCode, zPCode); nCardSent++; } go = 0; /* Process the reply that came back from the server */ while( blob_line(&recv, &xfer.line) ){ if( g.fHttpTrace ){ printf("\rGOT: %.*s", (int)blob_size(&xfer.line), blob_buffer(&xfer.line)); } if( blob_buffer(&xfer.line)[0]=='#' ){ const char *zLine = blob_buffer(&xfer.line); if( memcmp(zLine, "# timestamp ", 12)==0 ){ char zTime[20]; double rDiff; sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]); rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g", zTime, rArrivalTime); if( rDiff<0.0 ) rDiff = -rDiff; if( rDiff>9e98 ) rDiff = 0.0; if( (rDiff*24.0*3600.0)>=60.0 ){ fossil_warning("*** time skew *** server time differs by %s", db_timespan_name(rDiff)); g.clockSkewSeen = 1; } } continue; } xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); nCardRcvd++; if( !g.cgiOutput && !g.fQuiet && recv.nUsed>0 ){ pctDone = (recv.iCursor*100)/recv.nUsed; if( pctDone!=lastPctDone ){ printf("\rprocessed: %d%% ", pctDone); lastPctDone = pctDone; fflush(stdout); } } /* file UUID SIZE \n CONTENT ** file UUID DELTASRC SIZE \n CONTENT ** ** Receive a file transmitted from the server. */ if( blob_eq(&xfer.aToken[0],"file") ){ xfer_accept_file(&xfer, cloneFlag); }else /* gimme UUID ** ** Server is requesting a file. If the file is a manifest, assume ** that the server will also want to know all of the content files ** associated with the manifest and send those too. |
︙ | ︙ | |||
1154 1155 1156 1157 1158 1159 1160 | if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){ fossil_fatal("server loop"); } if( zPCode==0 ){ zPCode = mprintf("%b", &xfer.aToken[2]); db_set("project-code", zPCode, 0); } | | | 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 | if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){ fossil_fatal("server loop"); } if( zPCode==0 ){ zPCode = mprintf("%b", &xfer.aToken[2]); db_set("project-code", zPCode, 0); } blob_appendf(&send, "clone 2 %d\n", cloneSeqno); nCardSent++; }else /* config NAME SIZE \n CONTENT ** ** Receive a configuration value from the server. */ |
︙ | ︙ | |||
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 | ** ** Each cookie received overwrites the prior cookie from the ** same server. */ if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ db_set("cookie", blob_str(&xfer.aToken[1]), 0); }else /* message MESSAGE ** ** Print a message. Similar to "error" but does not stop processing. ** ** If the "login failed" message is seen, clear the sync password prior ** to the next cycle. | > > > > > > > > > > > | 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 | ** ** Each cookie received overwrites the prior cookie from the ** same server. */ if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ db_set("cookie", blob_str(&xfer.aToken[1]), 0); }else /* clone_seqno N ** ** When doing a clone, the server tries to send all of its artifacts ** in sequence. This card indicates the sequence number of the next ** blob that needs to be sent. If N<=0 that indicates that all blobs ** have been sent. */ if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){ blob_is_int(&xfer.aToken[1], &cloneSeqno); }else /* message MESSAGE ** ** Print a message. Similar to "error" but does not stop processing. ** ** If the "login failed" message is seen, clear the sync password prior ** to the next cycle. |
︙ | ︙ | |||
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 | ** there are still phantoms, then go another round. */ nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile; if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){ go = 1; mxPhantomReq = nFileRecv*2; if( mxPhantomReq<200 ) mxPhantomReq = 200; } nCardRcvd = 0; xfer.nFileRcvd = 0; xfer.nDeltaRcvd = 0; xfer.nDanglingFile = 0; /* If we have one or more files queued to send, then go ** another round */ if( xfer.nFileSent+xfer.nDeltaSent>0 ){ go = 1; } /* If this is a clone, the go at least two rounds */ if( cloneFlag && nCycle==1 ) go = 1; }; transport_stats(&nSent, &nRcvd, 1); fossil_print("Total network traffic: %d bytes sent, %d bytes received\n", nSent, nRcvd); transport_close(); transport_global_shutdown(); db_multi_exec("DROP TABLE onremote"); manifest_crosslink_end(); db_end_transaction(0); } | > > > > > > | 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 | ** there are still phantoms, then go another round. */ nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile; if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){ go = 1; mxPhantomReq = nFileRecv*2; if( mxPhantomReq<200 ) mxPhantomReq = 200; }else if( cloneFlag && nFileRecv>0 ){ go = 1; } nCardRcvd = 0; xfer.nFileRcvd = 0; xfer.nDeltaRcvd = 0; xfer.nDanglingFile = 0; /* If we have one or more files queued to send, then go ** another round */ if( xfer.nFileSent+xfer.nDeltaSent>0 ){ go = 1; } /* If this is a clone, the go at least two rounds */ if( cloneFlag && nCycle==1 ) go = 1; /* Stop the cycle if the server sends a "clone_seqno 0" card */ if( cloneSeqno<=0 ) go = 0; }; transport_stats(&nSent, &nRcvd, 1); fossil_print("Total network traffic: %d bytes sent, %d bytes received\n", nSent, nRcvd); transport_close(); transport_global_shutdown(); db_multi_exec("DROP TABLE onremote"); manifest_crosslink_end(); content_enable_dephantomize(1); db_end_transaction(0); } |
Changes to src/zip.c.
︙ | ︙ | |||
100 101 102 103 104 105 106 | c = zName[i+1]; zName[i+1] = 0; for(j=0; j<nDir; j++){ if( strcmp(zName, azDir[j])==0 ) break; } if( j>=nDir ){ nDir++; | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | c = zName[i+1]; zName[i+1] = 0; for(j=0; j<nDir; j++){ if( strcmp(zName, azDir[j])==0 ) break; } if( j>=nDir ){ nDir++; azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir); azDir[j] = mprintf("%s", zName); zip_add_file(zName, 0); } zName[i+1] = c; } } } |
︙ | ︙ | |||
311 312 313 314 315 316 317 | ** added to as part of the zip file. It may be 0 or an empty string, ** in which case it is ignored. The intention is to create a zip which ** politely expands into a subdir instead of filling your current dir ** with source files. For example, pass a UUID or "ProjectName". ** */ void zip_of_baseline(int rid, Blob *pZip, const char *zDir){ | < | | > < < > | | > | | | | | | | | | | | | > | > | | < < > | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | ** added to as part of the zip file. It may be 0 or an empty string, ** in which case it is ignored. The intention is to create a zip which ** politely expands into a subdir instead of filling your current dir ** with source files. For example, pass a UUID or "ProjectName". ** */ void zip_of_baseline(int rid, Blob *pZip, const char *zDir){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pZip); return; } blob_zero(&hash); blob_zero(&filename); zip_open(); if( zDir && zDir[0] ){ blob_appendf(&filename, "%s/", zDir); } nPrefix = blob_size(&filename); pManifest = manifest_get(rid, CFTYPE_MANIFEST); if( pManifest ){ char *zName; zip_set_timedate(pManifest->rDate); if( db_get_boolean("manifest", 0) ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); zip_add_folders(zName); zip_add_file(zName, &mfile); sha1sum_blob(&mfile, &hash); blob_reset(&mfile); blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); zip_add_file(zName, &hash); blob_reset(&hash); } manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ int fid = uuid_to_rid(pFile->zUuid, 0); if( fid ){ content_get(fid, &file); blob_resize(&filename, nPrefix); blob_append(&filename, pFile->zName, -1); zName = blob_str(&filename); zip_add_folders(zName); zip_add_file(zName, &file); blob_reset(&file); } } }else{ blob_reset(&mfile); } manifest_destroy(pManifest); blob_reset(&filename); zip_close(pZip); } /* ** COMMAND: zip ** |
︙ | ︙ |
Changes to test/delta1.test.
︙ | ︙ | |||
30 31 32 33 34 35 36 | # some random changes in "./t2". Then call test-delta on the # two files to make sure that deltas between these two files # work properly. # set filelist [glob $testdir/*] foreach f $filelist { set base [file root [file tail $f]] | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | # some random changes in "./t2". Then call test-delta on the # two files to make sure that deltas between these two files # work properly. # set filelist [glob $testdir/*] foreach f $filelist { set base [file root [file tail $f]] protOut "base=$base f=$f" set f1 [read_file $f] write_file t1 $f1 for {set i 0} {$i<100} {incr i} { write_file t2 [random_changes $f1 1 1 0 0.1] fossil test-delta t1 t2 test delta-$base-$i-1 {$RESULT=="ok"} write_file t2 [random_changes $f1 1 1 0 0.2] |
︙ | ︙ |
Changes to test/merge1.test.
︙ | ︙ | |||
75 76 77 78 79 80 81 | 111 - This is line one OF the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t23 { | | | | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | 111 - This is line one OF the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t23 { <<<<<<< BEGIN MERGE CONFLICT 111 - This is line ONE of the demo program - 1111 ============================ 111 - This is line one OF the demo program - 1111 >>>>>>> END MERGE CONFLICT 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { <<<<<<< BEGIN MERGE CONFLICT 111 - This is line one OF the demo program - 1111 ============================ 111 - This is line ONE of the demo program - 1111 >>>>>>> END MERGE CONFLICT 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } fossil test-3 t1 t3 t2 a32 test merge1-2.1 {[same_file t32 a32]} |
︙ | ︙ | |||
156 157 158 159 160 161 162 | write_file_indented t3 { 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { | | | | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | write_file_indented t3 { 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { <<<<<<< BEGIN MERGE CONFLICT ============================ 000 - Zero lines added to the beginning of - 0000 111 - This is line one of the demo program - 1111 >>>>>>> END MERGE CONFLICT 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t23 { <<<<<<< BEGIN MERGE CONFLICT 000 - Zero lines added to the beginning of - 0000 111 - This is line one of the demo program - 1111 ============================ >>>>>>> END MERGE CONFLICT 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } fossil test-3 t1 t3 t2 a32 test merge1-4.1 {[same_file t32 a32]} |
︙ | ︙ | |||
291 292 293 294 295 296 297 | KLMN OPQR STUV XYZ. } write_file_indented t23 { abcd | | | | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | KLMN OPQR STUV XYZ. } write_file_indented t23 { abcd <<<<<<< BEGIN MERGE CONFLICT efgh 2 ijkl 2 mnop 2 qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ============================ efgh ijkl mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF GHIJ >>>>>>> END MERGE CONFLICT KLMN OPQR STUV XYZ. } fossil test-3 t1 t2 t3 a23 test merge1-7.1 {[same_file t23 a23]} |
︙ | ︙ | |||
352 353 354 355 356 357 358 | STUV XYZ. } write_file_indented t23 { abcd efgh 2 ijkl 2 | | | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | STUV XYZ. } write_file_indented t23 { abcd efgh 2 ijkl 2 <<<<<<< BEGIN MERGE CONFLICT mnop qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ============================ mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF GHIJ >>>>>>> END MERGE CONFLICT KLMN OPQR STUV XYZ. } fossil test-3 t1 t2 t3 a23 test merge1-7.2 {[same_file t23 a23]} |
Changes to test/merge3.test.
︙ | ︙ | |||
26 27 28 29 30 31 32 | proc merge-test {testid basis v1 v2 result} { write_file t1 [join [string trim $basis] \n]\n write_file t2 [join [string trim $v1] \n]\n write_file t3 [join [string trim $v2] \n]\n fossil test-3-way-merge t1 t2 t3 t4 set x [read_file t4] | | | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | proc merge-test {testid basis v1 v2 result} { write_file t1 [join [string trim $basis] \n]\n write_file t2 [join [string trim $v1] \n]\n write_file t3 [join [string trim $v2] \n]\n fossil test-3-way-merge t1 t2 t3 t4 set x [read_file t4] regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x regsub -all {============================} $x {=} x regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x set x [split [string trim $x] \n] set result [string trim $result] if {$x!=$result} { protOut " Expected \[$result\]" protOut " Got \[$x\]" test merge3-$testid 0 } else { test merge3-$testid 1 } } merge-test 1 { |
︙ | ︙ |
Changes to test/merge4.test.
︙ | ︙ | |||
27 28 29 30 31 32 33 | proc merge-test {testid basis v1 v2 result1 result2} { write_file t1 [join [string trim $basis] \n]\n write_file t2 [join [string trim $v1] \n]\n write_file t3 [join [string trim $v2] \n]\n fossil test-3-way-merge t1 t2 t3 t4 fossil test-3-way-merge t1 t3 t2 t5 set x [read_file t4] | | | | | | | | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | proc merge-test {testid basis v1 v2 result1 result2} { write_file t1 [join [string trim $basis] \n]\n write_file t2 [join [string trim $v1] \n]\n write_file t3 [join [string trim $v2] \n]\n fossil test-3-way-merge t1 t2 t3 t4 fossil test-3-way-merge t1 t3 t2 t5 set x [read_file t4] regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x regsub -all {============================} $x {=} x regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x set x [split [string trim $x] \n] set y [read_file t5] regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $y {>} y regsub -all {============================} $y {=} y regsub -all {>>>>>>> END MERGE CONFLICT} $y {<} y set y [split [string trim $y] \n] set result1 [string trim $result1] if {$x!=$result1} { protOut " Expected \[$result1\]" protOut " Got \[$x\]" test merge3-$testid 0 } else { set result2 [string trim $result2] if {$y!=$result2} { protOut " Expected \[$result2\]" protOut " Got \[$y\]" test merge3-$testid 0 } else { test merge3-$testid 1 } } } |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | set i [lsearch $argv -halt] if {$i>=0} { set HALT 1 set argv [lreplace $argv $i $i] } else { set HALT 0 } if {[llength $argv]==0} { foreach f [lsort [glob $testdir/*.test]] { set base [file root [file tail $f]] lappend argv $base } } # Run the fossil program # proc fossil {args} { global fossilexe set cmd $fossilexe foreach a $args { lappend cmd $a } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | set i [lsearch $argv -halt] if {$i>=0} { set HALT 1 set argv [lreplace $argv $i $i] } else { set HALT 0 } set i [lsearch $argv -prot] if {$i>=0} { set PROT 1 set argv [lreplace $argv $i $i] } else { set PROT 0 } if {[llength $argv]==0} { foreach f [lsort [glob $testdir/*.test]] { set base [file root [file tail $f]] lappend argv $base } } # start protocol # proc protInit {cmd} { if {$::PROT} { set out [open "prot" w] fconfigure $out -translation platform puts $out "starting tests with:$cmd" close $out } } # write protocol # proc protOut {msg} { puts "$msg" if {$::PROT} { set out [open "prot" a] fconfigure $out -translation platform puts $out "$msg" close $out } } # Run the fossil program # proc fossil {args} { global fossilexe set cmd $fossilexe foreach a $args { lappend cmd $a } protOut $cmd flush stdout set rc [catch {eval exec $cmd} result] global RESULT CODE set CODE $rc set RESULT $result } |
︙ | ︙ | |||
98 99 100 101 102 103 104 | # Perform a test # proc test {name expr} { global bad_test set r [uplevel 1 [list expr $expr]] if {$r} { | | | | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | # Perform a test # proc test {name expr} { global bad_test set r [uplevel 1 [list expr $expr]] if {$r} { protOut "test $name OK" } else { protOut "test $name FAILED!" lappend bad_test $name if {$::HALT} exit } } set bad_test {} # Return a random string N characters long. |
︙ | ︙ | |||
165 166 167 168 169 170 171 172 | } } append out \n$line } return [string range $out 1 end] } foreach testfile $argv { | > | | | 197 198 199 200 201 202 203 204 205 206 207 208 209 | } } append out \n$line } return [string range $out 1 end] } protInit $fossilexe foreach testfile $argv { protOut "***** $testfile ******" source $testdir/$testfile.test } protOut "[llength $bad_test] errors: $bad_test" |
Added win/Makefile.PellesCGMake.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | # ########################################################################### # # HowTo # ----- # # This is a Makefile to compile fossil with PellesC from # http://www.smorgasbordet.com/pellesc/index.htm # In addition to the Compiler envrionment, you need # gmake from http://sourceforge.net/projects/unxutils/, Pelles make version # couldn't handle the complex dependencies in this build # zlib sources # Then you do # 1. create a directory PellesC in the project root directory # 2. Change the variables PellesCDir/ZLIBSRCDIR to the path of your installation # 3. open a dos prompt window and change working directory into PellesC (step 1) # 4. run gmake -f ..\win\Makefile.PellesCGMake # # this file is tested with # PellesC 5.00.13 # gmake 3.80 # zlib sources 1.2.5 # Windows XP SP 2 # and # PellesC 6.00.4 # gmake 3.80 # zlib sources 1.2.5 # Windows 7 Home Premium # # ########################################################################### # PellesCDir=c:\Programme\PellesC # Select between 32/64 bit code, default is 32 bit #TARGETVERSION=64 ifeq ($(TARGETVERSION),64) # 64 bit version TARGETMACHINE_CC=amd64 TARGETMACHINE_LN=amd64 TARGETEXTEND=64 else # 32 bit version TARGETMACHINE_CC=x86 TARGETMACHINE_LN=ix86 TARGETEXTEND= endif # define the project directories B=.. SRCDIR=$(B)/src/ WINDIR=$(B)/win/ ZLIBSRCDIR=../../zlib/ # define linker command and options LINK=$(PellesCDir)/bin/polink.exe LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib Crtmt$(TARGETEXTEND).lib # define standard C-compiler and flags, used to compile # the fossil binary. Some special definitions follow for # special files follow CC=$(PellesCDir)\bin\pocc.exe DEFINES=-DFOSSIL_I18N=0 -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES) INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR) # define commands for building the windows resource files RESOURCE=fossil.res RC=$(PellesCDir)\bin\porc.exe RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION) # define the special utilities files, needed to generate # the automatically generated source files UTILS=translate.exe mkindex.exe makeheaders.exe UTILS_OBJ=$(UTILS:.exe=.obj) UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c)) # define the sqlite files, which need special flags on compile SQLITESRC=sqlite3.c ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf)) SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 # define the th scripting files, which need special flags on compile THSRC=th.c th_lang.c ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf)) THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj)) # define the zlib files, needed by this compile ZLIBSRC=adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf)) ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj)) # define all fossil sources, using the standard compile and # source generation. These are all files in SRCDIR, which are not # mentioned as special files above: ORIGSRC=$(filter-out $(UTILS_SRC) $(ORIGTHSRC) $(ORIGSQLITESRC),$(wildcard $(SRCDIR)*.c)) SRC=$(subst $(SRCDIR),,$(ORIGSRC)) TRANSLATEDSRC=$(SRC:.c=_.c) TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) # main target file is the application APPLICATION=fossil.exe # ########################################################################### # define the standard make target .PHONY: default default: page_index.h headers $(APPLICATION) # ########################################################################### # symbolic target to generate the source generate utils .PHONY: utils utils: $(UTILS) # link utils $(UTILS) version.exe: %.exe: %.obj $(LINK) $(LINKFLAGS) -out:"$@" $< # compiling standard fossil utils $(UTILS_OBJ): %.obj: $(SRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # compile special windows utils version.obj: $(WINDIR)version.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # ########################################################################### # generate the translated c-source files $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe translate.exe $< >$@ # ########################################################################### # generate the index source, containing all web references,.. page_index.h: $(TRANSLATEDSRC) mkindex.exe mkindex.exe $(TRANSLATEDSRC) >$@ # ########################################################################### # extracting version info from manifest VERSION.h: version.exe ..\manifest.uuid ..\manifest version.exe ..\manifest.uuid ..\manifest > $@ # ########################################################################### # generate the simplified headers headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h echo Done >$@ # ########################################################################### # compile C sources with relevant options $(TRANSLATEDOBJ): %_.obj: %_.c %.h $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" $(SQLITEOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)%.h $(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@" $(THOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)th.h $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" $(ZLIBOBJ): %.obj: $(ZLIBSRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # ########################################################################### # create the windows resource with icon and version info $(RESOURCE): %.res: ../win/%.rc ../win/*.ico $(RC) $(RCFLAGS) $< -Fo"$@" # ########################################################################### # link the application $(APPLICATION): $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE) $(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE) # ########################################################################### # cleanup .PHONY: clean clean: del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj del /F $(TRANSLATEDSRC) del /F *.h headers del /F $(RESOURCE) .PHONY: clobber clobber: clean del /F *.exe |
Added win/Makefile.dmc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | # DO NOT EDIT # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run # "tclsh src/makemake.tcl dmc > win/Makefile.dmc" # to regenerate this file. B = .. SRCDIR = $B\src OBJDIR = . O = .obj E = .exe # Maybe DMDIR, SSL or INCL needs adjustment DMDIR = c:\DM INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = DMCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp I18N = -DFOSSIL_I18N=0 CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\shell$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E headers fossil.res $(OBJ) $(OBJDIR)\link cd $(OBJDIR) $(DMDIR)\bin\link @link fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc +echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 shell th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR)\translate.c $(BCC) -o$@ $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** version$E: $B\win\version.c $(BCC) -o$@ $** $(OBJDIR)\shell$O : $(SRCDIR)\shell.c $(TCC) -o$@ -c -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) -o$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) -o$@ -c $** VERSION.h : version$E $B\manifest.uuid $B\manifest +$** > $@ page_index.h: mkindex$E $(SRC) +$** > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E $(OBJDIR)\add$O : add_.c add.h $(TCC) -o$@ -c add_.c add_.c : $(SRCDIR)\add.c +translate$E $** > $@ $(OBJDIR)\allrepo$O : allrepo_.c allrepo.h $(TCC) -o$@ -c allrepo_.c allrepo_.c : $(SRCDIR)\allrepo.c +translate$E $** > $@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) -o$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c +translate$E $** > $@ $(OBJDIR)\bag$O : bag_.c bag.h $(TCC) -o$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c +translate$E $** > $@ $(OBJDIR)\blob$O : blob_.c blob.h $(TCC) -o$@ -c blob_.c blob_.c : $(SRCDIR)\blob.c +translate$E $** > $@ $(OBJDIR)\branch$O : branch_.c branch.h $(TCC) -o$@ -c branch_.c branch_.c : $(SRCDIR)\branch.c +translate$E $** > $@ $(OBJDIR)\browse$O : browse_.c browse.h $(TCC) -o$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c +translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) -o$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c +translate$E $** > $@ $(OBJDIR)\cgi$O : cgi_.c cgi.h $(TCC) -o$@ -c cgi_.c cgi_.c : $(SRCDIR)\cgi.c +translate$E $** > $@ $(OBJDIR)\checkin$O : checkin_.c checkin.h $(TCC) -o$@ -c checkin_.c checkin_.c : $(SRCDIR)\checkin.c +translate$E $** > $@ $(OBJDIR)\checkout$O : checkout_.c checkout.h $(TCC) -o$@ -c checkout_.c checkout_.c : $(SRCDIR)\checkout.c +translate$E $** > $@ $(OBJDIR)\clearsign$O : clearsign_.c clearsign.h $(TCC) -o$@ -c clearsign_.c clearsign_.c : $(SRCDIR)\clearsign.c +translate$E $** > $@ $(OBJDIR)\clone$O : clone_.c clone.h $(TCC) -o$@ -c clone_.c clone_.c : $(SRCDIR)\clone.c +translate$E $** > $@ $(OBJDIR)\comformat$O : comformat_.c comformat.h $(TCC) -o$@ -c comformat_.c comformat_.c : $(SRCDIR)\comformat.c +translate$E $** > $@ $(OBJDIR)\configure$O : configure_.c configure.h $(TCC) -o$@ -c configure_.c configure_.c : $(SRCDIR)\configure.c +translate$E $** > $@ $(OBJDIR)\content$O : content_.c content.h $(TCC) -o$@ -c content_.c content_.c : $(SRCDIR)\content.c +translate$E $** > $@ $(OBJDIR)\db$O : db_.c db.h $(TCC) -o$@ -c db_.c db_.c : $(SRCDIR)\db.c +translate$E $** > $@ $(OBJDIR)\delta$O : delta_.c delta.h $(TCC) -o$@ -c delta_.c delta_.c : $(SRCDIR)\delta.c +translate$E $** > $@ $(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) -o$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c +translate$E $** > $@ $(OBJDIR)\descendants$O : descendants_.c descendants.h $(TCC) -o$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c +translate$E $** > $@ $(OBJDIR)\diff$O : diff_.c diff.h $(TCC) -o$@ -c diff_.c diff_.c : $(SRCDIR)\diff.c +translate$E $** > $@ $(OBJDIR)\diffcmd$O : diffcmd_.c diffcmd.h $(TCC) -o$@ -c diffcmd_.c diffcmd_.c : $(SRCDIR)\diffcmd.c +translate$E $** > $@ $(OBJDIR)\doc$O : doc_.c doc.h $(TCC) -o$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c +translate$E $** > $@ $(OBJDIR)\event$O : event_.c event.h $(TCC) -o$@ -c event_.c event_.c : $(SRCDIR)\event.c +translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) -o$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c +translate$E $** > $@ $(OBJDIR)\event$O : event_.c event.h $(TCC) -o$@ -c event_.c event_.c : $(SRCDIR)\event.c +translate$E $** > $@ $(OBJDIR)\file$O : file_.c file.h $(TCC) -o$@ -c file_.c file_.c : $(SRCDIR)\file.c +translate$E $** > $@ $(OBJDIR)\finfo$O : finfo_.c finfo.h $(TCC) -o$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c +translate$E $** > $@ $(OBJDIR)\graph$O : graph_.c graph.h $(TCC) -o$@ -c graph_.c graph_.c : $(SRCDIR)\graph.c +translate$E $** > $@ $(OBJDIR)\http$O : http_.c http.h $(TCC) -o$@ -c http_.c http_.c : $(SRCDIR)\http.c +translate$E $** > $@ $(OBJDIR)\http_socket$O : http_socket_.c http_socket.h $(TCC) -o$@ -c http_socket_.c http_socket_.c : $(SRCDIR)\http_socket.c +translate$E $** > $@ $(OBJDIR)\http_ssl$O : http_ssl_.c http_ssl.h $(TCC) -o$@ -c http_ssl_.c http_ssl_.c : $(SRCDIR)\http_ssl.c +translate$E $** > $@ $(OBJDIR)\http_transport$O : http_transport_.c http_transport.h $(TCC) -o$@ -c http_transport_.c http_transport_.c : $(SRCDIR)\http_transport.c +translate$E $** > $@ $(OBJDIR)\info$O : info_.c info.h $(TCC) -o$@ -c info_.c info_.c : $(SRCDIR)\info.c +translate$E $** > $@ $(OBJDIR)\login$O : login_.c login.h $(TCC) -o$@ -c login_.c login_.c : $(SRCDIR)\login.c +translate$E $** > $@ $(OBJDIR)\main$O : main_.c main.h $(TCC) -o$@ -c main_.c main_.c : $(SRCDIR)\main.c +translate$E $** > $@ $(OBJDIR)\manifest$O : manifest_.c manifest.h $(TCC) -o$@ -c manifest_.c manifest_.c : $(SRCDIR)\manifest.c +translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) -o$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c +translate$E $** > $@ $(OBJDIR)\merge$O : merge_.c merge.h $(TCC) -o$@ -c merge_.c merge_.c : $(SRCDIR)\merge.c +translate$E $** > $@ $(OBJDIR)\merge3$O : merge3_.c merge3.h $(TCC) -o$@ -c merge3_.c merge3_.c : $(SRCDIR)\merge3.c +translate$E $** > $@ $(OBJDIR)\name$O : name_.c name.h $(TCC) -o$@ -c name_.c name_.c : $(SRCDIR)\name.c +translate$E $** > $@ $(OBJDIR)\pivot$O : pivot_.c pivot.h $(TCC) -o$@ -c pivot_.c pivot_.c : $(SRCDIR)\pivot.c +translate$E $** > $@ $(OBJDIR)\popen$O : popen_.c popen.h $(TCC) -o$@ -c popen_.c popen_.c : $(SRCDIR)\popen.c +translate$E $** > $@ $(OBJDIR)\pqueue$O : pqueue_.c pqueue.h $(TCC) -o$@ -c pqueue_.c pqueue_.c : $(SRCDIR)\pqueue.c +translate$E $** > $@ $(OBJDIR)\printf$O : printf_.c printf.h $(TCC) -o$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c +translate$E $** > $@ $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h $(TCC) -o$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c +translate$E $** > $@ $(OBJDIR)\report$O : report_.c report.h $(TCC) -o$@ -c report_.c report_.c : $(SRCDIR)\report.c +translate$E $** > $@ $(OBJDIR)\rss$O : rss_.c rss.h $(TCC) -o$@ -c rss_.c rss_.c : $(SRCDIR)\rss.c +translate$E $** > $@ $(OBJDIR)\schema$O : schema_.c schema.h $(TCC) -o$@ -c schema_.c schema_.c : $(SRCDIR)\schema.c +translate$E $** > $@ $(OBJDIR)\search$O : search_.c search.h $(TCC) -o$@ -c search_.c search_.c : $(SRCDIR)\search.c +translate$E $** > $@ $(OBJDIR)\setup$O : setup_.c setup.h $(TCC) -o$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c +translate$E $** > $@ $(OBJDIR)\sha1$O : sha1_.c sha1.h $(TCC) -o$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c +translate$E $** > $@ $(OBJDIR)\shun$O : shun_.c shun.h $(TCC) -o$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c +translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) -o$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c +translate$E $** > $@ $(OBJDIR)\stat$O : stat_.c stat.h $(TCC) -o$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c +translate$E $** > $@ $(OBJDIR)\style$O : style_.c style.h $(TCC) -o$@ -c style_.c style_.c : $(SRCDIR)\style.c +translate$E $** > $@ $(OBJDIR)\sync$O : sync_.c sync.h $(TCC) -o$@ -c sync_.c sync_.c : $(SRCDIR)\sync.c +translate$E $** > $@ $(OBJDIR)\tag$O : tag_.c tag.h $(TCC) -o$@ -c tag_.c tag_.c : $(SRCDIR)\tag.c +translate$E $** > $@ $(OBJDIR)\th_main$O : th_main_.c th_main.h $(TCC) -o$@ -c th_main_.c th_main_.c : $(SRCDIR)\th_main.c +translate$E $** > $@ $(OBJDIR)\timeline$O : timeline_.c timeline.h $(TCC) -o$@ -c timeline_.c timeline_.c : $(SRCDIR)\timeline.c +translate$E $** > $@ $(OBJDIR)\tkt$O : tkt_.c tkt.h $(TCC) -o$@ -c tkt_.c tkt_.c : $(SRCDIR)\tkt.c +translate$E $** > $@ $(OBJDIR)\tktsetup$O : tktsetup_.c tktsetup.h $(TCC) -o$@ -c tktsetup_.c tktsetup_.c : $(SRCDIR)\tktsetup.c +translate$E $** > $@ $(OBJDIR)\undo$O : undo_.c undo.h $(TCC) -o$@ -c undo_.c undo_.c : $(SRCDIR)\undo.c +translate$E $** > $@ $(OBJDIR)\update$O : update_.c update.h $(TCC) -o$@ -c update_.c update_.c : $(SRCDIR)\update.c +translate$E $** > $@ $(OBJDIR)\url$O : url_.c url.h $(TCC) -o$@ -c url_.c url_.c : $(SRCDIR)\url.c +translate$E $** > $@ $(OBJDIR)\user$O : user_.c user.h $(TCC) -o$@ -c user_.c user_.c : $(SRCDIR)\user.c +translate$E $** > $@ $(OBJDIR)\verify$O : verify_.c verify.h $(TCC) -o$@ -c verify_.c verify_.c : $(SRCDIR)\verify.c +translate$E $** > $@ $(OBJDIR)\vfile$O : vfile_.c vfile.h $(TCC) -o$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c +translate$E $** > $@ $(OBJDIR)\wiki$O : wiki_.c wiki.h $(TCC) -o$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c +translate$E $** > $@ $(OBJDIR)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) -o$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c +translate$E $** > $@ $(OBJDIR)\winhttp$O : winhttp_.c winhttp.h $(TCC) -o$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c +translate$E $** > $@ $(OBJDIR)\xfer$O : xfer_.c xfer.h $(TCC) -o$@ -c xfer_.c xfer_.c : $(SRCDIR)\xfer.c +translate$E $** > $@ $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h @copy /Y nul: headers |
Added win/Makefile.msc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 | # DO NOT EDIT # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run # "tclsh src/makemake.tcl msc > win/Makefile.msc" # to regenerate this file. B = .. SRCDIR = $B\src OBJDIR = . O = .obj E = .exe # Maybe MSCDIR, SSL, ZLIB, or INCL needs adjustment MSCDIR = c:\msc # Uncomment below for SSL support SSL = SSLLIB = #SSL = -DFOSSIL_ENABLE_SSL=1 #SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib # zlib options # When using precompiled from http://zlib.net/zlib125-dll.zip #ZINCDIR = C:\zlib125-dll\include #ZLIBDIR = C:\zlib125-dll\lib #ZLIB = zdll.lib ZINCDIR = $(MSCDIR)\extra\include ZLIBDIR = $(MSCDIR)\extra\lib ZLIB = zlib.lib INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR) MSCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp I18N = -DFOSSIL_I18N=0 CFLAGS = -nologo -MT -O2 BCC = $(CC) $(CFLAGS) TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(ZLIB) ws2_32.lib $(SSLLIB) LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR) SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O APPNAME = $(OBJDIR)\fossil$(E) all: $(OBJDIR) $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\linkopts cd $(OBJDIR) link -LINK -OUT:$@ $(LIBDIR) @linkopts $(OBJDIR)\linkopts: $B\win\Makefile.msc echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 th th_lang > $@ echo $(LIBS) >> $@ $(OBJDIR): @-mkdir $@ translate$E: $(SRCDIR)\translate.c $(BCC) $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) $** version$E: $B\win\version.c $(BCC) $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) /Fo$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) /Fo$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : version$E $B\manifest.uuid $B\manifest $** > $@ page_index.h: mkindex$E $(SRC) $** > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map -del headers linkopts realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E $(OBJDIR)\add$O : add_.c add.h $(TCC) /Fo$@ -c add_.c add_.c : $(SRCDIR)\add.c translate$E $** > $@ $(OBJDIR)\allrepo$O : allrepo_.c allrepo.h $(TCC) /Fo$@ -c allrepo_.c allrepo_.c : $(SRCDIR)\allrepo.c translate$E $** > $@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) /Fo$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c translate$E $** > $@ $(OBJDIR)\bag$O : bag_.c bag.h $(TCC) /Fo$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c translate$E $** > $@ $(OBJDIR)\blob$O : blob_.c blob.h $(TCC) /Fo$@ -c blob_.c blob_.c : $(SRCDIR)\blob.c translate$E $** > $@ $(OBJDIR)\branch$O : branch_.c branch.h $(TCC) /Fo$@ -c branch_.c branch_.c : $(SRCDIR)\branch.c translate$E $** > $@ $(OBJDIR)\browse$O : browse_.c browse.h $(TCC) /Fo$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) /Fo$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c translate$E $** > $@ $(OBJDIR)\cgi$O : cgi_.c cgi.h $(TCC) /Fo$@ -c cgi_.c cgi_.c : $(SRCDIR)\cgi.c translate$E $** > $@ $(OBJDIR)\checkin$O : checkin_.c checkin.h $(TCC) /Fo$@ -c checkin_.c checkin_.c : $(SRCDIR)\checkin.c translate$E $** > $@ $(OBJDIR)\checkout$O : checkout_.c checkout.h $(TCC) /Fo$@ -c checkout_.c checkout_.c : $(SRCDIR)\checkout.c translate$E $** > $@ $(OBJDIR)\clearsign$O : clearsign_.c clearsign.h $(TCC) /Fo$@ -c clearsign_.c clearsign_.c : $(SRCDIR)\clearsign.c translate$E $** > $@ $(OBJDIR)\clone$O : clone_.c clone.h $(TCC) /Fo$@ -c clone_.c clone_.c : $(SRCDIR)\clone.c translate$E $** > $@ $(OBJDIR)\comformat$O : comformat_.c comformat.h $(TCC) /Fo$@ -c comformat_.c comformat_.c : $(SRCDIR)\comformat.c translate$E $** > $@ $(OBJDIR)\configure$O : configure_.c configure.h $(TCC) /Fo$@ -c configure_.c configure_.c : $(SRCDIR)\configure.c translate$E $** > $@ $(OBJDIR)\content$O : content_.c content.h $(TCC) /Fo$@ -c content_.c content_.c : $(SRCDIR)\content.c translate$E $** > $@ $(OBJDIR)\db$O : db_.c db.h $(TCC) /Fo$@ -c db_.c db_.c : $(SRCDIR)\db.c translate$E $** > $@ $(OBJDIR)\delta$O : delta_.c delta.h $(TCC) /Fo$@ -c delta_.c delta_.c : $(SRCDIR)\delta.c translate$E $** > $@ $(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) /Fo$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c translate$E $** > $@ $(OBJDIR)\descendants$O : descendants_.c descendants.h $(TCC) /Fo$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c translate$E $** > $@ $(OBJDIR)\diff$O : diff_.c diff.h $(TCC) /Fo$@ -c diff_.c diff_.c : $(SRCDIR)\diff.c translate$E $** > $@ $(OBJDIR)\diffcmd$O : diffcmd_.c diffcmd.h $(TCC) /Fo$@ -c diffcmd_.c diffcmd_.c : $(SRCDIR)\diffcmd.c translate$E $** > $@ $(OBJDIR)\doc$O : doc_.c doc.h $(TCC) /Fo$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) /Fo$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c translate$E $** > $@ $(OBJDIR)\file$O : file_.c file.h $(TCC) /Fo$@ -c file_.c file_.c : $(SRCDIR)\file.c translate$E $** > $@ $(OBJDIR)\finfo$O : finfo_.c finfo.h $(TCC) /Fo$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c translate$E $** > $@ $(OBJDIR)\graph$O : graph_.c graph.h $(TCC) /Fo$@ -c graph_.c graph_.c : $(SRCDIR)\graph.c translate$E $** > $@ $(OBJDIR)\http$O : http_.c http.h $(TCC) /Fo$@ -c http_.c http_.c : $(SRCDIR)\http.c translate$E $** > $@ $(OBJDIR)\http_socket$O : http_socket_.c http_socket.h $(TCC) /Fo$@ -c http_socket_.c http_socket_.c : $(SRCDIR)\http_socket.c translate$E $** > $@ $(OBJDIR)\http_ssl$O : http_ssl_.c http_ssl.h $(TCC) /Fo$@ -c http_ssl_.c http_ssl_.c : $(SRCDIR)\http_ssl.c translate$E $** > $@ $(OBJDIR)\http_transport$O : http_transport_.c http_transport.h $(TCC) /Fo$@ -c http_transport_.c http_transport_.c : $(SRCDIR)\http_transport.c translate$E $** > $@ $(OBJDIR)\info$O : info_.c info.h $(TCC) /Fo$@ -c info_.c info_.c : $(SRCDIR)\info.c translate$E $** > $@ $(OBJDIR)\login$O : login_.c login.h $(TCC) /Fo$@ -c login_.c login_.c : $(SRCDIR)\login.c translate$E $** > $@ $(OBJDIR)\main$O : main_.c main.h $(TCC) /Fo$@ -c main_.c main_.c : $(SRCDIR)\main.c translate$E $** > $@ $(OBJDIR)\manifest$O : manifest_.c manifest.h $(TCC) /Fo$@ -c manifest_.c manifest_.c : $(SRCDIR)\manifest.c translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) /Fo$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c translate$E $** > $@ $(OBJDIR)\merge$O : merge_.c merge.h $(TCC) /Fo$@ -c merge_.c merge_.c : $(SRCDIR)\merge.c translate$E $** > $@ $(OBJDIR)\merge3$O : merge3_.c merge3.h $(TCC) /Fo$@ -c merge3_.c merge3_.c : $(SRCDIR)\merge3.c translate$E $** > $@ $(OBJDIR)\name$O : name_.c name.h $(TCC) /Fo$@ -c name_.c name_.c : $(SRCDIR)\name.c translate$E $** > $@ $(OBJDIR)\pivot$O : pivot_.c pivot.h $(TCC) /Fo$@ -c pivot_.c pivot_.c : $(SRCDIR)\pivot.c translate$E $** > $@ $(OBJDIR)\popen$O : popen_.c popen.h $(TCC) /Fo$@ -c popen_.c popen_.c : $(SRCDIR)\popen.c translate$E $** > $@ $(OBJDIR)\pqueue$O : pqueue_.c pqueue.h $(TCC) /Fo$@ -c pqueue_.c pqueue_.c : $(SRCDIR)\pqueue.c translate$E $** > $@ $(OBJDIR)\printf$O : printf_.c printf.h $(TCC) /Fo$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c translate$E $** > $@ $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h $(TCC) /Fo$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c translate$E $** > $@ $(OBJDIR)\report$O : report_.c report.h $(TCC) /Fo$@ -c report_.c report_.c : $(SRCDIR)\report.c translate$E $** > $@ $(OBJDIR)\rss$O : rss_.c rss.h $(TCC) /Fo$@ -c rss_.c rss_.c : $(SRCDIR)\rss.c translate$E $** > $@ $(OBJDIR)\schema$O : schema_.c schema.h $(TCC) /Fo$@ -c schema_.c schema_.c : $(SRCDIR)\schema.c translate$E $** > $@ $(OBJDIR)\search$O : search_.c search.h $(TCC) /Fo$@ -c search_.c search_.c : $(SRCDIR)\search.c translate$E $** > $@ $(OBJDIR)\setup$O : setup_.c setup.h $(TCC) /Fo$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c translate$E $** > $@ $(OBJDIR)\sha1$O : sha1_.c sha1.h $(TCC) /Fo$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c translate$E $** > $@ $(OBJDIR)\shun$O : shun_.c shun.h $(TCC) /Fo$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) /Fo$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c translate$E $** > $@ $(OBJDIR)\stat$O : stat_.c stat.h $(TCC) /Fo$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c translate$E $** > $@ $(OBJDIR)\style$O : style_.c style.h $(TCC) /Fo$@ -c style_.c style_.c : $(SRCDIR)\style.c translate$E $** > $@ $(OBJDIR)\sync$O : sync_.c sync.h $(TCC) /Fo$@ -c sync_.c sync_.c : $(SRCDIR)\sync.c translate$E $** > $@ $(OBJDIR)\tag$O : tag_.c tag.h $(TCC) /Fo$@ -c tag_.c tag_.c : $(SRCDIR)\tag.c translate$E $** > $@ $(OBJDIR)\th_main$O : th_main_.c th_main.h $(TCC) /Fo$@ -c th_main_.c th_main_.c : $(SRCDIR)\th_main.c translate$E $** > $@ $(OBJDIR)\timeline$O : timeline_.c timeline.h $(TCC) /Fo$@ -c timeline_.c timeline_.c : $(SRCDIR)\timeline.c translate$E $** > $@ $(OBJDIR)\tkt$O : tkt_.c tkt.h $(TCC) /Fo$@ -c tkt_.c tkt_.c : $(SRCDIR)\tkt.c translate$E $** > $@ $(OBJDIR)\tktsetup$O : tktsetup_.c tktsetup.h $(TCC) /Fo$@ -c tktsetup_.c tktsetup_.c : $(SRCDIR)\tktsetup.c translate$E $** > $@ $(OBJDIR)\undo$O : undo_.c undo.h $(TCC) /Fo$@ -c undo_.c undo_.c : $(SRCDIR)\undo.c translate$E $** > $@ $(OBJDIR)\update$O : update_.c update.h $(TCC) /Fo$@ -c update_.c update_.c : $(SRCDIR)\update.c translate$E $** > $@ $(OBJDIR)\url$O : url_.c url.h $(TCC) /Fo$@ -c url_.c url_.c : $(SRCDIR)\url.c translate$E $** > $@ $(OBJDIR)\user$O : user_.c user.h $(TCC) /Fo$@ -c user_.c user_.c : $(SRCDIR)\user.c translate$E $** > $@ $(OBJDIR)\verify$O : verify_.c verify.h $(TCC) /Fo$@ -c verify_.c verify_.c : $(SRCDIR)\verify.c translate$E $** > $@ $(OBJDIR)\vfile$O : vfile_.c vfile.h $(TCC) /Fo$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c translate$E $** > $@ $(OBJDIR)\wiki$O : wiki_.c wiki.h $(TCC) /Fo$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c translate$E $** > $@ $(OBJDIR)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) /Fo$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c translate$E $** > $@ $(OBJDIR)\winhttp$O : winhttp_.c winhttp.h $(TCC) /Fo$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c translate$E $** > $@ $(OBJDIR)\xfer$O : xfer_.c xfer.h $(TCC) /Fo$@ -c xfer_.c xfer_.c : $(SRCDIR)\xfer.c translate$E $** > $@ $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) /Fo$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h @copy /Y nul: headers |
Added win/fossil.ico.
cannot compute difference between binary files
Added win/fossil.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <windows.h> #include "VERSION.h" #define _RC_COMPILE_ #include "config.h" LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEFLAGSMASK 0x3F FILEFLAGS 0x0 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "FileDescription", "distributed source code control system with integrated wiki and ticket-system\0" VALUE "Comments", "compiler: "COMPILER_NAME"\0" VALUE "FileVersion", MANIFEST_UUID"("COMPILER_NAME"\0" VALUE "InternalName", "fossil\0" VALUE "LegalCopyright", "Copyright (c) "MANIFEST_YEAR" D. Richard Hipp\0" VALUE "OriginalFilename", "fossil.exe\0" VALUE "ProductName", "fossil\0" VALUE "ProductVersion", MANIFEST_VERSION" "MANIFEST_DATE" UTC\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 0x4B0 END END 8001 ICON "fossil.ico" |
Added win/include/dirent.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | /***************************************************************************** * dirent.h - dirent API for Microsoft Visual Studio * * Copyright (C) 2006 Toni Ronkko * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * ``Software''), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Dec 15, 2009, John Cunningham * Added rewinddir member function * * Jan 18, 2008, Toni Ronkko * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string * between multi-byte and unicode representations. This makes the * code simpler and also allows the code to be compiled under MingW. Thanks * to Azriel Fasten for the suggestion. * * Mar 4, 2007, Toni Ronkko * Bug fix: due to the strncpy_s() function this file only compiled in * Visual Studio 2005. Using the new string functions only when the * compiler version allows. * * Nov 2, 2006, Toni Ronkko * Major update: removed support for Watcom C, MS-DOS and Turbo C to * simplify the file, updated the code to compile cleanly on Visual * Studio 2005 with both unicode and multi-byte character strings, * removed rewinddir() as it had a bug. * * Aug 20, 2006, Toni Ronkko * Removed all remarks about MSVC 1.0, which is antiqued now. Simplified * comments by removing SGML tags. * * May 14 2002, Toni Ronkko * Embedded the function definitions directly to the header so that no * source modules need to be included in the Visual Studio project. Removed * all the dependencies to other projects so that this very header can be * used independently. * * May 28 1998, Toni Ronkko * First version. *****************************************************************************/ #ifndef DIRENT_H #define DIRENT_H #include <windows.h> #include <string.h> #include <assert.h> typedef struct dirent { char d_name[MAX_PATH + 1]; /* current dir entry (multi-byte char string) */ WIN32_FIND_DATAA data; /* file attributes */ } dirent; typedef struct DIR { dirent current; /* Current directory entry */ int cached; /* Indicates un-processed entry in memory */ HANDLE search_handle; /* File search handle */ char patt[MAX_PATH + 3]; /* search pattern (3 = pattern + "\\*\0") */ } DIR; /* Forward declarations */ static DIR *opendir (const char *dirname); static struct dirent *readdir (DIR *dirp); static int closedir (DIR *dirp); static void rewinddir(DIR* dirp); /* Use the new safe string functions introduced in Visual Studio 2005 */ #if defined(_MSC_VER) && _MSC_VER >= 1400 # define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE) #else # define STRNCPY(dest,src,size) strncpy((dest),(src),(size)) #endif /***************************************************************************** * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static DIR *opendir(const char *dirname) { DIR *dirp; assert (dirname != NULL); assert (strlen (dirname) < MAX_PATH); /* construct new DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp != NULL) { char *p; /* take directory name... */ STRNCPY (dirp->patt, dirname, sizeof(dirp->patt)); dirp->patt[MAX_PATH] = '\0'; /* ... and append search pattern to it */ p = strchr (dirp->patt, '\0'); if (dirp->patt < p && *(p-1) != '\\' && *(p-1) != ':') { *p++ = '\\'; } *p++ = '*'; *p = '\0'; /* open stream and retrieve first file */ dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* invalid search pattern? */ free (dirp); return NULL; } /* there is an un-processed directory entry in memory now */ dirp->cached = 1; } return dirp; } /***************************************************************************** * Read a directory entry, and return a pointer to a dirent structure * containing the name of the entry in d_name field. Individual directory * entries returned by this very function include regular files, * sub-directories, pseudo-directories "." and "..", but also volume labels, * hidden files and system files may be returned. */ static struct dirent *readdir(DIR *dirp) { assert (dirp != NULL); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* directory stream was opened/rewound incorrectly or ended normally */ return NULL; } /* get next directory entry */ if (dirp->cached != 0) { /* a valid directory entry already in memory */ dirp->cached = 0; } else { /* read next directory entry from disk */ if (FindNextFileA (dirp->search_handle, &dirp->current.data) == FALSE) { /* the very last file has been processed or an error occured */ FindClose (dirp->search_handle); dirp->search_handle = INVALID_HANDLE_VALUE; return NULL; } } /* copy as a multibyte character string */ STRNCPY ( dirp->current.d_name, dirp->current.data.cFileName, sizeof(dirp->current.d_name) ); dirp->current.d_name[MAX_PATH] = '\0'; return &dirp->current; } /***************************************************************************** * Close directory stream opened by opendir() function. Close of the * directory stream invalidates the DIR structure as well as any previously * read directory entry. */ static int closedir(DIR *dirp) { assert (dirp != NULL); /* release search handle */ if (dirp->search_handle != INVALID_HANDLE_VALUE) { FindClose (dirp->search_handle); dirp->search_handle = INVALID_HANDLE_VALUE; } /* release directory handle */ free (dirp); return 0; } /***************************************************************************** * Resets the position of the directory stream to which dirp refers to the * beginning of the directory. It also causes the directory stream to refer * to the current state of the corresponding directory, as a call to opendir() * would have done. If dirp does not refer to a directory stream, the effect * is undefined. */ static void rewinddir(DIR* dirp) { /* release search handle */ if (dirp->search_handle != INVALID_HANDLE_VALUE) { FindClose (dirp->search_handle); dirp->search_handle = INVALID_HANDLE_VALUE; } /* open new search handle and retrieve first file */ dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* invalid search pattern? */ free (dirp); return; } /* there is an un-processed directory entry in memory now */ dirp->cached = 1; } #endif /*DIRENT_H*/ |
Added win/include/unistd.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #ifndef _UNISTD_H #define _UNISTD_H 1 /* This file intended to serve as a drop-in replacement for * unistd.h on Windows * Please add functionality as neeeded */ #include <stdlib.h> #include <io.h> #define srandom srand #define random rand #if defined(__DMC__) #endif #if defined(_WIN32) #define _CRT_SECURE_NO_WARNINGS 1 #ifndef F_OK #define F_OK 0 #endif /* not F_OK */ #ifndef X_OK #define X_OK 1 #endif /* not X_OK */ #ifndef R_OK #define R_OK 2 #endif /* not R_OK */ #ifndef W_OK #define W_OK 4 #endif /* not W_OK */ #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif #define access _access #define ftruncate _chsize #define ssize_t int #endif /* unistd.h */ |
Added win/version.c.
> > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <stdio.h> int main(int argc, char *argv[]){ FILE *m,*u; char b[10240]; u = fopen(argv[1],"r"); fgets(b, sizeof(b)-1,u); b[strlen(b)-1] =0; printf("#define MANIFEST_UUID \"%s\"\n",b); printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); m = fopen(argv[2],"r"); while(b == fgets(b, sizeof(b)-1,m)){ if(0 == strncmp("D ",b,2)){ printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); return 0; } } return 1; } |
Changes to www/branching.wiki.
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | concerned, there is no difference. The distinction is in the intent. In figure 2, the fact that check-in 2 has multiple children is an accident that stems from concurrent development. In figure 4, giving check-in 2 multiple children is a deliberate act. So, to a good approximation, we define forking to be by accident and branching to be by intent. Apart from that, they are the same. <h2>Tags And Properties</h2> Tags and properties are used in fossil to help express the intent, and thus to distinguish between forks and branches. Figure 5 shows the same scenario as figure 4 but with tags and properties added: <center><table border=1 cellpadding=10 hspace=10 vspace=10> | > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | concerned, there is no difference. The distinction is in the intent. In figure 2, the fact that check-in 2 has multiple children is an accident that stems from concurrent development. In figure 4, giving check-in 2 multiple children is a deliberate act. So, to a good approximation, we define forking to be by accident and branching to be by intent. Apart from that, they are the same. <a name="tags"></a> <h2>Tags And Properties</h2> Tags and properties are used in fossil to help express the intent, and thus to distinguish between forks and branches. Figure 5 shows the same scenario as figure 4 but with tags and properties added: <center><table border=1 cellpadding=10 hspace=10 vspace=10> |
︙ | ︙ |
Added www/event.wiki.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <title>Events</title> <h2>What Is An "Event"?</h2> In Fossil, and "event" is a special kind of [./wikitheory.wiki | wiki page] that is associated with a point in time rather than having a page name. Each event causes a single entry to appear on the [/timeline | Timeline Page]. Clicking on the hyperlink of the timeline entry cause a jump to the wiki content for the event. The wiki content, the timeline entry text, the time of the event, and the timeline background color can all be edited. As with check-ins, wiki, and tickets, all events automatically synchronize to other repositories. Hence, events can be viewed, created, and edited off-line. And the complete edit history for events is maintained for auditing purposes. Possible uses for events include: * <b>Milestones</b>. Project milestones, such as releases or beta-test cycles, can be recorded as events. The timeline entry for the event can be something simple like "Version 1.2.3" perhaps with a bright color background to draw attention to the entry and the wiki content can contain release notes, for example. * <b>Blog Entries</b>. Blog entries from developers describing the current state of a project, or rational for various design decisions, or roadmaps for future development, can be entered as events. * <b>Process Checkpoints</b>. For projects that have a formal process, events can be used to record the completion or the initiation of various process steps. For example, an event can be used to record the successful completion of a long-running test, perhaps with performance results and details of where the test was run and who ran it recorded in the wiki content. * <b>News Articles</b>. Significant occurrences in the lifecycle of a project can be recorded as news articles using events. Perhaps the domain name of the canonical website for a project changes, or new server hardware is obtained. Such happenings are appropriate for reporting as news. * <b>Announcements</b>. Changes to the composition of the development team or acquisition of new project sponsors can be communicated as announcements which can be implemented as events. No project is required to use events. But events can help many projects stay better organized and provide a better historical record of the development progress. <h2>Viewing Events</h2> Because events are considered a special kind of wiki, users must have permission to read wiki in order read events. Enable the "j" permission under the /Setup/Users menu in order to give specific users or user classes the ability to view wiki and events. Events show up on the timeline. Click on the hyperlink beside the event title to see the details of the event. <h2>Creating And Editing Events</h2> There is a hyperlink under the /Wiki menu that can be used to create new events. And there is a submenu hyperlink on event displays for editing existing events. Users must have check-in privileges (permission "i") in order to create or edit events. In addition, users must have create-wiki privilege (permission "f") to create new events and edit-wiki privilege (permission "k") in order to edit existing events. If the first non-whitespace text of the event wiki content is <title>...</title> then that markup is omitted from the body of the wiki pages and is instead displayed as the page title. |
Changes to www/fileformat.wiki.
︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 | <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> </ul> | > | > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> <li> [#event | Events] </li> </ul> These seven artifact types are described in the sequel. In the current implementation (as of 2009-01-25) the artifacts that make up a fossil repository are stored in in as delta- and zlib-compressed blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This is an implementation detail and might change in a future release. For the purpose of this article "file format" means the format of the artifacts, not how the artifacts are stored on disk. It is the artifact format that is intended to be enduring. The specifics of how artifacts are stored on disk, though stable, is not intended to live as long as the artifact format. All of the artifacts can be extracted from a Fossil repository using the "fossil deconstruct" command. <a name="manifest"></a> <h2>1.0 The Manifest</h2> A manifest defines a check-in or version of the project source tree. The manifest contains a list of artifacts for each file in the project and the corresponding filenames, as |
︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | No card may be duplicated. The entire manifest may be PGP clear-signed, but otherwise it may contain no additional text or data beyond what is described here. Allowed cards in the manifest are as follows: <blockquote> <b>C</b> <i>checkin-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br> <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> <b>P</b> <i>SHA1-hash</i>+<br> <b>R</b> <i>repository-checksum</i><br> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> <b>U</b> <i>user-login</i><br> <b>Z</b> <i>manifest-checksum</i> </blockquote> A manifest must have exactly one C-card. The sole argument to the C-card is a check-in comment that describes the check-in that the manifest defines. The check-in comment is text. The following escape sequences are applied to the text: A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A | > > > > > > > > > | | < | > > > | | > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | No card may be duplicated. The entire manifest may be PGP clear-signed, but otherwise it may contain no additional text or data beyond what is described here. Allowed cards in the manifest are as follows: <blockquote> <b>B</b> <i>baseline-manifest</i><br> <b>C</b> <i>checkin-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br> <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> <b>P</b> <i>SHA1-hash</i>+<br> <b>R</b> <i>repository-checksum</i><br> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> <b>U</b> <i>user-login</i><br> <b>Z</b> <i>manifest-checksum</i> </blockquote> A manifest may optionally have a single B-card. The B-card specifies another manifest that serves as the "baseline" for this manifest. A manifest that has a B-card is called a delta-manifest and a manifest that omits the B-card is a baseline-manifest. The other manifest identified by the argument of the B-card must be a baseline-manifest. A baseline-manifest records the complete contents of a checkin. A delta-manifest records only changes from its baseline. A manifest must have exactly one C-card. The sole argument to the C-card is a check-in comment that describes the check-in that the manifest defines. The check-in comment is text. The following escape sequences are applied to the text: A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A newline (ASCII 0x0a) is "\n" (ASCII 0x5C, x6E). A backslash (ASCII 0x5C) is represented as two backslashes "\\". Apart from space and newline, no other whitespace characters are allowed in the check-in comment. Nor are any unprintable characters allowed in the comment. A manifest must have exactly one D-card. The sole argument to the D-card is a date-time stamp in the ISO8601 format. The date and time should be in coordinated universal time (UTC). The format is: <blockquote> <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i> </blockquote> A manifest has zero or more F-cards. Each F-card identifies a file that is part of the check-in. There are one, two, three, or four arguments. The first argument is the pathname of the file in the check-in relative to the root of the project file hierarchy. No ".." or "." directories are allowed within the filename. Space characters are escaped as in C-card comment text. Backslash characters and newlines are not allowed within filenames. The directory separator character is a forward slash (ASCII 0x2F). The second argument to the F-card is the full 40-character lower-case hexadecimal SHA1 hash of the content artifact. The second argument is required for baseline manifests but is optional for delta manifests. When the second argument to the F-card is omitted, it means that the file has been deleted relative to the baseline. The optional 3rd argument defines any special access permissions associated with the file. The only special code currently defined is "x" which means that the file is executable. All files are always readable and writable. This can be expressed by "w" permission if desired but is optional. The file format might be extended with new permission letters in the future. The optional 4th argument is the name of the same file as it existed in the parent check-in. If the name of the file is unchanged from its parent, then the 4th argument is omitted. A manifest has zero or one P-cards. Most manifests have one P-card. The P-card has a varying number of arguments that defines other manifests from which the current manifest |
︙ | ︙ | |||
163 164 165 166 167 168 169 | the manifest itself) in strict sorted lexicographical order, take the pathname of the file relative to the root of the repository, append a single space (ASCII 0x20), the size of the file in ASCII decimal, a single newline character (ASCII 0x0A), and the complete text of the file. Compute the MD5 checksum of the result. | | > | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | the manifest itself) in strict sorted lexicographical order, take the pathname of the file relative to the root of the repository, append a single space (ASCII 0x20), the size of the file in ASCII decimal, a single newline character (ASCII 0x0A), and the complete text of the file. Compute the MD5 checksum of the result. A manifest might contain one or more T-cards used to set [./branching.wiki#tags | tags or properties] on the check-in. The format of the T-card is the same as described in <i>Control Artifacts</i> section below, except that the second argument is the single characcter "<b>*</b>" instead of an artifact ID. The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". T-cards are typically added to manifests in order to set the <b>branch</b> property and a symbolic name when the check-in is intended to start a new branch. Each manifest has a single U-card. The argument to the U-card is the login of the user who created the manifest. The login name is encoded using the same character escapes as is used for the check-in comment argument to the C-card. A manifest has an optional Z-card as its last line. The argument to the Z-card is a 32-character lowercase hexadecimal MD5 hash of all prior lines of the manifest up to and including the newline character that immediately precedes the "Z". The Z-card is just a sanity check to prove that the manifest is well-formed and consistent. A sample manifest from Fossil itself can be seen |
︙ | ︙ | |||
260 261 262 263 264 265 266 | one or more T cards. No other cards or other text is allowed in a control artifact. Control artifacts might be PGP clearsigned. The D card and the Z card of a control artifact are the same as in a manifest. | > | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | one or more T cards. No other cards or other text is allowed in a control artifact. Control artifacts might be PGP clearsigned. The D card and the Z card of a control artifact are the same as in a manifest. The T card represents a [./branching.wiki#tags | tag or property] that is applied to some other artifact. The T card has two or three values. The second argument is the 40 character lowercase artifact ID of the artifact to which the tag is to be applied. The first value is the tag name. The first character of the tag is either "+", "-", or "*". A "+" means the tag should be added to the artifact. The "-" means the tag should be removed. The "*" character means the tag should be added to the artifact |
︙ | ︙ | |||
322 323 324 325 326 327 328 | The W card is used to specify the text of the wiki page. The argument to the W card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the W card. The wiki text is always followed by one extra newline. An example wiki artifact can be seen | | | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | The W card is used to specify the text of the wiki page. The argument to the W card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the W card. The wiki text is always followed by one extra newline. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. <a name="tktchng"></a> <h2>5.0 Ticket Changes</h2> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: |
︙ | ︙ | |||
373 374 375 376 377 378 379 | An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h2>6.0 Attachments</h2> An attachment artifact associates some other artifact that is the | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 | An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h2>6.0 Attachments</h2> An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page or event to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: <blockquote> <b>A</b> <i>filename target</i> ?<i>source</i>?<br /> <b>C</b> <i>comment</i><br /> <b>D</b> <i>time-and-date-stamp</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </blockquote> The A card specifies a filename for the attachment in its first argument. The second argument to the A card is the name of the wiki page or ticket or event to which the attachment is connected. The third argument is either missing or else it is the 40-character artifact ID of the attachment itself. A missing third argument means that the attachment should be deleted. The C card is an optional comment describing what the attachment is about. The C card is optional, but there can only be one. A single D card is required to give the date and time when the attachment was applied. A single U card gives the name of the user to added the attachment. If an attachment is added anonymously, then the U card may be omitted. The Z card is the usual checksum over the rest of the attachment artifact. <a name="event"></a> <h2>7.0 Events</h2> An event artifact associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Events can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on an event artifact: <blockquote> <b>C</b> <i>comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>E</b> <i>event-time</i> <i>event-id</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> <b>T</b> <b>+</b><i>tag-name</i> <b>*</b> <i>value</i><br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </blockquote> The C card contains text that is displayed on the timeline for the event. Exactly one C card is required on an event artifact. A single D card is required to give the date and time when the event artifact was created. This is different from the time at which the event occurs. A single E card gives the time of the event (the point on the timeline where the event is displayed) and a unique identifier for the event. When there are multiple artifacts with the same event-id, the one with the most recent D card is the only one used. The event-id must be a 40-character lower-case hexadecimal string. The option P card specifies a prior event with the same event-id from which the current event is an edit. The P card is a hint to the system that it might be space efficient to store one event as a delta of the other. An event might contain one or more T-cards used to set [./branching.wiki#tags | tags or properties] on the event. The format of the T-card is the same as described in [#ctrl | Control Artifacts] section above, except that the second argument is the single characcter "<b>*</b>" instead of an artifact ID and the name is always prefaced by "<b>+</b>". The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the name means that tags can only be add and they can only be non-propagating tags. A an event, T cards are normally used to set the background display color for timelines. The optional U card gives name of the user who entered the event. A single W card provides wiki text for the document associated with the event. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the usual checksum over the rest of the attachment artifact. <a name="summary"></a> <h2>8.0 Card Summary</h2> The following table summaries the various kinds of cards that appear on Fossil artifacts: <table border=1 width="100%"> <tr> <th rowspan=2 valign=bottom>Card Format</th> <th colspan=7>Used By</th> </tr> <tr> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> <th>Event</th> </tr> <tr> <td><b>A</b> <i>filename target source</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>X</b></td> <td> </td> </tr> <tr> <td><b>B</b> <i>baseline</i></td> <td align=center><b>X</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>C</b> <i>comment-text</i></td> <td align=center><b>X</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>D</b> <i>date-time-stamp</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>E</b> <i>event-time event-id</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>F</b> <i>filename uuid permissions oldname</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>J</b> <i>name value</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>K</b> <i>ticket-uuid</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>L</b> <i>wiki-title</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>M</b> <i>uuid</i></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>P</b> <i>uuid ...</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>R</b> <i>md5sum</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <tr> <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>U</b> <i>username</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>W</b> <i>size</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>Z</b> <i>md5sum</i></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> </table> |
Changes to www/index.wiki.
︙ | ︙ | |||
38 39 40 41 42 43 44 | There are plenty of open-source version control systems available on the internet these days. What makes Fossil worthy of attention? 1. <b>Bug Tracking And Wiki</b> - In addition to doing [./concepts.wiki | distributed version control] like Git and Mercurial, | | | > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | There are plenty of open-source version control systems available on the internet these days. What makes Fossil worthy of attention? 1. <b>Bug Tracking And Wiki</b> - In addition to doing [./concepts.wiki | distributed version control] like Git and Mercurial, Fossil also supports [./bugtheory.wiki | distributed bug tracking], [./wikitheory.wiki | distributed wiki], and a [./event.wiki | distributed blog] mechanism all in a single integrated package. 2. <b>Web Interface</b> - Fossil has a built-in and easy-to-use [./webui.wiki | web interface] that simplifies project tracking and promotes situational awareness. Simply type "fossil ui" from within any check-out and Fossil automatically opens your web browser in a page that gives detailed |
︙ | ︙ | |||
86 87 88 89 90 91 92 | used by the [./selfhost.wiki | self-hosting fossil repositories]. 7. <b>Robust & Reliable</b> - Fossil stores content using an [./fileformat.wiki | enduring file format] in an SQLite database so that transactions are atomic even if interrupted by a power loss or system crash. Furthermore, automatic [./selfcheck.wiki | self-checks] verify that all aspects of | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | used by the [./selfhost.wiki | self-hosting fossil repositories]. 7. <b>Robust & Reliable</b> - Fossil stores content using an [./fileformat.wiki | enduring file format] in an SQLite database so that transactions are atomic even if interrupted by a power loss or system crash. Furthermore, automatic [./selfcheck.wiki | self-checks] verify that all aspects of the repository are consistent prior to each commit. In over three years of operation, no work has ever been lost after having been committed to a Fossil repository. <hr> <h3>Links For Fossil Users:</h3> * [./reviews.wiki | Testimonials] from satisfied fossil users. |
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 | designed to be readable, searchable, and extensible by people not yet born. * A tutorial on [./branching.wiki | branching], what it means and how to do it using fossil. * The [./selfcheck.wiki | automatic self-check] mechanism helps insure project integrity. * Fossil contains a [./wikitheory.wiki | built-in wiki]. * There is a [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives] available for discussing fossil issues. * [./stats.wiki | Performance statistics] taken from real-world projects hosted on fossil. * How to [./shunning.wiki | delete content] from a fossil repository. | > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | designed to be readable, searchable, and extensible by people not yet born. * A tutorial on [./branching.wiki | branching], what it means and how to do it using fossil. * The [./selfcheck.wiki | automatic self-check] mechanism helps insure project integrity. * Fossil contains a [./wikitheory.wiki | built-in wiki]. * An [./event.wiki | Event] is a special kind of wiki page associated with a point in time rather than a name. * There is a [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives] available for discussing fossil issues. * [./stats.wiki | Performance statistics] taken from real-world projects hosted on fossil. * How to [./shunning.wiki | delete content] from a fossil repository. |
︙ | ︙ |
Changes to www/password.wiki.
︙ | ︙ | |||
58 59 60 61 62 63 64 | all login for that user. Thus, to lock a user out of the system, one has only to set their password to an empty string, using either the web interface or direct SQL manipulation of the USER table. Note also that the password field is essentially ignored for the special users named "anonymous", "developer", "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol | | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | all login for that user. Thus, to lock a user out of the system, one has only to set their password to an empty string, using either the web interface or direct SQL manipulation of the USER table. Note also that the password field is essentially ignored for the special users named "anonymous", "developer", "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords. <h2>Web Interface Authentication</h2> When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. The server then hashes the password and compares it against the value stored in USER.PW. If they match, the server sets a cookie on the client to record the login. This cookie contains a large amount of high-quality randomness and is thus intractable to guess. The value of the cookie and the IP address of the client is stored in the USER.COOKIE and USER.IPADDR fields of the USER table on the server. The USER.CEXPIRE field holds an expiration date for the cookie, encoded as a julian day number. On all subsequent HTTP requests, the cookie value is matched against the USER table to enable access to the repository. |
︙ | ︙ |
Changes to www/qandc.wiki.
︙ | ︙ | |||
61 62 63 64 65 66 67 | fossil repository <a href="http://www.sqlite.org/docsrc/">here</a>, for example. Other projects are also adopting fossil. But fossil does not yet have the massive user base of git or mercurial. </blockquote> <b>Fossil looks like the bug tracker that would be in your | | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | fossil repository <a href="http://www.sqlite.org/docsrc/">here</a>, for example. Other projects are also adopting fossil. But fossil does not yet have the massive user base of git or mercurial. </blockquote> <b>Fossil looks like the bug tracker that would be in your Linksys Router's administration screen.</b> <blockquote> <p>I take a pragmatic approach to software: form follows function. To me, it is more important to have a reliable, fast, efficient, enduring, and simple DVCS than one that looks pretty.</p> <p>On the other hand, if you have patches that improve the appearance |
︙ | ︙ |
Changes to www/quickstart.wiki.
︙ | ︙ | |||
230 231 232 233 234 235 236 | <p>Or</b> <blockquote> <b>fossil ui</b> <i>repository-filename</i> </blockquote> | | > > | | > | | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | <p>Or</b> <blockquote> <b>fossil ui</b> <i>repository-filename</i> </blockquote> <p>The <b>ui</b> command is intended for accessing the web interface from a local desktop. The <b>ui</b> command binds to the loopback IP address only (and is thus makes the web interface visible only on the local machine) and it automatically start your web browser pointing at the server. For cross-machine collaboration, use the <b>server</b> command, which binds on all IP addresses and does not try to start a web browser. You can omit the <i>repository-filename</i> if you are within a checked-out local tree. The <b>server</b> uses port 8080 by default but you can specify a different port using the <b>-port</b> option.</p> <p>Command-line servers like this are useful when two people want to share a repository on temporary or ad-hoc basis. For a more permanent installation, you should use either the CGI server or the inetd server. <a name="cgiserver"></a> To use the CGI server, create a CGI script that |
︙ | ︙ |
Changes to www/server.wiki.
︙ | ︙ | |||
92 93 94 95 96 97 98 | If you are using "inetd" to serve your repository, then you simply need to add "/usr/bin/stunnel" (perhaps on a different path, depending on your setup) before the command line to launch Fossil. </p> <p> At this stage, the standalone server (e.g. "fossil server") does not support SSL. </p> </blockquote> | | > > > | > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | If you are using "inetd" to serve your repository, then you simply need to add "/usr/bin/stunnel" (perhaps on a different path, depending on your setup) before the command line to launch Fossil. </p> <p> At this stage, the standalone server (e.g. "fossil server") does not support SSL. </p> </blockquote> <h2>Various security concerns with hosted repositories</h2> <blockquote> <p> There are two main concerns relating to usage of Fossil for sharing sensitive information (source or any other data): <ul> <li>Interception of the Fossil synchronization stream, thereby capturing data, and <li>Direct access to the Fossil repository on the server </ul> </p> <p> Regarding the first, it is adequate to secure the server using SSL, and disallowing any non-SSL access. The data stream will be encrypted by the HTTPS protocol, rendering the data reasonably secure. The truly paranoid may wish to deploy <i>ssh</i> encrypted tunnels, but that is quite a bit more difficult and cumbersome to set up (particularly for a larger number of users). </p> <p> As far as direct access to the repository, the same steps must be taken as for any other internet-facing data-store. Access passwords to any disk-accessing accounts should be strong (and preferably changed from time to time). However, the data in the repository itself are <i>not</i> encrypted (unless you save encrypted data yourself), and so the system administrators of your server will be able to access your data (as with any hosting service setup). The only workaround in this case is to host the server yourself, in which case you will need to allocate resources to deal with administration issues. </p> </blockquote> </nowiki> |
Changes to www/wikitheory.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <h1>Wiki In [./index.wiki | Fossil]</h1> Fossil uses [/wiki_rules | wiki markup] for many things: * Stand-alone wiki pages. * Description and comments in [./bugtheory.wiki | bug reports]. * Check-in comments. * [./embeddeddoc.wiki | Embedded documentation] files whose name ends in "wiki". The [/wiki_rules | formatting rules] for fossil wiki are designed to be simple and intuitive. The idea is that wiki provides paragraph breaks, numbered and bulleted lists, and hyperlinking for simple documents together with a safe subset of HTML for more complex formatting tasks. | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <h1>Wiki In [./index.wiki | Fossil]</h1> Fossil uses [/wiki_rules | wiki markup] for many things: * Stand-alone wiki pages. * Description and comments in [./bugtheory.wiki | bug reports]. * Check-in comments. * [./embeddeddoc.wiki | Embedded documentation] files whose name ends in "wiki". * [./event.wiki | Event descriptions]. The [/wiki_rules | formatting rules] for fossil wiki are designed to be simple and intuitive. The idea is that wiki provides paragraph breaks, numbered and bulleted lists, and hyperlinking for simple documents together with a safe subset of HTML for more complex formatting tasks. |
︙ | ︙ |