Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge in trunk (release 2.17) |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | wcontent-subsets |
| Files: | files | file ages | folders |
| SHA3-256: |
315351f4acab9df8fc3d5f0292f87f33 |
| User & Date: | george 2021-10-09 19:50:28.542 |
Context
|
2022-01-21
| ||
| 20:13 | Merge from trunk check-in: 5c0515e20c user: george tags: wcontent-subsets | |
|
2021-10-09
| ||
| 19:50 | Merge in trunk (release 2.17) check-in: 315351f4ac user: george tags: wcontent-subsets | |
| 14:43 | Version 2.17 check-in: f48180f2ff user: drh tags: trunk, release, version-2.17 | |
|
2021-08-15
| ||
| 23:27 | Merge from trunk check-in: 282c6f956f user: george tags: wcontent-subsets | |
Changes
Deleted art/CollRev1.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/CollRev2.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/CollRev3.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/CollRev4.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/branching.odp.
cannot compute difference between binary files
Deleted art/concept1.dia.
cannot compute difference between binary files
Deleted art/concept2.dia.
cannot compute difference between binary files
Deleted art/delta1.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta2.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta3.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta4.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta5.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta6.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/encode10.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to skins/ardoise/css.txt.
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
a {
background-color: transparent;
color: #ff8000;
text-decoration: unset
}
a:active,
a:hover,
| < < < < | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
a {
background-color: transparent;
color: #ff8000;
text-decoration: unset
}
a:active,
a:hover,
abbr[title] {
border-bottom: 1px dotted
}
b,
optgroup,
strong,
td.usetupEditLabel {
|
| ︙ | ︙ | |||
1117 1118 1119 1120 1121 1122 1123 |
.tl-line.warp {
background: #600000
}
table.login_out .login_out_label {
font-weight: 700;
text-align: right
}
| < | < | < | < | > | | < | | | > | | | > | > > > > | > | < | > | < | | > | > | > > > > | 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 |
.tl-line.warp {
background: #600000
}
table.login_out .login_out_label {
font-weight: 700;
text-align: right
}
table.diff {
width: 100%;
overflow: auto;
font-size: 1rem;
background: #000;
border-radius: 5px;
}
table.diff pre {
font-size: 1.15rem;
scrollbar-color: black #999;
}
table.udiff pre {
padding: 10px 0
}
td.difftxt {
width: 52rem;
}
td.diffln ins {
background-color: #559855;
color: #000;
text-decoration: none;
}
td.diffln del {
background-color: #c55;
color: #000;
text-decoration: none;
}
td.difftxt del {
background-color: inherit;
text-decoration: none;
}
td.difftxt del > del {
background-color: #c55;
color: #000;
text-decoration: none;
}
td.difftxt ins {
background-color: inherit;
text-decoration: none;
}
td.difftxt ins > ins {
background-color: #559855;
color: #000;
text-decoration: none;
}
tr.diffskip.jchunk {
background-color: #404040;
}
tr.diffskip > td.chunkctrl .jcbutton {
background-color: black;
}
table.report {
width: 100%;
cursor: auto;
margin: 0 0 1em;
color: #000
}
table.report thead {
|
| ︙ | ︙ |
Changes to skins/black_and_white/css.txt.
1 2 3 4 5 6 7 |
/* 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;
| | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* 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;
text-size-adjust: none;
}
/* consistent colours */
h2 {
color: #333;
}
h3 {
|
| ︙ | ︙ |
Changes to skins/blitz/css.txt.
| ︙ | ︙ | |||
1129 1130 1131 1132 1133 1134 1135 |
table.login_out td {
border: 0;
}
/* Diff displays
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
| | | | 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 |
table.login_out td {
border: 0;
}
/* Diff displays
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.diff {
width: 100%;
overflow: auto;
border: 1px solid #ccc;
padding: 5px;
font-size: 1rem;
}
table.diff:focus {
outline: none;
}
/* Ticket Reports
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.report {
|
| ︙ | ︙ |
Changes to skins/bootstrap/css.txt.
| ︙ | ︙ | |||
4398 4399 4400 4401 4402 4403 4404 |
height: 4px;
background: #000;
}
body.branch .submenu > a.timeline-link {
color: black;
}
| > > > > > > > > > > > > > > > > > > > > > > > > | 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 4426 4427 4428 |
height: 4px;
background: #000;
}
body.branch .submenu > a.timeline-link {
color: black;
}
tr.diffskip > td.chunkctrl .jcbutton {
min-width: 3.5ex;
max-width: revert;
}
/* Bootstrap installs a 'table' class on tables which causes its
styles to be more specific matches than our diff tables, so we have
to fight that fire with more fire... */
table.diff.table>thead>tr>th, table.diff.table>tbody>tr>th,
table.diff.table>tfoot>tr>th, table.diff.table>thead>tr>td,
table.diff.table>tbody>tr>td, table.diff.table>tfoot>tr>td {
padding: 0;
line-height: revert;
vertical-align: top;
border-top: none;
}
table.diff tr.diffskip.jchunk > td {
padding: 0.25em 0.5em;
}
table.diff pre {
border: none;
word-wrap: initial;
}
|
Changes to skins/darkmode/css.txt.
| ︙ | ︙ | |||
449 450 451 452 453 454 455 | border-left: 1px dashed #bbb; background: rgba(255,255,255,0); } /************************************************************************ diffs... ************************************************************************/ | | | | > | | | > | > > > > | > | | > | > | > | > > > | | 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 |
border-left: 1px dashed #bbb;
background: rgba(255,255,255,0);
}
/************************************************************************
diffs...
************************************************************************/
td.diffln ins {
background-color: #559855;
color: #000;
text-decoration: none;
}
td.diffln del {
background-color: #c55;
color: #000;
text-decoration: none;
}
td.difftxt del {
background-color: inherit;
text-decoration: none;
}
td.difftxt del > del {
background-color: #c55;
color: #000;
text-decoration: none;
}
td.difftxt ins {
background-color: inherit;
text-decoration: none;
}
td.difftxt ins > ins {
background-color: #559855;
color: #000;
text-decoration: none;
}
tr.diffskip.jchunk {
background-color: black;
}
tr.diffskip > td.chunkctrl .jcbutton {
background-color: #303536;
}
/************************************************************************
************************************************************************/
body.wikiedit #fossil-status-bar,
body.fileedit #fossil-status-bar{
border-radius: 0.25em 0.25em 0 0;
|
| ︙ | ︙ | |||
505 506 507 508 509 510 511 |
font-weight: bold;
}
button,
input,
optgroup,
select,
textarea {
| | > > > > > > > | 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 |
font-weight: bold;
}
button,
input,
optgroup,
select,
textarea {
background: inherit;
color: inherit;
font: inherit;
margin: 0
}
button {
background-color: rgba(45,45,45,0.75);
}
input, textarea, select {
border: 1px solid rgba(127, 201, 255, 0.9);
padding: 1px;
}
select {
color: #1f1f1f;
background: #ffffffe0;
}
.capsumOff {
background-color: #222;
}
.capsumRead {
background-color: #262;
}
.capsumWrite {
|
| ︙ | ︙ | |||
545 546 547 548 549 550 551 |
}
body.forum .forumPostBody > div blockquote {
border: 1px inset;
padding: 0 0.5em;
}
| < < < < | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
}
body.forum .forumPostBody > div blockquote {
border: 1px inset;
padding: 0 0.5em;
}
body.report table.report tr td { color: black }
body.report table.report a { color: blue }
body.tkt td.tktDspValue { color: black }
body.tkt td.tktDspValue a { color: blue }
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #442800;
}
|
Changes to skins/default/css.txt.
| ︙ | ︙ | |||
156 157 158 159 160 161 162 |
.footer {
border-top: 1px solid #ccc;
padding: 10px;
font-size: 0.7em;
margin-top: 10px;
color: #ccc;
}
| < < < < < < < < < < < | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
.footer {
border-top: 1px solid #ccc;
padding: 10px;
font-size: 0.7em;
margin-top: 10px;
color: #ccc;
}
/* Forum */
.forum a:visited {
color: #6A7F94;
}
|
| ︙ | ︙ |
Changes to skins/eagle/css.txt.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
div.logo {
display: table-cell;
text-align: center;
vertical-align: bottom;
font-weight: bold;
color: white;
min-width: 50px;
| < > > > > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
div.logo {
display: table-cell;
text-align: center;
vertical-align: bottom;
font-weight: bold;
color: white;
min-width: 50px;
white-space: nowrap;
position: relative;
filter: drop-shadow(2px 4px 6px rgba(0,0,0,0.75));
top: 0.5em;
right: -0.5em;
}
div.logo img{
border-radius: 2mm;
}
/* The page title centered at the top of each page */
div.title {
display: table-cell;
font-size: 2em;
font-weight: bold;
|
| ︙ | ︙ | |||
255 256 257 258 259 260 261 | border-top: 0px dashed #fff; border-left: 1px dashed #fff; background: rgba(255,255,255,0); } /* Side-by-side diff */ | | < | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
border-top: 0px dashed #fff;
border-left: 1px dashed #fff;
background: rgba(255,255,255,0);
}
/* Side-by-side diff */
table.splitdiff {
background-color: #485D7B;
font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
font-size: 8pt;
border-collapse:collapse;
white-space: pre;
border: 1px #000 dashed;
margin-left: auto;
margin-right: auto;
}
/* format for the layout table, used for the captcha display */
table.captcha {
|
| ︙ | ︙ | |||
318 319 320 321 322 323 324 |
/* List of files in a timeline */
ul.filelist {
margin-top: 3px;
line-height: 100%;
}
/* side-by-side diff display */
| | | | | | | < < | | > > | < | < < | | < < > | | 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 |
/* List of files in a timeline */
ul.filelist {
margin-top: 3px;
line-height: 100%;
}
/* side-by-side diff display */
div.splitdiff {
font-family: monospace;
font-size: smaller;
white-space: pre;
}
/* context diff display */
table.udiff {
font-family: monospace;
white-space: pre;
}
/* added code in a diff */
td.difftxt ins > ins, td.diffln ins {
background-color: rgb(100, 200, 100);
}
td.difftxt ins {
background-color: inherit;
}
/* deleted in a diff */
td.difftxt del > del, td.diffln del {
background-color: rgb(230, 110, 110);
}
td.difftxt del {
background-color: inherit;
}
tr.diffskip.jchunk {
background-color: #7EA2D9;
}
tr.diffskip > td.chunkctrl .jcbutton{
color: white;
background-color: #485D7B;
}
.fileage tr:hover {
background-color: #7EA2D9;
}
span.modpending {
color: #c0c0c0;
font-style: italic;
}
span.forum_author {
|
| ︙ | ︙ | |||
417 418 419 420 421 422 423 |
background-color: #7EA2D9;
}
.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
background-color: #455978;
}
| < < < < < | 418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
background-color: #7EA2D9;
}
.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
background-color: #455978;
}
.capsumOff {
background-color: #bbbbbb;
}
.capsumRead {
background-color: #006d00;
}
.capsumWrite {
|
| ︙ | ︙ |
Changes to skins/eagle/header.txt.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
set logourl $baseurl
}
} else {
set logourl $baseurl
}
return $logourl
}
| > > | > > > > | | | | > | > | | | | | | | < < | 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 |
set logourl $baseurl
}
} else {
set logourl $baseurl
}
return $logourl
}
if {1} {
# Link logo to the top of the current domain
set logourl [getLogoUrl $baseurl]
} else {
# Link logo to the top of the current repo
set logourl $baseurl
}
</th1>
<a href="$logourl">
<img src="$logo_image_url" border="0" alt="$project_name">
</a>
</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><small><div id="clock"></div></small></div>
</div>
<th1>html "<script nonce='$nonce'>"</th1>
(function updateClock(){
var e = document.getElementById("clock");
if(!e) return;
if(!updateClock.fmt){
updateClock.fmt = function(n){
return n < 10 ? '0' + n : n;
};
}
var d = new Date();
e.innerHTML = d.getUTCFullYear()+ '-' +
updateClock.fmt(d.getUTCMonth() + 1) + '-' +
updateClock.fmt(d.getUTCDate()) + ' ' +
updateClock.fmt(d.getUTCHours()) + ':' +
updateClock.fmt(d.getUTCMinutes());
setTimeout(updateClock,(60-d.getUTCSeconds())*1000);
})();
</script>
<div class="mainmenu"><th1>
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>\n"
builtin_request_js hbmenu.js
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {set url $home$url}
|
| ︙ | ︙ |
Changes to skins/khaki/css.txt.
1 2 3 4 5 6 |
/* General settings for the entire page */
body {
margin: 0ex 0ex;
padding: 0px;
background-color: #fef3bc;
font-family: sans-serif;
| | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* General settings for the entire page */
body {
margin: 0ex 0ex;
padding: 0px;
background-color: #fef3bc;
font-family: sans-serif;
text-size-adjust: none;
}
/* The project logo in the upper left-hand corner of each page */
div.logo {
display: inline;
text-align: center;
vertical-align: bottom;
|
| ︙ | ︙ | |||
168 169 170 171 172 173 174 |
div.forumPostBody blockquote {
border-width: 1pt;
border-radius: 0.25em;
border-style: solid;
padding: 0 0.5em;
}
| > > > > > > > > > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
div.forumPostBody blockquote {
border-width: 1pt;
border-radius: 0.25em;
border-style: solid;
padding: 0 0.5em;
}
tr.diffskip > td.chunkctrl .jcbutton {
color: white;
background-color: #a09048;
}
tr.diffskip.jchunk {
background-color: #c0af58;
}
|
Changes to skins/plain_gray/css.txt.
1 2 3 4 5 6 |
/* General settings for the entire page */
body {
margin: 0ex 1ex;
padding: 0px;
background-color: white;
font-family: sans-serif;
| | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* General settings for the entire page */
body {
margin: 0ex 1ex;
padding: 0px;
background-color: white;
font-family: sans-serif;
text-size-adjust: none;
}
/* The page title centered at the top of each page */
div.title {
display: table-cell;
font-size: 1.5em;
font-weight: bold;
|
| ︙ | ︙ |
Changes to skins/xekri/css.txt.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
font-size: 1em;
min-height: 100%;
}
body {
margin: 0;
padding: 0;
| | < < | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
font-size: 1em;
min-height: 100%;
}
body {
margin: 0;
padding: 0;
text-size-adjust: none;
}
a {
color: #40a0ff;
}
a:hover {
|
| ︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 |
/**************************************
* Main Area: Header
*/
div.header {
margin: 0.5rem auto 0 auto;
}
div.logo img {
| > > > > > > > > > > < | | | 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 |
/**************************************
* Main Area: Header
*/
div.header {
margin: 0.5rem auto 0 auto;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
}
div.logo {
display: inline;
max-height: 4em;
max-width: 4em;
flex: 0 1 auto;
}
div.logo img {
padding: 0;
box-shadow: 2px 4px 6px rgba(180,180,180,0.70);
border-radius: 2mm;
}
div.logo br {
display: none;
}
div.logo nobr {
|
| ︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
color: #3297f9;
font-family: Verdana, sans-serif;
font-weight: bold;
font-size: 2.5rem;
padding: 0.5rem;
text-align: center;
text-shadow: 3px 3px 1px #000;
}
div.status {
color: #ee0;
font-size: 1rem;
padding: 0.25rem;
text-align: right;
text-shadow: 2px 2px 1px #000;
}
/**************************************
* Main Area: Global Menu
*/
| > > | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
color: #3297f9;
font-family: Verdana, sans-serif;
font-weight: bold;
font-size: 2.5rem;
padding: 0.5rem;
text-align: center;
text-shadow: 3px 3px 1px #000;
flex: 10 0 auto;
}
div.status {
color: #ee0;
font-size: 1rem;
padding: 0.25rem;
text-align: right;
text-shadow: 2px 2px 1px #000;
flex: 0 1 auto;
}
/**************************************
* Main Area: Global Menu
*/
|
| ︙ | ︙ | |||
226 227 228 229 230 231 232 |
div.footer div {
background-color: #222;
box-shadow: 3px 3px 1px #000;
border-radius: 0 0 1rem 1rem;
margin: 0 0 10px 0;
| | | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
div.footer div {
background-color: #222;
box-shadow: 3px 3px 1px #000;
border-radius: 0 0 1rem 1rem;
margin: 0 0 10px 0;
padding: 0.25rem 0.75rem;
}
div.footer div.page-time {
float: left;
}
div.footer div.fossil-info {
|
| ︙ | ︙ | |||
261 262 263 264 265 266 267 268 269 | padding: 0.1rem 1rem; } /************************************** * Diffs */ /* Code Added */ | > > > > > > > | > | < < | < | > > > > | < | | | | | < < < < < | < | | < < < < < < | 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 |
padding: 0.1rem 1rem;
}
/**************************************
* Diffs
*/
tr.diffskip.jchunk {
background-color: black;
}
tr.diffskip > td.chunkctrl .jcbutton {
background-color: #303536;
}
/* Code Added */
td.diffln ins,
td.difftxt ins > ins {
background-color: #7f7;
color: #000;
}
td.difftxt ins {
background-color: inherit;
}
/* Code Deleted */
td.diffln del,
td.difftxt del > del {
background-color: #f77;
color: #000;
}
td.difftxt del {
background-color: inherit;
}
/**************************************
* Diffs : Side-By-Side
*/
/* display (column-based) */
table.splitdiff {
border-spacing: 0;
font-size: 0.85rem;
}
table.splitdiff pre {
border: 0;
margin: 0 0.5em;
padding: 0;
}
table.splitdiff td {
padding: 0;
vertical-align: top;
}
/* line number column */
td.diffln {
color: #ee0;
padding-right: 0.75em;
text-align: right;
}
/* diff text column */
td.difftxt {
background-color: #111;
overflow-x: auto;
width: 45em;
}
/* diff marker column */
td.diffsep {
padding: 0 0.5em;
}
/**************************************
* Diffs : Unified
*/
table.udiff pre {
background-color: #111;
}
/**************************************
* File List : Flat
*/
table.browser {
width: 100%;
|
| ︙ | ︙ | |||
1144 1145 1146 1147 1148 1149 1150 |
color: black;
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #444;
}
| > > > > > > > > > > > > > > | 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 |
color: black;
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #444;
}
body.chat div.header, body.chat div.footer,
body.chat div.mainmenu, body.chat div.submenu,
body.chat div.content {
margin-left: 0.5em;
margin-right: 0.5em;
margin-top: auto/*eliminates unnecessary scrollbars*/;
}
body.chat.chat-only-mode div.content {
max-width: revert;
}
body.chat #chat-user-list .chat-user{
color: white;
}
|
Changes to skins/xekri/header.txt.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
set logourl $baseurl
}
} else {
set logourl $baseurl
}
return $logourl
}
| > > | > > > > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
set logourl $baseurl
}
} else {
set logourl $baseurl
}
return $logourl
}
if {1} {
# Link logo to the top of the current domain
set logourl [getLogoUrl $baseurl]
} else {
# Link logo to the top of the current repo
set logourl $baseurl
}
</th1>
<a href="$logourl">
<img src="$logo_image_url" border="0" alt="$project_name">
</a>
</div>
<div class="title">$<title></div>
<div class="status"><nobr><th1>
|
| ︙ | ︙ |
Changes to src/ajax.c.
| ︙ | ︙ | |||
147 148 149 150 151 152 153 154 | } } /* ** Renders diffs for ajax routes. pOrig is the "original" (v1) content ** and pContent is the locally-edited (v2) content. diffFlags is any ** set of flags suitable for passing to text_diff(). */ | > > > > > | > > > > | < < | | 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 |
}
}
/*
** Renders diffs for ajax routes. pOrig is the "original" (v1) content
** and pContent is the locally-edited (v2) content. diffFlags is any
** set of flags suitable for passing to text_diff().
**
** zOrigHash, if not NULL, must be the SCM-side hash of pOrig's
** contents. If set, additional information may be built into
** the diff output to enable dynamic loading of additional
** diff context.
*/
void ajax_render_diff(Blob * pOrig, const char * zOrigHash,
Blob *pContent, u64 diffFlags){
Blob out = empty_blob;
DiffConfig DCfg;
diff_config_init(&DCfg, diffFlags);
DCfg.zLeftHash = zOrigHash;
text_diff(pOrig, pContent, &out, &DCfg);
if(blob_size(&out)==0){
/* nothing to do */
}else{
CX("%b",&out);
}
blob_reset(&out);
}
/*
** Uses P(zKey) to fetch a CGI environment variable. If that var is
** NULL or starts with '0' or 'f' then this function returns false,
|
| ︙ | ︙ | |||
255 256 257 258 259 260 261 |
*zFn = PD("filename",P("fn"));
if (*zFn) ++rc;
}
return rc;
}
/*
| | | > > | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
*zFn = PD("filename",P("fn"));
if (*zFn) ++rc;
}
return rc;
}
/*
** AJAX route /ajax/preview-text
**
** Required query parameters:
**
** filename=name of content, for use in determining the
** mimetype/render mode.
**
** content=text
**
** Optional query parameters:
**
** render_mode=integer (AJAX_RENDER_xxx) (default=AJAX_RENDER_GUESS)
**
** ln=0 or 1 to disable/enable line number mode in
** AJAX_RENDER_PLAIN_TEXT mode.
|
| ︙ | ︙ | |||
356 357 358 359 360 361 362 |
int cmp_ajax_route_name(const void *a, const void *b){
const AjaxRoute * rA = (const AjaxRoute*)a;
const AjaxRoute * rB = (const AjaxRoute*)b;
return fossil_strcmp(rA->zName, rB->zName);
}
/*
| | | 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
int cmp_ajax_route_name(const void *a, const void *b){
const AjaxRoute * rA = (const AjaxRoute*)a;
const AjaxRoute * rB = (const AjaxRoute*)b;
return fossil_strcmp(rA->zName, rB->zName);
}
/*
** WEBPAGE: ajax hidden
**
** The main dispatcher for shared ajax-served routes. Requires the
** 'name' parameter be the main route's name (as defined in a list in
** this function), noting that fossil automatically assigns all path
** parts after "ajax" to "name", e.g. /ajax/foo/bar assigns
** name=foo/bar.
**
|
| ︙ | ︙ |
Changes to src/alerts.c.
| ︙ | ︙ | |||
126 127 128 129 130 131 132 |
db_exec_sql(zAlertInit);
return;
}
if( db_table_has_column("repository","subscriber","lastContact") ){
return;
}
db_multi_exec(
| | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
db_exec_sql(zAlertInit);
return;
}
if( db_table_has_column("repository","subscriber","lastContact") ){
return;
}
db_multi_exec(
"DROP TABLE IF EXISTS repository.alert_bounce;\n"
"ALTER TABLE repository.subscriber ADD COLUMN lastContact INT;\n"
"UPDATE subscriber SET lastContact=mtime/86400;"
);
if( db_table_has_column("repository","pending_alert","sentMod") ){
return;
}
db_multi_exec(
|
| ︙ | ︙ |
Changes to src/allrepo.c.
| ︙ | ︙ | |||
89 90 91 92 93 94 95 | ** ** extras Shows "extra" files from all local checkouts. The command ** line options supported by the extra command itself, if any ** are present, are passed along verbatim. ** ** fts-config Run the "fts-config" command on all repositories. ** | > | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | ** ** extras Shows "extra" files from all local checkouts. The command ** line options supported by the extra command itself, if any ** are present, are passed along verbatim. ** ** fts-config Run the "fts-config" command on all repositories. ** ** git CMD Do the "git export" or "git status" command (which every ** is specified by CMD) on all repositories for which ** a Git mirror has been previously established. ** ** info Run the "info" command on all repositories. ** ** pull Run a "pull" operation on all repositories. Only the ** --verbose option is supported. ** |
| ︙ | ︙ |
Changes to src/attach.c.
| ︙ | ︙ | |||
657 658 659 660 661 662 663 664 665 666 667 668 669 670 |
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
if( cnt==0 ){
@ %s(zHeader)
}
cnt++;
@ <li>
@ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a>
@ added by %h(zDispUser) on
hyperlink_to_date(zDate, ".");
@ [%z(href("%R/ainfo/%!S",zUuid))details</a>]
@ </li>
}
if( cnt ){
@ </ul>
| > | 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 |
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
if( cnt==0 ){
@ %s(zHeader)
}
cnt++;
@ <li>
@ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a>
@ [<a href="%R/attachdownload/%t(zFile)?page=%t(zTarget)&file=%t(zFile)">download</a>]
@ added by %h(zDispUser) on
hyperlink_to_date(zDate, ".");
@ [%z(href("%R/ainfo/%!S",zUuid))details</a>]
@ </li>
}
if( cnt ){
@ </ul>
|
| ︙ | ︙ |
Changes to src/blob.c.
| ︙ | ︙ | |||
273 274 275 276 277 278 279 | pBlob->aData = (char*)zEmpty; pBlob->iCursor = 0; pBlob->blobFlags = 0; pBlob->xRealloc = blobReallocStatic; } /* | | > > < > > | | < | < > | | | > > > > > > > > | < | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
pBlob->aData = (char*)zEmpty;
pBlob->iCursor = 0;
pBlob->blobFlags = 0;
pBlob->xRealloc = blobReallocStatic;
}
/*
** Append text or data to the end of a blob. Or, if pBlob==NULL, send
** the text to standard output in terminal mode, or to standard CGI output
** in CGI mode.
**
** If nData<0 then output all of aData up to the first 0x00 byte.
**
** Use the blob_append() routine in all application code. The blob_append()
** routine is faster, but blob_append_full() handles all the corner cases.
** The blob_append() routine automatically calls blob_append_full() if
** necessary.
*/
static void blob_append_full(Blob *pBlob, const char *aData, int nData){
sqlite3_int64 nNew;
/* assert( aData!=0 || nData==0 ); // omitted for speed */
/* blob_is_init(pBlob); // omitted for speed */
if( nData<0 ) nData = strlen(aData);
if( nData==0 ) return;
if( pBlob==0 ){
if( g.cgiOutput ){
pBlob = cgi_output_blob();
}else{
fossil_puts(aData, 0, nData);
return;
}
}
nNew = pBlob->nUsed;
nNew += nData;
if( nNew >= pBlob->nAlloc ){
nNew += pBlob->nAlloc;
nNew += 100;
if( nNew>=0x7fff0000 ){
blob_panic();
}
pBlob->xRealloc(pBlob, (int)nNew);
if( pBlob->nUsed + nData >= pBlob->nAlloc ){
blob_panic();
}
}
memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
pBlob->nUsed += nData;
pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
}
void blob_append(Blob *pBlob, const char *aData, int nData){
sqlite3_int64 nUsed;
/* assert( aData!=0 || nData==0 ); // omitted for speed */
if( nData<=0 || pBlob==0 || pBlob->nUsed + nData >= pBlob->nAlloc ){
blob_append_full(pBlob, aData, nData);
return;
}
nUsed = pBlob->nUsed;
pBlob->nUsed += nData;
pBlob->aData[pBlob->nUsed] = 0;
memcpy(&pBlob->aData[nUsed], aData, nData);
}
/*
** Append a string literal to a blob.
*/
#if INTERFACE
#define blob_append_string(BLOB,STR) blob_append(BLOB,STR,sizeof(STR)-1)
#endif
/*
** Append a single character to the blob. If pBlob is zero then the
** character is written directly to stdout.
*/
void blob_append_char(Blob *pBlob, char c){
if( pBlob==0 || pBlob->nUsed+1 >= pBlob->nAlloc ){
blob_append_full(pBlob, &c, 1);
}else{
pBlob->aData[pBlob->nUsed++] = c;
}
}
/*
** Copy a blob. pTo is reinitialized to be a copy of pFrom.
*/
void blob_copy(Blob *pTo, Blob *pFrom){
blob_is_init(pFrom);
blob_zero(pTo);
blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom));
}
/*
** Append the second blob onto the end of the first blob and reset the
** second blob. If the first blob (pTo) is NULL, then the content
** of the second blob is written to stdout or to CGI depending on if the
** Fossil is running in terminal or CGI mode.
*/
void blob_append_xfer(Blob *pTo, Blob *pFrom){
blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom));
blob_reset(pFrom);
}
/*
** Write into pOut, a string literal representation for the first n bytes
** of z[]. The string literal representation is compatible with C, TCL,
** and JSON. Double-quotes are added to both ends. Double-quote and
** backslash characters are escaped.
*/
void blob_append_tcl_literal(Blob *pOut, const char *z, int n){
int i;
blob_append_char(pOut, '"');
for(i=0; i<n; i++){
char c = z[i];
switch( c ){
case '\r': c = 'r';
case '[':
case ']':
case '$':
case '"':
case '\\':
blob_append_char(pOut, '\\');
default:
blob_append_char(pOut, c);
}
}
blob_append_char(pOut, '"');
}
void blob_append_json_literal(Blob *pOut, const char *z, int n){
int i;
blob_append_char(pOut, '"');
for(i=0; i<n; i++){
char c = z[i];
switch( c ){
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07: c += '0' - 0x00; blob_append(pOut, "\\u000",5); break;
case 0x0b:
case 0x0e:
case 0x0f: c += 'a' - 0x0a; blob_append(pOut, "\\u000",5); break;
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19: c += '0' - 0x10; blob_append(pOut, "\\u001",5); break;
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f: c += 'a' - 0x1a; blob_append(pOut, "\\u001",5); break;
case '\b': c = 'b'; blob_append_char(pOut, '\\'); break;
case '\t': c = 't'; blob_append_char(pOut, '\\'); break;
case '\r': c = 'r'; blob_append_char(pOut, '\\'); break;
case '\n': c = 'n'; blob_append_char(pOut, '\\'); break;
case '\f': c = 'f'; blob_append_char(pOut, '\\'); break;
case '"': blob_append_char(pOut, '\\'); break;
case '\\': blob_append_char(pOut, '\\'); break;
default: break;
}
blob_append_char(pOut, c);
}
blob_append_char(pOut, '"');
}
/*
** Return a pointer to a null-terminated string for a blob.
*/
char *blob_str(Blob *p){
blob_is_init(p);
if( p->nUsed==0 ){
|
| ︙ | ︙ | |||
833 834 835 836 837 838 839 |
int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){
int i;
for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
return i;
}
/*
| | > < | | | | | | < < | | | | | < | | 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 |
int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){
int i;
for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
return i;
}
/*
** Do printf-style string rendering and append the results to a blob. Or
** if pBlob==0, do printf-style string rendering directly to stdout.
**
** The blob_appendf() version sets the BLOBFLAG_NotSQL bit in Blob.blobFlags
** whereas blob_append_sql() does not.
*/
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
va_list ap;
va_start(ap, zFormat);
vxprintf(pBlob, zFormat, ap);
va_end(ap);
if( pBlob ) pBlob->blobFlags |= BLOBFLAG_NotSQL;
}
void blob_append_sql(Blob *pBlob, const char *zFormat, ...){
va_list ap;
va_start(ap, zFormat);
vxprintf(pBlob, zFormat, ap);
va_end(ap);
}
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
vxprintf(pBlob, zFormat, ap);
}
/*
** Initialize a blob to the data on an input channel. Return
** the number of bytes read into the blob. Any prior content
** of the blob is discarded, not freed.
*/
|
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
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 | ** ** name Name of the branch ** mtime Time of last checkin on this branch ** isclosed True if the branch is closed ** mergeto Another branch this branch was merged into ** nckin Number of checkins on this branch ** ckin Hash of the last checkin on this branch ** bgclr Background color for this branch */ static const char createBrlistQuery[] = @ CREATE TEMP TABLE IF NOT EXISTS tmp_brlist AS @ SELECT @ tagxref.value AS name, @ max(event.mtime) AS mtime, @ EXISTS(SELECT 1 FROM tagxref AS tx @ WHERE tx.rid=tagxref.rid @ AND tx.tagid=(SELECT tagid FROM tag WHERE tagname='closed') @ AND tx.tagtype>0) AS isclosed, @ (SELECT tagxref.value @ FROM plink CROSS JOIN tagxref @ WHERE plink.pid=event.objid @ AND tagxref.rid=plink.cid @ AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='branch') @ AND tagtype>0) AS mergeto, @ count(*) AS nckin, @ (SELECT uuid FROM blob WHERE rid=tagxref.rid) AS ckin, | > | > | 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 | ** ** name Name of the branch ** mtime Time of last checkin on this branch ** isclosed True if the branch is closed ** mergeto Another branch this branch was merged into ** nckin Number of checkins on this branch ** ckin Hash of the last checkin on this branch ** isprivate True if the branch is private ** bgclr Background color for this branch */ static const char createBrlistQuery[] = @ CREATE TEMP TABLE IF NOT EXISTS tmp_brlist AS @ SELECT @ tagxref.value AS name, @ max(event.mtime) AS mtime, @ EXISTS(SELECT 1 FROM tagxref AS tx @ WHERE tx.rid=tagxref.rid @ AND tx.tagid=(SELECT tagid FROM tag WHERE tagname='closed') @ AND tx.tagtype>0) AS isclosed, @ (SELECT tagxref.value @ FROM plink CROSS JOIN tagxref @ WHERE plink.pid=event.objid @ AND tagxref.rid=plink.cid @ AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='branch') @ AND tagtype>0) AS mergeto, @ count(*) AS nckin, @ (SELECT uuid FROM blob WHERE rid=tagxref.rid) AS ckin, @ event.bgcolor AS bgclr, @ EXISTS(SELECT 1 FROM private WHERE rid=tagxref.rid) AS isprivate @ FROM tagxref, tag, event @ WHERE tagxref.tagid=tag.tagid @ AND tagxref.tagtype>0 @ AND tag.tagname='branch' @ AND event.objid=tagxref.rid @ GROUP BY 1; ; |
| ︙ | ︙ | |||
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
*/
#define BRL_CLOSED_ONLY 0x001 /* Show only closed branches */
#define BRL_OPEN_ONLY 0x002 /* Show only open branches */
#define BRL_BOTH 0x003 /* Show both open and closed branches */
#define BRL_OPEN_CLOSED_MASK 0x003
#define BRL_ORDERBY_MTIME 0x004 /* Sort by MTIME. (otherwise sort by name)*/
#define BRL_REVERSE 0x008 /* Reverse the sort order */
#endif /* INTERFACE */
/*
** Prepare a query that will list branches.
**
** If (which<0) then the query pulls only closed branches. If
** (which>0) then the query pulls all (closed and opened)
** branches. Else the query pulls currently-opened branches.
*/
void branch_prepare_list_query(Stmt *pQuery, int brFlags, const char *zBrNameGlob){
Blob sql;
blob_init(&sql, 0, 0);
brlist_create_temp_table();
switch( brFlags & BRL_OPEN_CLOSED_MASK ){
case BRL_CLOSED_ONLY: {
blob_append_sql(&sql,
| > | | | > | 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 |
*/
#define BRL_CLOSED_ONLY 0x001 /* Show only closed branches */
#define BRL_OPEN_ONLY 0x002 /* Show only open branches */
#define BRL_BOTH 0x003 /* Show both open and closed branches */
#define BRL_OPEN_CLOSED_MASK 0x003
#define BRL_ORDERBY_MTIME 0x004 /* Sort by MTIME. (otherwise sort by name)*/
#define BRL_REVERSE 0x008 /* Reverse the sort order */
#define BRL_PRIVATE 0x010 /* Show only private branches */
#endif /* INTERFACE */
/*
** Prepare a query that will list branches.
**
** If (which<0) then the query pulls only closed branches. If
** (which>0) then the query pulls all (closed and opened)
** branches. Else the query pulls currently-opened branches.
*/
void branch_prepare_list_query(Stmt *pQuery, int brFlags, const char *zBrNameGlob){
Blob sql;
blob_init(&sql, 0, 0);
brlist_create_temp_table();
switch( brFlags & BRL_OPEN_CLOSED_MASK ){
case BRL_CLOSED_ONLY: {
blob_append_sql(&sql,
"SELECT name, isprivate FROM tmp_brlist WHERE isclosed"
);
break;
}
case BRL_BOTH: {
blob_append_sql(&sql,
"SELECT name, isprivate FROM tmp_brlist WHERE 1"
);
break;
}
case BRL_OPEN_ONLY: {
blob_append_sql(&sql,
"SELECT name, isprivate FROM tmp_brlist WHERE NOT isclosed"
);
break;
}
}
if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
if(zBrNameGlob) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob);
if( brFlags & BRL_ORDERBY_MTIME ){
blob_append_sql(&sql, " ORDER BY -mtime");
}else{
blob_append_sql(&sql, " ORDER BY name COLLATE nocase");
}
if( brFlags & BRL_REVERSE ){
|
| ︙ | ︙ | |||
583 584 585 586 587 588 589 590 591 592 | ** Print information about a branch ** ** > fossil branch list|ls ?OPTIONS? ?GLOB? ** ** List all branches. Options: ** -a|--all List all branches. Default show only open branches ** -c|--closed List closed branches. ** -r Reverse the sort order ** -t Show recently changed branches first ** | > | > | 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | ** Print information about a branch ** ** > fossil branch list|ls ?OPTIONS? ?GLOB? ** ** List all branches. Options: ** -a|--all List all branches. Default show only open branches ** -c|--closed List closed branches. ** -p List only private branches. ** -r Reverse the sort order ** -t Show recently changed branches first ** ** The current branch is marked with an asterisk. Private branches are ** marked with a hash sign. ** ** If GLOB is given, show only branches matching the pattern. ** ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? ** ** Create a new branch BRANCH-NAME off of check-in BASIS. ** Supported options for this subcommand include: |
| ︙ | ︙ | |||
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 |
char *zCurrent = 0;
const char *zBrNameGlob = 0;
int brFlags = BRL_OPEN_ONLY;
if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH;
if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY;
if( find_option("t",0,0)!=0 ) brFlags |= BRL_ORDERBY_MTIME;
if( find_option("r",0,0)!=0 ) brFlags |= BRL_REVERSE;
if( g.argc >= 4 ) zBrNameGlob = g.argv[3];
if( g.localOpen ){
vid = db_lget_int("checkout", 0);
zCurrent = db_text(0, "SELECT value FROM tagxref"
" WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
}
branch_prepare_list_query(&q, brFlags, zBrNameGlob);
while( db_step(&q)==SQLITE_ROW ){
const char *zBr = db_column_text(&q, 0);
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
| > > | > > | 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 |
char *zCurrent = 0;
const char *zBrNameGlob = 0;
int brFlags = BRL_OPEN_ONLY;
if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH;
if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY;
if( find_option("t",0,0)!=0 ) brFlags |= BRL_ORDERBY_MTIME;
if( find_option("r",0,0)!=0 ) brFlags |= BRL_REVERSE;
if( find_option("p",0,0)!=0 ) brFlags |= BRL_PRIVATE;
if( g.argc >= 4 ) zBrNameGlob = g.argv[3];
if( g.localOpen ){
vid = db_lget_int("checkout", 0);
zCurrent = db_text(0, "SELECT value FROM tagxref"
" WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
}
branch_prepare_list_query(&q, brFlags, zBrNameGlob);
while( db_step(&q)==SQLITE_ROW ){
const char *zBr = db_column_text(&q, 0);
int isPriv = zCurrent!=0 && db_column_int(&q, 1)==1;
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
fossil_print("%s%s%s\n",
( (brFlags & BRL_PRIVATE) ? " " : ( isPriv ? "#" : " ") ),
(isCur ? "* " : " "), zBr);
}
db_finalize(&q);
}else if( strncmp(zCmd,"new",n)==0 ){
branch_new();
}else if( strncmp(zCmd,"close",5)==0 ){
if(g.argc<4){
usage("branch close branch-name(s)...");
|
| ︙ | ︙ |
Changes to src/builtin.c.
| ︙ | ︙ | |||
621 622 623 624 625 626 627 628 629 630 631 632 633 634 |
fossil_free(zName);
zName = db_get("project-code", "");
CX("projectCode: %!j,\n", zName);
fossil_free(zName);
CX("/* Length of UUID hashes for display purposes. */");
CX("hashDigits: %d, hashDigitsUrl: %d,\n",
hash_digits(0), hash_digits(1));
CX("editStateMarkers: {"
"/*Symbolic markers to denote certain edit states.*/"
"isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n");
CX("confirmerButtonTicks: 3 "
"/*default fossil.confirmer tick count.*/,\n");
/* Inject certain info about the current skin... */
CX("skin:{");
| > > | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
fossil_free(zName);
zName = db_get("project-code", "");
CX("projectCode: %!j,\n", zName);
fossil_free(zName);
CX("/* Length of UUID hashes for display purposes. */");
CX("hashDigits: %d, hashDigitsUrl: %d,\n",
hash_digits(0), hash_digits(1));
CX("diffContextLines: %d,\n",
diff_context_lines(0));
CX("editStateMarkers: {"
"/*Symbolic markers to denote certain edit states.*/"
"isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n");
CX("confirmerButtonTicks: 3 "
"/*default fossil.confirmer tick count.*/,\n");
/* Inject certain info about the current skin... */
CX("skin:{");
|
| ︙ | ︙ | |||
700 701 702 703 704 705 706 707 708 |
** entries: all known deps of this one. Each
** REQUIRES an EXPLICIT trailing \0, including
** the final one! */
} fjs[] = {
/* This list ordering isn't strictly important. */
{"confirmer", 0, 0},
{"copybutton", 0, "dom\0"},
{"dom", 0, 0},
{"fetch", 0, 0},
| > < | 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 |
** entries: all known deps of this one. Each
** REQUIRES an EXPLICIT trailing \0, including
** the final one! */
} fjs[] = {
/* This list ordering isn't strictly important. */
{"confirmer", 0, 0},
{"copybutton", 0, "dom\0"},
{"diff", 0, "dom\0fetch\0"},
{"dom", 0, 0},
{"fetch", 0, 0},
{"numbered-lines", 0, "popupwidget\0copybutton\0"},
{"pikchr", 0, "dom\0"},
{"popupwidget", 0, "dom\0"},
{"storage", 0, 0},
{"tabs", 0, "dom\0"}
};
const int nFjs = sizeof(fjs) / sizeof(fjs[0]);
|
| ︙ | ︙ |
Changes to src/chat.c.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
/*
** Outputs JS code to initialize a list of chat alert audio files for
** use by the chat front-end client. A handful of builtin files
** (from alerts/\*.wav) and all unversioned files matching
** alert-sounds/\*.{mp3,ogg,wav} are included.
*/
static void chat_emit_alert_list(void){
| < | > > < < < < | | < | | | | | | | | | < > | 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 |
/*
** Outputs JS code to initialize a list of chat alert audio files for
** use by the chat front-end client. A handful of builtin files
** (from alerts/\*.wav) and all unversioned files matching
** alert-sounds/\*.{mp3,ogg,wav} are included.
*/
static void chat_emit_alert_list(void){
unsigned int i;
const char * azBuiltins[] = {
"builtin/alerts/plunk.wav",
"builtin/alerts/bflat2.wav",
"builtin/alerts/bflat3.wav",
"builtin/alerts/bloop.wav"
};
CX("window.fossil.config.chat.alerts = [\n");
for(i=0; i < sizeof(azBuiltins)/sizeof(azBuiltins[0]); ++i){
CX("%s%!j", i ? ", " : "", azBuiltins[i]);
}
if( db_table_exists("repository","unversioned") ){
Stmt q = empty_Stmt;
db_prepare(&q, "SELECT 'uv/'||name FROM unversioned "
"WHERE content IS NOT NULL "
"AND (name LIKE 'alert-sounds/%%.wav' "
"OR name LIKE 'alert-sounds/%%.mp3' "
"OR name LIKE 'alert-sounds/%%.ogg')");
while(SQLITE_ROW==db_step(&q)){
CX(", %!j", db_column_text(&q, 0));
}
db_finalize(&q);
}
CX("\n];\n");
}
/* Settings that can be used to control chat */
/*
** SETTING: chat-initial-history width=10 default=50
**
|
| ︙ | ︙ | |||
127 128 129 130 131 132 133 | ** For maximum efficiency, it is best to choose the longest delay that ** does not cause timeouts in intermediate proxies or web server. */ /* ** SETTING: chat-alert-sound width=10 ** ** This is the name of the builtin sound file to use for the alert tone. | | > > > > > > > > > > > > > > > > > > > > > < | | | < | | > | > | > > > > > | | < < < < < > > > > > > > > > > > > | > > > > > > > > > > | > | > | > < | | 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 |
** For maximum efficiency, it is best to choose the longest delay that
** does not cause timeouts in intermediate proxies or web server.
*/
/*
** SETTING: chat-alert-sound width=10
**
** This is the name of the builtin sound file to use for the alert tone.
** The value must be the name of a builtin WAV file.
*/
/*
** WEBPAGE: chat
**
** Start up a browser-based chat session.
**
** This is the main page that humans use to access the chatroom. Simply
** point a web-browser at /chat and the screen fills with the latest
** chat messages, and waits for new one.
**
** Other /chat-OP pages are used by XHR requests from this page to
** send new chat message, delete older messages, or poll for changes.
*/
void chat_webpage(void){
char *zAlert;
char *zProjectName;
char * zInputPlaceholder0; /* Common text input placeholder value */
const char *zPaperclip =
"<svg height=\"8.0\" width=\"16.0\"><path "
"stroke=\"rgb(100,100,100)\" "
"d=\"M 15.93452,3.2530441 "
"A 4.1499493,4.1265346 0 0 0 11.804809,6.5256284e-4 H 2.8582923 A "
"2.8239899,2.8080565 0 0 0 0.68965668,0.96142476 2.874599,2.8583801 "
"0 0 0 0.03119302,3.2388108 2.7632589,2.7476682 0 0 0 "
"0.81132923,4.7689293 3.168132,3.1502569 0 0 0 3.0300653,5.66565 l "
"7.7297897,-4e-7 a 1.6802234,1.6707433 0 0 0 0.0072,-3.3377933 H "
"5.6138192 v 1.0105899 l 5.1460358,-0.00712 a 0.66804062,0.66427143 "
"0 0 1 0,1.3237305 l -7.7226325,0.00712 A 2.0243655,2.0129437 0 0 1 "
"1.0332029,3.0964741 1.8522944,1.8418435 0 0 1 2.8511351,1.0041257 h "
"8.9465169 a 3.1478884,3.1301275 0 0 1 3.134859,2.4339559 3.0365483,"
"3.0194156 0 0 1 -0.629835,2.4908908 3.0365483,3.0194156 0 0 1 "
"-2.31178,1.0746415 l -7.5437026,-0.014233 -0.00716,1.0034736 "
"7.5365456,0.00715 a 4.048731,4.0258875 0 0 0 3.957938,-4.7469259 z\""
"/></svg>";
login_check_credentials();
if( !g.perm.Chat ){
login_needed(g.anon.Chat);
return;
}
zAlert = mprintf("%s/builtin/%s", g.zBaseURL,
db_get("chat-alert-sound","alerts/plunk.wav"));
zProjectName = db_get("project-name","Unnamed project");
zInputPlaceholder0 =
mprintf("Type markdown-formatted message for %h.", zProjectName);
style_set_current_feature("chat");
style_header("Chat");
@ <div id='chat-input-area'>
@ <div id='chat-input-line' class='single-line'>
@ <div contenteditable id="chat-input-field" \
@ data-placeholder0="%h(zInputPlaceholder0)" \
@ data-placeholder="%h(zInputPlaceholder0)" \
@ class=""></div>
@ <div id='chat-buttons-wrapper'>
@ <span class='cbutton' id="chat-button-preview" \
@ title="Preview message (Shift-Enter)">👁</span>
@ <span class='cbutton' id="chat-button-attach" \
@ title="Attach file to message">%s(zPaperclip)</span>
@ <span class='cbutton' id="chat-button-settings" \
@ title="Configure chat">⚙</span>
@ <span class='cbutton' id="chat-button-submit" \
@ title="Send message (Ctrl-Enter)">📤</span>
@ </div>
@ </div>
@ <div id='chat-input-file-area'>
@ <div class='file-selection-wrapper hidden'>
@ <input type="file" name="file" id="chat-input-file">
@ </div>
@ <div id="chat-drop-details"></div>
@ </div>
@ </div>
@ <div id='chat-user-list-wrapper' class='hidden'>
@ <div class='legend'>
@ <span class='help-buttonlet'>
@ Users who have messages in the currently-loaded list.<br><br>
@ <strong>Tap a user name</strong> to filter messages
@ on that user and tap again to clear the filter.<br><br>
@ <strong>Tap the title</strong> of this widget to toggle
@ the list on and off.
@ </span>
@ <span>Active users (sorted by last message time)</span>
@ </div>
@ <div id='chat-user-list'></div>
@ </div>
@ <div id='chat-preview' class='hidden chat-view'>
@ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header>
@ <div id='chat-preview-content' class='message-widget-content'></div>
@ <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div>
@ </div>
@ <div id='chat-config' class='hidden chat-view'>
@ <div id='chat-config-options'></div>
/* ^^^populated client-side */
@ <button>Close Settings</button>
@ </div>
@ <div id='chat-messages-wrapper' class='chat-view'>
/* New chat messages get inserted immediately after this element */
@ <span id='message-inject-point'></span>
@ </div>
fossil_free(zProjectName);
fossil_free(zInputPlaceholder0);
builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
"pikchr", "confirmer", NULL);
/* Always in-line the javascript for the chat page */
@ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
/* We need an onload handler to ensure that window.fossil is
initialized before the chat init code runs. */
@ window.addEventListener('load', function(){
@ document.body.classList.add('chat');
@ /*^^^for skins which add their own BODY tag */;
@ window.fossil.config.chat = {
@ fromcli: %h(PB("cli")?"true":"false"),
@ alertSound: "%h(zAlert)",
@ initSize: %d(db_get_int("chat-initial-history",50)),
@ imagesInline: !!%d(db_get_boolean("chat-inline-images",1))
@ };
ajax_emit_js_preview_modes(0);
chat_emit_alert_list();
@ }, false);
@ </script>
builtin_request_js("fossil.page.chat.js");
style_finish_page();
}
/* Definition of repository tables used by chat
*/
static const char zChatSchema1[] =
@ CREATE TABLE repository.chat(
|
| ︙ | ︙ | |||
281 282 283 284 285 286 287 |
CX("{\"msgs\":[{");
}else{
CX("{");
}
CX("\"isError\": true, \"xfrom\": null,");
CX("\"mtime\": %!j, \"lmtime\": %!j,", zTime, zTime);
CX("\"xmsg\": \"Missing permissions or not logged in. "
| | | | 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 |
CX("{\"msgs\":[{");
}else{
CX("{");
}
CX("\"isError\": true, \"xfrom\": null,");
CX("\"mtime\": %!j, \"lmtime\": %!j,", zTime, zTime);
CX("\"xmsg\": \"Missing permissions or not logged in. "
"Try <a href='%R/login?g=chat'>logging in</a>.\"");
if(fAsMessageList){
CX("}]}");
}else{
CX("}");
}
fossil_free(zTime);
}
/*
** WEBPAGE: chat-send hidden
**
** This page receives (via XHR) a new chat-message and/or a new file
** to be entered into the chat history.
**
** On success it responds with an empty response: the new message
** should be fetched via /chat-poll. On error, e.g. login expiry,
** it emits a JSON response in the same form as described for
|
| ︙ | ︙ | |||
344 345 346 347 348 349 350 |
blob_reset(&b);
}
db_commit_transaction();
}
/*
** This routine receives raw (user-entered) message text and transforms
| | | < | < < < < < < < | < < < < < | < < < | < < < < < < | < < < < > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 |
blob_reset(&b);
}
db_commit_transaction();
}
/*
** This routine receives raw (user-entered) message text and transforms
** it into HTML that is safe to insert using innerHTML. As of 2021-09-19,
** it does so by using markdown_to_html() to convert markdown-formatted
** zMsg to HTML.
**
** Space to hold the returned string is obtained from fossil_malloc()
** and must be freed by the caller.
*/
static char *chat_format_to_html(const char *zMsg){
Blob out;
blob_init(&out, "", 0);
if(*zMsg){
Blob bIn;
blob_init(&bIn, zMsg, (int)strlen(zMsg));
markdown_to_html(&bIn, NULL, &out);
}
return blob_str(&out);
}
/*
** COMMAND: test-chat-formatter
**
** Usage: %fossil test-chat-formatter STRING ...
|
| ︙ | ︙ | |||
439 440 441 442 443 444 445 |
zOut = chat_format_to_html(g.argv[i]);
fossil_print("[%d]: %s\n", i, zOut);
fossil_free(zOut);
}
}
/*
| | | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
zOut = chat_format_to_html(g.argv[i]);
fossil_print("[%d]: %s\n", i, zOut);
fossil_free(zOut);
}
}
/*
** WEBPAGE: chat-poll hidden
**
** The chat page generated by /chat using an XHR to this page to
** request new chat content. A typical invocation is:
**
** /chat-poll/N
** /chat-poll?name=N
**
|
| ︙ | ︙ | |||
648 649 650 651 652 653 654 | db_finalize(&q1); blob_append(&json, "\n]}", 3); cgi_set_content(&json); return; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 |
db_finalize(&q1);
blob_append(&json, "\n]}", 3);
cgi_set_content(&json);
return;
}
/*
** WEBPAGE: chat-fetch-one hidden
**
** /chat-fetch-one/N
**
** Fetches a single message with the given ID, if available.
**
** Options:
**
** raw = the xmsg field will be returned unparsed.
**
** Response is either a single object in the format returned by
** /chat-poll (without the wrapper array) or a JSON-format error
** response, as documented for ajax_route_error().
*/
void chat_fetch_one(void){
Blob json = empty_blob; /* The json to be constructed and returned */
const int fRaw = PD("raw",0)!=0;
const int msgid = atoi(PD("name","0"));
Stmt q;
login_check_credentials();
if( !g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
chat_create_tables();
cgi_set_content_type("application/json");
db_prepare(&q,
"SELECT datetime(mtime), xfrom, xmsg, length(file),"
" fname, fmime, lmtime"
" FROM chat WHERE msgid=%d AND mdel IS NULL",
msgid);
if(SQLITE_ROW==db_step(&q)){
const char *zDate = db_column_text(&q, 0);
const char *zFrom = db_column_text(&q, 1);
const char *zRawMsg = db_column_text(&q, 2);
const int nByte = db_column_int(&q, 3);
const char *zFName = db_column_text(&q, 4);
const char *zFMime = db_column_text(&q, 5);
const char *zLMtime = db_column_text(&q, 7);
blob_appendf(&json,"{\"msgid\": %d,", msgid);
blob_appendf(&json, "\"mtime\":\"%.10sT%sZ\",", zDate, zDate+11);
if( zLMtime && zLMtime[0] ){
blob_appendf(&json, "\"lmtime\":%!j,", zLMtime);
}
blob_append(&json, "\"xfrom\":", -1);
if(zFrom){
blob_appendf(&json, "%!j,", zFrom);
}else{
/* see https://fossil-scm.org/forum/forumpost/e0be0eeb4c */
blob_appendf(&json, "null,");
}
blob_appendf(&json, "\"uclr\":%!j,",
user_color(zFrom ? zFrom : "nobody"));
blob_append(&json,"\"xmsg\":", 7);
if(fRaw){
blob_appendf(&json, "%!j,", zRawMsg);
}else{
char * zMsg = chat_format_to_html(zRawMsg ? zRawMsg : "");
blob_appendf(&json, "%!j,", zMsg);
fossil_free(zMsg);
}
if( nByte==0 ){
blob_appendf(&json, "\"fsize\":0");
}else{
blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
nByte, zFName, zFMime);
}
blob_append(&json,"}",1);
cgi_set_content(&json);
}else{
ajax_route_error(404,"Chat message #%d not found.", msgid);
}
db_finalize(&q);
}
/*
** WEBPAGE: chat-download hidden
**
** Download the CHAT.FILE attachment associated with a single chat
** entry. The "name" query parameter begins with an integer that
** identifies the particular chat message. The integer may be followed
** by a / and a filename, which will indicate to the browser to use
** the indicated name when saving the file.
*/
|
| ︙ | ︙ | |||
681 682 683 684 685 686 687 | db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid); cgi_set_content_type(zMime); cgi_set_content(&r); } /* | | | 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid);
cgi_set_content_type(zMime);
cgi_set_content(&r);
}
/*
** WEBPAGE: chat-delete hidden
**
** Delete the chat entry identified by the name query parameter.
** Invoking fetch("chat-delete/"+msgid) from javascript in the client
** will delete a chat entry from the CHAT table.
**
** This routine both deletes the identified chat entry and also inserts
** a new entry with the current timestamp and with:
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
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 |
blob_append(&prompt,
"#\n"
"# All merged-in branches will be closed due to the --integrate flag\n"
"#\n", -1
);
}
if( p->verboseFlag ){
blob_appendf(&prompt,
"#\n%.78c\n"
"# The following diff is excluded from the commit message:\n#\n",
'#'
);
if( g.aCommitFile ){
FileDirList *diffFiles;
int i;
diffFiles = fossil_malloc_zero((g.argc-1) * sizeof(*diffFiles));
for( i=0; g.aCommitFile[i]!=0; ++i ){
diffFiles[i].zName = db_text(0,
"SELECT pathname FROM vfile WHERE id=%d", g.aCommitFile[i]);
if( fossil_strcmp(diffFiles[i].zName, "." )==0 ){
diffFiles[0].zName[0] = '.';
diffFiles[0].zName[1] = 0;
break;
}
diffFiles[i].nName = strlen(diffFiles[i].zName);
diffFiles[i].nUsed = 0;
}
| > > > < < | | < < | 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 |
blob_append(&prompt,
"#\n"
"# All merged-in branches will be closed due to the --integrate flag\n"
"#\n", -1
);
}
if( p->verboseFlag ){
DiffConfig DCfg;
blob_appendf(&prompt,
"#\n%.78c\n"
"# The following diff is excluded from the commit message:\n#\n",
'#'
);
diff_options(&DCfg, 0, 1);
DCfg.diffFlags |= DIFF_VERBOSE;
if( g.aCommitFile ){
FileDirList *diffFiles;
int i;
diffFiles = fossil_malloc_zero((g.argc-1) * sizeof(*diffFiles));
for( i=0; g.aCommitFile[i]!=0; ++i ){
diffFiles[i].zName = db_text(0,
"SELECT pathname FROM vfile WHERE id=%d", g.aCommitFile[i]);
if( fossil_strcmp(diffFiles[i].zName, "." )==0 ){
diffFiles[0].zName[0] = '.';
diffFiles[0].zName[1] = 0;
break;
}
diffFiles[i].nName = strlen(diffFiles[i].zName);
diffFiles[i].nUsed = 0;
}
diff_against_disk(0, &DCfg, diffFiles, &prompt);
for( i=0; diffFiles[i].zName; ++i ){
fossil_free(diffFiles[i].zName);
}
fossil_free(diffFiles);
}else{
diff_against_disk(0, &DCfg, 0, &prompt);
}
}
prompt_for_user_comment(pComment, &prompt);
blob_reset(&prompt);
}
/*
|
| ︙ | ︙ | |||
2571 2572 2573 2574 2575 2576 2577 |
/* Step 2: Insert records for all modified files into the blob
** table. If there were arguments passed to this command, only
** the identified files are inserted (if they have been modified).
*/
db_prepare(&q,
"SELECT id, %Q || pathname, mrid, %s, %s, %s FROM vfile "
| | | 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 |
/* Step 2: Insert records for all modified files into the blob
** table. If there were arguments passed to this command, only
** the identified files are inserted (if they have been modified).
*/
db_prepare(&q,
"SELECT id, %Q || pathname, mrid, %s, %s, %s FROM vfile "
"WHERE chnged<>0 AND NOT deleted AND is_selected(id)",
g.zLocalRoot,
glob_expr("pathname", db_get("crlf-glob",db_get("crnl-glob",""))),
glob_expr("pathname", db_get("binary-glob","")),
glob_expr("pathname", db_get("encoding-glob",""))
);
while( db_step(&q)==SQLITE_ROW ){
int id, rid;
|
| ︙ | ︙ | |||
2609 2610 2611 2612 2613 2614 2615 |
file_relative_name(zFullname, &fname, 0);
fossil_print("possible unresolved merge conflict in %s\n",
blob_str(&fname));
blob_reset(&fname);
}
nrid = content_put(&content);
blob_reset(&content);
| > | | | | | | > | 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 |
file_relative_name(zFullname, &fname, 0);
fossil_print("possible unresolved merge conflict in %s\n",
blob_str(&fname));
blob_reset(&fname);
}
nrid = content_put(&content);
blob_reset(&content);
if( nrid!=rid ){
if( rid>0 ){
content_deltify(rid, &nrid, 1, 0);
}
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d, mhash=NULL WHERE id=%d",
nrid,nrid,id);
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
}
}
db_finalize(&q);
if( nConflict && !allowConflict ){
fossil_fatal("abort due to unresolved merge conflicts; "
"use --allow-conflict to override");
}else if( abortCommit ){
fossil_fatal("one or more files were converted on your request; "
|
| ︙ | ︙ |
Changes to src/clone.c.
| ︙ | ︙ | |||
263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
if( nErr ){
file_delete(zRepo);
fossil_fatal("server returned an error - clone aborted");
}
db_open_repository(zRepo);
}
db_begin_transaction();
fossil_print("Rebuilding repository meta-data...\n");
rebuild_db(0, 1, 0);
if( !noCompress ){
fossil_print("Extra delta compression... "); fflush(stdout);
extra_deltification();
fossil_print("\n");
}
| > > > > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
if( nErr ){
file_delete(zRepo);
fossil_fatal("server returned an error - clone aborted");
}
db_open_repository(zRepo);
}
db_begin_transaction();
if( db_exists("SELECT 1 FROM delta WHERE srcId IN phantom") ){
fossil_fatal("there are unresolved deltas -"
" the clone is probably incomplete and unusable.");
}
fossil_print("Rebuilding repository meta-data...\n");
rebuild_db(0, 1, 0);
if( !noCompress ){
fossil_print("Extra delta compression... "); fflush(stdout);
extra_deltification();
fossil_print("\n");
}
|
| ︙ | ︙ | |||
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
/*
** Set SSH options discovered in global variables (set from command line
** options).
*/
void clone_ssh_db_set_options(void){
if( g.zSshCmd && g.zSshCmd[0] ){
db_set("ssh-command", g.zSshCmd, 0);
}
}
/*
** WEBPAGE: download
**
** Provide a simple page that enables newbies to download the latest tarball or
| > > | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
/*
** Set SSH options discovered in global variables (set from command line
** options).
*/
void clone_ssh_db_set_options(void){
if( g.zSshCmd && g.zSshCmd[0] ){
db_unprotect(PROTECT_ALL);
db_set("ssh-command", g.zSshCmd, 0);
db_protect_pop();
}
}
/*
** WEBPAGE: download
**
** Provide a simple page that enables newbies to download the latest tarball or
|
| ︙ | ︙ |
Changes to src/color.c.
| ︙ | ︙ | |||
77 78 79 80 81 82 83 | ** The setting is a list of space-separated words pairs. The first word ** of each pair is a login name. The second word is an alternative name ** used by the color chooser algorithm. ** ** This list is intended to be relatively short. The idea is to only use ** this map to resolve color collisions between common users. ** | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
** The setting is a list of space-separated words pairs. The first word
** of each pair is a login name. The second word is an alternative name
** used by the color chooser algorithm.
**
** This list is intended to be relatively short. The idea is to only use
** this map to resolve color collisions between common users.
**
** Visit /hash-color-test?rand for a list of suggested names for the
** second word of each pair in the list.
*/
char *user_color(const char *zLogin){
static int once = 0;
static int nMap = 0;
static char **azMap = 0;
static int *anMap = 0;
|
| ︙ | ︙ |
Changes to src/configure.c.
| ︙ | ︙ | |||
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 |
blob_read_from_file(&in, g.argv[3], ExtFILE);
db_begin_transaction();
if( zMethod[0]=='i' ){
groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE;
}else{
groupMask = CONFIGSET_ALL;
}
configure_receive_all(&in, groupMask);
db_end_transaction(0);
}else
if( strncmp(zMethod, "pull", n)==0
|| strncmp(zMethod, "push", n)==0
|| strncmp(zMethod, "sync", n)==0
){
int mask;
| > > | 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 |
blob_read_from_file(&in, g.argv[3], ExtFILE);
db_begin_transaction();
if( zMethod[0]=='i' ){
groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE;
}else{
groupMask = CONFIGSET_ALL;
}
db_unprotect(PROTECT_USER);
configure_receive_all(&in, groupMask);
db_protect_pop();
db_end_transaction(0);
}else
if( strncmp(zMethod, "pull", n)==0
|| strncmp(zMethod, "push", n)==0
|| strncmp(zMethod, "sync", n)==0
){
int mask;
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
1912 1913 1914 1915 1916 1917 1918 |
if( file_size(zDbName, ExtFILE)<1024*3 ){
char *zHome = file_dirname(zDbName);
int rc;
if( file_isdir(zHome, ExtFILE)==0 ){
file_mkdir(zHome, ExtFILE, 0);
}
rc = file_access(zHome, W_OK);
| < > | 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 |
if( file_size(zDbName, ExtFILE)<1024*3 ){
char *zHome = file_dirname(zDbName);
int rc;
if( file_isdir(zHome, ExtFILE)==0 ){
file_mkdir(zHome, ExtFILE, 0);
}
rc = file_access(zHome, W_OK);
if( rc ){
if( isOptional ) return 0;
fossil_fatal("home directory \"%s\" must be writeable", zHome);
}
db_init_database(zDbName, zConfigSchema, (char*)0);
fossil_free(zHome);
}
if( file_access(zDbName, W_OK) ){
if( isOptional ) return 0;
fossil_fatal("configuration file %s must be writeable", zDbName);
}
if( useAttach ){
db_open_or_attach(zDbName, "configdb");
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
527 528 529 530 531 532 533 |
ul.filelist {
margin-top: 3px;
line-height: 100%;
}
ul.filelist li {
padding-top: 1px;
}
| > > | | > > | | > > > > > > > > | | | > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > | > > > | > | | | > | | > | | > > | > > | > | | > | | > > > > > > > > > | 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 |
ul.filelist {
margin-top: 3px;
line-height: 100%;
}
ul.filelist li {
padding-top: 1px;
}
/* Rules governing diff layout and colors */
table.diff {
width: 100%;
border-spacing: 0;
border-radius: 5px;
border: 1px solid black;
font-size: 80%;
}
table.diff td.diffln{
padding: 0;
}
table.diff td.diffln > pre{
padding: 0 0.25em 0 0.5em;
margin: 0;
}
table.diff td {
vertical-align: top;
padding: 0;
overflow: hidden /*work around inner PRE slight overflow/overlap*/;
}
table.diff pre {
margin: 0 0 0 0;
padding: 0 0.5em;
line-height: 1.275/*for mobile: forum post e6f4ee7de98b55c0*/;
text-size-adjust: none
/* ^^^ attempt to keep mobile from inflating some text */;
}
table.diff pre > ins,
table.diff pre > del {
/* Fill platform-dependent color gaps caused by
inflated line-height */;
padding: 0.062em 0 0.062em 0;
}
table.diff pre > ins > *,
table.diff pre > del > *{
/* Avoid odd-looking color swatches in conjunction with
(table.diff pre > ins/del) padding */
padding: inherit;
}
table.diff td.diffln > pre {
padding: 0 0.35em 0 0.5em;
}
table.diff td.difftxt > pre {
min-width: 100%;
max-width: 100%;
}
table.diff td > pre {
/* Workaround for "slight wiggle" when using mouse-wheel in some FF
versions, apparently caused by the increased line-height forcing
these elements to be a *tick* larger than they should be but not
large enough to force a scroll bar to show up. */
overflow-y: hidden;
}
tr.diffskip.jchunk {
/* jchunk gets added from JS to diffskip rows when they are
plugged into the /jchunk route. */
background-color: aliceblue;
padding: 0;
}
tr.diffskip.jchunk > td {
padding: 0.25em 0.5em;
margin: 0;
}
tr.diffskip.jchunk:hover {
/*background-color: rgba(127,127,127,0.5);
cursor: pointer;*/
}
tr.diffskip > td.chunkctrl {
text-align: left;
font-family: monospace;
}
tr.diffskip > td.chunkctrl > div {
display: flex;
align-items: center;
}
tr.diffskip > td.chunkctrl > div > span.error {
padding: 0.25em 0.5em;
border-radius: 0.5em;
}
tr.diffskip > td.chunkctrl .jcbutton
/* class name .button breaks w/ some skins! */ {
min-width: 3.5ex;
max-width: 3.5ex;
text-align: center;
display: inline-block;
padding: 0.1em 1em;
margin: 0 1em 0 0;
background-color: rgba(127,127,127,0.2);
border-style: outset;
border-width: 0;
border-radius: 0.5em;
opacity: 0.7;
}
tr.diffskip > td.chunkctrl .jcbutton.up:not(.down){
/* Simulate an arrow pointing up */
border-radius: 3em 3em 0.25em 0.25em;
}
tr.diffskip > td.chunkctrl .jcbutton.down:not(.up){
/* Simulate an arrow pointing down */
border-radius: 0.25em 0.25em 3em 3em;
}
tr.diffskip > td.chunkctrl .jcbutton > span {
/* In order to increase the glyph size w/o increasing the em-based
button size or border-radius, we need an extra layer of DOM
element for the glyph. */
font-size: 150%;
}
tr.diffskip > td.chunkctrl .jcbutton.up > span::before {
content: '⇡';
}
tr.diffskip > td.chunkctrl .jcbutton.down > span::before {
content: '⇣';
}
tr.diffskip > td.chunkctrl .jcbutton.up.down > span::before {
content: '⇡⇣';
}
tr.diffskip > td.chunkctrl .jcbutton:hover {
cursor: pointer;
opacity: 1;
filter: contrast(1);
}
td.diffln {
width: 1px;
text-align: right;
padding: 0 1em 0 0;
}
td.difflne {
padding-bottom: 0.4em;
}
td.diffsep {
width: 1px;
padding: 0 0.3em 0 0.5em;
}
td.difftxt pre {
overflow-x: auto;
}
td.diffln ins {
background-color: #a0e4b2;
text-decoration: none;
}
td.diffln del {
background-color: #ffc0c0;
text-decoration: none;
}
td.difftxt del {
background-color: #ffe8e8;
text-decoration: none;
}
td.difftxt del > del {
background-color: #ffc0c0;
text-decoration: none;
font-weight: bold;
}
td.difftxt del > del.edit {
background-color: #c0c0ff;
text-decoration: none;
font-weight: bold;
}
td.difftxt ins {
background-color: #dafbe1;
text-decoration: none;
}
td.difftxt ins > ins {
background-color: #a0e4b2;
text-decoration: none;
font-weight: bold;
}
td.difftxt ins > ins.edit {
background-color: #c0c0ff;
text-decoration: none;
font-weight: bold;
}
span.modpending {
color: #b03800;
font-style: italic;
}
pre.th1result {
white-space: pre-wrap;
word-wrap: break-word;
|
| ︙ | ︙ | |||
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 |
border-color: inherit;
min-height: 1.5em;
font-size: 1.2em;
padding: 0.2em;
margin: 0.25em 0;
flex: 0 0 auto;
}
.font-size-100 {
font-size: 100%;
}
.font-size-125 {
font-size: 125%;
}
.font-size-150 {
| > > > | 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 |
border-color: inherit;
min-height: 1.5em;
font-size: 1.2em;
padding: 0.2em;
margin: 0.25em 0;
flex: 0 0 auto;
}
.font-size-80 {
font-size: 80%;
}
.font-size-100 {
font-size: 100%;
}
.font-size-125 {
font-size: 125%;
}
.font-size-150 {
|
| ︙ | ︙ | |||
1174 1175 1176 1177 1178 1179 1180 |
margin: 0;
vertical-align: top;
padding: 0.25em 0 0 0 /*prevents slight overlap at top */;
}
table.numbered-lines td.line-numbers {
width: 4.5em;
}
| | | | > | | | | 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 |
margin: 0;
vertical-align: top;
padding: 0.25em 0 0 0 /*prevents slight overlap at top */;
}
table.numbered-lines td.line-numbers {
width: 4.5em;
}
table.numbered-lines td.line-numbers > pre {
margin: 0.25em/*must match top PADDING of td.file-content
> pre > code*/ 0 0 0;
padding: 0;
}
table.numbered-lines td.line-numbers span {
display: inline-block;
margin: 0;
padding: 0;
line-height: inherit;
font-size: inherit;
font-family: inherit;
cursor: pointer;
white-space: pre;
margin-right: 2px/*keep selection from nudging the right column */;
text-align: right;
}
table.numbered-lines td.line-numbers span:hover {
background-color: rgba(112, 112, 112, 0.25);
}
table.numbered-lines td.file-content {
padding-left: 0.25em;
}
table.numbered-lines td.file-content > pre,
table.numbered-lines td.file-content > pre > code {
|
| ︙ | ︙ | |||
1251 1252 1253 1254 1255 1256 1257 |
margin-top: -2px/*restore alignment*/;
}
.fossil-tooltip {
text-align: center;
padding: 0.2em 1em;
border: 1px solid black;
| | | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 |
margin-top: -2px/*restore alignment*/;
}
.fossil-tooltip {
text-align: center;
padding: 0.2em 1em;
border: 1px solid black;
border-radius: 0.5em;
position: absolute;
display: inline-block;
z-index: 19/*below default skin's hamburger popup*/;
box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
background-color: inherit;
color: inherit;
}
|
| ︙ | ︙ | |||
1469 1470 1471 1472 1473 1474 1475 | /* Hide image when sources are being shown. */ position: absolute !important; opacity: 0 !important; pointer-events: none !important; display: none !important; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 |
/* Hide image when sources are being shown. */
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
/* An icon element intended for use as a button/menu for
accessing app-specific settings. */
.settings-icon {
/* Icon source: https://de.wikipedia.org/wiki/Datei:OOjs_UI_icon_settings.svg
MIT License. */
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg \
|
| ︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 |
}
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 |
}
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
input[type="checkbox"].diff-toggle {
float: right;
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
|
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | #if INTERFACE /* ** Flag parameters to the text_diff() routine used to control the formatting ** of the diff output. */ | < < | | | | | | | | | | | | | | > > > > > > > | 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 |
#if INTERFACE
/*
** Flag parameters to the text_diff() routine used to control the formatting
** of the diff output.
*/
#define DIFF_IGNORE_EOLWS 0x00000001 /* Ignore end-of-line whitespace */
#define DIFF_IGNORE_ALLWS 0x00000003 /* Ignore all whitespace */
#define DIFF_SIDEBYSIDE 0x00000004 /* Generate a side-by-side diff */
#define DIFF_VERBOSE 0x00000008 /* Missing shown as empty files */
#define DIFF_BRIEF 0x00000010 /* Show filenames only */
#define DIFF_HTML 0x00000020 /* Render for HTML */
#define DIFF_LINENO 0x00000040 /* Show line numbers */
#define DIFF_NUMSTAT 0x00000080 /* Show line count of changes */
#define DIFF_NOOPT 0x00000100 /* Suppress optimizations (debug) */
#define DIFF_INVERT 0x00000200 /* Invert the diff (debug) */
#define DIFF_CONTEXT_EX 0x00000400 /* Use context even if zero */
#define DIFF_NOTTOOBIG 0x00000800 /* Only display if not too big */
#define DIFF_STRIP_EOLCR 0x00001000 /* Strip trailing CR */
#define DIFF_SLOW_SBS 0x00002000 /* Better but slower side-by-side */
#define DIFF_WEBPAGE 0x00004000 /* Complete webpage */
#define DIFF_BROWSER 0x00008000 /* The --browser option */
#define DIFF_JSON 0x00010000 /* JSON output */
#define DIFF_DEBUG 0x00020000 /* Debugging diff output */
#define DIFF_RAW 0x00040000 /* Raw triples - for debugging */
#define DIFF_TCL 0x00080000 /* For the --tk option */
#define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */
/*
** These error messages are shared in multiple locations. They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
"cannot compute difference between binary files\n"
|
| ︙ | ︙ | |||
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
"whitespace changes only\n"
/*
** Maximum length of a line in a text file, in bytes. (2**15 = 32768 bytes)
*/
#define LENGTH_MASK_SZ 15
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
#endif /* INTERFACE */
/*
** Information about each line of a file being diffed.
**
** The lower LENGTH_MASK_SZ bits of the hash (DLine.h) are the length
** of the line. If any line is longer than LENGTH_MASK characters,
** the file is considered binary.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
"whitespace changes only\n"
/*
** Maximum length of a line in a text file, in bytes. (2**15 = 32768 bytes)
*/
#define LENGTH_MASK_SZ 15
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
/*
** An instance of this object describes the formatting and processing
** details desired of a "diff" operation.
**
** Conceptually, this object is as an encoding of the command-line options
** for the "fossil diff" command. That is not a precise description, though,
** because not all diff operations are started from the command-line. But
** the idea is sound.
**
** Information encoded by this object includes but is not limited to:
**
** * The desired output format (unified vs. side-by-side,
** TCL, JSON, HTML vs. plain-text).
**
** * Number of lines of context surrounding each difference block
**
** * Width of output columns for text side-by-side diffop
*/
struct DiffConfig {
u64 diffFlags; /* Diff flags */
int nContext; /* Number of lines of context */
int wColumn; /* Column width in -y mode */
u32 nFile; /* Number of files diffed so far */
const char *zDiffCmd; /* External diff command to use instead of builtin */
const char *zBinGlob; /* GLOB pattern for binary files */
ReCompiled *pRe; /* Show only changes matching this pattern */
const char *zLeftHash; /* HASH-id of the left file */
};
#endif /* INTERFACE */
/*
** Initialize memory for a DiffConfig based on just a diffFlags integer.
*/
DiffConfig *diff_config_init(DiffConfig *pCfg, u64 diffFlags){
memset(pCfg, 0, sizeof(*pCfg));
pCfg->diffFlags = diffFlags;
return pCfg;
}
/*
** Information about each line of a file being diffed.
**
** The lower LENGTH_MASK_SZ bits of the hash (DLine.h) are the length
** of the line. If any line is longer than LENGTH_MASK characters,
** the file is considered binary.
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 100 101 102 103 104 | }; /* ** Length of a dline */ #define LENGTH(X) ((X)->n) /* ** A context for running a raw diff. ** ** The aEdit[] array describes the raw diff. Each triple of integers in ** aEdit[] means: ** ** (1) COPY: Number of lines aFrom and aTo have in common | > > > > > | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | }; /* ** Length of a dline */ #define LENGTH(X) ((X)->n) /* ** Number of diff chunks generated */ static int nChunk = 0; /* ** A context for running a raw diff. ** ** The aEdit[] array describes the raw diff. Each triple of integers in ** aEdit[] means: ** ** (1) COPY: Number of lines aFrom and aTo have in common |
| ︙ | ︙ | |||
122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
/*
** Count the number of lines in the input string. Include the last line
** in the count even if it lacks the \n terminator. If an empty string
** is specified, the number of lines is zero. For the purposes of this
** function, a string is considered empty if it contains no characters
** -OR- it contains only NUL characters.
*/
int count_lines(
const char *z,
int n,
int *pnLine
){
int nLine;
| > > > > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
/*
** Count the number of lines in the input string. Include the last line
** in the count even if it lacks the \n terminator. If an empty string
** is specified, the number of lines is zero. For the purposes of this
** function, a string is considered empty if it contains no characters
** -OR- it contains only NUL characters.
**
** Returns true if the input seems to be plain text input, else false.
** If it returns false, pnLine is not modified, else it is set to the
** number of lines in z.
*/
int count_lines(
const char *z,
int n,
int *pnLine
){
int nLine;
|
| ︙ | ︙ | |||
236 237 238 239 240 241 242 | *pnLine = nLine; return a; } /* ** Return zero if two DLine elements are identical. */ | | | | | | | < < | < < < < < < < < < < < < < | < | | < | < < < | | < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 |
*pnLine = nLine;
return a;
}
/*
** Return zero if two DLine elements are identical.
*/
static int compare_dline(const DLine *pA, const DLine *pB){
if( pA->h!=pB->h ) return 1;
return memcmp(pA->z,pB->z, pA->h&LENGTH_MASK);
}
/*
** Return zero if two DLine elements are identical, ignoring
** all whitespace. The indent field of pA/pB already points
** to the first non-space character in the string.
*/
static int compare_dline_ignore_allws(const DLine *pA, const DLine *pB){
int a = pA->indent, b = pB->indent;
if( pA->h==pB->h ){
while( a<pA->n || b<pB->n ){
if( a<pA->n && b<pB->n && pA->z[a++] != pB->z[b++] ) return 1;
while( a<pA->n && fossil_isspace(pA->z[a])) ++a;
while( b<pB->n && fossil_isspace(pB->z[b])) ++b;
}
return pA->n-a != pB->n-b;
}
return 1;
}
/*
** Return true if the regular expression *pRe matches any of the
** N dlines
*/
static int re_dline_match(
ReCompiled *pRe, /* The regular expression to be matched */
const DLine *aDLine, /* First of N DLines to compare against */
int N /* Number of DLines to check */
){
while( N-- ){
if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){
return 1;
}
aDLine++;
}
return 0;
}
/*
** Append a single line of context-diff output to pOut.
*/
static void appendDiffLine(
Blob *pOut, /* Where to write the line of output */
char cPrefix, /* One of " ", "+", or "-" */
DLine *pLine /* The line to be output */
){
blob_append_char(pOut, cPrefix);
blob_append(pOut, pLine->z, pLine->n);
blob_append_char(pOut, '\n');
}
/*
** Add two line numbers to the beginning of an output line for a context
** diff. One or the other of the two numbers might be zero, which means
** to leave that number field blank.
*/
static void appendDiffLineno(Blob *pOut, int lnA, int lnB){
if( lnA>0 ){
blob_appendf(pOut, "%6d ", lnA);
}else{
blob_append(pOut, " ", 7);
}
if( lnB>0 ){
blob_appendf(pOut, "%6d ", lnB);
}else{
blob_append(pOut, " ", 8);
}
}
/*
** Output a patch-style text diff.
*/
static void contextDiff(
DContext *p, /* The difference */
Blob *pOut, /* Output a context diff to here */
DiffConfig *pCfg /* Configuration options */
){
DLine *A; /* Left side of the diff */
DLine *B; /* Right side of the diff */
int a = 0; /* Index of next line in A[] */
int b = 0; /* Index of next line in B[] */
int *R; /* Array of COPY/DELETE/INSERT triples */
int r; /* Index into R[] */
int nr; /* Number of COPY/DELETE/INSERT triples to process */
int mxr; /* Maximum value for r */
int na, nb; /* Number of lines shown from A and B */
int i, j; /* Loop counters */
int m; /* Number of lines to output */
int skip; /* Number of lines to skip */
static int nChunk = 0; /* Number of diff chunks seen so far */
int nContext; /* Number of lines of context */
int showLn; /* Show line numbers */
int showDivider = 0; /* True to show the divider between diff blocks */
nContext = diff_context_lines(pCfg);
showLn = (pCfg->diffFlags & DIFF_LINENO)!=0;
A = p->aFrom;
B = p->aTo;
R = p->aEdit;
mxr = p->nEdit;
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
for(r=0; r<mxr; r += 3*nr){
/* Figure out how many triples to show in a single block */
for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
/* printf("r=%d nr=%d\n", r, nr); */
/* For the current block comprising nr triples, figure out
** how many lines of A and B are to be displayed
*/
if( R[r]>nContext ){
na = nb = nContext;
skip = R[r] - nContext;
}else{
|
| ︙ | ︙ | |||
429 430 431 432 433 434 435 |
** the previous block.
*/
nChunk++;
if( showLn ){
if( !showDivider ){
/* Do not show a top divider */
showDivider = 1;
| < < < < < | | | | | | | | | | < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < > | > | | < | < < < < | < | < < < < | < < < < < < < < < < > > > | < | 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 |
** the previous block.
*/
nChunk++;
if( showLn ){
if( !showDivider ){
/* Do not show a top divider */
showDivider = 1;
}else{
blob_appendf(pOut, "%.80c\n", '.');
}
}else{
/*
* If the patch changes an empty file or results in an empty file,
* the block header must use 0,0 as position indicator and not 1,0.
* Otherwise, patch would be confused and may reject the diff.
*/
blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
na ? a+skip+1 : a+skip, na,
nb ? b+skip+1 : b+skip, nb);
blob_append(pOut, "\n", 1);
}
/* Show the initial common area */
a += skip;
b += skip;
m = R[r] - skip;
for(j=0; j<m; j++){
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1);
appendDiffLine(pOut, ' ', &A[a+j]);
}
a += m;
b += m;
/* Show the differences */
for(i=0; i<nr; i++){
m = R[r+i*3+1];
for(j=0; j<m; j++){
if( showLn ) appendDiffLineno(pOut, a+j+1, 0);
appendDiffLine(pOut, '-', &A[a+j]);
}
a += m;
m = R[r+i*3+2];
for(j=0; j<m; j++){
if( showLn ) appendDiffLineno(pOut, 0, b+j+1);
appendDiffLine(pOut, '+', &B[b+j]);
}
b += m;
if( i<nr-1 ){
m = R[r+i*3+3];
for(j=0; j<m; j++){
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1);
appendDiffLine(pOut, ' ', &A[a+j]);
}
b += m;
a += m;
}
}
/* Show the final common area */
assert( nr==i );
m = R[r+nr*3];
if( m>nContext ) m = nContext;
for(j=0; j<m; j++){
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1);
appendDiffLine(pOut, ' ', &A[a+j]);
}
}
}
#define MX_CSN 8 /* Maximum number of change spans across a change region */
/*
** A description of zero or more (up to MX_CSN) areas of difference
** between two lines of text.
*/
typedef struct LineChange LineChange;
struct LineChange {
int n; /* Number of change spans */
struct Span {
int iStart1; /* Byte offset to start of a change on the left */
int iLen1; /* Length of the left change in bytes */
int iStart2; /* Byte offset to start of a change on the right */
int iLen2; /* Length of the change on the right in bytes */
int isMin; /* True if this change is known to have no useful subdivs */
} a[MX_CSN]; /* Array of change spans, sorted order */
};
/*
** The two text segments zLeft and zRight are known to be different on
** both ends, but they might have a common segment in the middle. If
** they do not have a common segment, return 0. If they do have a large
** common segment, return 1 and before doing so set:
**
|
| ︙ | ︙ | |||
682 683 684 685 686 687 688 |
static int textLCS(
const char *zLeft, int nA, /* String on the left */
const char *zRight, int nB, /* String on the right */
int *aLCS /* Identify bounds of LCS here */
){
const unsigned char *zA = (const unsigned char*)zLeft; /* left string */
const unsigned char *zB = (const unsigned char*)zRight; /* right string */
| | | | > | < | | > > > > > > > > > | > > > | > | | < > | > > > > > | | < > > > | < | < > | < | | > | | | | | < < < < | | < < < | | | > > > > > > > > > > | > > > > > > > > > | > > | < < | | | < | < < | | < < > | | < | > | > > > > > | | > | > | > > > > > > | > > > > | | | > > | > > > > > > > > | | > | | | | > | < < > > | | < | > > > > | < | < | < > > < < < < > | > | | 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 |
static int textLCS(
const char *zLeft, int nA, /* String on the left */
const char *zRight, int nB, /* String on the right */
int *aLCS /* Identify bounds of LCS here */
){
const unsigned char *zA = (const unsigned char*)zLeft; /* left string */
const unsigned char *zB = (const unsigned char*)zRight; /* right string */
int i, j, k; /* Loop counters */
int lenBest = 0; /* Match length to beat */
for(i=0; i<nA-lenBest; i++){
unsigned char cA = zA[i];
if( (cA&0xc0)==0x80 ) continue;
for(j=0; j<nB-lenBest; j++ ){
if( zB[j]==cA ){
for(k=1; j+k<nB && i+k<nA && zB[j+k]==zA[i+k]; k++){}
while( (zB[j+k]&0xc0)==0x80 ){ k--; }
if( k>lenBest ){
lenBest = k;
aLCS[0] = i;
aLCS[1] = i+k;
aLCS[2] = j;
aLCS[3] = j+k;
}
}
}
}
return lenBest>0;
}
/*
** Find the smallest spans that are different between two text strings that
** are known to be different on both ends.
*/
static int textLineChanges(
const char *zLeft, int nA, /* String on the left */
const char *zRight, int nB, /* String on the right */
LineChange *p /* Write results here */
){
p->n = 1;
p->a[0].iStart1 = 0;
p->a[0].iLen1 = nA;
p->a[0].iStart2 = 0;
p->a[0].iLen2 = nB;
p->a[0].isMin = 0;
while( p->n<MX_CSN-1 ){
int mxi = -1;
int mxLen = -1;
int x, i;
int aLCS[4];
struct Span *a, *b;
for(i=0; i<p->n; i++){
if( p->a[i].isMin ) continue;
x = p->a[i].iLen1;
if( p->a[i].iLen2<x ) x = p->a[i].iLen2;
if( x>mxLen ){
mxLen = x;
mxi = i;
}
}
if( mxLen<6 ) break;
x = textLCS(zLeft + p->a[mxi].iStart1, p->a[mxi].iLen1,
zRight + p->a[mxi].iStart2, p->a[mxi].iLen2, aLCS);
if( x==0 ){
p->a[mxi].isMin = 1;
continue;
}
a = p->a+mxi;
b = a+1;
if( mxi<p->n-1 ){
memmove(b+1, b, sizeof(*b)*(p->n-mxi-1));
}
p->n++;
b->iStart1 = a->iStart1 + aLCS[1];
b->iLen1 = a->iLen1 - aLCS[1];
a->iLen1 = aLCS[0];
b->iStart2 = a->iStart2 + aLCS[3];
b->iLen2 = a->iLen2 - aLCS[3];
a->iLen2 = aLCS[2];
b->isMin = 0;
}
return p->n;
}
/*
** Return true if the string starts with n spaces
*/
static int allSpaces(const char *z, int n){
int i;
for(i=0; i<n && fossil_isspace(z[i]); i++){}
return i==n;
}
/*
** Try to improve the human-readability of the LineChange p.
**
** (1) If the first change span shows a change of indentation, try to
** move that indentation change to the left margin.
**
** (2) Try to shift changes so that they begin or end with a space.
*/
static void improveReadability(
const char *zA, /* Left line of the change */
const char *zB, /* Right line of the change */
LineChange *p /* The LineChange to be adjusted */
){
int j, n, len;
if( p->n<1 ) return;
/* (1) Attempt to move indentation changes to the left margin */
if( p->a[0].iLen1==0
&& (len = p->a[0].iLen2)>0
&& (j = p->a[0].iStart2)>0
&& zB[0]==zB[j]
&& allSpaces(zB, j)
){
for(n=1; n<len && n<j && zB[j]==zB[j+n]; n++){}
if( n<len ){
memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n);
p->n++;
p->a[0] = p->a[1];
p->a[1].iStart2 += n;
p->a[1].iLen2 -= n;
p->a[0].iLen2 = n;
}
p->a[0].iStart1 = 0;
p->a[0].iStart2 = 0;
}else
if( p->a[0].iLen2==0
&& (len = p->a[0].iLen1)>0
&& (j = p->a[0].iStart1)>0
&& zA[0]==zA[j]
&& allSpaces(zA, j)
){
for(n=1; n<len && n<j && zA[j]==zA[j+n]; n++){}
if( n<len ){
memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n);
p->n++;
p->a[0] = p->a[1];
p->a[1].iStart1 += n;
p->a[1].iLen1 -= n;
p->a[0].iLen1 = n;
}
p->a[0].iStart1 = 0;
p->a[0].iStart2 = 0;
}
/* (2) Try to shift changes so that they begin or end with a
** space. (TBD) */
}
/*
** Given two lines of text, pFrom and pTo, compute a set of changes
** between those two lines, for enhanced display purposes.
**
** The result is written into the LineChange object given by the
** third parameter.
*/
static void oneLineChange(
const DLine *pLeft, /* Left line of the change */
const DLine *pRight, /* Right line of the change */
LineChange *p /* OUTPUT: Write the results here */
){
int nLeft; /* Length of left line in bytes */
int nRight; /* Length of right line in bytes */
int nShort; /* Shortest of left and right */
int nPrefix; /* Length of common prefix */
int nSuffix; /* Length of common suffix */
int nCommon; /* Total byte length of suffix and prefix */
const char *zLeft; /* Text of the left line */
const char *zRight; /* Text of the right line */
int nLeftDiff; /* nLeft - nPrefix - nSuffix */
int nRightDiff; /* nRight - nPrefix - nSuffix */
nLeft = pLeft->n;
zLeft = pLeft->z;
nRight = pRight->n;
zRight = pRight->z;
nShort = nLeft<nRight ? nLeft : nRight;
nPrefix = 0;
while( nPrefix<nShort && zLeft[nPrefix]==zRight[nPrefix] ){
nPrefix++;
}
if( nPrefix<nShort ){
while( nPrefix>0 && (zLeft[nPrefix]&0xc0)==0x80 ) nPrefix--;
}
nSuffix = 0;
if( nPrefix<nShort ){
while( nSuffix<nShort
&& zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){
nSuffix++;
}
if( nSuffix<nShort ){
while( nSuffix>0 && (zLeft[nLeft-nSuffix]&0xc0)==0x80 ) nSuffix--;
}
if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0;
}
nCommon = nPrefix + nSuffix;
/* If the prefix and suffix overlap, that means that we are dealing with
** a pure insertion or deletion of text that can have multiple alignments.
** Try to find an alignment to begins and ends on whitespace, or on
** punctuation, rather than in the middle of a name or number.
*/
if( nCommon > nShort ){
int iBest = -1;
int iBestVal = -1;
int i;
int nLong = nLeft<nRight ? nRight : nLeft;
int nGap = nLong - nShort;
for(i=nShort-nSuffix; i<=nPrefix; i++){
int iVal = 0;
|
| ︙ | ︙ | |||
852 853 854 855 856 857 858 859 860 |
if( iVal>iBestVal ){
iBestVal = iVal;
iBest = i;
}
}
nPrefix = iBest;
nSuffix = nShort - nPrefix;
}
| > | | < | | | < < < < < < | | < | | | < < | | | | < < < | | | | < | | > | < | < < < | < < < < < < < < < < | | < < < < < < < < | < < | | | | > | > | > > > > > > > > | > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > | | | | > > > > > > > > > < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | > | | | | | > < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > > > > > > > > > | | > | > > > > | | > > > > > | > > > > > | | 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 |
if( iVal>iBestVal ){
iBestVal = iVal;
iBest = i;
}
}
nPrefix = iBest;
nSuffix = nShort - nPrefix;
nCommon = nPrefix + nSuffix;
}
/* A single chunk of text inserted */
if( nCommon==nLeft ){
p->n = 1;
p->a[0].iStart1 = nPrefix;
p->a[0].iLen1 = 0;
p->a[0].iStart2 = nPrefix;
p->a[0].iLen2 = nRight - nCommon;
improveReadability(zLeft, zRight, p);
return;
}
/* A single chunk of text deleted */
if( nCommon==nRight ){
p->n = 1;
p->a[0].iStart1 = nPrefix;
p->a[0].iLen1 = nLeft - nCommon;
p->a[0].iStart2 = nPrefix;
p->a[0].iLen2 = 0;
improveReadability(zLeft, zRight, p);
return;
}
/* At this point we know that there is a chunk of text that has
** changed between the left and the right. Check to see if there
** is a large unchanged section in the middle of that changed block.
*/
nLeftDiff = nLeft - nCommon;
nRightDiff = nRight - nCommon;
if( nLeftDiff >= 4
&& nRightDiff >= 4
&& textLineChanges(&zLeft[nPrefix], nLeftDiff,
&zRight[nPrefix], nRightDiff, p)>1
){
int i;
for(i=0; i<p->n; i++){
p->a[i].iStart1 += nPrefix;
p->a[i].iStart2 += nPrefix;
}
improveReadability(zLeft, zRight, p);
return;
}
/* If all else fails, show a single big change between left and right */
p->n = 1;
p->a[0].iStart1 = nPrefix;
p->a[0].iLen1 = nLeft - nCommon;
p->a[0].iStart2 = nPrefix;
p->a[0].iLen2 = nRight - nCommon;
improveReadability(zLeft, zRight, p);
}
/*
** COMMAND: test-line-diff
** Usage: %fossil% test-line-diff STRING1 STRING2
**
** Show the differences between the two strings. Used for testing
** the oneLineChange() routine in the diff logic.
*/
void test_line_diff(void){
DLine a, b;
LineChange chng;
int i, j, x;
if( g.argc!=4 ) usage("STRING1 STRING2");
a.z = g.argv[2];
a.n = (int)strlen(a.z);
b.z = g.argv[3];
b.n = (int)strlen(b.z);
oneLineChange(&a, &b, &chng);
fossil_print("left: [%s]\n", a.z);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart1;
int len = chng.a[i].iLen1;
if( len ){
if( x==0 ){ fossil_print("%*s", 8, ""); }
while( ofst > x ){
if( (a.z[x]&0xc0)!=0x80 ) fossil_print(" ");
x++;
}
for(j=0; j<len; j++, x++){
if( (a.z[x]&0xc0)!=0x80 ) fossil_print("%d",i);
}
}
}
if( x ) fossil_print("\n");
fossil_print("right: [%s]\n", b.z);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart2;
int len = chng.a[i].iLen2;
if( len ){
if( x==0 ){ fossil_print("%*s", 8, ""); }
while( ofst > x ){
if( (b.z[x]&0xc0)!=0x80 ) fossil_print(" ");
x++;
}
for(j=0; j<len; j++, x++){
if( (b.z[x]&0xc0)!=0x80 ) fossil_print("%d",i);
}
}
}
if( x ) fossil_print("\n");
}
/*
** Minimum of two values
*/
static int minInt(int a, int b){ return a<b ? a : b; }
/*
** This is an abstract superclass for an object that accepts difference
** lines and formats them for display. Subclasses of this object format
** the diff output in different ways.
**
** To subclass, create an instance of the DiffBuilder object and fill
** in appropriate method implementations.
*/
typedef struct DiffBuilder DiffBuilder;
struct DiffBuilder {
void (*xSkip)(DiffBuilder*, unsigned int, int);
void (*xCommon)(DiffBuilder*,const DLine*);
void (*xInsert)(DiffBuilder*,const DLine*);
void (*xDelete)(DiffBuilder*,const DLine*);
void (*xReplace)(DiffBuilder*,const DLine*, const DLine*);
void (*xEdit)(DiffBuilder*,const DLine*,const DLine*);
void (*xEnd)(DiffBuilder*);
unsigned int lnLeft; /* Lines seen on the left (delete) side */
unsigned int lnRight; /* Lines seen on the right (insert) side */
unsigned int nPending; /* Number of pending lines */
int eState; /* State of the output */
int width; /* Display width */
Blob *pOut; /* Output blob */
Blob aCol[5]; /* Holding blobs */
DiffConfig *pCfg; /* Configuration information */
};
/************************* DiffBuilderDebug ********************************/
/* This version of DiffBuilder is used for debugging the diff and diff
** diff formatter logic. It is accessed using the (undocumented) --debug
** option to the diff command. The output is human-readable text that
** describes the various method calls that are invoked agains the DiffBuilder
** object.
*/
static void dfdebugSkip(DiffBuilder *p, unsigned int n, int isFinal){
blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)%s\n",
n, p->lnLeft+1, p->lnLeft+n, p->lnRight+1, p->lnRight+n,
isFinal ? " FINAL" : "");
p->lnLeft += n;
p->lnRight += n;
}
static void dfdebugCommon(DiffBuilder *p, const DLine *pLine){
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut, "COMMON %8u %8u %.*s\n",
p->lnLeft, p->lnRight, (int)pLine->n, pLine->z);
}
static void dfdebugInsert(DiffBuilder *p, const DLine *pLine){
p->lnRight++;
blob_appendf(p->pOut, "INSERT %8d %.*s\n",
p->lnRight, (int)pLine->n, pLine->z);
}
static void dfdebugDelete(DiffBuilder *p, const DLine *pLine){
p->lnLeft++;
blob_appendf(p->pOut, "DELETE %8u %.*s\n",
p->lnLeft, (int)pLine->n, pLine->z);
}
static void dfdebugReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut, "REPLACE %8u %.*s\n",
p->lnLeft, (int)pX->n, pX->z);
blob_appendf(p->pOut, " %8u %.*s\n",
p->lnRight, (int)pY->n, pY->z);
}
static void dfdebugEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
int i, j;
int x;
LineChange chng;
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut, "EDIT %8u %.*s\n",
p->lnLeft, (int)pX->n, pX->z);
oneLineChange(pX, pY, &chng);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart1;
int len = chng.a[i].iLen1;
if( len ){
char c = '0' + i;
if( x==0 ){ blob_appendf(p->pOut, "%*s", 26, ""); }
while( ofst > x ){
if( (pX->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, ' ');
x++;
}
for(j=0; j<len; j++, x++){
if( (pX->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, c);
}
}
}
if( x ) blob_append_char(p->pOut, '\n');
blob_appendf(p->pOut, " %8u %.*s\n",
p->lnRight, (int)pY->n, pY->z);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart2;
int len = chng.a[i].iLen2;
if( len ){
char c = '0' + i;
if( x==0 ){ blob_appendf(p->pOut, "%*s", 26, ""); }
while( ofst > x ){
if( (pY->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, ' ');
x++;
}
for(j=0; j<len; j++, x++){
if( (pY->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, c);
}
}
}
if( x ) blob_append_char(p->pOut, '\n');
}
static void dfdebugEnd(DiffBuilder *p){
blob_appendf(p->pOut, "END with %u lines left and %u lines right\n",
p->lnLeft, p->lnRight);
fossil_free(p);
}
static DiffBuilder *dfdebugNew(Blob *pOut){
DiffBuilder *p = fossil_malloc(sizeof(*p));
p->xSkip = dfdebugSkip;
p->xCommon = dfdebugCommon;
p->xInsert = dfdebugInsert;
p->xDelete = dfdebugDelete;
p->xReplace = dfdebugReplace;
p->xEdit = dfdebugEdit;
p->xEnd = dfdebugEnd;
p->lnLeft = p->lnRight = 0;
p->pOut = pOut;
return p;
}
/************************* DiffBuilderTcl ********************************/
/*
** This formatter outputs a description of the diff formatted as TCL, for
** use by the --tk option to "diff". See also the "diff.tcl" file. The
** output can be viewed directly using the --tcl option.
**
** There is one line per method call:
**
** SKIP n -- Skip "n" lines of input
** COM string -- "string" is an unchanged context line
** INS string -- "string" is in the right file only
** DEL string -- "string" is in the left file only
** EDIT string .... -- Complex edit between left and right
**
** The EDIT verb will be followed by 3*N or 3*N+1 strings. The triples
** each show:
**
** 1. Common text
** 2. Text from the left side
** 3. Text on the right that replaces (2) from the left
**
** For inserted text (2) will be an empty string. For deleted text, (3)
** will be an empty string. (1) might be empty for the first triple if
** the line begins with an edit. After all triples, there might be one
** additional string which is a common suffix.
*/
static void dftclSkip(DiffBuilder *p, unsigned int n, int isFinal){
blob_appendf(p->pOut, "SKIP %u\n", n);
}
static void dftclCommon(DiffBuilder *p, const DLine *pLine){
blob_appendf(p->pOut, "COM ");
blob_append_tcl_literal(p->pOut, pLine->z, pLine->n);
blob_append_char(p->pOut, '\n');
}
static void dftclInsert(DiffBuilder *p, const DLine *pLine){
blob_append(p->pOut, "INS ", -1);
blob_append_tcl_literal(p->pOut, pLine->z, pLine->n);
blob_append_char(p->pOut, '\n');
}
static void dftclDelete(DiffBuilder *p, const DLine *pLine){
blob_append(p->pOut, "DEL ", -1);
blob_append_tcl_literal(p->pOut, pLine->z, pLine->n);
blob_append_char(p->pOut, '\n');
}
static void dftclReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){
blob_append(p->pOut, "EDIT \"\" ", -1);
blob_append_tcl_literal(p->pOut, pX->z, pX->n);
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pY->z, pY->n);
blob_append_char(p->pOut, '\n');
}
static void dftclEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
int i, x;
LineChange chng;
blob_append(p->pOut, "EDIT", 4);
oneLineChange(pX, pY, &chng);
for(i=x=0; i<chng.n; i++){
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut,
pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
}
if( x<pX->n ){
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, pX->n - x);
}
blob_append_char(p->pOut, '\n');
}
static void dftclEnd(DiffBuilder *p){
fossil_free(p);
}
static DiffBuilder *dftclNew(Blob *pOut){
DiffBuilder *p = fossil_malloc(sizeof(*p));
p->xSkip = dftclSkip;
p->xCommon = dftclCommon;
p->xInsert = dftclInsert;
p->xDelete = dftclDelete;
p->xReplace = dftclReplace;
p->xEdit = dftclEdit;
p->xEnd = dftclEnd;
p->pOut = pOut;
return p;
}
/************************* DiffBuilderJson ********************************/
/*
** This formatter generates a JSON array that describes the difference.
**
** The Json array consists of integer opcodes with each opcode followed
** by zero or more arguments:
**
** Syntax Mnemonic Description
** ----------- -------- --------------------------
** 0 END This is the end of the diff
** 1 INTEGER SKIP Skip N lines from both files
** 2 STRING COMMON The line show by STRING is in both files
** 3 STRING INSERT The line STRING is in only the right file
** 4 STRING DELETE The STRING line is in only the left file
** 5 SUBARRAY EDIT One line is different on left and right.
**
** The SUBARRAY is an array of 3*N+1 strings with N>=0. The triples
** represent common-text, left-text, and right-text. The last string
** in SUBARRAY is the common-suffix. Any string can be empty if it does
** not apply.
*/
static void dfjsonSkip(DiffBuilder *p, unsigned int n, int isFinal){
blob_appendf(p->pOut, "1,%u,\n", n);
}
static void dfjsonCommon(DiffBuilder *p, const DLine *pLine){
blob_append(p->pOut, "2,",2);
blob_append_json_literal(p->pOut, pLine->z, (int)pLine->n);
blob_append(p->pOut, ",\n",2);
}
static void dfjsonInsert(DiffBuilder *p, const DLine *pLine){
blob_append(p->pOut, "3,",2);
blob_append_json_literal(p->pOut, pLine->z, (int)pLine->n);
blob_append(p->pOut, ",\n",2);
}
static void dfjsonDelete(DiffBuilder *p, const DLine *pLine){
blob_append(p->pOut, "4,",2);
blob_append_json_literal(p->pOut, pLine->z, (int)pLine->n);
blob_append(p->pOut, ",\n",2);
}
static void dfjsonReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){
blob_append(p->pOut, "5,[\"\",",-1);
blob_append_json_literal(p->pOut, pX->z, (int)pX->n);
blob_append(p->pOut, ",",1);
blob_append_json_literal(p->pOut, pY->z, (int)pY->n);
blob_append(p->pOut, ",\"\"],\n",-1);
}
static void dfjsonEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
int i, x;
LineChange chng;
blob_append(p->pOut, "5,[", 3);
oneLineChange(pX, pY, &chng);
for(i=x=0; i<chng.n; i++){
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut,
pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
}
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, pX->n - x);
blob_append(p->pOut, "],\n",3);
}
static void dfjsonEnd(DiffBuilder *p){
blob_append(p->pOut, "0]}", 3);
fossil_free(p);
}
static DiffBuilder *dfjsonNew(Blob *pOut){
DiffBuilder *p = fossil_malloc(sizeof(*p));
p->xSkip = dfjsonSkip;
p->xCommon = dfjsonCommon;
p->xInsert = dfjsonInsert;
p->xDelete = dfjsonDelete;
p->xReplace = dfjsonReplace;
p->xEdit = dfjsonEdit;
p->xEnd = dfjsonEnd;
p->lnLeft = p->lnRight = 0;
p->pOut = pOut;
blob_append_char(pOut, '[');
return p;
}
/************************* DiffBuilderUnified********************************/
/* This formatter generates a unified diff for HTML.
**
** The result is a <table> with four columns. The four columns hold:
**
** 1. The line numbers for the first file.
** 2. The line numbers for the second file.
** 3. The "diff mark": "+" or "-" or just a space
** 4. Text of the line
**
** Inserted lines are marked with <ins> and deleted lines are marked
** with <del>. The whole line is marked this way, not just the part that
** changed. The part that change has an additional nested <ins> or <del>.
** The CSS needs to be set up such that a single <ins> or <del> gives a
** light background and a nested <ins> or <del> gives a darker background.
** Additional attributes (like bold font) might also be added to nested
** <ins> and <del> since those are the characters that have actually
** changed.
**
** Accumulator strategy:
**
** * Delete line numbers are output directly to p->pOut
** * Insert line numbers accumulate in p->aCol[0].
** * Separator marks accumulate in p->aCol[1].
** * Change text accumulates in p->aCol[2].
** * Pending insert line numbers go into p->aCol[3].
** * Pending insert text goes into p->aCol[4].
**
** eState is 1 if text has an open <del>
*/
static void dfunifiedFinishDelete(DiffBuilder *p){
if( p->eState==0 ) return;
blob_append(p->pOut, "</del>", 6);
blob_append(&p->aCol[2], "</del>", 6);
p->eState = 0;
}
static void dfunifiedFinishInsert(DiffBuilder *p){
unsigned int i;
if( p->nPending==0 ) return;
dfunifiedFinishDelete(p);
/* Blank lines for delete line numbers for each inserted line */
for(i=0; i<p->nPending; i++) blob_append_char(p->pOut, '\n');
/* Insert line numbers */
blob_append(&p->aCol[0], "<ins>", 5);
blob_append_xfer(&p->aCol[0], &p->aCol[3]);
blob_append(&p->aCol[0], "</ins>", 6);
/* "+" marks for the separator on inserted lines */
for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2);
/* Text of the inserted lines */
blob_append(&p->aCol[2], "<ins>", 5);
blob_append_xfer(&p->aCol[2], &p->aCol[4]);
blob_append(&p->aCol[2], "</ins>", 6);
p->nPending = 0;
}
static void dfunifiedFinishRow(DiffBuilder *p){
dfunifiedFinishDelete(p);
dfunifiedFinishInsert(p);
if( blob_size(&p->aCol[0])==0 ) return;
blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n", -1);
blob_append_xfer(p->pOut, &p->aCol[0]);
blob_append(p->pOut, "</pre></td><td class=\"diffsep\"><pre>\n", -1);
blob_append_xfer(p->pOut, &p->aCol[1]);
blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtu\"><pre>\n",-1);
blob_append_xfer(p->pOut, &p->aCol[2]);
blob_append(p->pOut, "</pre></td></tr>\n", -1);
}
static void dfunifiedStartRow(DiffBuilder *p){
if( blob_size(&p->aCol[0])>0 ) return;
blob_appendf(p->pOut,"<tr id=\"chunk%d\">"
"<td class=\"diffln difflnl\"><pre>\n", ++nChunk);
}
static void dfunifiedSkip(DiffBuilder *p, unsigned int n, int isFinal){
dfunifiedFinishRow(p);
if( p->pCfg && p->pCfg->zLeftHash ){
blob_appendf(p->pOut,
"<tr class=\"diffskip\" data-startln=\"%d\" data-endln=\"%d\""
" id=\"skip%xh%xi%x\">\n",
p->lnLeft+1, p->lnLeft+n,
nChunk, p->lnLeft, n);
}else{
blob_append(p->pOut, "<tr>", 4);
}
blob_append(p->pOut, "<td class=\"diffln difflne\">"
"︙</td><td></td><td></td></tr>\n", -1);
p->lnLeft += n;
p->lnRight += n;
}
static void dfunifiedCommon(DiffBuilder *p, const DLine *pLine){
dfunifiedStartRow(p);
dfunifiedFinishDelete(p);
dfunifiedFinishInsert(p);
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
blob_appendf(&p->aCol[0],"%d\n",p->lnRight);
blob_append_char(&p->aCol[1], '\n');
htmlize_to_blob(&p->aCol[2], pLine->z, (int)pLine->n);
blob_append_char(&p->aCol[2], '\n');
}
static void dfunifiedInsert(DiffBuilder *p, const DLine *pLine){
dfunifiedStartRow(p);
p->lnRight++;
blob_appendf(&p->aCol[3],"%d\n", p->lnRight);
blob_append(&p->aCol[4], "<ins>", 5);
htmlize_to_blob(&p->aCol[4], pLine->z, (int)pLine->n);
blob_append(&p->aCol[4], "</ins>\n", 7);
p->nPending++;
}
static void dfunifiedDelete(DiffBuilder *p, const DLine *pLine){
dfunifiedStartRow(p);
dfunifiedFinishInsert(p);
if( p->eState==0 ){
dfunifiedFinishInsert(p);
blob_append(p->pOut, "<del>", 5);
blob_append(&p->aCol[2], "<del>", 5);
p->eState = 1;
}
p->lnLeft++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
blob_append_char(&p->aCol[0],'\n');
blob_append(&p->aCol[1],"-\n",2);
blob_append(&p->aCol[2], "<del>", 5);
htmlize_to_blob(&p->aCol[2], pLine->z, (int)pLine->n);
blob_append(&p->aCol[2], "</del>\n", 7);
}
static void dfunifiedReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){
dfunifiedStartRow(p);
if( p->eState==0 ){
dfunifiedFinishInsert(p);
blob_append(p->pOut, "<del>", 5);
blob_append(&p->aCol[2], "<del>", 5);
p->eState = 1;
}
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
blob_append_char(&p->aCol[0], '\n');
blob_append(&p->aCol[1], "-\n", 2);
htmlize_to_blob(&p->aCol[2], pX->z, pX->n);
blob_append_char(&p->aCol[2], '\n');
blob_appendf(&p->aCol[3],"%d\n", p->lnRight);
htmlize_to_blob(&p->aCol[4], pY->z, pY->n);
blob_append_char(&p->aCol[4], '\n');
p->nPending++;
}
static void dfunifiedEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
int i;
int x;
LineChange chng;
oneLineChange(pX, pY, &chng);
dfunifiedStartRow(p);
if( p->eState==0 ){
dfunifiedFinishInsert(p);
blob_append(p->pOut, "<del>", 5);
blob_append(&p->aCol[2], "<del>", 5);
p->eState = 1;
}
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
blob_append_char(&p->aCol[0], '\n');
blob_append(&p->aCol[1], "-\n", 2);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart1;
int len = chng.a[i].iLen1;
if( len ){
htmlize_to_blob(&p->aCol[2], pX->z+x, ofst - x);
x = ofst;
blob_append(&p->aCol[2], "<del>", 5);
htmlize_to_blob(&p->aCol[2], pX->z+x, len);
x += len;
blob_append(&p->aCol[2], "</del>", 6);
}
}
htmlize_to_blob(&p->aCol[2], pX->z+x, pX->n - x);
blob_append_char(&p->aCol[2], '\n');
blob_appendf(&p->aCol[3],"%d\n", p->lnRight);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart2;
int len = chng.a[i].iLen2;
if( len ){
htmlize_to_blob(&p->aCol[4], pY->z+x, ofst - x);
x = ofst;
blob_append(&p->aCol[4], "<ins>", 5);
htmlize_to_blob(&p->aCol[4], pY->z+x, len);
x += len;
blob_append(&p->aCol[4], "</ins>", 6);
}
}
htmlize_to_blob(&p->aCol[4], pY->z+x, pY->n - x);
blob_append_char(&p->aCol[4], '\n');
p->nPending++;
}
static void dfunifiedEnd(DiffBuilder *p){
dfunifiedFinishRow(p);
blob_append(p->pOut, "</table>\n",-1);
fossil_free(p);
}
static DiffBuilder *dfunifiedNew(Blob *pOut, DiffConfig *pCfg){
DiffBuilder *p = fossil_malloc(sizeof(*p));
p->xSkip = dfunifiedSkip;
p->xCommon = dfunifiedCommon;
p->xInsert = dfunifiedInsert;
p->xDelete = dfunifiedDelete;
p->xReplace = dfunifiedReplace;
p->xEdit = dfunifiedEdit;
p->xEnd = dfunifiedEnd;
p->lnLeft = p->lnRight = 0;
p->eState = 0;
p->nPending = 0;
p->pOut = pOut;
if( pCfg->zLeftHash ){
blob_appendf(pOut, "<table class=\"diff udiff\" data-lefthash=\"%s\">\n",
pCfg->zLeftHash);
}else{
blob_append(pOut, "<table class=\"diff udiff\">\n", -1);
}
blob_init(&p->aCol[0], 0, 0);
blob_init(&p->aCol[1], 0, 0);
blob_init(&p->aCol[2], 0, 0);
blob_init(&p->aCol[3], 0, 0);
blob_init(&p->aCol[4], 0, 0);
p->pCfg = pCfg;
return p;
}
/************************* DiffBuilderSplit ******************************/
/* This formatter creates a side-by-side diff in HTML. The output is a
** <table> with 5 columns:
**
** 1. Line numbers for the first file.
** 2. Text for the first file.
** 3. The difference mark. "<", ">", "|" or blank
** 4. Line numbers for the second file.
** 5. Text for the second file.
**
** The <ins> and <del> strategy is the same as for unified diff above.
** In fact, the same CSS can be used for both.
**
** Accumulator strategy:
**
** * Left line numbers are output directly to p->pOut
** * Left text accumulates in p->aCol[0].
** * Edit marks accumulates in p->aCol[1].
** * Right line numbers accumulate in p->aCol[2].
** * Right text accumulates in p->aCol[3].
**
** eState:
** 0 In common block
** 1 Have <del> on the left
** 2 Have <ins> on the right
** 3 Have <del> on left and <ins> on the right
*/
static void dfsplitChangeState(DiffBuilder *p, int newState){
if( p->eState == newState ) return;
if( (p->eState&1)==0 && (newState & 1)!=0 ){
blob_append(p->pOut, "<del>", 5);
blob_append(&p->aCol[0], "<del>", 5);
p->eState |= 1;
}else if( (p->eState&1)!=0 && (newState & 1)==0 ){
blob_append(p->pOut, "</del>", 6);
blob_append(&p->aCol[0], "</del>", 6);
p->eState &= ~1;
}
if( (p->eState&2)==0 && (newState & 2)!=0 ){
blob_append(&p->aCol[2], "<ins>", 5);
blob_append(&p->aCol[3], "<ins>", 5);
p->eState |= 2;
}else if( (p->eState&2)!=0 && (newState & 2)==0 ){
blob_append(&p->aCol[2], "</ins>", 6);
blob_append(&p->aCol[3], "</ins>", 6);
p->eState &= ~2;
}
}
static void dfsplitFinishRow(DiffBuilder *p){
if( blob_size(&p->aCol[0])==0 ) return;
dfsplitChangeState(p, 0);
blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtl\"><pre>\n",-1);
blob_append_xfer(p->pOut, &p->aCol[0]);
blob_append(p->pOut, "</pre></td><td class=\"diffsep\"><pre>\n", -1);
blob_append_xfer(p->pOut, &p->aCol[1]);
blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n",-1);
blob_append_xfer(p->pOut, &p->aCol[2]);
blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtr\"><pre>\n",-1);
blob_append_xfer(p->pOut, &p->aCol[3]);
blob_append(p->pOut, "</pre></td></tr>\n", -1);
}
static void dfsplitStartRow(DiffBuilder *p){
if( blob_size(&p->aCol[0])>0 ) return;
blob_appendf(p->pOut,"<tr id=\"chunk%d\">"
"<td class=\"diffln difflnl\"><pre>\n", ++nChunk);
p->eState = 0;
}
static void dfsplitSkip(DiffBuilder *p, unsigned int n, int isFinal){
dfsplitFinishRow(p);
if( p->pCfg && p->pCfg->zLeftHash ){
blob_appendf(p->pOut,
"<tr class=\"diffskip\" data-startln=\"%d\" data-endln=\"%d\""
" id=\"skip%xh%xi%x\">\n",
p->lnLeft+1, p->lnLeft+n,
nChunk,p->lnLeft,n);
}else{
blob_append(p->pOut, "<tr>", 4);
}
blob_append(p->pOut,
"<td class=\"diffln difflnl difflne\">︙</td>"
"<td></td><td></td>"
"<td class=\"diffln difflnr difflne\">︙</td>"
"<td/td></tr>\n", -1);
p->lnLeft += n;
p->lnRight += n;
}
static void dfsplitCommon(DiffBuilder *p, const DLine *pLine){
dfsplitStartRow(p);
dfsplitChangeState(p, 0);
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
htmlize_to_blob(&p->aCol[0], pLine->z, (int)pLine->n);
blob_append_char(&p->aCol[0], '\n');
blob_append_char(&p->aCol[1], '\n');
blob_appendf(&p->aCol[2],"%d\n",p->lnRight);
htmlize_to_blob(&p->aCol[3], pLine->z, (int)pLine->n);
blob_append_char(&p->aCol[3], '\n');
}
static void dfsplitInsert(DiffBuilder *p, const DLine *pLine){
dfsplitStartRow(p);
dfsplitChangeState(p, 2);
p->lnRight++;
blob_append_char(p->pOut, '\n');
blob_append_char(&p->aCol[0], '\n');
blob_append(&p->aCol[1], ">\n", -1);
blob_appendf(&p->aCol[2],"%d\n", p->lnRight);
blob_append(&p->aCol[3], "<ins>", 5);
htmlize_to_blob(&p->aCol[3], pLine->z, (int)pLine->n);
blob_append(&p->aCol[3], "</ins>\n", 7);
}
static void dfsplitDelete(DiffBuilder *p, const DLine *pLine){
dfsplitStartRow(p);
dfsplitChangeState(p, 1);
p->lnLeft++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
blob_append(&p->aCol[0], "<del>", 5);
htmlize_to_blob(&p->aCol[0], pLine->z, (int)pLine->n);
blob_append(&p->aCol[0], "</del>\n", 7);
blob_append(&p->aCol[1], "<\n", -1);
blob_append_char(&p->aCol[2],'\n');
blob_append_char(&p->aCol[3],'\n');
}
static void dfsplitReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){
dfsplitStartRow(p);
dfsplitChangeState(p, 3);
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
htmlize_to_blob(&p->aCol[0], pX->z, pX->n);
blob_append_char(&p->aCol[0], '\n');
blob_append(&p->aCol[1], "|\n", 2);
blob_appendf(&p->aCol[2],"%d\n", p->lnRight);
htmlize_to_blob(&p->aCol[3], pY->z, pY->n);
blob_append_char(&p->aCol[3], '\n');
}
static void dfsplitEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
int i;
int x;
LineChange chng;
oneLineChange(pX, pY, &chng);
dfsplitStartRow(p);
dfsplitChangeState(p, 3);
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%d\n", p->lnLeft);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart1;
int len = chng.a[i].iLen1;
if( len ){
htmlize_to_blob(&p->aCol[0], pX->z+x, ofst - x);
x = ofst;
if( chng.a[i].iLen2 ){
blob_append(&p->aCol[0], "<del class='edit'>", -1);
}else{
blob_append(&p->aCol[0], "<del>", 5);
}
htmlize_to_blob(&p->aCol[0], pX->z+x, len);
x += len;
blob_append(&p->aCol[0], "</del>", 6);
}
}
htmlize_to_blob(&p->aCol[0], pX->z+x, pX->n - x);
blob_append_char(&p->aCol[0], '\n');
blob_append(&p->aCol[1], "|\n", 2);
blob_appendf(&p->aCol[2],"%d\n", p->lnRight);
for(i=x=0; i<chng.n; i++){
int ofst = chng.a[i].iStart2;
int len = chng.a[i].iLen2;
if( len ){
htmlize_to_blob(&p->aCol[3], pY->z+x, ofst - x);
x = ofst;
if( chng.a[i].iLen1 ){
blob_append(&p->aCol[3], "<ins class='edit'>", -1);
}else{
blob_append(&p->aCol[3], "<ins>", 5);
}
htmlize_to_blob(&p->aCol[3], pY->z+x, len);
x += len;
blob_append(&p->aCol[3], "</ins>", 6);
}
}
htmlize_to_blob(&p->aCol[3], pY->z+x, pY->n - x);
blob_append_char(&p->aCol[3], '\n');
}
static void dfsplitEnd(DiffBuilder *p){
dfsplitFinishRow(p);
blob_append(p->pOut, "</table>\n",-1);
fossil_free(p);
}
static DiffBuilder *dfsplitNew(Blob *pOut, DiffConfig *pCfg){
DiffBuilder *p = fossil_malloc(sizeof(*p));
p->xSkip = dfsplitSkip;
p->xCommon = dfsplitCommon;
p->xInsert = dfsplitInsert;
p->xDelete = dfsplitDelete;
p->xReplace = dfsplitReplace;
p->xEdit = dfsplitEdit;
p->xEnd = dfsplitEnd;
p->lnLeft = p->lnRight = 0;
p->eState = 0;
p->pOut = pOut;
if( pCfg->zLeftHash ){
blob_appendf(pOut,
"<table class=\"diff splitdiff\" data-lefthash=\"%s\">\n",
pCfg->zLeftHash);
}else{
blob_append(pOut, "<table class=\"diff splitdiff\">\n", -1);
}
blob_init(&p->aCol[0], 0, 0);
blob_init(&p->aCol[1], 0, 0);
blob_init(&p->aCol[2], 0, 0);
blob_init(&p->aCol[3], 0, 0);
blob_init(&p->aCol[4], 0, 0);
p->pCfg = pCfg;
return p;
}
/************************* DiffBuilderSbs ******************************/
/* This formatter creates a side-by-side diff in text.
*/
static void dfsbsSkip(DiffBuilder *p, unsigned int n, int isFinal){
if( (p->lnLeft || p->lnRight) && !isFinal ){
blob_appendf(p->pOut, "%.*c\n", p->width*2 + 16, '.');
}
p->lnLeft += n;
p->lnRight += n;
}
/*
** Append at least iMin characters (not bytes) and at most iMax characters
** from pX onto the into of p.
**
** This comment contains multibyte unicode characters (ü, Æ, ð) in order
** to test the ability of the diff code to handle such characters.
*/
static void sbs_append_chars(Blob *p, int iMin, int iMax, const DLine *pX){
int i;
const char *z = pX->z;
for(i=0; i<iMax && i<pX->n; i++){
char c = z[i];
blob_append_char(p, c);
if( (c&0xc0)==0x80 ){ iMin++; iMax++; }
}
while( i<iMin ){
blob_append_char(p, ' ');
i++;
}
}
static void dfsbsCommon(DiffBuilder *p, const DLine *pLine){
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%6u ", p->lnLeft);
sbs_append_chars(p->pOut, p->width, p->width, pLine);
blob_appendf(p->pOut," %6u ", p->lnRight);
sbs_append_chars(p->pOut, 0, p->width, pLine);
blob_append_char(p->pOut, '\n');
}
static void dfsbsInsert(DiffBuilder *p, const DLine *pLine){
p->lnRight++;
blob_appendf(p->pOut,"%6s %*s > %6u ",
"", p->width, "", p->lnRight);
sbs_append_chars(p->pOut, 0, p->width, pLine);
blob_append_char(p->pOut, '\n');
}
static void dfsbsDelete(DiffBuilder *p, const DLine *pLine){
p->lnLeft++;
blob_appendf(p->pOut,"%6u ", p->lnLeft);
sbs_append_chars(p->pOut, p->width, p->width, pLine);
blob_append(p->pOut," <\n", 3);
}
static void dfsbsEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
p->lnLeft++;
p->lnRight++;
blob_appendf(p->pOut,"%6u ", p->lnLeft);
sbs_append_chars(p->pOut, p->width, p->width, pX);
blob_appendf(p->pOut, " | %6u ", p->lnRight);
sbs_append_chars(p->pOut, 0, p->width, pY);
blob_append_char(p->pOut, '\n');
}
static void dfsbsEnd(DiffBuilder *p){
fossil_free(p);
}
static DiffBuilder *dfsbsNew(Blob *pOut, DiffConfig *pCfg){
DiffBuilder *p = fossil_malloc(sizeof(*p));
p->xSkip = dfsbsSkip;
p->xCommon = dfsbsCommon;
p->xInsert = dfsbsInsert;
p->xDelete = dfsbsDelete;
p->xReplace = dfsbsEdit;
p->xEdit = dfsbsEdit;
p->xEnd = dfsbsEnd;
p->lnLeft = p->lnRight = 0;
p->width = diff_width(pCfg);
p->pOut = pOut;
return p;
}
/****************************************************************************/
/*
** Return the number between 0 and 100 that is smaller the closer pA and
** pB match. Return 0 for a perfect match. Return 100 if pA and pB are
** completely different.
**
** The current algorithm is as follows:
**
** (1) Remove leading and trailing whitespace.
** (2) Truncate both strings to at most 250 characters
** (3) If the two strings have a common prefix, measure that prefix
** (4) Find the length of the longest common subsequence that is
** at least 150% longer than the common prefix.
** (5) Longer common subsequences yield lower scores.
*/
static int match_dline(const DLine *pA, const DLine *pB){
const char *zA; /* Left string */
const char *zB; /* right string */
int nA; /* Bytes in zA[] */
int nB; /* Bytes in zB[] */
int nMin;
int nPrefix;
int avg; /* Average length of A and B */
int i, j, k; /* Loop counters */
int best = 0; /* Longest match found so far */
int score; /* Final score. 0..100 */
unsigned char c; /* Character being examined */
unsigned char aFirst[256]; /* aFirst[X] = index in zB[] of first char X */
unsigned char aNext[252]; /* aNext[i] = index in zB[] of next zB[i] char */
zA = pA->z;
zB = pB->z;
nA = pA->n;
nB = pB->n;
while( nA>0 && (unsigned char)zA[0]<=' ' ){ nA--; zA++; }
while( nA>0 && (unsigned char)zA[nA-1]<=' ' ){ nA--; }
while( nB>0 && (unsigned char)zB[0]<=' ' ){ nB--; zB++; }
while( nB>0 && (unsigned char)zB[nB-1]<=' ' ){ nB--; }
if( nA>250 ) nA = 250;
if( nB>250 ) nB = 250;
avg = (nA+nB)/2;
if( avg==0 ) return 0;
nMin = nA;
if( nB<nMin ) nMin = nB;
if( nMin==0 ) return 68;
for(nPrefix=0; nPrefix<nMin && zA[nPrefix]==zB[nPrefix]; nPrefix++){}
best = 0;
if( nPrefix>5 && nPrefix>nMin/2 ){
best = nPrefix*3/2;
if( best>=avg - 2 ) best = avg - 2;
}
if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0;
memset(aFirst, 0xff, sizeof(aFirst));
zA--; zB--; /* Make both zA[] and zB[] 1-indexed */
for(i=nB; i>0; i--){
c = (unsigned char)zB[i];
aNext[i] = aFirst[c];
aFirst[c] = i;
}
for(i=1; i<=nA-best; i++){
c = (unsigned char)zA[i];
for(j=aFirst[c]; j<nB-best && memcmp(&zA[i],&zB[j],best)==0; j = aNext[j]){
int limit = minInt(nA-i, nB-j);
for(k=best; k<=limit && zA[k+i]==zB[k+j]; k++){}
if( k>best ) best = k;
}
}
score = (best>=avg) ? 0 : (avg - best)*100/avg;
#if 0
fprintf(stderr, "A: [%.*s]\nB: [%.*s]\nbest=%d avg=%d score=%d\n",
nA, zA+1, nB, zB+1, best, avg, score);
#endif
/* Return the result */
return score;
}
/*
** COMMAND: test-line-match
** Usage: %fossil test-line-match STRING1 STRING2
**
** Return a score from 0 to 100 that is how similar STRING1 is to
** STRING2. Smaller numbers mean more similar. 0 is an exact match.
**
** This command is used to test to match_dline() function in the
** internal Fossil diff logic.
*/
void test_dline_match(void){
DLine a, b;
int x;
if( g.argc!=4 ) usage("STRING1 STRING2");
a.z = g.argv[2];
a.n = (int)strlen(a.z);
b.z = g.argv[3];
b.n = (int)strlen(b.z);
x = match_dline(&a, &b);
fossil_print("%d\n", x);
}
/*
** The threshold at which diffBlockAlignment transitions from the
** O(N*N) Wagner minimum-edit-distance algorithm to a less process
** O(NlogN) divide-and-conquer approach.
*/
#define DIFF_ALIGN_MX 1225
/*
** There is a change block in which nLeft lines of text on the left are
** converted into nRight lines of text on the right. This routine computes
** how the lines on the left line up with the lines on the right.
**
** The return value is a buffer of unsigned characters, obtained from
** fossil_malloc(). (The caller needs to free the return value using
** fossil_free().) Entries in the returned array have values as follows:
**
** 1. Delete the next line of pLeft.
** 2. Insert the next line of pRight.
** 3. The next line of pLeft changes into the next line of pRight.
** 4. Delete one line from pLeft and add one line to pRight.
**
** The length of the returned array will be at most nLeft+nRight bytes.
** If the first bytes is 4, that means we could not compute reasonable
** alignment between the two blocks.
**
** Algorithm: Wagner's minimum edit-distance algorithm, modified by
** adding a cost to each match based on how well the two rows match
** each other. Insertion and deletion costs are 50. Match costs
** are between 0 and 100 where 0 is a perfect match 100 is a complete
** mismatch.
*/
static unsigned char *diffBlockAlignment(
const DLine *aLeft, int nLeft, /* Text on the left */
const DLine *aRight, int nRight, /* Text on the right */
DiffConfig *pCfg, /* Configuration options */
int *pNResult /* OUTPUT: Bytes of result */
){
int i, j, k; /* Loop counters */
int *a; /* One row of the Wagner matrix */
int *pToFree; /* Space that needs to be freed */
unsigned char *aM; /* Wagner result matrix */
int nMatch, iMatch; /* Number of matching lines and match score */
int aBuf[100]; /* Stack space for a[] if nRight not to big */
if( nLeft==0 ){
aM = fossil_malloc( nRight + 2 );
memset(aM, 2, nRight);
*pNResult = nRight;
return aM;
}
if( nRight==0 ){
aM = fossil_malloc( nLeft + 2 );
memset(aM, 1, nLeft);
*pNResult = nLeft;
return aM;
}
/* For large alignments, use a divide and conquer algorithm that is
** O(NlogN). The result is not as precise, but this whole thing is an
** approximation anyhow, and the faster response time is an acceptable
** trade-off for reduced precision.
*/
if( nLeft*nRight>DIFF_ALIGN_MX && (pCfg->diffFlags & DIFF_SLOW_SBS)==0 ){
const DLine *aSmall; /* The smaller of aLeft and aRight */
const DLine *aBig; /* The larger of aLeft and aRight */
int nSmall, nBig; /* Size of aSmall and aBig. nSmall<=nBig */
int iDivSmall, iDivBig; /* Divider point for aSmall and aBig */
int iDivLeft, iDivRight; /* Divider point for aLeft and aRight */
unsigned char *a1, *a2; /* Results of the alignments on two halves */
int n1, n2; /* Number of entries in a1 and a2 */
int score, bestScore; /* Score and best score seen so far */
if( nLeft>nRight ){
aSmall = aRight;
nSmall = nRight;
aBig = aLeft;
nBig = nLeft;
}else{
aSmall = aLeft;
nSmall = nLeft;
aBig = aRight;
nBig = nRight;
}
iDivBig = nBig/2;
iDivSmall = nSmall/2;
bestScore = 10000;
for(i=0; i<nSmall; i++){
score = match_dline(aBig+iDivBig, aSmall+i) + abs(i-nSmall/2)*2;
if( score<bestScore ){
bestScore = score;
iDivSmall = i;
}
}
if( aSmall==aRight ){
iDivRight = iDivSmall;
iDivLeft = iDivBig;
}else{
iDivRight = iDivBig;
iDivLeft = iDivSmall;
}
a1 = diffBlockAlignment(aLeft,iDivLeft,aRight,iDivRight,pCfg,&n1);
a2 = diffBlockAlignment(aLeft+iDivLeft, nLeft-iDivLeft,
aRight+iDivRight, nRight-iDivRight,
pCfg, &n2);
a1 = fossil_realloc(a1, n1+n2 );
memcpy(a1+n1,a2,n2);
fossil_free(a2);
*pNResult = n1+n2;
return a1;
}
/* If we reach this point, we will be doing an O(N*N) Wagner minimum
** edit distance to compute the alignment.
*/
if( nRight < count(aBuf)-1 ){
pToFree = 0;
a = aBuf;
}else{
a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
}
aM = fossil_malloc( (nLeft+1)*(nRight+1) );
/* Compute the best alignment */
for(i=0; i<=nRight; i++){
aM[i] = 2;
a[i] = i*50;
}
aM[0] = 0;
for(j=1; j<=nLeft; j++){
int p = a[0];
a[0] = p+50;
aM[j*(nRight+1)] = 1;
for(i=1; i<=nRight; i++){
int m = a[i-1]+50;
int d = 2;
if( m>a[i]+50 ){
m = a[i]+50;
d = 1;
}
if( m>p ){
int score = match_dline(&aLeft[j-1], &aRight[i-1]);
if( (score<=90 || (i<j+1 && i>j-1)) && m>p+score ){
m = p+score;
d = 3 | score*4;
}
}
p = a[i];
a[i] = m;
aM[j*(nRight+1)+i] = d;
|
| ︙ | ︙ | |||
1138 1139 1140 1141 1142 1143 1144 |
}
k--;
aM[k] = aM[j*(nRight+1)+i];
}
k++;
i = (nRight+1)*(nLeft+1) - k;
memmove(aM, &aM[k], i);
| | < < < < < < < < < < < < < < < < | > > > > | < | | | | < < > | | | | | | | | | | | | < < | < < < < | < < < < < < < < < < < < < < < < | | | < | > | 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 |
}
k--;
aM[k] = aM[j*(nRight+1)+i];
}
k++;
i = (nRight+1)*(nLeft+1) - k;
memmove(aM, &aM[k], i);
*pNResult = i;
/* Return the result */
fossil_free(pToFree);
return aM;
}
/*
** R[] is an array of six integer, two COPY/DELETE/INSERT triples for a
** pair of adjacent differences. Return true if the gap between these
** two differences is so small that they should be rendered as a single
** edit.
*/
static int smallGap(const int *R, int ma, int mb){
int m = R[3];
ma += R[4] + m;
mb += R[5] + m;
if( ma*mb>DIFF_ALIGN_MX ) return 0;
return m<=2 || m<=(R[1]+R[2]+R[4]+R[5])/8;
}
/*
** Format a diff using a DiffBuilder object
*/
static void formatDiff(
DContext *p, /* The computed diff */
DiffConfig *pCfg, /* Configuration options */
DiffBuilder *pBuilder /* The formatter object */
){
const DLine *A; /* Left side of the diff */
const DLine *B; /* Right side of the diff */
unsigned int a = 0; /* Index of next line in A[] */
unsigned int b = 0; /* Index of next line in B[] */
const int *R; /* Array of COPY/DELETE/INSERT triples */
unsigned int r; /* Index into R[] */
unsigned int nr; /* Number of COPY/DELETE/INSERT triples to process */
unsigned int mxr; /* Maximum value for r */
unsigned int na, nb; /* Number of lines shown from A and B */
unsigned int i, j; /* Loop counters */
unsigned int m, ma, mb;/* Number of lines to output */
signed int skip = 0; /* Number of lines to skip */
unsigned int nContext; /* Lines of context above and below each change */
nContext = diff_context_lines(pCfg);
A = p->aFrom;
B = p->aTo;
R = p->aEdit;
mxr = p->nEdit;
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
for(r=0; r<mxr; r += 3*nr){
/* Figure out how many triples to show in a single block */
for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
/* If there is a regex, skip this block (generate no diff output)
** if the regex matches or does not match both insert and delete.
** Only display the block if one side matches but the other side does
** not.
*/
if( pCfg->pRe ){
int hideBlock = 1;
int xa = a, xb = b;
for(i=0; hideBlock && i<nr; i++){
int c1, c2;
xa += R[r+i*3];
xb += R[r+i*3];
c1 = re_dline_match(pCfg->pRe, &A[xa], R[r+i*3+1]);
c2 = re_dline_match(pCfg->pRe, &B[xb], R[r+i*3+2]);
hideBlock = c1==c2;
xa += R[r+i*3+1];
xb += R[r+i*3+2];
}
if( hideBlock ){
a = xa;
b = xb;
continue;
}
}
/* Figure out how many lines of A and B are to be displayed
** for this change block.
*/
if( R[r]>nContext ){
na = nb = nContext;
skip = R[r] - nContext;
}else{
na = nb = R[r];
skip = 0;
|
| ︙ | ︙ | |||
1279 1280 1281 1282 1283 1284 1285 |
nb += R[r+nr*3];
}
for(i=1; i<nr; i++){
na += R[r+i*3];
nb += R[r+i*3];
}
| | > > > | | < | < < | > | < | < < | | | < < < | | < < < < < < | < < < > | | | | > | > > | > | < < < | < | > > > > | > | | | > > | | > > > | > | | | | | | < < < < < < < < < < < < | | < < < < | < < < < < < | < | > | > | | > > < < | < < < | < < | < < < | < < < < < > | | | | | 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 |
nb += R[r+nr*3];
}
for(i=1; i<nr; i++){
na += R[r+i*3];
nb += R[r+i*3];
}
/* Show the initial common area */
a += skip;
b += skip;
m = R[r] - skip;
if( r ) skip -= nContext;
if( skip>0 ){
if( skip<nContext ){
/* If the amount to skip is less that the context band, then
** go ahead and show the skip band as it is not worth eliding */
for(j=0; j<skip; j++){
pBuilder->xCommon(pBuilder, &A[a+j-skip]);
}
}else{
pBuilder->xSkip(pBuilder, skip, 0);
}
}
for(j=0; j<m; j++){
pBuilder->xCommon(pBuilder, &A[a+j]);
}
a += m;
b += m;
/* Show the differences */
for(i=0; i<nr; i++){
int nAlign;
unsigned char *alignment;
ma = R[r+i*3+1]; /* Lines on left but not on right */
mb = R[r+i*3+2]; /* Lines on right but not on left */
/* Try merging the current block with subsequent blocks, if the
** subsequent blocks are nearby and there result isn't too big.
*/
while( i<nr-1 && smallGap(&R[r+i*3],ma,mb) ){
i++;
m = R[r+i*3];
ma += R[r+i*3+1] + m;
mb += R[r+i*3+2] + m;
}
/* Try to find an alignment for the lines within this one block */
alignment = diffBlockAlignment(&A[a], ma, &B[b], mb, pCfg, &nAlign);
for(j=0; ma+mb>0; j++){
assert( j<nAlign );
switch( alignment[j] ){
case 1: {
/* Delete one line from the left */
pBuilder->xDelete(pBuilder, &A[a]);
ma--;
a++;
break;
}
case 2: {
/* Insert one line on the right */
pBuilder->xInsert(pBuilder, &B[b]);
assert( mb>0 );
mb--;
b++;
break;
}
case 3: {
/* The left line is changed into the right line */
if( p->xDiffer(&A[a], &B[b])==0 ){
pBuilder->xCommon(pBuilder, &A[a]);
}else{
pBuilder->xEdit(pBuilder, &A[a], &B[b]);
}
assert( ma>0 && mb>0 );
ma--;
mb--;
a++;
b++;
break;
}
case 4: {
/* Delete from left then separately insert on the right */
pBuilder->xReplace(pBuilder, &A[a], &B[b]);
ma--;
a++;
mb--;
b++;
break;
}
}
}
assert( nAlign==j );
fossil_free(alignment);
if( i<nr-1 ){
m = R[r+i*3+3];
for(j=0; j<m; j++){
pBuilder->xCommon(pBuilder, &A[a+j]);
}
b += m;
a += m;
}
}
/* Show the final common area */
assert( nr==i );
m = R[r+nr*3];
if( m>nContext ) m = nContext;
for(j=0; j<m && j<nContext; j++){
pBuilder->xCommon(pBuilder, &A[a+j]);
}
}
if( R[r]>nContext ){
pBuilder->xSkip(pBuilder, R[r] - nContext, 1);
}
pBuilder->xEnd(pBuilder);
}
/*
** 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.
*/
|
| ︙ | ︙ | |||
1499 1500 1501 1502 1503 1504 1505 |
){
int i, j, k; /* Loop counters */
int n; /* Loop limit */
DLine *pA, *pB; /* Pointers to lines */
int iSX, iSY, iEX, iEY; /* Current match */
int skew = 0; /* How lopsided is the match */
int dist = 0; /* Distance of match from center */
| | | 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 |
){
int i, j, k; /* Loop counters */
int n; /* Loop limit */
DLine *pA, *pB; /* Pointers to lines */
int iSX, iSY, iEX, iEY; /* Current match */
int skew = 0; /* How lopsided is the match */
int dist = 0; /* Distance of match from center */
int mid; /* Center of the chng */
int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
int iSXp, iSYp, iEXp, iEYp; /* Previous match */
sqlite3_int64 bestScore; /* Best score so far */
sqlite3_int64 score; /* Score for current candidate LCS */
int span; /* combined width of the input sequences */
span = (iE1 - iS1) + (iE2 - iS2);
|
| ︙ | ︙ | |||
1813 1814 1815 1816 1817 1818 1819 |
lnFrom += del;
lnTo += ins;
}
}
/*
** Extract the number of lines of context from diffFlags. Supply an
| | > > | > > | | | > > > | | | 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 |
lnFrom += del;
lnTo += ins;
}
}
/*
** Extract the number of lines of context from diffFlags. Supply an
** appropriate default if no context width is specified. If pCfg is
** NULL then the compile-time default is used (which gets propagated
** to JS-side state by certain pages).
*/
int diff_context_lines(DiffConfig *pCfg){
const int dflt = 5;
if(pCfg!=0){
int n = pCfg->nContext;
if( n<=0 && (pCfg->diffFlags & DIFF_CONTEXT_EX)==0 ) n = dflt;
return n;
}else{
return dflt;
}
}
/*
** Extract the width of columns for side-by-side diff. Supply an
** appropriate default if no width is given.
**
** Calculate the default automatically, based on terminal's current width:
** term-width = 2*diff-col + diff-marker + 1
** diff-col = lineno + lmargin + text-width + rmargin
**
** text-width = (term-width - diff-marker - 1)/2 - lineno - lmargin - rmargin
*/
int diff_width(DiffConfig *pCfg){
int w = pCfg->wColumn;
if( w==0 ){
static struct {
unsigned int lineno, lmargin, text, rmargin, marker;
} sbsW = { 5, 2, 0, 0, 3 };
const unsigned int wMin = 24, wMax = 132;
unsigned int tw = terminal_get_width(80);
unsigned int twMin =
|
| ︙ | ︙ | |||
1868 1869 1870 1871 1872 1873 1874 |
blob_appendf(pOut, "<p class=\"generalError\">%s</p>", msg);
}else{
blob_append(pOut, msg, -1);
}
}
/*
| | > | > | > > | | > | | > > | > | < | | | | | | | | | | | | | > > > > > > > > > > > > > > | > > > > > > | > > > > > > | > > | | | | | | | | | | | | | | > > > | | | | > | > > > > > > | > | > > | | | | > | > > > > > > > | | > > > > > > > > > > > > > > | | | < < < < < < < < < < < < < < < < < < < < < < < | < < > > > | > > > > > > > > > > > > | < > > | < < > | > | < > > | < | > | | 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 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 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 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 |
blob_appendf(pOut, "<p class=\"generalError\">%s</p>", msg);
}else{
blob_append(pOut, msg, -1);
}
}
/*
** Generate a report of the differences between files pA_Blob and pB_Blob.
**
** If pOut!=NULL then append text to pOut that will be the difference,
** formatted according to flags in diffFlags. The pOut Blob must have
** already been initialized.
**
** If pOut==NULL then no formatting occurs. Instead, this routine
** returns a pointer to an array of integers. The integers come in
** triples. The elements of each triple are:
**
** 1. The number of lines to copy
** 2. The number of lines to delete
** 3. The number of lines to insert
**
** The return vector is terminated bin a triple of all zeros. The caller
** should free the returned vector using fossil_free().
**
** This diff utility does not work on binary files. If a binary
** 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 diff here if not NULL */
DiffConfig *pCfg /* Configuration options */
){
int ignoreWs; /* Ignore whitespace */
DContext c;
if( pCfg->diffFlags & DIFF_INVERT ){
Blob *pTemp = pA_Blob;
pA_Blob = pB_Blob;
pB_Blob = pTemp;
}
ignoreWs = (pCfg->diffFlags & DIFF_IGNORE_ALLWS)!=0;
blob_to_utf8_no_bom(pA_Blob, 0);
blob_to_utf8_no_bom(pB_Blob, 0);
/* Prepare the input files */
memset(&c, 0, sizeof(c));
if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
c.xDiffer = compare_dline_ignore_allws;
}else{
c.xDiffer = compare_dline;
}
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, pCfg->diffFlags);
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, pCfg->diffFlags);
if( c.aFrom==0 || c.aTo==0 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
if( pOut ){
diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags);
}
return 0;
}
/* Compute the difference */
diff_all(&c);
if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
fossil_free(c.aEdit);
if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, pCfg->diffFlags);
return 0;
}
if( (pCfg->diffFlags & DIFF_NOTTOOBIG)!=0 ){
int i, m, n;
int *a = c.aEdit;
int mx = c.nEdit;
for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; }
if( n>10000 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
fossil_free(c.aEdit);
if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, pCfg->diffFlags);
return 0;
}
}
if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
diff_optimize(&c);
}
if( pOut ){
if( pCfg->diffFlags & DIFF_NUMSTAT ){
int nDel = 0, nIns = 0, i;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
blob_appendf(pOut, "%10d %10d", nIns, nDel);
}
}else if( pCfg->diffFlags & DIFF_RAW ){
const int *R = c.aEdit;
unsigned int r;
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
blob_appendf(pOut, " copy %6d delete %6d insert %6d\n",
R[r], R[r+1], R[r+2]);
}
}else if( pCfg->diffFlags & DIFF_JSON ){
DiffBuilder *pBuilder = dfjsonNew(pOut);
formatDiff(&c, pCfg, pBuilder);
blob_append_char(pOut, '\n');
}else if( pCfg->diffFlags & DIFF_TCL ){
DiffBuilder *pBuilder = dftclNew(pOut);
formatDiff(&c, pCfg, pBuilder);
}else if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
DiffBuilder *pBuilder;
if( pCfg->diffFlags & DIFF_HTML ){
pBuilder = dfsplitNew(pOut, pCfg);
}else{
pBuilder = dfsbsNew(pOut, pCfg);
}
formatDiff(&c, pCfg, pBuilder);
}else if( pCfg->diffFlags & DIFF_DEBUG ){
DiffBuilder *pBuilder = dfdebugNew(pOut);
formatDiff(&c, pCfg, pBuilder);
}else if( pCfg->diffFlags & DIFF_HTML ){
DiffBuilder *pBuilder = dfunifiedNew(pOut, pCfg);
formatDiff(&c, pCfg, pBuilder);
}else{
contextDiff(&c, pOut, pCfg);
}
fossil_free(c.aFrom);
fossil_free(c.aTo);
fossil_free(c.aEdit);
return 0;
}else{
/* If a context diff is not requested, then return the
** array of COPY/DELETE/INSERT triples.
*/
free(c.aFrom);
free(c.aTo);
return c.aEdit;
}
}
/*
** Initialize the DiffConfig object using command-line options.
**
** Process diff-related command-line options and return an appropriate
** "diffFlags" integer.
**
** --brief Show filenames only DIFF_BRIEF
** -c|--context N N lines of context. nContext
** --html Format for HTML DIFF_HTML
** --invert Invert the diff DIFF_INVERT
** -n|--linenum Show line numbers DIFF_LINENO
** --noopt Disable optimization DIFF_NOOPT
** --numstat Show change counts DIFF_NUMSTAT
** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
** --unified Unified diff. ~DIFF_SIDEBYSIDE
** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
** -W|--width N N character lines. wColumn
** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
*/
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
u64 diffFlags = 0;
const char *z;
int f;
memset(pCfg, 0, sizeof(*pCfg));
if( find_option("ignore-trailing-space","Z",0)!=0 ){
diffFlags = DIFF_IGNORE_EOLWS;
}
if( find_option("ignore-all-space","w",0)!=0 ){
diffFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */
}
if( find_option("strip-trailing-cr",0,0)!=0 ){
diffFlags |= DIFF_STRIP_EOLCR;
}
if( !bUnifiedTextOnly ){
if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
if( find_option("yy",0,0)!=0 ){
diffFlags |= DIFF_SIDEBYSIDE | DIFF_SLOW_SBS;
}
if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
if( find_option("webpage",0,0)!=0 ){
diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO;
}
if( find_option("browser","b",0)!=0 ){
diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER;
}
if( find_option("by",0,0)!=0 ){
diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER
|DIFF_SIDEBYSIDE;
}
if( find_option("json",0,0)!=0 ){
diffFlags |= DIFF_JSON;
}
if( find_option("tcl",0,0)!=0 ){
diffFlags |= DIFF_TCL;
}
/* Undocumented and unsupported flags used for development
** debugging and analysis: */
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
}
if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
pCfg->nContext = f;
diffFlags |= DIFF_CONTEXT_EX;
}
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
pCfg->wColumn = f;
}
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
if( find_option("internal","i",0)==0
&& (diffFlags & (DIFF_HTML|DIFF_TCL|DIFF_DEBUG|DIFF_JSON))==0
){
pCfg->zDiffCmd = find_option("command", 0, 1);
if( pCfg->zDiffCmd==0 ) pCfg->zDiffCmd = diff_command_external(isGDiff);
if( pCfg->zDiffCmd ){
const char *zDiffBinary;
pCfg->zBinGlob = diff_get_binary_glob();
zDiffBinary = find_option("diff-binary", 0, 1);
if( zDiffBinary ){
if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
}else if( db_get_boolean("diff-binary", 1) ){
diffFlags |= DIFF_INCBINARY;
}
}
}
if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
/* Deprecated, but retained for script compatibility. */
else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;
pCfg->diffFlags = diffFlags;
}
/*
** COMMAND: test-diff
** COMMAND: xdiff
**
** Usage: %fossil xdiff [options] FILE1 FILE2
**
** Compute an "external diff" between two files. By "external diff" we mean
** a diff between two disk files that are not necessarily under management.
** In other words, this command provides a mechanism to use Fossil's file
** difference engine on arbitrary disk files. See the "diff" command for
** computing differences between files that are* under management.
**
** This command prints the differences between the two files FILE1 and FILE2.
** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
** See the "diff" command for a full list of command-line options.
**
** This command used to be called "test-diff". The older "test-diff" spelling
** still works, for compatibility.
*/
void xdiff_cmd(void){
Blob a, b, out;
const char *zRe; /* Regex filter for diff output */
DiffConfig DCfg;
if( find_option("tk",0,0)!=0 ){
diff_tk("xdiff", 2);
return;
}
find_option("i",0,0);
find_option("v",0,0);
diff_options(&DCfg, 0, 0);
zRe = find_option("regexp","e",1);
if( zRe ){
const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
if( zErr ) fossil_fatal("regex error: %s", zErr);
}
verify_all_options();
if( g.argc!=4 ) usage("FILE1 FILE2");
blob_zero(&out);
diff_begin(&DCfg);
diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
blob_read_from_file(&a, g.argv[2], ExtFILE);
blob_read_from_file(&b, g.argv[3], ExtFILE);
text_diff(&a, &b, &out, &DCfg);
blob_write_to_file(&out, "-");
diff_end(&DCfg, 0);
re_free(DCfg.pRe);
}
/**************************************************************************
** The basic difference engine is above. What follows is the annotation
** engine. Both are in the same file since they share many components.
*/
|
| ︙ | ︙ | |||
2140 2141 2142 2143 2144 2145 2146 |
** will release it when it is finished with it.
*/
static int annotation_start(Annotator *p, Blob *pInput, u64 diffFlags){
int i;
memset(p, 0, sizeof(*p));
if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
| | | | 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 |
** will release it when it is finished with it.
*/
static int annotation_start(Annotator *p, Blob *pInput, u64 diffFlags){
int i;
memset(p, 0, sizeof(*p));
if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
p->c.xDiffer = compare_dline_ignore_allws;
}else{
p->c.xDiffer = compare_dline;
}
p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
diffFlags);
if( p->c.aTo==0 ){
return 1;
}
p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
|
| ︙ | ︙ |
Added src/diff.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/* Refinements to the display of unified and side-by-side diffs.
**
** In all cases, the table columns tagged with "difftxt" are expanded,
** where possible, to fill the width of the screen.
**
** For a side-by-side diff, if either column is two wide to fit on the
** display, scrollbars are added. The scrollbars are linked, so that
** both sides scroll together. Left and right arrows also scroll.
*/
window.addEventListener('load',function(){
var SCROLL_LEN = 25;
function initDiff(diff){
var txtCols = diff.querySelectorAll('td.difftxt');
var txtPres = diff.querySelectorAll('td.difftxt pre');
var width = 0;
if(txtPres.length>=2){
width = Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth);
}
var i;
for(i=0; i<txtCols.length; i++){
txtCols[i].style.width = width + 'px';
txtPres[i].style.maxWidth = width + 'px';
txtPres[i].style.width = width + 'px';
txtPres[i].onscroll = function(e){
for(var j=0; j<txtPres.length; j++) txtPres[j].scrollLeft = this.scrollLeft;
};
}
diff.tabIndex = 0;
diff.onkeydown = function(e){
e = e || event;
var len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode];
if( !len ) return;
txtPres[0].scrollLeft += len;
return false;
};
}
var i, diffs = document.querySelectorAll('table.splitdiff')
for(i=0; i<diffs.length; i++){
initDiff(diffs[i]);
}
const checkWidth = function f(){
if(undefined === f.lastWidth){
f.lastWidth = 0;
}
if( document.body.clientWidth===f.lastWidth ) return;
f.lastWidth = document.body.clientWidth;
var w = f.lastWidth*0.5 - 100;
if(!f.colsL){
f.colsL = document.querySelectorAll('td.difftxtl pre');
}
for(let i=0; i<f.colsL.length; i++){
f.colsL[i].style.width = w + "px";
f.colsL[i].style.maxWidth = w + "px";
}
if(!f.colsR){
f.colsR = document.querySelectorAll('td.difftxtr pre');
}
for(let i=0; i<f.colsR.length; i++){
f.colsR[i].style.width = w + "px";
f.colsR[i].style.maxWidth = w + "px";
}
if(!f.allDiffs){
f.allDiffs = document.querySelectorAll('table.diff');
}
w = f.lastWidth;
for(let i=0; i<f.allDiffs.length; i++){
f.allDiffs[i].style.width = '100%'; // setting to w causes unsightly horiz. scrollbar
f.allDiffs[i].style.maxWidth = w + "px";
}
};
checkWidth();
window.addEventListener('resize', checkWidth);
}, false);
|
Changes to src/diff.tcl.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 | TXT_COL_BG #ffffff TXT_COL_FG #000000 MKR_COL_BG #444444 MKR_COL_FG #dddddd CHNG_BG #d0d0ff ADD_BG #c0ffc0 RM_BG #ffc0c0 | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | TXT_COL_BG #ffffff TXT_COL_FG #000000 MKR_COL_BG #444444 MKR_COL_FG #dddddd CHNG_BG #d0d0ff ADD_BG #c0ffc0 RM_BG #ffc0c0 HR_FG #444444 HR_PAD_TOP 4 HR_PAD_BTM 8 FN_BG #444444 FN_FG #ffffff FN_PAD 5 ERR_FG #ee0000 PADX 5 |
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
fconfigure $in -encoding utf-8
set difftxt [split [read $in] \n]
close $in
}
set N [llength $difftxt]
set ii 0
set nDiffs 0
| > > | < < | | | > > | | | > > | | | > | > > | > | > | > > | > | > > | > | > > | > > | < < | > > > < > > > > > > > > | | < > > > > > | | < | | | < | | > > > | < < | < < | | | | > | | | > > | | > > > > > | > | > > > | 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 |
fconfigure $in -encoding utf-8
set difftxt [split [read $in] \n]
close $in
}
set N [llength $difftxt]
set ii 0
set nDiffs 0
set n1 0
set n2 0
array set widths {txt 3 ln 3 mkr 1}
while {[set line [getLine $difftxt $N ii]] != -1} {
switch -- [lindex $line 0] {
FILE {
incr nDiffs
foreach wx [list [string length $n1] [string length $n2]] {
if {$wx>$widths(ln)} {set widths(ln) $wx}
}
.lnA insert end \n fn \n -
.txtA insert end [lindex $line 1]\n fn \n -
.mkr insert end \n fn \n -
.lnB insert end \n fn \n -
.txtB insert end [lindex $line 2]\n fn \n -
.wfiles.lb insert end [lindex $line 2]
set n1 0
set n2 0
}
SKIP {
set n [lindex $line 1]
incr n1 $n
incr n2 $n
.lnA insert end ...\n hrln
.txtA insert end [string repeat . 30]\n hrtxt
.mkr insert end \n hrln
.lnB insert end ...\n hrln
.txtB insert end [string repeat . 30]\n hrtxt
}
COM {
set x [lindex $line 1]
incr n1
incr n2
.lnA insert end $n1\n -
.txtA insert end $x\n -
.mkr insert end \n -
.lnB insert end $n2\n -
.txtB insert end $x\n -
}
INS {
set x [lindex $line 1]
incr n2
.lnA insert end \n -
.txtA insert end \n -
.mkr insert end >\n -
.lnB insert end $n2\n -
.txtB insert end $x add \n -
}
DEL {
set x [lindex $line 1]
incr n1
.lnA insert end $n1\n -
.txtA insert end $x rm \n -
.mkr insert end <\n -
.lnB insert end \n -
.txtB insert end \n -
}
EDIT {
incr n1
incr n2
.lnA insert end $n1\n -
.lnB insert end $n2\n -
.mkr insert end |\n -
set nn [llength $line]
for {set i 1} {$i<$nn} {incr i 3} {
set x [lindex $line $i]
if {$x ne ""} {
.txtA insert end $x -
.txtB insert end $x -
}
if {$i+2<$nn} {
set x1 [lindex $line [expr {$i+1}]]
set x2 [lindex $line [expr {$i+2}]]
if {"$x1" eq ""} {
.txtB insert end $x2 add
} elseif {"$x2" eq ""} {
.txtA insert end $x1 rm
} else {
.txtA insert end $x1 chng
.txtB insert end $x2 chng
}
}
}
.txtA insert end \n -
.txtB insert end \n -
}
"" {
foreach wx [list [string length $n1] [string length $n2]] {
if {$wx>$widths(ln)} {set widths(ln) $wx}
}
}
default {
.lnA insert end \n -
.txtA insert end $line\n err
.mkr insert end \n -
.lnB insert end \n -
.txtB insert end $line\n err
}
}
}
foreach c [cols] {
set type [colType $c]
if {$type ne "txt"} {
$c config -width $widths($type)
|
| ︙ | ︙ | |||
328 329 330 331 332 333 334 |
text .mkr
foreach c [cols] {
set keyPrefix [string toupper [colType $c]]_COL_
if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
$c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
-padx $CFG(PADX) -yscroll sync-y
| | | > > | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
text .mkr
foreach c [cols] {
set keyPrefix [string toupper [colType $c]]_COL_
if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
$c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
-padx $CFG(PADX) -yscroll sync-y
$c tag config hrln -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \
-foreground $CFG(HR_FG) -justify right
$c tag config hrtxt -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \
-foreground $CFG(HR_FG) -justify center
$c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD)
bindtags $c ". $c Text all"
bind $c <1> {focus %W}
}
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** ** This file contains code used to implement the "diff" command */ #include "config.h" #include "diffcmd.h" #include <assert.h> /* ** Use the right null device for the platform. */ #if defined(_WIN32) # define NULL_DEVICE "NUL" #else # define NULL_DEVICE "/dev/null" | > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** ** This file contains code used to implement the "diff" command */ #include "config.h" #include "diffcmd.h" #include <assert.h> /* includes needed to catch interrupts */ #ifdef _WIN32 # include <windows.h> #else # include <signal.h> #endif /* ** Use the right null device for the platform. */ #if defined(_WIN32) # define NULL_DEVICE "NUL" #else # define NULL_DEVICE "/dev/null" |
| ︙ | ︙ | |||
104 105 106 107 108 109 110 | } return 0; } /* ** Print the "Index:" message that patches wants to see at the top of a diff. */ | | > | > > | < < < < < < | > | > > > > > | < | > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > < < < < < < < < < < < < < < | < | | | < < < | < | < | | < < < < | | < < < < | | | | | 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 |
}
return 0;
}
/*
** Print the "Index:" message that patches wants to see at the top of a diff.
*/
void diff_print_index(const char *zFile, DiffConfig *pCfg, Blob *pOut){
if( (pCfg->diffFlags &
(DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|DIFF_JSON|
DIFF_WEBPAGE|DIFF_TCL))==0
){
blob_appendf(pOut, "Index: %s\n%.66c\n", zFile, '=');
}
}
/*
** Print the +++/--- filename lines or whatever filename information
** is appropriate for the output format.
*/
void diff_print_filenames(
const char *zLeft, /* Name of the left file */
const char *zRight, /* Name of the right file */
DiffConfig *pCfg, /* Diff configuration */
Blob *pOut /* Write to this blob, or stdout of this is NULL */
){
u64 diffFlags = pCfg->diffFlags;
if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
/* no-op */
}else if( diffFlags & DIFF_DEBUG ){
blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
}else if( diffFlags & DIFF_WEBPAGE ){
if( fossil_strcmp(zLeft,zRight)==0 ){
blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);
}else{
blob_appendf(pOut,"<h1>%h ⇆ %h</h1>\n", zLeft, zRight);
}
}else if( diffFlags & (DIFF_TCL|DIFF_JSON) ){
if( diffFlags & DIFF_TCL ){
blob_append(pOut, "FILE ", 5);
blob_append_tcl_literal(pOut, zLeft, (int)strlen(zLeft));
blob_append_char(pOut, ' ');
blob_append_tcl_literal(pOut, zRight, (int)strlen(zRight));
blob_append_char(pOut, '\n');
}else{
if( pOut ) blob_trim(pOut);
blob_append(pOut, (pCfg->nFile==0 ? "[{" : ",\n{"), -1);
pCfg->nFile++;
blob_append(pOut, "\n \"leftname\":", -1);
blob_append_json_literal(pOut, zLeft, (int)strlen(zLeft));
blob_append(pOut, ",\n \"rightname\":", -1);
blob_append_json_literal(pOut, zRight, (int)strlen(zRight));
blob_append(pOut, ",\n \"diff\":\n", -1);
}
}else if( diffFlags & DIFF_SIDEBYSIDE ){
int w = diff_width(pCfg);
int n1 = strlen(zLeft);
int n2 = strlen(zRight);
int x;
if( n1==n2 && fossil_strcmp(zLeft,zRight)==0 ){
if( n1>w*2 ) n1 = w*2;
x = w*2+17 - (n1+2);
blob_appendf(pOut, "%.*c %.*s %.*c\n",
x/2, '=', n1, zLeft, (x+1)/2, '=');
}else{
if( w<20 ) w = 20;
if( n1>w-10 ) n1 = w - 10;
if( n2>w-10 ) n2 = w - 10;
blob_appendf(pOut, "%.*c %.*s %.*c versus %.*c %.*s %.*c\n",
(w-n1+10)/2, '=', n1, zLeft, (w-n1+1)/2, '=',
(w-n2)/2, '=', n2, zRight, (w-n2+1)/2, '=');
}
}else{
blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight);
}
}
/*
** Default header text for diff with --webpage
*/
static const char zWebpageHdr[] =
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ h1 {
@ font-size: 150%;
@ }
@
@ table.diff {
@ width: 100%;
@ border-spacing: 0;
@ border: 1px solid black;
@ }
@ table.diff td {
@ vertical-align: top;
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ }
@ td.diffln {
@ width: 1px;
@ text-align: right;
@ padding: 0 1em 0 0;
@ }
@ td.difflne {
@ padding-bottom: 0.4em;
@ }
@ td.diffsep {
@ width: 1px;
@ padding: 0 0.3em 0 1em;
@ }
@ td.difftxt pre {
@ overflow-x: auto;
@ }
@ td.diffln ins {
@ background-color: #a0e4b2;
@ text-decoration: none;
@ }
@ td.diffln del {
@ background-color: #ffc0c0;
@ text-decoration: none;
@ }
@ td.difftxt del {
@ background-color: #ffe8e8;
@ text-decoration: none;
@ }
@ td.difftxt del > del {
@ background-color: #ffc0c0;
@ text-decoration: none;
@ font-weight: bold;
@ }
@ td.difftxt del > del.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
@ td.difftxt ins {
@ background-color: #dafbe1;
@ text-decoration: none;
@ }
@ td.difftxt ins > ins {
@ background-color: #a0e4b2;
@ text-decoration: none;
@ font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
@
@ </style>
@ </head>
@ <body>
;
const char zWebpageEnd[] =
@ </body>
@ </html>
;
/*
** State variables used by the --browser option for diff. These must
** be static variables, not elements of DiffConfig, since they are
** used by the interrupt handler.
*/
static char *tempDiffFilename; /* File holding the diff HTML */
static FILE *diffOut; /* Open to write into tempDiffFilename */
/* Amount of delay (in milliseconds) between launching the
** web browser and deleting the temporary file used by --browser
*/
#ifndef FOSSIL_BROWSER_DIFF_DELAY
# define FOSSIL_BROWSER_DIFF_DELAY 5000 /* 5 seconds by default */
#endif
/*
** If we catch a single while writing the temporary file for the --browser
** diff output, then delete the temporary file and exit.
*/
static void diff_www_interrupt(int NotUsed){
(void)NotUsed;
if( diffOut ) fclose(diffOut);
if( tempDiffFilename ) file_delete(tempDiffFilename);
exit(1);
}
#ifdef _WIN32
static BOOL WINAPI diff_console_ctrl_handler(DWORD dwCtrlType){
if( dwCtrlType==CTRL_C_EVENT ) diff_www_interrupt(0);
return FALSE;
}
#endif
/*
** Do preliminary setup and output before computing a diff.
**
** For --browser, redirect stdout to a temporary file that will
** hold the result. Make arrangements to delete that temporary
** file if the diff is interrupted.
**
** For --browser and --webpage, output the HTML header.
*/
void diff_begin(DiffConfig *pCfg){
if( (pCfg->diffFlags & DIFF_BROWSER)!=0 ){
tempDiffFilename = fossil_temp_filename();
tempDiffFilename = sqlite3_mprintf("%z.html", tempDiffFilename);
diffOut = fossil_freopen(tempDiffFilename,"wb",stdout);
if( diffOut==0 ){
fossil_fatal("unable to create temporary file \"%s\"",
tempDiffFilename);
}
#ifndef _WIN32
signal(SIGINT, diff_www_interrupt);
#else
SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
#endif
}
if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
fossil_print("%s",zWebpageHdr);
fflush(stdout);
}
}
/* Do any final output required by a diff and complete the diff
** process.
**
** For --browser and --webpage, output any javascript required by
** the diff. (Currently JS is only needed for side-by-side diffs).
**
** For --browser, close the connection to the temporary file, then
** launch a web browser to view the file. After a delay
** of FOSSIL_BROWSER_DIFF_DELAY milliseconds, delete the temp file.
*/
void diff_end(DiffConfig *pCfg, int nErr){
if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
const unsigned char *zJs = builtin_file("diff.js", 0);
fossil_print("<script>\n%s</script>\n", zJs);
}
fossil_print("%s", zWebpageEnd);
}
if( (pCfg->diffFlags & DIFF_BROWSER)!=0 && nErr==0 ){
char *zCmd = mprintf("%s %$", fossil_web_browser(), tempDiffFilename);
fclose(diffOut);
diffOut = fossil_freopen(NULL_DEVICE, "wb", stdout);
fossil_system(zCmd);
fossil_free(zCmd);
diffOut = 0;
sqlite3_sleep(FOSSIL_BROWSER_DIFF_DELAY);
file_delete(tempDiffFilename);
sqlite3_free(tempDiffFilename);
tempDiffFilename = 0;
}
if( (pCfg->diffFlags & DIFF_JSON)!=0 && pCfg->nFile>0 ){
fossil_print("]\n");
}
}
/*
** 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.
*/
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 */
DiffConfig *pCfg, /* Flags to control the diff */
Blob *pOut /* Blob to store diff output */
){
if( pCfg->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, ExtFILE)<0 ){
zName2 = NULL_DEVICE;
}else{
blob_read_from_file(&file2, zFile2, ExtFILE);
zName2 = zName;
}
/* Compute and output the differences */
if( pCfg->diffFlags & DIFF_BRIEF ){
if( blob_compare(pFile1, &file2) ){
fossil_print("CHANGED %s\n", zName);
}
}else{
blob_zero(&out);
text_diff(pFile1, &file2, &out, pCfg);
if( blob_size(&out) ){
if( pCfg->diffFlags & DIFF_NUMSTAT ){
blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
}else{
diff_print_filenames(zName, zName2, pCfg, pOut);
blob_appendf(pOut, "%s\n", blob_str(&out));
}
}
blob_reset(&out);
}
/* Release memory resources */
blob_reset(&file2);
}else{
Blob nameFile1; /* Name of temporary file to old pFile1 content */
Blob cmd; /* Text of command to run */
if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
Blob file2;
if( looks_like_binary(pFile1) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
return;
}
if( pCfg->zBinGlob ){
Glob *pBinary = glob_create(pCfg->zBinGlob);
if( glob_match(pBinary, zName) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
glob_free(pBinary);
return;
}
glob_free(pBinary);
}
|
| ︙ | ︙ | |||
267 268 269 270 271 272 273 |
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
file_tempname(&nameFile1, zFile2, "orig");
blob_write_to_file(pFile1, blob_str(&nameFile1));
/* Construct the external diff command */
blob_zero(&cmd);
| | | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
file_tempname(&nameFile1, zFile2, "orig");
blob_write_to_file(pFile1, blob_str(&nameFile1));
/* Construct the external diff command */
blob_zero(&cmd);
blob_append(&cmd, pCfg->zDiffCmd, -1);
if( pCfg->diffFlags & DIFF_INVERT ){
blob_append_escaped_arg(&cmd, zFile2, 1);
blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
}else{
blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
blob_append_escaped_arg(&cmd, zFile2, 1);
}
|
| ︙ | ︙ | |||
302 303 304 305 306 307 308 | ** When using an external diff program, zBinGlob contains the GLOB patterns ** for file names to treat as binary. If fIncludeBinary is zero, these files ** will be skipped in addition to files that may contain binary content. */ void diff_file_mem( Blob *pFile1, /* In memory content to compare from */ Blob *pFile2, /* In memory content to compare to */ | < < < < < | | | | | | | | | | | | 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 |
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
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 */
DiffConfig *pCfg /* Diff flags */
){
if( pCfg->diffFlags & DIFF_BRIEF ) return;
if( pCfg->zDiffCmd==0 ){
Blob out; /* Diff output text */
blob_zero(&out);
text_diff(pFile1, pFile2, &out, pCfg);
if( pCfg->diffFlags & DIFF_NUMSTAT ){
fossil_print("%s %s\n", blob_str(&out), zName);
}else{
diff_print_filenames(zName, zName, pCfg, 0);
fossil_print("%s\n", blob_str(&out));
}
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
Blob temp1;
Blob temp2;
if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
return;
}
if( pCfg->zBinGlob ){
Glob *pBinary = glob_create(pCfg->zBinGlob);
if( glob_match(pBinary, zName) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
glob_free(pBinary);
return;
}
glob_free(pBinary);
}
}
/* Construct a temporary file names */
file_tempname(&temp1, zName, "before");
file_tempname(&temp2, zName, "after");
blob_write_to_file(pFile1, blob_str(&temp1));
blob_write_to_file(pFile2, blob_str(&temp2));
/* Construct the external diff command */
blob_zero(&cmd);
blob_append(&cmd, pCfg->zDiffCmd, -1);
blob_append_escaped_arg(&cmd, blob_str(&temp1), 1);
blob_append_escaped_arg(&cmd, blob_str(&temp2), 1);
/* Run the external diff command */
fossil_system(blob_str(&cmd));
/* Delete the temporary file and clean up memory used */
|
| ︙ | ︙ | |||
385 386 387 388 389 390 391 | ** ** When using an external diff program, zBinGlob contains the GLOB patterns ** for file names to treat as binary. If fIncludeBinary is zero, these files ** will be skipped in addition to files that may contain binary content. */ void diff_against_disk( const char *zFrom, /* Version to difference from */ | < < < | | | | | 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 |
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
void diff_against_disk(
const char *zFrom, /* Version to difference from */
DiffConfig *pCfg, /* Flags controlling diff output */
FileDirList *pFileDir, /* Which files to diff */
Blob *pOut /* Blob to output diff instead of stdout */
){
int vid;
Blob sql;
Stmt q;
int asNewFile; /* Treat non-existant files as empty files */
int isNumStat; /* True for --numstat */
asNewFile = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT|DIFF_HTML))!=0;
isNumStat = (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_TCL|DIFF_HTML))!=0;
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, CKSIG_ENOTFILE);
blob_zero(&sql);
db_begin_transaction();
if( zFrom ){
int rid = name_to_typed_rid(zFrom, "ci");
if( !is_a_version(rid) ){
|
| ︙ | ︙ | |||
483 484 485 486 487 488 489 |
}else if( isChnged==5 ){
if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}
if( showDiff ){
Blob content;
| < | | < | | < < < < | | < | < < < < | | < < | < | < < < | | | < | > | < | | < | 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 |
}else if( isChnged==5 ){
if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}
if( showDiff ){
Blob content;
if( !isLink != !file_islink(zFullName) ){
diff_print_index(zPathname, pCfg, 0);
diff_print_filenames(zPathname, zPathname, pCfg, 0);
fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
continue;
}
if( srcid>0 ){
content_get(srcid, &content);
}else{
blob_zero(&content);
}
diff_print_index(zPathname, pCfg, pOut);
diff_file(&content, zFullName, zPathname, pCfg, pOut);
blob_reset(&content);
}
blob_reset(&fname);
}
db_finalize(&q);
db_end_transaction(1); /* ROLLBACK */
}
/*
** Run a diff between the undo buffer and files on disk.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_against_undo(
DiffConfig *pCfg, /* Flags controlling diff output */
FileDirList *pFileDir /* List of files and directories to diff */
){
Stmt q;
Blob content;
db_prepare(&q, "SELECT pathname, content FROM undo");
blob_init(&content, 0, 0);
while( db_step(&q)==SQLITE_ROW ){
char *zFullName;
const char *zFile = (const char*)db_column_text(&q, 0);
if( !file_dir_match(pFileDir, zFile) ) continue;
zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
db_column_blob(&q, 1, &content);
diff_file(&content, zFullName, zFile, pCfg, 0);
fossil_free(zFullName);
blob_reset(&content);
}
db_finalize(&q);
}
/*
** Show the difference between two files identified by ManifestFile
** entries.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_manifest_entry(
struct ManifestFile *pFrom,
struct ManifestFile *pTo,
DiffConfig *pCfg
){
Blob f1, f2;
int rid;
const char *zName;
if( pFrom ){
zName = pFrom->zName;
}else if( pTo ){
zName = pTo->zName;
}else{
zName = DIFF_NO_NAME;
}
if( pCfg->diffFlags & DIFF_BRIEF ) return;
diff_print_index(zName, pCfg, 0);
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, pCfg);
blob_reset(&f1);
blob_reset(&f2);
}
/*
** Output the differences between two check-ins.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_two_versions(
const char *zFrom,
const char *zTo,
DiffConfig *pCfg,
FileDirList *pFileDir
){
Manifest *pFrom, *pTo;
ManifestFile *pFromFile, *pToFile;
int asNewFlag = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT))!=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 = fossil_strcmp(pFromFile->zName, pToFile->zName);
}
if( cmp<0 ){
if( file_dir_match(pFileDir, pFromFile->zName) ){
if( (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_HTML))==0 ){
fossil_print("DELETED %s\n", pFromFile->zName);
}
if( asNewFlag ){
diff_manifest_entry(pFromFile, 0, pCfg);
}
}
pFromFile = manifest_file_next(pFrom,0);
}else if( cmp>0 ){
if( file_dir_match(pFileDir, pToFile->zName) ){
if( (pCfg->diffFlags &
(DIFF_NUMSTAT|DIFF_HTML|DIFF_TCL|DIFF_JSON))==0 ){
fossil_print("ADDED %s\n", pToFile->zName);
}
if( asNewFlag ){
diff_manifest_entry(0, pToFile, pCfg);
}
}
pToFile = manifest_file_next(pTo,0);
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
/* No changes */
(void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}else{
if( file_dir_match(pFileDir, pToFile->zName) ){
if( pCfg->diffFlags & DIFF_BRIEF ){
fossil_print("CHANGED %s\n", pFromFile->zName);
}else{
diff_manifest_entry(pFromFile, pToFile, pCfg);
}
}
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}
}
manifest_destroy(pFrom);
|
| ︙ | ︙ | |||
717 718 719 720 721 722 723 |
void diff_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
blob_zero(&script);
| | > | 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 |
void diff_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
blob_zero(&script);
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
g.nameOfExe, zSubCmd);
find_option("tcl",0,0);
find_option("html",0,0);
find_option("side-by-side","y",0);
find_option("internal","i",0);
find_option("verbose","v",0);
zTclsh = find_option("tclsh",0,1);
if( zTclsh==0 ){
zTclsh = db_get("tclsh",0);
|
| ︙ | ︙ | |||
769 770 771 772 773 774 775 |
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
| < < < < < < < < < < < < < < < | > > > > > > > > > > > | | | < < | | < < | | > | | | | | < < < > > > > > | > > > > | | < < < < < < < > < < < < < < < | 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 |
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
/*
** Returns the GLOB pattern for file names that should be treated as binary
** by the diff subsystem, if any.
*/
const char *diff_get_binary_glob(void){
const char *zBinGlob = find_option("binary", 0, 1);
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
return zBinGlob;
}
/*
** COMMAND: diff
** COMMAND: gdiff
**
** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
**
** Show the difference between the current version of each of the FILEs
** specified (as they exist on disk) and that same file as it was checked
** out. Or if the FILE arguments are omitted, show all unsaved changes
** currently in the working check-out.
**
** The default output format is a "unified patch" (the same as the
** output of "diff -u" on most unix systems). Many alternative formats
** are available. A few of the more useful alternatives:
**
** --tk Pop up a TCL/TK-based GUI to show the diff
** --by Show a side-by-side diff in the default web browser
** -b Show a linear diff in the default web browser
** -y Show a text side-by-side diff
** --webpage Format output as HTML
** --webpage -y HTML output in the side-by-side format
**
** The "--from VERSION" option is used it specifies the source check-in
** for the diff operation. If not specified, the source check-in is the
** base check-in for the current check-out. Similarly, the "--to VERSION"
** option specifies the check-in from which the second version of the file
** or files is taken. If there is no "--to" option then the (possibly edited)
** files in the current check-out are used. The "--checkin VERSION" option
** shows the changes made by check-in VERSION relative to its primary parent.
** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
**
** The "-i" command-line option forces the use of Fossils own 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 "--diff-binary" option enables or disables the inclusion of binary files
** when using an external diff program.
**
** The "--binary" option causes files matching the glob PATTERN to be treated
** as binary when considering if they should be used with external diff program.
** This option overrides the "binary-glob" setting.
**
** These command show differences between managed files. Use the "fossil xdiff"
** command to see differences in unmanaged files.
**
** Options:
** --binary PATTERN Treat files that match the glob PATTERN
** as binary
** --branch BRANCH Show diff of all changes on BRANCH
** --brief Show filenames only
** -b|--browser Show the diff output in a web-browser
** --by Shorthand for "--browser -y"
** --checkin VERSION Show diff of all changes in VERSION
** --command PROG External diff program. Overrides "diff-command"
** -c|--context N Show N lines of context around each change
** --diff-binary BOOL Include binary files with external commands
** --exec-abs-paths Force absolute path names on external commands
** --exec-rel-paths Force relative path names on external commands
** -r|--from VERSION Select VERSION as source for the diff
** -w|--ignore-all-space Ignore white space when comparing lines
** -i|--internal Use internal diff logic
** --json Output formatted as JSON
** -N|--new-file Alias for --verbose
** --numstat Show only the number of lines delete and added
** -y|--side-by-side Side-by-side diff
** --strip-trailing-cr Strip trailing CR
** --tcl TCL-formated output used internally by --tk
** --tclsh PATH TCL/TK used for --tk (default: "tclsh")
** --tk Launch a Tcl/Tk GUI for display
** --to VERSION Select VERSION as target for the diff
** --undo Diff against the "undo" buffer
** --unified Unified diff
** -v|--verbose Output complete text of added or deleted files
** --webpage Format output as a stand-alone HTML webpage
** -W|--width N Width of lines in side-by-side diff
** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
*/
void diff_cmd(void){
int isGDiff; /* True for gdiff. False for normal diff */
const char *zFrom; /* Source version number */
const char *zTo; /* Target version number */
const char *zCheckin; /* Check-in version number */
const char *zBranch; /* Branch to diff */
int againstUndo = 0; /* Diff against files in the undo buffer */
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
DiffConfig DCfg; /* Diff configuration object */
if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
diff_tk("diff", 2);
return;
}
isGDiff = g.argv[1][0]=='g';
zFrom = find_option("from", "r", 1);
zTo = find_option("to", 0, 1);
zCheckin = find_option("checkin", 0, 1);
zBranch = find_option("branch", 0, 1);
againstUndo = find_option("undo",0,0)!=0;
if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
" or --branch");
}
if( zBranch ){
if( zTo || zFrom || zCheckin ){
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
|
| ︙ | ︙ | |||
914 915 916 917 918 919 920 |
if( zTo==0 || againstUndo ){
db_must_be_within_tree();
}else if( zFrom==0 ){
fossil_fatal("must use --from if --to is present");
}else{
db_find_and_open_repository(0, 0);
}
| < | < < < < | 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 |
if( zTo==0 || againstUndo ){
db_must_be_within_tree();
}else if( zFrom==0 ){
fossil_fatal("must use --from if --to is present");
}else{
db_find_and_open_repository(0, 0);
}
diff_options(&DCfg, isGDiff, 0);
determine_exec_relative_option(1);
verify_all_options();
if( g.argc>=3 ){
int i;
Blob fname;
pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) );
memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1));
|
| ︙ | ︙ | |||
951 952 953 954 955 956 957 958 959 960 961 962 |
"SELECT uuid FROM blob, plink"
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
ridTo);
if( zFrom==0 ){
fossil_fatal("check-in %s has no parent", zTo);
}
}
if( againstUndo ){
if( db_lget_int("undo_available",0)==0 ){
fossil_print("No undo or redo is available\n");
return;
}
| > | < | < | < > | > > | | 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 |
"SELECT uuid FROM blob, plink"
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
ridTo);
if( zFrom==0 ){
fossil_fatal("check-in %s has no parent", zTo);
}
}
diff_begin(&DCfg);
if( againstUndo ){
if( db_lget_int("undo_available",0)==0 ){
fossil_print("No undo or redo is available\n");
return;
}
diff_against_undo(&DCfg, pFileDir);
}else if( zTo==0 ){
diff_against_disk(zFrom, &DCfg, pFileDir, 0);
}else{
diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
}
if( pFileDir ){
int i;
for(i=0; pFileDir[i].zName; i++){
if( pFileDir[i].nUsed==0
&& strcmp(pFileDir[0].zName,".")!=0
&& !file_isdir(g.argv[i+2], ExtFILE)
){
fossil_fatal("not found: '%s'", g.argv[i+2]);
}
fossil_free(pFileDir[i].zName);
}
fossil_free(pFileDir);
}
diff_end(&DCfg, 0);
if ( DCfg.diffFlags & DIFF_NUMSTAT ){
fossil_print("%10d %10d TOTAL over %d changed files\n",
g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
}
}
/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
**
** Show a patch that goes from check-in FROM to check-in TO.
*/
void vpatch_page(void){
const char *zFrom = P("from");
const char *zTo = P("to");
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
cgi_set_content_type("text/plain");
diff_config_init(&DCfg, DIFF_VERBOSE);
diff_two_versions(zFrom, zTo, &DCfg, 0);
}
|
Changes to src/dispatch.c.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ #define CMDFLAG_COMMAND 0x0010 /* A command */ #define CMDFLAG_SETTING 0x0020 /* A setting */ #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret POST content */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0038 /* Match anything */ #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ #endif /* INTERFACE */ | > > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ #define CMDFLAG_COMMAND 0x0010 /* A command */ #define CMDFLAG_SETTING 0x0020 /* A setting */ #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret POST content */ /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0038 /* Match anything */ #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ #endif /* INTERFACE */ |
| ︙ | ︙ | |||
549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
fossil_print("-->\n");
fossil_print("<!-- start_all_help -->\n");
}else{
fossil_print("---\n");
}
for(i=0; i<MX_COMMAND; i++){
if( (aCommand[i].eCmdFlags & mask)==0 ) continue;
if( useHtml ){
Blob html;
blob_init(&html, 0, 0);
help_to_html(aCommand[i].zHelp, &html);
fossil_print("<h1>%h</h1>\n", aCommand[i].zName);
fossil_print("%s\n<hr>\n", blob_str(&html));
blob_reset(&html);
| > | 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 |
fossil_print("-->\n");
fossil_print("<!-- start_all_help -->\n");
}else{
fossil_print("---\n");
}
for(i=0; i<MX_COMMAND; i++){
if( (aCommand[i].eCmdFlags & mask)==0 ) continue;
else if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
if( useHtml ){
Blob html;
blob_init(&html, 0, 0);
help_to_html(aCommand[i].zHelp, &html);
fossil_print("<h1>%h</h1>\n", aCommand[i].zName);
fossil_print("%s\n<hr>\n", blob_str(&html));
blob_reset(&html);
|
| ︙ | ︙ | |||
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 |
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :"";
const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":"";
if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
@ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a></li>
}
@ </ul></div>
@ <a name='webpages'></a>
@ <h1>Available web UI pages:</h1>
@ <div class="columns" style="column-width: 18ex;">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( '/'!=*z ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li>
}else{
@ <li>%s(z+1)</li>
}
}
@ </ul></div>
@ <a name='unsupported'></a>
@ <h1>Unsupported commands:</h1>
@ <div class="columns" style="column-width: 20ex;">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( strncmp(z,"test",4)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
}else{
@ <li>%s(z)</li>
}
}
@ </ul></div>
@ <a name='settings'></a>
@ <h1>Settings:</h1>
@ <div class="columns" style="column-width: 20ex;">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
}else{
@ <li>%s(z)</li>
}
}
@ </ul></div>
| > > > > | 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 |
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :"";
const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":"";
if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
@ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a></li>
}
@ </ul></div>
@ <a name='webpages'></a>
@ <h1>Available web UI pages:</h1>
@ <div class="columns" style="column-width: 18ex;">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( '/'!=*z ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li>
}else{
@ <li>%s(z+1)</li>
}
}
@ </ul></div>
@ <a name='unsupported'></a>
@ <h1>Unsupported commands:</h1>
@ <div class="columns" style="column-width: 20ex;">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( strncmp(z,"test",4)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
}else{
@ <li>%s(z)</li>
}
}
@ </ul></div>
@ <a name='settings'></a>
@ <h1>Settings:</h1>
@ <div class="columns" style="column-width: 20ex;">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
}else{
@ <li>%s(z)</li>
}
}
@ </ul></div>
|
| ︙ | ︙ | |||
983 984 985 986 987 988 989 990 991 992 993 994 995 996 |
if( verboseFlag ){
display_all_help(cmdMask, useHtml, 0);
}else{
int i, nCmd;
const char *aCmd[MX_COMMAND];
for(i=nCmd=0; i<MX_COMMAND; i++){
if( (aCommand[i].eCmdFlags & cmdMask)==0 ) continue;
aCmd[nCmd++] = aCommand[i].zName;
}
multi_column_list(aCmd, nCmd);
}
}
/*
| > | 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 |
if( verboseFlag ){
display_all_help(cmdMask, useHtml, 0);
}else{
int i, nCmd;
const char *aCmd[MX_COMMAND];
for(i=nCmd=0; i<MX_COMMAND; i++){
if( (aCommand[i].eCmdFlags & cmdMask)==0 ) continue;
else if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
aCmd[nCmd++] = aCommand[i].zName;
}
multi_column_list(aCmd, nCmd);
}
}
/*
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 | fossil_path_free(uName); fossil_unicode_free(uMode); #else FILE *f = fopen(zName, zMode); #endif return f; } /* ** Works like fclose() except that: ** ** 1) is a no-op if f is 0 or if it is stdin. ** ** 2) If f is one of (stdout, stderr), it is flushed but not closed. | > > > > > > > > > > > > > > > > | 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 |
fossil_path_free(uName);
fossil_unicode_free(uMode);
#else
FILE *f = fopen(zName, zMode);
#endif
return f;
}
/*
** Wrapper for freopen() that understands UTF8 arguments.
*/
FILE *fossil_freopen(const char *zName, const char *zMode, FILE *stream){
#ifdef _WIN32
wchar_t *uMode = fossil_utf8_to_unicode(zMode);
wchar_t *uName = fossil_utf8_to_path(zName, 0);
FILE *f = _wfreopen(uName, uMode, stream);
fossil_path_free(uName);
fossil_unicode_free(uMode);
#else
FILE *f = freopen(zName, zMode, stream);
#endif
return f;
}
/*
** Works like fclose() except that:
**
** 1) is a no-op if f is 0 or if it is stdin.
**
** 2) If f is one of (stdout, stderr), it is flushed but not closed.
|
| ︙ | ︙ |
Changes to src/fileedit.c.
| ︙ | ︙ | |||
1142 1143 1144 1145 1146 1147 1148 1149 |
if(!zContent){
zContent = "";
}
cgi_set_content_type("text/html");
blob_init(&content, zContent, -1);
{
Blob orig = empty_blob;
content_get(frid, &orig);
| > | > | 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 |
if(!zContent){
zContent = "";
}
cgi_set_content_type("text/html");
blob_init(&content, zContent, -1);
{
Blob orig = empty_blob;
char * const zOrigUuid = rid_to_uuid(frid);
content_get(frid, &orig);
ajax_render_diff(&orig, zOrigUuid, &content, diffFlags);
fossil_free(zOrigUuid);
blob_reset(&orig);
}
fossil_free(zRevUuid);
blob_reset(&content);
}
/*
|
| ︙ | ︙ | |||
1843 1844 1845 1846 1847 1848 1849 |
">");
{
/******* Commit flags/options *******/
CX("<div class='fileedit-options flex-container flex-row'>");
style_labeled_checkbox("cb-dry-run",
"dry_run", "Dry-run?", "1",
0,
| | | 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 |
">");
{
/******* Commit flags/options *******/
CX("<div class='fileedit-options flex-container flex-row'>");
style_labeled_checkbox("cb-dry-run",
"dry_run", "Dry-run?", "1",
0,
"In dry-run mode, the Commit button performs "
"all work needed for committing changes but "
"then rolls back the transaction, and thus "
"does not really commit.");
style_labeled_checkbox("cb-allow-fork",
"allow_fork", "Allow fork?", "1",
cimi.flags & CIMINI_ALLOW_FORK,
"Allow committing to create a fork?");
|
| ︙ | ︙ | |||
1997 1998 1999 2000 2001 2002 2003 | ** used for dynamically toggling certain UI components on and off. ** Must come after window.fossil has been intialized and before ** fossil.page.fileedit.js. Potential TODO: move this into the ** window.fossil bootstrapping so that we don't have to "fulfill" ** the JS multiple times. */ ajax_emit_js_preview_modes(1); | | | 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 |
** used for dynamically toggling certain UI components on and off.
** Must come after window.fossil has been intialized and before
** fossil.page.fileedit.js. Potential TODO: move this into the
** window.fossil bootstrapping so that we don't have to "fulfill"
** the JS multiple times.
*/
ajax_emit_js_preview_modes(1);
builtin_fossil_js_bundle_or("diff", NULL);
builtin_request_js("fossil.page.fileedit.js");
builtin_fulfill_js_requests();
{
/* Dynamically populate the editor, display any error in the err
** blob, and/or switch to tab #0, where the file selector
** lives. The extra C scopes here correspond to JS-level scopes,
** to improve grokability. */
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
317 318 319 320 321 322 323 | ** ci=HASH identify a particular version of a file and then ** track changes to that file across renames ** m=HASH Mark this particular file version. ** n=NUM Show the first NUM changes only ** name=FILENAME (Required) name of file whose history to show ** brbg Background color by branch name ** ubg Background color by user name | | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | ** ci=HASH identify a particular version of a file and then ** track changes to that file across renames ** m=HASH Mark this particular file version. ** n=NUM Show the first NUM changes only ** name=FILENAME (Required) name of file whose history to show ** brbg Background color by branch name ** ubg Background color by user name ** from=HASH Ancestors only (not descendants) of the version of ** the file in this particular check-in. ** to=HASH If both from= and to= are supplied, only show those ** changes on the direct path between the two given ** checkins. ** showid Show RID values for debugging ** showsql Show the SQL query used to gather the data for ** the graph |
| ︙ | ︙ |
Changes to src/forum.c.
| ︙ | ︙ | |||
1049 1050 1051 1052 1053 1054 1055 |
char *zGoto;
login_check_credentials();
if( !g.perm.WrForum ){
login_needed(g.anon.WrForum);
return;
}
if( sqlite3_strglob("*edit*", g.zPath)==0 ){
| | | | 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 |
char *zGoto;
login_check_credentials();
if( !g.perm.WrForum ){
login_needed(g.anon.WrForum);
return;
}
if( sqlite3_strglob("*edit*", g.zPath)==0 ){
zGoto = mprintf("forume2?fpid=%S",PD("fpid",""));
isEdit = 1;
}else{
zGoto = mprintf("forume1");
isEdit = 0;
}
if( login_is_individual() ){
if( isEdit ){
forumedit_page();
}else{
forumnew_page();
|
| ︙ | ︙ |
Name change from src/fossil.info-diff.js to src/fossil.diff.js.
1 2 |
"use strict";
window.fossil.onPageLoad(function(){
| > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/**
diff-related JS APIs for fossil.
*/
"use strict";
window.fossil.onPageLoad(function(){
/**
Adds toggle checkboxes to each file entry in the diff views for
/info and similar pages.
*/
const D = window.fossil.dom;
const addToggle = function(diffElem){
const sib = diffElem.previousElementSibling,
btn = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
if(!sib) return;
D.append(sib,btn);
btn.addEventListener('click', function(){
diffElem.classList.toggle('hidden');
}, false);
};
document.querySelectorAll('table.diff').forEach(addToggle);
});
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
const Diff = F.diff = {
e:{/*certain cached DOM elements*/},
config: {
chunkLoadLines: (
F.config.diffContextLines * 3
/*per /chat discussion*/
) || 20,
chunkFetch: {
/* Default callack handlers for Diff.fetchArtifactChunk(),
unless overridden by options passeed to that function. */
beforesend: function(){},
aftersend: function(){},
onerror: function(e){
console.error("XHR error: ",e);
}
}
}
};
/**
Uses the /jchunk AJAX route to fetch specific lines of a given
artifact. The argument must be an Object suitable for passing as
the second argument to fossil.fetch(). Its urlParams property
must be an object with these properties:
{
name: full hash of the target file,
from: first 1-based line number of the file to fetch (inclusive),
to: last 1-based line number of the file to fetch (inclusive)
}
The fetchOpt object is NOT cloned for use by the call: it is used
as-is and may be modified by this call. Thus callers "really
should" pass a temporary object, not a long-lived one.
If fetchOpt does not define any of the (beforesend, aftersend,
onerror) callbacks, the defaults from fossil.diff.config.chunkFetch
are used, so any given client page may override those to provide
page-level default handling.
Note that onload callback is ostensibly optional but this
function is not of much use without an onload
handler. Conversely, the default onerror handler is often
customized on a per-page basis to send the error output somewhere
where the user can see it.
The response, on success, will be an array of strings, each entry
being one line from the requested artifact. If the 'to' line is
greater than the length of the file, the array will be shorter
than (to-from) lines.
The /jchunk route reports errors via JSON objects with
an "error" string property describing the problem.
This is an async operation. Returns the fossil object.
*/
Diff.fetchArtifactChunk = function(fetchOpt){
if(!fetchOpt.beforesend) fetchOpt.beforesend = Diff.config.chunkFetch.beforesend;
if(!fetchOpt.aftersend) fetchOpt.aftersend = Diff.config.chunkFetch.aftersend;
if(!fetchOpt.onerror) fetchOpt.onerror = Diff.config.chunkFetch.onerror;
fetchOpt.responseType = 'json';
return F.fetch('jchunk', fetchOpt);
};
/**
Extracts either the starting or ending line number from a
line-numer column in the given tr. isSplit must be true if tr
represents a split diff, else false. Expects its tr to be valid:
GIGO applies. Returns the starting line number if getStart, else
the ending line number. Returns the line number from the LHS file
if getLHS is true, else the RHS.
*/
const extractLineNo = function f(getLHS, getStart, tr, isSplit){
if(!f.rx){
f.rx = {
start: /^\s*(\d+)/,
end: /(\d+)\n?$/
}
}
const td = tr.querySelector('td:nth-child('+(
/* TD element with the line numbers */
getLHS ? 1 : (isSplit ? 4 : 2)
)+')');
const m = f.rx[getStart ? 'start' : 'end'].exec(td.innerText);
return m ? +m[1] : undefined/*"shouldn't happen"*/;
};
/**
Installs chunk-loading controls into TR.diffskip element tr.
Each instance corresponds to a single TR.diffskip element.
The goal is to base these controls roughly on github's, a good
example of which, for use as a model, is:
https://github.com/msteveb/autosetup/commit/235925e914a52a542
*/
const ChunkLoadControls = function(tr){
this.$fetchQueue = [];
this.e = {/*DOM elements*/
tr: tr,
table: tr.parentElement/*TBODY*/.parentElement
};
this.isSplit = this.e.table.classList.contains('splitdiff')/*else udiff*/;
this.fileHash = this.e.table.dataset.lefthash;
tr.$chunker = this /* keep GC from reaping this */;
this.pos = {
/* These line numbers correspond to the LHS file. Because the
contents are common to both sides, we have the same number
for the RHS, but need to extract those line numbers from the
neighboring TR blocks */
startLhs: +tr.dataset.startln,
endLhs: +tr.dataset.endln
};
D.clearElement(tr);
this.e.td = D.addClass(
/* Holder for our UI controls */
D.attr(D.td(tr), 'colspan', this.isSplit ? 5 : 4),
'chunkctrl'
);
this.e.msgWidget = D.addClass(D.span(), 'hidden');
this.e.btnWrapper = D.div();
D.append(this.e.td, this.e.btnWrapper);
/**
Depending on various factors, we need one or more of:
- A single button to load the initial chunk incrementally
- A single button to load all lines then remove this control
- Two buttons: one to load upwards, one to load downwards
- A single button to load the final chunk incrementally
*/
if(tr.nextElementSibling){
this.pos.next = {
startLhs: extractLineNo(true, true, tr.nextElementSibling, this.isSplit),
startRhs: extractLineNo(false, true, tr.nextElementSibling, this.isSplit)
};
}
if(tr.previousElementSibling){
this.pos.prev = {
endLhs: extractLineNo(true, false, tr.previousElementSibling, this.isSplit),
endRhs: extractLineNo(false, false, tr.previousElementSibling, this.isSplit)
};
}
let btnUp = false, btnDown = false;
/**
this.pos.next refers to the line numbers in the next TR's chunk.
this.pos.prev refers to the line numbers in the previous TR's chunk.
*/
if(this.pos.prev && this.pos.next
&& ((this.pos.next.startLhs - this.pos.prev.endLhs)
<= Diff.config.chunkLoadLines)){
/* Place a single button to load the whole block, rather
than separate up/down buttons. */
btnDown = false;
btnUp = this.createButton(this.FetchType.FillGap);
}else{
/* Figure out which chunk-load buttons to add... */
if(this.pos.prev){
btnDown = this.createButton(this.FetchType.PrevDown);
}
if(this.pos.next){
btnUp = this.createButton(this.FetchType.NextUp);
}
}
//this.e.btnUp = btnUp;
//this.e.btnDown = btnDown;
if(btnUp) D.append(this.e.btnWrapper, btnUp);
if(btnDown) D.append(this.e.btnWrapper, btnDown);
D.append(this.e.btnWrapper, this.e.msgWidget);
/* For debugging only... */
this.e.posState = D.span();
D.append(this.e.btnWrapper, this.e.posState);
this.updatePosDebug();
};
ChunkLoadControls.prototype = {
/** An "enum" of values describing the types of context
fetches/operations performed by this type. The values in this
object must not be changed without modifying all logic which
relies on their relative order. */
FetchType:{
/** Append context to the bottom of the previous diff chunk. */
PrevDown: 1,
/** Fill a complete gap between the previous/next diff chunks
or at the start of the next chunk or end of the previous
chunks. */
FillGap: 0,
/** Prepend context to the start of the next diff chunk. */
NextUp: -1,
/** Process the next queued action. */
ProcessQueue: 0x7fffffff
},
/**
Creates and returns a button element for fetching a chunk in
the given fetchType (as documented for fetchChunk()).
*/
createButton: function(fetchType){
let b;
switch(fetchType){
case this.FetchType.PrevDown:
b = D.append(
D.addClass(D.span(), 'down'),
D.span(/*glyph holder*/)
);
break;
case this.FetchType.FillGap:
b = D.append(
D.addClass(D.span(), 'up', 'down'),
D.span(/*glyph holder*/)
);
break;
case this.FetchType.NextUp:
b = D.append(
D.addClass(D.span(), 'up'),
D.span(/*glyph holder*/)
);
break;
default:
throw new Error("Internal API misuse: unexpected fetchType value "+fetchType);
}
D.addClass(b, 'jcbutton');
b.addEventListener('click', ()=>this.fetchChunk(fetchType),false);
return b;
},
updatePosDebug: function(){
if(this.e.posState){
D.clearElement(this.e.posState);
//D.append(D.clearElement(this.e.posState), JSON.stringify(this.pos));
}
return this;
},
/* Attempt to clean up resources and remove some circular references to
that GC can do the right thing. */
destroy: function(){
delete this.$fetchQueue;
D.remove(this.e.tr);
delete this.e.tr.$chunker;
delete this.e.tr;
delete this.e;
delete this.pos;
},
/**
If the gap between this.pos.endLhs/startLhs is less than or equal to
Diff.config.chunkLoadLines then this function replaces any up/down buttons
with a gap-filler button, else it's a no-op. Returns this object.
As a special case, do not apply this at the start or bottom
of the diff, only between two diff chunks.
*/
maybeReplaceButtons: function(){
if(this.pos.next && this.pos.prev
&& (this.pos.endLhs - this.pos.startLhs <= Diff.config.chunkLoadLines)){
D.clearElement(this.e.btnWrapper);
D.append(this.e.btnWrapper, this.createButton(this.FetchType.FillGap));
if( this.$fetchQueue && this.$fetchQueue.length>1 ){
this.$fetchQueue[1] = this.FetchType.FillGap;
this.$fetchQueue.length = 2;
}
}
return this;
},
/**
Callack for /jchunk responses.
*/
injectResponse: function f(fetchType/*as for fetchChunk()*/,
urlParam/*from fetchChunk()*/,
lines/*response lines*/){
if(!lines.length){
/* No more data to load */
this.destroy();
return this;
}
this.msg(false);
//console.debug("Loaded line range ",
//urlParam.from,"-",urlParam.to, "fetchType ",fetchType);
const lineno = [],
trPrev = this.e.tr.previousElementSibling,
trNext = this.e.tr.nextElementSibling,
doAppend = (
!!trPrev && fetchType>=this.FetchType.FillGap
) /* true to append to previous TR, else prepend to NEXT TR */;
const tr = doAppend ? trPrev : trNext;
const joinTr = (
this.FetchType.FillGap===fetchType && trPrev && trNext
) ? trNext : false
/* Truthy if we want to combine trPrev, the new content, and
trNext into trPrev and then remove trNext. */;
let i, td;
if(!f.convertLines){
/* Reminder: string.replaceAll() is a relatively new
JS feature, not available in some still-widely-used
browser versions. */
f.rx = [[/&/g, '&'], [/</g, '<']];
f.convertLines = function(li){
var s = li.join('\n');
f.rx.forEach((a)=>s=s.replace(a[0],a[1]));
return s + '\n';
};
}
if(1){ // LHS line numbers...
const selector = '.difflnl > pre';
td = tr.querySelector(selector);
const lnTo = Math.min(urlParam.to,
urlParam.from +
lines.length - 1/*b/c request range is inclusive*/);
for( i = urlParam.from; i <= lnTo; ++i ){
lineno.push(i);
}
const lineNoTxt = lineno.join('\n')+'\n';
const content = [td.innerHTML];
if(doAppend) content.push(lineNoTxt);
else content.unshift(lineNoTxt);
if(joinTr){
content.push(trNext.querySelector(selector).innerHTML);
}
td.innerHTML = content.join('');
}
if(1){// code block(s)...
const selector = '.difftxt > pre';
td = tr.querySelectorAll(selector);
const code = f.convertLines(lines);
let joinNdx = 0/*selector[X] index to join together*/;
td.forEach(function(e){
const content = [e.innerHTML];
if(doAppend) content.push(code);
else content.unshift(code);
if(joinTr){
content.push(trNext.querySelectorAll(selector)[joinNdx++].innerHTML)
}
e.innerHTML = content.join('');
});
}
if(1){// Add blank lines in (.diffsep>pre)
const selector = '.diffsep > pre';
td = tr.querySelector(selector);
for(i = 0; i < lineno.length; ++i) lineno[i] = '';
const blanks = lineno.join('\n')+'\n';
const content = [td.innerHTML];
if(doAppend) content.push(blanks);
else content.unshift(blanks);
if(joinTr){
content.push(trNext.querySelector(selector).innerHTML);
}
td.innerHTML = content.join('');
}
if(this.FetchType.FillGap===fetchType){
/* Closing the whole gap between two chunks or a whole gap
at the start or end of a diff. */
// RHS line numbers...
let startLnR = this.pos.prev
? this.pos.prev.endRhs+1 /* Closing the whole gap between two chunks
or end-of-file gap. */
: this.pos.next.startRhs - lines.length /* start-of-file gap */;
lineno.length = lines.length;
for( i = startLnR; i < startLnR + lines.length; ++i ){
lineno[i-startLnR] = i;
}
const selector = '.difflnr > pre';
td = tr.querySelector(selector);
const lineNoTxt = lineno.join('\n')+'\n';
lineno.length = 0;
const content = [td.innerHTML];
if(doAppend) content.push(lineNoTxt);
else content.unshift(lineNoTxt);
if(joinTr){
content.push(trNext.querySelector(selector).innerHTML);
}
td.innerHTML = content.join('');
if(joinTr) D.remove(joinTr);
Diff.checkTableWidth(true);
this.destroy();
return this;
}else if(this.FetchType.PrevDown===fetchType){
/* Append context to previous TR. */
// RHS line numbers...
let startLnR = this.pos.prev.endRhs+1;
lineno.length = lines.length;
for( i = startLnR; i < startLnR + lines.length; ++i ){
lineno[i-startLnR] = i;
}
this.pos.startLhs += lines.length;
this.pos.prev.endRhs += lines.length;
this.pos.prev.endLhs += lines.length;
const selector = '.difflnr > pre';
td = tr.querySelector(selector);
const lineNoTxt = lineno.join('\n')+'\n';
lineno.length = 0;
const content = [td.innerHTML];
if(doAppend) content.push(lineNoTxt);
else content.unshift(lineNoTxt);
td.innerHTML = content.join('');
if(lines.length < (urlParam.to - urlParam.from)){
/* No more data. */
this.destroy();
}else{
this.maybeReplaceButtons();
this.updatePosDebug();
}
Diff.checkTableWidth(true);
return this;
}else if(this.FetchType.NextUp===fetchType){
/* Prepend content to next TR. */
// RHS line numbers...
if(doAppend){
throw new Error("Internal precondition violation: doAppend is true.");
}
let startLnR = this.pos.next.startRhs - lines.length;
lineno.length = lines.length;
for( i = startLnR; i < startLnR + lines.length; ++i ){
lineno[i-startLnR] = i;
}
this.pos.endLhs -= lines.length;
this.pos.next.startRhs -= lines.length;
this.pos.next.startLhs -= lines.length;
const selector = '.difflnr > pre';
td = tr.querySelector(selector);
const lineNoTxt = lineno.join('\n')+'\n';
lineno.length = 0;
td.innerHTML = lineNoTxt + td.innerHTML;
if(this.pos.endLhs<1
|| lines.length < (urlParam.to - urlParam.from)){
/* No more data. */
this.destroy();
}else{
this.maybeReplaceButtons();
this.updatePosDebug();
}
Diff.checkTableWidth(true);
return this;
}else{
throw new Error("Unexpected 'fetchType' value.");
}
},
/**
Sets this widget's message to the given text. If the message
represents an error, the first argument must be truthy, else it
must be falsy. Returns this object.
*/
msg: function(isError,txt){
if(txt){
if(isError) D.addClass(this.e.msgWidget, 'error');
else D.removeClass(this.e.msgWidget, 'error');
D.append(
D.removeClass(D.clearElement(this.e.msgWidget), 'hidden'),
txt);
}else{
D.addClass(D.clearElement(this.e.msgWidget), 'hidden');
}
return this;
},
/**
Fetches and inserts a line chunk. fetchType is:
this.FetchType.NextUp = upwards from next chunk (this.pos.next)
this.FetchType.FillGap = the whole gap between this.pos.prev
and this.pos.next, or the whole gap before/after the
initial/final chunk in the diff.
this.FetchType.PrevDown = downwards from the previous chunk
(this.pos.prev)
Those values are set at the time this object is initialized but
one instance of this class may have 2 buttons, one each for
fetchTypes NextUp and PrevDown.
This is an async operation. While it is in transit, any calls
to this function will have no effect except (possibly) to emit
a warning. Returns this object.
*/
fetchChunk: function(fetchType){
if( !this.$fetchQueue ) return this; // HACKHACK: are we destroyed?
if( fetchType==this.FetchType.ProcessQueue ){
this.$fetchQueue.shift();
if( this.$fetchQueue.length==0 ) return this;
//console.log('fetchChunk: processing queue ...');
}
else{
this.$fetchQueue.push(fetchType);
if( this.$fetchQueue.length!=1 ) return this;
//console.log('fetchChunk: processing user input ...');
}
fetchType = this.$fetchQueue[0];
if( fetchType==this.FetchType.ProcessQueue ){
/* Unexpected! Clear queue so recovery (manual restart) is possible. */
this.$fetchQueue.length = 0;
return this;
}
/* Forewarning, this is a bit confusing: when fetching the
previous lines, we're doing so on behalf of the *next* diff
chunk (this.pos.next), and vice versa. */
if(fetchType===this.FetchType.NextUp && !this.pos.next
|| fetchType===this.FetchType.PrevDown && !this.pos.prev){
console.error("Attempt to fetch diff lines but don't have any.");
return this;
}
this.msg(false,"Fetching diff chunk...");
const self = this;
const fOpt = {
urlParams:{
name: this.fileHash, from: 0, to: 0
},
aftersend: ()=>this.msg(false),
onload: function(list){
self.injectResponse(fetchType,up,list);
if( !self.$fetchQueue || self.$fetchQueue.length==0 ) return;
/* Keep queue length > 0, or clicks stalled during (unusually lengthy)
injectResponse() may sneak in as soon as setTimeout() allows, find
an empty queue, and therefore start over with queue processing. */
self.$fetchQueue[0] = self.FetchType.ProcessQueue;
setTimeout(self.fetchChunk.bind(self,self.FetchType.ProcessQueue));
}
};
const up = fOpt.urlParams;
if(fetchType===this.FetchType.FillGap){
/* Easiest case: filling a whole gap. */
up.from = this.pos.startLhs;
up.to = this.pos.endLhs;
}else if(this.FetchType.PrevDown===fetchType){
/* Append to previous TR. */
if(!this.pos.prev){
console.error("Attempt to fetch next diff lines but don't have any.");
return this;
}
up.from = this.pos.prev.endLhs + 1;
up.to = up.from +
Diff.config.chunkLoadLines - 1/*b/c request range is inclusive*/;
if( this.pos.next && this.pos.next.startLhs <= up.to ){
up.to = this.pos.next.startLhs - 1;
fetchType = this.FetchType.FillGap;
}
}else{
/* Prepend to next TR */
if(!this.pos.next){
console.error("Attempt to fetch previous diff lines but don't have any.");
return this;
}
up.to = this.pos.next.startLhs - 1;
up.from = Math.max(1, up.to - Diff.config.chunkLoadLines + 1);
if( this.pos.prev && this.pos.prev.endLhs >= up.from ){
up.from = this.pos.prev.endLhs + 1;
fetchType = this.FetchType.FillGap;
}
}
//console.debug("fetchChunk(",fetchType,")",up);
fOpt.onerror = function(err){
if(self.e/*guard against a late-stage onerror() call*/){
self.msg(true,err.message);
self.$fetchQueue.length = 0;
}else{
Diff.config.chunkFetch.onerror.call(this,err);
}
};
Diff.fetchArtifactChunk(fOpt);
return this;
}
};
/**
Adds context-loading buttons to one or more tables. The argument
may be a forEach-capable list of diff table elements, a query
selector string matching 0 or more diff tables, or falsy, in
which case all relevant diff tables are set up. It tags each
table it processes to that it will not be processed multiple
times by subsequent calls to this function.
Note that this only works for diffs which have been marked up
with certain state, namely table.dataset.lefthash and TR
entries which hold state related to browsing context.
*/
Diff.setupDiffContextLoad = function(tables){
if('string'===typeof tables){
tables = document.querySelectorAll(tables);
}else if(!tables){
tables = document.querySelectorAll('table.diff[data-lefthash]:not(.diffskipped)');
}
/* Potential performance-related TODO: instead of installing all
of these at once, install them as the corresponding TR is
scrolled into view. */
tables.forEach(function(table){
if(table.classList.contains('diffskipped') || !table.dataset.lefthash) return;
D.addClass(table, 'diffskipped'/*avoid processing these more than once */);
table.querySelectorAll('tr.diffskip[data-startln]').forEach(function(tr){
new ChunkLoadControls(D.addClass(tr, 'jchunk'));
});
});
return F;
};
Diff.setupDiffContextLoad();
});
/* Refinements to the display of unified and side-by-side diffs.
**
** In all cases, the table columns tagged with "difftxt" are expanded,
** where possible, to fill the width of the screen.
**
** For a side-by-side diff, if either column is two wide to fit on the
** display, scrollbars are added. The scrollbars are linked, so that
** both sides scroll together. Left and right arrows also scroll.
*/
window.fossil.onPageLoad(function(){
const SCROLL_LEN = 25;
const F = window.fossil, D = F.dom, Diff = F.diff;
var lastWidth;
Diff.checkTableWidth = function f(force){
if(undefined === f.contentNode){
f.contentNode = document.querySelector('div.content');
}
force = true;
const parentCS = window.getComputedStyle(f.contentNode);
const parentWidth = (
//document.body.clientWidth;
//parentCS.width;
f.contentNode.clientWidth
- parseFloat(parentCS.marginLeft) - parseFloat(parentCS.marginRight)
);
if( !force && parentWidth===lastWidth ) return this;
lastWidth = parentWidth;
let w = lastWidth*0.5 - 100;
//console.debug( "w = ",w,", lastWidth =",lastWidth," body = ",document.body.clientWidth);
if(force || !f.colsL){
f.colsL = document.querySelectorAll('td.difftxtl pre');
}
f.colsL.forEach(function(e){
e.style.width = w + "px";
e.style.maxWidth = w + "px";
});
if(force || !f.colsR){
f.colsR = document.querySelectorAll('td.difftxtr pre');
}
f.colsR.forEach(function(e){
e.style.width = w + "px";
e.style.maxWidth = w + "px";
});
if(0){ // seems to be unnecessary
if(!f.allDiffs){
f.allDiffs = document.querySelectorAll('table.diff');
}
w = lastWidth;
f.allDiffs.forEach(function f(e){
if(0 && !f.$){
f.$ = e.getClientRects()[0];
console.debug("diff table w =",w," f.$x",f.$);
w - 2*f.$.x /* left margin (assume right==left, for simplicity) */;
}
e.style.maxWidth = w + "px";
});
//console.debug("checkTableWidth(",force,") lastWidth =",lastWidth);
}
return this;
};
const scrollLeft = function(event){
//console.debug("scrollLeft",this,event);
const table = this.parentElement/*TD*/.parentElement/*TR*/.
parentElement/*TBODY*/.parentElement/*TABLE*/;
table.$txtPres.forEach((e)=>(e===this) ? 1 : (e.scrollLeft = this.scrollLeft));
return false;
};
Diff.initTableDiff = function f(diff){
if(!diff){
let i, diffs = document.querySelectorAll('table.splitdiff');
for(i=0; i<diffs.length; ++i){
f.call(this, diffs[i]);
}
return this;
}
diff.$txtCols = diff.querySelectorAll('td.difftxt');
diff.$txtPres = diff.querySelectorAll('td.difftxt pre');
var width = 0;
diff.$txtPres.forEach(function(e){
if(width < e.scrollWidth) width = e.scrollWidth;
});
//console.debug("diff.$txtPres =",diff.$txtPres);
diff.$txtCols.forEach((e)=>e.style.width = width + 'px');
diff.$txtPres.forEach(function(e){
e.style.maxWidth = width + 'px';
e.style.width = width + 'px';
if(!e.classList.contains('scroller')){
D.addClass(e, 'scroller');
e.addEventListener('scroll', scrollLeft, false);
}
});
diff.tabIndex = 0;
if(!diff.classList.contains('scroller')){
D.addClass(diff, 'scroller');
diff.addEventListener('keydown', function(e){
e = e || event;
const len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode];
if( !len ) return;
this.$txtPres[0].scrollLeft += len;
/* ^^^ bug: if there is a 2nd column and it has a scrollbar
but txtPres[0] does not, no scrolling happens here. We need
to find the widest of txtPres and scroll that one. Example:
Checkin a7fbefee38a1c522 file diff.c */
return false;
}, false);
}
return this;
}
window.fossil.page.tweakSbsDiffs = function(){
document.querySelectorAll('table.splitdiff').forEach((e)=>Diff.initTableDiff(e));
Diff.checkTableWidth();
};
Diff.initTableDiff().checkTableWidth();
window.addEventListener('resize', ()=>Diff.checkTableWidth());
}, false);
|
Changes to src/fossil.dom.js.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
dom.pre = dom.createElemFactory('pre');
dom.header = dom.createElemFactory('header');
dom.footer = dom.createElemFactory('footer');
dom.section = dom.createElemFactory('section');
dom.span = dom.createElemFactory('span');
dom.strong = dom.createElemFactory('strong');
dom.em = dom.createElemFactory('em');
/**
Returns a LABEL element. If passed an argument,
it must be an id or an HTMLElement with an id,
and that id is set as the 'for' attribute of the
label. If passed 2 arguments, the 2nd is text or
a DOM element to append to the label.
*/
| > > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
dom.pre = dom.createElemFactory('pre');
dom.header = dom.createElemFactory('header');
dom.footer = dom.createElemFactory('footer');
dom.section = dom.createElemFactory('section');
dom.span = dom.createElemFactory('span');
dom.strong = dom.createElemFactory('strong');
dom.em = dom.createElemFactory('em');
dom.ins = dom.createElemFactory('ins');
dom.del = dom.createElemFactory('del');
/**
Returns a LABEL element. If passed an argument,
it must be an id or an HTMLElement with an id,
and that id is set as the 'for' attribute of the
label. If passed 2 arguments, the 2nd is text or
a DOM element to append to the label.
*/
|
| ︙ | ︙ | |||
115 116 117 118 119 120 121 |
dom.hr = dom.createElemFactory('hr');
dom.br = dom.createElemFactory('br');
/** Returns a new TEXT node which contains the text of all of the
arguments appended together. */
dom.text = function(/*...*/){
return document.createTextNode(argsToArray(arguments).join(''));
};
| > > | > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
dom.hr = dom.createElemFactory('hr');
dom.br = dom.createElemFactory('br');
/** Returns a new TEXT node which contains the text of all of the
arguments appended together. */
dom.text = function(/*...*/){
return document.createTextNode(argsToArray(arguments).join(''));
};
/** Returns a new Button element with the given optional
label and on-click event listener function. */
dom.button = function(label,callback){
const b = this.create('button');
if(label) b.appendChild(this.text(label));
if('function' === typeof callback){
b.addEventListener('click', callback, false);
}
return b;
};
/**
Returns a TEXTAREA element.
Usages:
|
| ︙ | ︙ | |||
427 428 429 430 431 432 433 |
const a = argsToArray(arguments);
a.unshift('remove');
return domAddRemoveClass.apply(this, a);
};
/**
Toggles CSS class c on e (a single element for forEach-capable
| | | 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
const a = argsToArray(arguments);
a.unshift('remove');
return domAddRemoveClass.apply(this, a);
};
/**
Toggles CSS class c on e (a single element for forEach-capable
collection of elements). Returns its first argument.
*/
dom.toggleClass = function f(e,c){
if(e.forEach){
e.forEach((x)=>x.classList.toggle(c));
}else{
e.classList.toggle(c);
}
|
| ︙ | ︙ | |||
673 674 675 676 677 678 679 680 681 682 683 684 685 686 |
dom.flashOnce.defaultTimeMs = 400;
/**
A DOM event handler which simply passes event.target
to dom.flashOnce().
*/
dom.flashOnce.eventHandler = (event)=>dom.flashOnce(event.target)
/**
Attempts to copy the given text to the system clipboard. Returns
true if it succeeds, else false.
*/
dom.copyTextToClipboard = function(text){
if( window.clipboardData && window.clipboardData.setData ){
window.clipboardData.setData('Text',text);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
dom.flashOnce.defaultTimeMs = 400;
/**
A DOM event handler which simply passes event.target
to dom.flashOnce().
*/
dom.flashOnce.eventHandler = (event)=>dom.flashOnce(event.target)
/**
This variant of flashOnce() flashes the element e n times
for a duration of howLongMs milliseconds then calls the
afterFlashCallback() callback. It may also be called with 2
or 3 arguments, in which case:
2 arguments: default flash time and no callback.
3 arguments: 3rd may be a flash delay time or a callback
function.
Returns this object but the flashing is asynchronous.
Depending on system load and related factors, a multi-flash
animation might stutter and look suboptimal.
*/
dom.flashNTimes = function(e,n,howLongMs,afterFlashCallback){
const args = argsToArray(arguments);
args.splice(1,1);
if(arguments.length===3 && 'function'===typeof howLongMs){
afterFlashCallback = howLongMs;
howLongMs = args[1] = this.flashOnce.defaultTimeMs;
}else if(arguments.length<3){
args[1] = this.flashOnce.defaultTimeMs;
}
n = +n;
const self = this;
const cb = args[2] = function f(){
if(--n){
setTimeout(()=>self.flashOnce(e, howLongMs, f),
howLongMs+(howLongMs*0.1)/*we need a slight gap here*/);
}else if(afterFlashCallback){
afterFlashCallback();
}
};
this.flashOnce.apply(this, args);
return this;
};
/**
Adds the given CSS class or array of CSS classes to the given
element or forEach-capable list of elements for howLongMs, then
removes it. If afterCallack is a function, it is called after the
CSS class is removed from all elements. If called with 3
arguments and the 3rd is a function, the 3rd is treated as a
callback and the default time (addClassBriefly.defaultTimeMs) is
used. If called with only 2 arguments, a time of
addClassBriefly.defaultTimeMs is used.
Passing a value of 0 for howLongMs causes the default value
to be applied.
Returns this object but the CSS removal is asynchronous.
*/
dom.addClassBriefly = function f(e, className, howLongMs, afterCallback){
if(arguments.length<4 && 'function'===typeof howLongMs){
afterCallback = howLongMs;
howLongMs = f.defaultTimeMs;
}else if(arguments.length<3 || !+howLongMs){
howLongMs = f.defaultTimeMs;
}
this.addClass(e, className);
setTimeout(function(){
dom.removeClass(e, className);
if(afterCallback) afterCallback();
}, howLongMs);
return this;
};
dom.addClassBriefly.defaultTimeMs = 1000;
/**
Attempts to copy the given text to the system clipboard. Returns
true if it succeeds, else false.
*/
dom.copyTextToClipboard = function(text){
if( window.clipboardData && window.clipboardData.setData ){
window.clipboardData.setData('Text',text);
|
| ︙ | ︙ |
Changes to src/fossil.fetch.js.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | - onload: callback(responseData) (default = output response to the console). In the context of the callback, the options object is "this", noting that this call may have amended the options object with state other than what the caller provided. - onerror: callback(Error object) (default = output error message to console.error() and fossil.error()). Triggered if the request | | | > | > > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | - onload: callback(responseData) (default = output response to the console). In the context of the callback, the options object is "this", noting that this call may have amended the options object with state other than what the caller provided. - onerror: callback(Error object) (default = output error message to console.error() and fossil.error()). Triggered if the request generates any response other than HTTP 200, suffers a connection error or timeout while awaiting a response, or if the onload() handler throws an exception. In the context of the callback, the options object is "this". Note that this function is intended to be used solely for error reporting, not error recovery. Because onerror() may be called if onload() throws, it is up to the caller to ensure that their onerror() callback references only state which is valid in such a case. - method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE! - payload: anything acceptable by XHR2.send(ARG) (DOMString, Document, FormData, Blob, File, ArrayBuffer), or a plain object or array, either of which gets JSON.stringify()'d. If payload is set then the method is automatically set to 'POST'. By default XHR2 |
| ︙ | ︙ |
Name change from src/chat.js to src/fossil.page.chat.js.
1 2 3 4 | /** This file contains the client-side implementation of fossil's /chat application. */ | | | 1 2 3 4 5 6 7 8 9 10 11 12 |
/**
This file contains the client-side implementation of fossil's /chat
application.
*/
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
const E1 = function(selector){
const e = document.querySelector(selector);
if(!e) throw new Error("missing required DOM element: "+selector);
return e;
};
/**
|
| ︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
const overlapsElemView = function(e,v) {
const r1 = e.getBoundingClientRect(),
r2 = v.getBoundingClientRect();
if(r1.top<=r2.bottom && r1.top>=r2.top) return true;
else if(r1.bottom<=r2.bottom && r1.bottom>=r2.top) return true;
return false;
};
(function(){
let dbg = document.querySelector('#debugMsg');
if(dbg){
/* This can inadvertently influence our flexbox layouts, so move
it out of the way. */
D.append(document.body,dbg);
}
})();
| > > > > > > > > > > > > > > > > > > > > > > | | | > > > | | | | | | | < | > | > > > | | | | | > > < > | > | < | | | < | > > > > > > > > > > > > > > > > > > > > | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 |
const overlapsElemView = function(e,v) {
const r1 = e.getBoundingClientRect(),
r2 = v.getBoundingClientRect();
if(r1.top<=r2.bottom && r1.top>=r2.top) return true;
else if(r1.bottom<=r2.bottom && r1.bottom>=r2.top) return true;
return false;
};
const addAnchorTargetBlank = (e)=>D.attr(e, 'target','_blank');
/**
Returns an almost-ISO8601 form of Date object d.
*/
const iso8601ish = function(d){
return d.toISOString()
.replace('T',' ').replace(/\.\d+/,'')
.replace('Z', ' zulu');
};
/** Returns the local time string of Date object d, defaulting
to the current time. */
const localTimeString = function ff(d){
d || (d = new Date());
return [
d.getFullYear(),'-',pad2(d.getMonth()+1/*sigh*/),
'-',pad2(d.getDate()),
' ',pad2(d.getHours()),':',pad2(d.getMinutes()),
':',pad2(d.getSeconds())
].join('');
};
(function(){
let dbg = document.querySelector('#debugMsg');
if(dbg){
/* This can inadvertently influence our flexbox layouts, so move
it out of the way. */
D.append(document.body,dbg);
}
})();
const ForceResizeKludge = (function(){
/* Workaround for Safari mayhem regarding use of vh CSS units....
We tried to use vh units to set the content area size for the
chat layout, but Safari chokes on that, so we calculate that
height here: 85% when in "normal" mode and 95% in chat-only
mode. Larger than ~95% is too big for Firefox on Android,
causing the input area to move off-screen.
While we're here, we also use this to cap the max-height
of the input field so that pasting huge text does not scroll
the upper area of the input widget off-screen. */
const elemsToCount = [
document.querySelector('body > div.header'),
document.querySelector('body > div.mainmenu'),
document.querySelector('body > #hbdrop'),
document.querySelector('body > div.footer')
];
const contentArea = E1('div.content');
const bcl = document.body.classList;
const resized = function f(){
if(f.$disabled) return;
const wh = window.innerHeight,
com = bcl.contains('chat-only-mode');
var ht;
var extra = 0;
if(com){
ht = wh;
}else{
elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
ht = wh - extra;
}
f.chat.e.inputField.style.maxHeight = (ht/2)+"px";
/* ^^^^ this is a middle ground between having no size cap
on the input field and having a fixed arbitrary cap. */;
contentArea.style.height =
contentArea.style.maxHeight = [
"calc(", (ht>=100 ? ht : 100), "px",
" - 0.75em"/*fudge value*/,")"
/* ^^^^ hypothetically not needed, but both Chrome/FF on
Linux will force scrollbars on the body if this value is
too small (<0.75em in my tests). */
].join('');
if(false){
console.debug("resized.",wh, extra, ht,
window.getComputedStyle(contentArea).maxHeight,
contentArea);
console.debug("Set input max height to: ",
f.chat.e.inputField.style.maxHeight);
}
};
var doit;
window.addEventListener('resize',function(ev){
clearTimeout(doit);
doit = setTimeout(resized, 100);
}, false);
return resized;
})();
ForceResizeKludge.$disabled = true/*gets deleted when setup is finished*/;
fossil.FRK = ForceResizeKludge/*for debugging*/;
const Chat = ForceResizeKludge.chat = (function(){
const cs = {
verboseErrors: false /* if true then certain, mostly extraneous,
error messages may be sent to the console. */,
e:{/*map of certain DOM elements.*/
messageInjectPoint: E1('#message-inject-point'),
pageTitle: E1('head title'),
loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
inputWrapper: E1("#chat-input-area"),
inputLine: E1('#chat-input-line'),
fileSelectWrapper: E1('#chat-input-file-area'),
viewMessages: E1('#chat-messages-wrapper'),
btnSubmit: E1('#chat-button-submit'),
btnAttach: E1('#chat-button-attach'),
inputField: E1('#chat-input-field'),
inputFile: E1('#chat-input-file'),
contentDiv: E1('div.content'),
viewConfig: E1('#chat-config'),
viewPreview: E1('#chat-preview'),
previewContent: E1('#chat-preview-content'),
btnPreview: E1('#chat-button-preview'),
views: document.querySelectorAll('.chat-view'),
activeUserListWrapper: E1('#chat-user-list-wrapper'),
activeUserList: E1('#chat-user-list')
},
me: F.user.name,
mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
pageIsActive: 'visible'===document.visibilityState,
changesSincePageHidden: 0,
notificationBubbleColor: 'white',
totalMessageCount: 0, // total # of inbound messages
//! Number of messages to load for the history buttons
loadMessageCount: Math.abs(F.config.chat.initSize || 20),
ajaxInflight: 0,
usersLastSeen:{
/* Map of user names to their most recent message time
(JS Date object). Only messages received by the chat client
are considered. */
/* Reminder: to convert a Julian time J to JS:
new Date((J - 2440587.5) * 86400000) */
},
filterState:{
activeUser: undefined,
match: function(uname){
return this.activeUser===uname || !this.activeUser;
}
},
/** Gets (no args) or sets (1 arg) the current input text field value,
taking into account single- vs multi-line input. The getter returns
a string and the setter returns this object. */
inputValue: function(){
const e = this.inputElement();
if(arguments.length){
e.innerText = arguments[0];
return this;
}
return e.innerText;
},
/** Asks the current user input field to take focus. Returns this. */
inputFocus: function(){
this.inputElement().focus();
return this;
},
/** Returns the current message input element. */
inputElement: function(){
return this.e.inputField;
},
/** Enables (if yes is truthy) or disables all elements in
* this.disableDuringAjax. */
enableAjaxComponents: function(yes){
D[yes ? 'enable' : 'disable'](this.disableDuringAjax);
return this;
},
|
| ︙ | ︙ | |||
231 232 233 234 235 236 237 |
return this;
},
/* Injects DOM element e as a new row in the chat, at the oldest
end of the list if atEnd is truthy, else at the newest end of
the list. */
injectMessageElem: function f(e, atEnd){
const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
| | > > > | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
return this;
},
/* Injects DOM element e as a new row in the chat, at the oldest
end of the list if atEnd is truthy, else at the newest end of
the list. */
injectMessageElem: function f(e, atEnd){
const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
holder = this.e.viewMessages,
prevMessage = this.e.newestMessage;
if(!this.filterState.match(e.dataset.xfrom)){
e.classList.add('hidden');
}
if(atEnd){
const fe = mip.nextElementSibling;
if(fe) mip.parentNode.insertBefore(e, fe);
else D.append(mip.parentNode, e);
}else{
D.append(holder,e);
this.e.newestMessage = e;
|
| ︙ | ︙ | |||
327 328 329 330 331 332 333 |
},
/** Tries to scroll the message area to...
<0 = top of the message list, >0 = bottom of the message list,
0 == the newest message (normally the same position as >1).
*/
scrollMessagesTo: function(where){
if(where<0){
| | | | > | > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > | | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < | 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 |
},
/** Tries to scroll the message area to...
<0 = top of the message list, >0 = bottom of the message list,
0 == the newest message (normally the same position as >1).
*/
scrollMessagesTo: function(where){
if(where<0){
Chat.e.viewMessages.scrollTop = 0;
}else if(where>0){
Chat.e.viewMessages.scrollTop = Chat.e.viewMessages.scrollHeight;
}else if(Chat.e.newestMessage){
Chat.e.newestMessage.scrollIntoView(false);
}
},
toggleChatOnlyMode: function(){
return this.chatOnlyMode(!this.isChatOnlyMode());
},
messageIsInView: function(e){
return e ? overlapsElemView(e, this.e.viewMessages) : false;
},
settings:{
get: (k,dflt)=>F.storage.get(k,dflt),
getBool: (k,dflt)=>F.storage.getBool(k,dflt),
set: function(k,v){
F.storage.set(k,v);
F.page.dispatchEvent('chat-setting',{key: k, value: v});
},
/* Toggles the boolean setting specified by k. Returns the
new value.*/
toggle: function(k){
const v = this.getBool(k);
this.set(k, !v);
return !v;
},
addListener: function(setting, f){
F.page.addEventListener('chat-setting', function(ev){
if(ev.detail.key===setting) f(ev.detail);
}, false);
},
/* Default values of settings. These are used for intializing
the setting event listeners and config view UI. */
defaults:{
/* When on, inbound images are displayed inlined, else as a
link to download the image. */
"images-inline": !!F.config.chat.imagesInline,
/* When on, ctrl-enter sends messages, else enter and
ctrl-enter both send them. */
"edit-ctrl-send": false,
/* When on, the edit field starts as a single line and
expands as the user types, and the relevant buttons are
laid out in a compact form. When off, the edit field and
buttons are larger. */
"edit-compact-mode": true,
/* When on, sets the font-family on messages and the edit
field to monospace. */
"monospace-messages": false,
/* When on, non-chat UI elements (page header/footer) are
hidden */
"chat-only-mode": false,
/* When set to a URI, it is assumed to be an audio file,
which gets played when new messages arrive. When true,
the first entry in the audio file selection list will be
used. */
"audible-alert": true,
/* When on, show the list of "active" users - those from
whom we have messages in the currently-loaded history
(noting that deletions are also messages). */
"active-user-list": false,
/* When on, the [active-user-list] setting includes the
timestamp of each user's most recent message. */
"active-user-list-timestamps": false,
/* When on, the [audible-alert] is played for one's own
messages, else it is only played for other users'
messages. */
"alert-own-messages": false
}
},
/** Plays a new-message notification sound IF the audible-alert
setting is true, else this is a no-op. Returns this.
*/
playNewMessageSound: function f(){
if(f.uri){
try{
if(!f.audio) f.audio = new Audio(f.uri);
f.audio.currentTime = 0;
f.audio.play();
}catch(e){
console.error("Audio playblack failed.", f.uri, e);
}
}
return this;
},
/**
Sets the current new-message audio alert URI (must be a
repository-relative path which responds with an audio
file). Pass a falsy value to disable audio alerts. Returns
this.
*/
setNewMessageSound: function f(uri){
delete this.playNewMessageSound.audio;
this.playNewMessageSound.uri = uri;
this.settings.set('audible-alert', uri);
return this;
},
/**
Expects e to be one of the elements in this.e.views.
The 'hidden' class is removed from e and added to
all other elements in that list. Returns e.
*/
setCurrentView: function(e){
if(e===this.e.currentView){
return e;
}
this.e.views.forEach(function(E){
if(e!==E) D.addClass(E,'hidden');
});
this.e.currentView = e;
if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow();
D.removeClass(e,'hidden');
this.animate(this.e.currentView, 'anim-fade-in-fast');
return this.e.currentView;
},
/**
Updates the "active user list" view if we are not currently
batch-loading messages and if the active user list UI element
is active.
*/
updateActiveUserList: function callee(){
if(this._isBatchLoading
|| this.e.activeUserListWrapper.classList.contains('hidden')){
return this;
}else if(!callee.sortUsersSeen){
/** Array.sort() callback. Expects an array of user names and
sorts them in last-received message order (newest first). */
const self = this;
callee.sortUsersSeen = function(l,r){
l = self.usersLastSeen[l];
r = self.usersLastSeen[r];
if(l && r) return r - l;
else if(l) return -1;
else if(r) return 1;
else return 0;
};
callee.addUserElem = function(u){
const uSpan = D.addClass(D.span(), 'chat-user');
const uDate = self.usersLastSeen[u];
if(self.filterState.activeUser===u){
uSpan.classList.add('selected');
}
uSpan.dataset.uname = u;
D.append(uSpan, u, "\n",
D.append(
D.addClass(D.span(),'timestamp'),
localTimeString(uDate)//.substr(5/*chop off year*/)
));
if(uDate.$uColor){
uSpan.style.backgroundColor = uDate.$uColor;
}
D.append(self.e.activeUserList, uSpan);
};
}
//D.clearElement(this.e.activeUserList);
D.remove(this.e.activeUserList.querySelectorAll('.chat-user'));
Object.keys(this.usersLastSeen).sort(
callee.sortUsersSeen
).forEach(callee.addUserElem);
return this;
},
/** Show or hide the active user list. Returns this object. */
showActiveUserList: function(yes){
if(0===arguments.length) yes = true;
this.e.activeUserListWrapper.classList[
yes ? 'remove' : 'add'
]('hidden');
D.removeClass(Chat.e.activeUserListWrapper, 'collapsed');
if(Chat.e.activeUserListWrapper.classList.contains('hidden')){
/* When hiding this element, undo all filtering */
Chat.setUserFilter(false);
/*Ideally we'd scroll the final message into view
now, but because viewMessages is currently hidden behind
viewConfig, scrolling is a no-op. */
Chat.scrollMessagesTo(1);
}else{
Chat.updateActiveUserList();
Chat.animate(Chat.e.activeUserListWrapper, 'anim-flip-v');
}
return this;
},
showActiveUserTimestamps: function(yes){
if(0===arguments.length) yes = true;
this.e.activeUserList.classList[yes ? 'add' : 'remove']('timestamps');
return this;
},
/**
Applies user name filter to all current messages, or clears
the filter if uname is falsy.
*/
setUserFilter: function(uname){
this.filterState.activeUser = uname;
const mw = this.e.viewMessages.querySelectorAll('.message-widget');
const self = this;
let eLast;
if(!uname){
D.removeClass(Chat.e.viewMessages.querySelectorAll('.message-widget.hidden'),
'hidden');
}else{
mw.forEach(function(w){
if(self.filterState.match(w.dataset.xfrom)){
w.classList.remove('hidden');
eLast = w;
}else{
w.classList.add('hidden');
}
});
}
if(eLast) eLast.scrollIntoView(false);
else this.scrollMessagesTo(1);
cs.e.activeUserList.querySelectorAll('.chat-user').forEach(function(e){
e.classList[uname===e.dataset.uname ? 'add' : 'remove']('selected');
});
return this;
},
/**
If animations are enabled, passes its arguments
to D.addClassBriefly(), else this is a no-op.
If cb is a function, it is called after the
CSS class is removed. Returns this object;
*/
animate: function f(e,a,cb){
if(!f.$disabled){
D.addClassBriefly(e, a, 0, cb);
}
return this;
}
};
if(D.attr(cs.e.inputField,'contenteditable','plaintext-only').isContentEditable){
cs.$browserHasPlaintextOnly = true;
}else{
/* Only the Chrome family supports contenteditable=plaintext-only */
cs.$browserHasPlaintextOnly = false;
D.attr(cs.e.inputField,'contenteditable','true');
}
cs.animate.$disabled = true;
F.fetch.beforesend = ()=>cs.ajaxStart();
F.fetch.aftersend = ()=>cs.ajaxEnd();
cs.pageTitleOrig = cs.e.pageTitle.innerText;
const qs = (e)=>document.querySelector(e);
const argsToArray = function(args){
return Array.prototype.slice.call(args,0);
};
/**
Reports an error via console.error() and as a toast message.
|
| ︙ | ︙ | |||
482 483 484 485 486 487 488 489 490 491 492 493 494 495 |
this.fetchLastMessageElem();
}
F.toast.message("Deleted message "+id+".");
}
return !!e;
};
/** Given a .message-row element, this function returns whethe the
current user may, at least hypothetically, delete the message
globally. A user may always delete a local copy of a
post. The server may trump this, e.g. if the login has been
cancelled after this page was loaded.
*/
cs.userMayDelete = function(eMsg){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
this.fetchLastMessageElem();
}
F.toast.message("Deleted message "+id+".");
}
return !!e;
};
/**
Toggles the given message between its parsed and plain-text
representations. It requires a server round-trip to collect the
plain-text form but caches it for subsequent toggles.
Expects the ID of a currently-loaded message or a
message-widget DOM elment from which it can extract an id.
This is an aync operation the first time it's passed a given
message and synchronous on subsequent calls for that
message. It is a no-op if id does not resolve to a loaded
message.
*/
cs.toggleTextMode = function(id){
var e;
if(id instanceof HTMLElement){
e = id;
id = e.dataset.msgid;
}else{
e = this.getMessageElemById(id);
}
if(!e || !id) return false;
else if(e.$isToggling) return;
e.$isToggling = true;
const content = e.querySelector('.message-widget-content');
if(!content.$elems){
content.$elems = [
content.firstElementChild, // parsed elem
undefined // plaintext elem
];
}else if(content.$elems[1]){
// We have both content types. Simply toggle them.
const child = (
content.firstElementChild===content.$elems[0]
? content.$elems[1]
: content.$elems[0]
);
delete e.$isToggling;
D.append(D.clearElement(content), child);
return;
}
// We need to fetch the plain-text version...
const self = this;
F.fetch('chat-fetch-one',{
urlParams:{ name: id, raw: true},
responseType: 'json',
onload: function(msg){
content.$elems[1] = D.append(D.pre(),msg.xmsg);
self.toggleTextMode(e);
},
aftersend:function(){
delete e.$isToggling;
Chat.ajaxEnd();
}
});
return true;
};
/** Given a .message-row element, this function returns whethe the
current user may, at least hypothetically, delete the message
globally. A user may always delete a local copy of a
post. The server may trump this, e.g. if the login has been
cancelled after this page was loaded.
*/
cs.userMayDelete = function(eMsg){
|
| ︙ | ︙ | |||
542 543 544 545 546 547 548 |
this.deleteMessageElem(id);
}
};
document.addEventListener('visibilitychange', function(ev){
cs.pageIsActive = ('visible' === document.visibilityState);
if(cs.pageIsActive){
cs.e.pageTitle.innerText = cs.pageTitleOrig;
| > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 |
this.deleteMessageElem(id);
}
};
document.addEventListener('visibilitychange', function(ev){
cs.pageIsActive = ('visible' === document.visibilityState);
if(cs.pageIsActive){
cs.e.pageTitle.innerText = cs.pageTitleOrig;
if(document.activeElement!==cs.inputElement()){
/* An attempt to resolve usability problem reported by Joe
M. where the Pale Moon browser is giving input focus to
the Preview button. The down-side of this is that it will
deselect any text which was previously selected on this
page. This also, unfortunately, places the focus at the
start of the element, rather than the last cursor position
(like a textarea would). */
setTimeout(()=>cs.inputFocus(), 0);
}
}
}, true);
cs.setCurrentView(cs.e.viewMessages);
cs.e.activeUserList.addEventListener('click', function f(ev){
/* Filter messages on a user clicked in activeUserList */
ev.stopPropagation();
ev.preventDefault();
let eUser = ev.target;
while(eUser!==this && !eUser.classList.contains('chat-user')){
eUser = eUser.parentNode;
}
if(eUser==this || !eUser) return false;
const uname = eUser.dataset.uname;
let eLast;
cs.setCurrentView(cs.e.viewMessages);
if(eUser.classList.contains('selected')){
/* If curently selected, toggle filter off */
eUser.classList.remove('selected');
cs.setUserFilter(false);
delete f.$eSelected;
}else{
if(f.$eSelected) f.$eSelected.classList.remove('selected');
f.$eSelected = eUser;
eUser.classList.add('selected');
cs.setUserFilter(uname);
}
return false;
}, false);
return cs;
})()/*Chat initialization*/;
/**
Custom widget type for rendering messages (one message per
instance). These are modelled after FIELDSET elements but we
don't use FIELDSET because of cross-browser inconsistencies in
features of the FIELDSET/LEGEND combination, e.g. inability to
align legends via CSS in Firefox and clicking-related
deficiencies in Safari.
*/
Chat.MessageWidget = (function(){
/**
Constructor. If passed an argument, it is passed to
this.setMessage() after initialization.
*/
const cf = function(){
this.e = {
body: D.addClass(D.div(), 'message-widget'),
tab: D.addClass(D.div(), 'message-widget-tab'),
content: D.addClass(D.div(), 'message-widget-content')
};
D.append(this.e.body, this.e.tab, this.e.content);
this.e.tab.setAttribute('role', 'button');
if(arguments.length){
this.setMessage(arguments[0]);
}
|
| ︙ | ︙ | |||
591 592 593 594 595 596 597 |
//d.getFullYear(),'-',pad2(d.getMonth()+1/*sigh*/),
//'-',pad2(d.getDate()), ' ',
d.getHours(),":",
(d.getMinutes()+100).toString().slice(1,3),
' ', dowMap[d.getDay()]
].join('');
};
| < < < < < < < < < < < | 864 865 866 867 868 869 870 871 872 873 874 875 876 877 |
//d.getFullYear(),'-',pad2(d.getMonth()+1/*sigh*/),
//'-',pad2(d.getDate()), ' ',
d.getHours(),":",
(d.getMinutes()+100).toString().slice(1,3),
' ', dowMap[d.getDay()]
].join('');
};
cf.prototype = {
scrollIntoView: function(){
this.e.content.scrollIntoView();
},
setMessage: function(m){
const ds = this.e.body.dataset;
ds.timestamp = m.mtime;
|
| ︙ | ︙ | |||
625 626 627 628 629 630 631 |
}
const d = new Date(m.mtime);
D.clearElement(this.e.tab);
var contentTarget = this.e.content;
var eXFrom /* element holding xfrom name */;
if(m.xfrom){
eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
| | | | | | 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 |
}
const d = new Date(m.mtime);
D.clearElement(this.e.tab);
var contentTarget = this.e.content;
var eXFrom /* element holding xfrom name */;
if(m.xfrom){
eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
const wrapper = D.append(
D.span(), eXFrom,
D.text(" #",(m.msgid||'???'),' @ ',theTime(d)))
D.append(this.e.tab, wrapper);
}else{/*notification*/
D.addClass(this.e.body, 'notification');
if(m.isError){
D.addClass([contentTarget, this.e.tab], 'error');
}
D.append(
this.e.tab,
|
| ︙ | ︙ | |||
678 679 680 681 682 683 684 |
// Hence, even though innerHTML is normally frowned upon, it is
// perfectly safe to use in this context.
if(m.xmsg instanceof Array){
// Used by Chat.reportErrorAsMessage()
D.append(contentTarget, m.xmsg);
}else{
contentTarget.innerHTML = m.xmsg;
| > > > | | > | | | | | | | | | 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 |
// Hence, even though innerHTML is normally frowned upon, it is
// perfectly safe to use in this context.
if(m.xmsg instanceof Array){
// Used by Chat.reportErrorAsMessage()
D.append(contentTarget, m.xmsg);
}else{
contentTarget.innerHTML = m.xmsg;
contentTarget.querySelectorAll('a').forEach(addAnchorTargetBlank);
if(F.pikchr){
F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
}
}
}
this.e.tab.firstElementChild.addEventListener('click', this._handleLegendClicked, false);
/*if(eXFrom){
eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
}*/
return this;
},
/* Event handler for clicking .message-user elements to show their
timestamps and a set of actions. */
_handleLegendClicked: function f(ev){
if(!f.popup){
/* "Popup" widget */
f.popup = {
e: D.addClass(D.div(), 'chat-message-popup'),
refresh:function(){
const eMsg = this.$eMsg/*.message-widget element*/;
if(!eMsg) return;
D.clearElement(this.e);
const d = new Date(eMsg.dataset.timestamp);
if(d.getMinutes().toString()!=="NaN"){
// Date works, render informative timestamps
const xfrom = eMsg.dataset.xfrom || 'server';
D.append(this.e,
|
| ︙ | ︙ | |||
731 732 733 734 735 736 737 |
btnDeleteLocal.addEventListener('click', function(){
self.hide();
Chat.deleteMessageElem(eMsg);
});
if(Chat.userMayDelete(eMsg)){
const btnDeleteGlobal = D.button("Delete globally");
D.append(toolbar, btnDeleteGlobal);
| | > > > > | | > > > > > > > | < > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | | > | > > > > | > > > > | < < | < < < | < < < | | | > | > > > | < < < | > | | | > > > > | | > > > > > | < < | < < < < | | > > > > | < | > | > > > > > > > > | > | < | < | < < | < | > | > | | > > > > > | > > > > > | < < < | > > > > > > > > > > > > > > > > > > | 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 |
btnDeleteLocal.addEventListener('click', function(){
self.hide();
Chat.deleteMessageElem(eMsg);
});
if(Chat.userMayDelete(eMsg)){
const btnDeleteGlobal = D.button("Delete globally");
D.append(toolbar, btnDeleteGlobal);
F.confirmer(btnDeleteGlobal,{
pinSize: true,
ticks: F.config.confirmerButtonTicks,
confirmText: "Confirm delete?",
onconfirm:function(){
self.hide();
Chat.deleteMessage(eMsg);
}
});
}
const toolbar3 = D.addClass(D.div(), 'toolbar');
D.append(this.e, toolbar3);
D.append(toolbar3, D.button(
"Locally remove all previous messages",
function(){
self.hide();
Chat.mnMsg = +eMsg.dataset.msgid;
var e = eMsg.previousElementSibling;
while(e && e.classList.contains('message-widget')){
const n = e.previousElementSibling;
D.remove(e);
e = n;
}
eMsg.scrollIntoView();
}
));
const toolbar2 = D.addClass(D.div(), 'toolbar');
D.append(this.e, toolbar2);
D.append(toolbar2, D.button(
"Toggle text mode", function(){
self.hide();
Chat.toggleTextMode(eMsg);
}));
if(eMsg.dataset.xfrom){
/* Add a link to the /timeline filtered on this user. */
const timelineLink = D.attr(
D.a(F.repoUrl('timeline',{
u: eMsg.dataset.xfrom,
y: 'a'
}), "User's Timeline"),
'target', '_blank'
);
D.append(toolbar2, timelineLink);
if(Chat.filterState.activeUser &&
Chat.filterState.match(eMsg.dataset.xfrom)){
/* Add a button to clear user filter and jump to
this message in its original context. */
D.append(
this.e,
D.append(
D.addClass(D.div(), 'toolbar'),
D.button(
"Message in context",
function(){
self.hide();
Chat.setUserFilter(false);
eMsg.scrollIntoView(false);
Chat.animate(
eMsg.firstElementChild, 'anim-flip-h'
//eMsg.firstElementChild, 'anim-flip-v'
//eMsg.childNodes, 'anim-rotate-360'
//eMsg.childNodes, 'anim-flip-v'
//eMsg, 'anim-flip-v'
);
})
)
);
}/*jump-to button*/
}
const tab = eMsg.querySelector('.message-widget-tab');
D.append(tab, this.e);
D.removeClass(this.e, 'hidden');
Chat.animate(this.e, 'anim-fade-in-fast');
}/*refresh()*/,
hide: function(){
delete this.$eMsg;
D.addClass(this.e, 'hidden');
D.clearElement(this.e);
},
show: function(tgtMsg){
if(tgtMsg === this.$eMsg){
this.hide();
return;
}
this.$eMsg = tgtMsg;
this.refresh();
}
}/*f.popup*/;
}/*end static init*/
let theMsg = ev.target;
while( theMsg && !theMsg.classList.contains('message-widget')){
theMsg = theMsg.parentNode;
}
if(theMsg) f.popup.show(theMsg);
}/*_handleLegendClicked()*/
};
return cf;
})()/*MessageWidget*/;
const BlobXferState = (function(){
/* State for paste and drag/drop */
const bxs = {
dropDetails: document.querySelector('#chat-drop-details'),
blob: undefined,
clear: function(){
this.blob = undefined;
D.clearElement(this.dropDetails);
Chat.e.inputFile.value = "";
}
};
/** Updates the paste/drop zone with details of the pasted/dropped
data. The argument must be a Blob or Blob-like object (File) or
it can be falsy to reset/clear that state.*/
const updateDropZoneContent = function(blob){
//console.debug("updateDropZoneContent()",blob);
const dd = bxs.dropDetails;
bxs.blob = blob;
D.clearElement(dd);
if(!blob){
Chat.e.inputFile.value = '';
return;
}
D.append(dd, "Attached: ", blob.name,
D.br(), "Size: ",blob.size);
const btn = D.button("Cancel");
D.append(dd, D.br(), btn);
btn.addEventListener('click', ()=>updateDropZoneContent(), false);
if(blob.type && (blob.type.startsWith("image/") || blob.type==='BITMAP')){
const img = D.img();
D.append(dd, D.br(), img);
const reader = new FileReader();
reader.onload = (e)=>img.setAttribute('src', e.target.result);
reader.readAsDataURL(blob);
}
};
Chat.e.inputFile.addEventListener('change', function(ev){
updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined)
});
/* Handle image paste from clipboard. TODO: figure out how we can
paste non-image binary data as if it had been selected via the
file selection element. */
const pasteListener = function(event){
const items = event.clipboardData.items,
item = items[0];
//console.debug("paste event",event.target,item,event);
//console.debug("paste event item",item);
if(item && item.type && ('file'===item.kind || 'BITMAP'===item.type)){
updateDropZoneContent(false/*clear prev state*/);
updateDropZoneContent(item.getAsFile());
event.stopPropagation();
event.preventDefault(true);
return false;
}
/* else continue propagating */
};
document.addEventListener('paste', pasteListener, true);
if(window.Selection && window.Range && !Chat.$browserHasPlaintextOnly){
/* Acrobatics to keep *some* installations of Firefox
from pasting formatting into contenteditable fields.
This also works on Chrome, but chrome has the
contenteditable=plaintext-only property which does this
for us. */
Chat.inputElement().addEventListener(
'paste',
function(ev){
if (ev.clipboardData && ev.clipboardData.getData) {
const pastedText = ev.clipboardData.getData('text/plain');
const selection = window.getSelection();
if (!selection.rangeCount) return false;
selection.deleteFromDocument(/*remove selected content*/);
selection.getRangeAt(0).insertNode(document.createTextNode(pastedText));
selection.collapseToEnd(/*deselect pasted text and set cursor at the end*/);
ev.preventDefault();
return false;
}
}, false);
}
const noDragDropEvents = function(ev){
/* contenteditable tries to do its own thing with dropped data,
which is not compatible with how we use it, so... */
ev.dataTransfer.effectAllowed = 'none';
ev.dataTransfer.dropEffect = 'none';
ev.preventDefault();
ev.stopPropagation();
return false;
};
['drop','dragenter','dragleave','dragend'].forEach(
(k)=>{
Chat.inputElement().addEventListener(k, noDragDropEvents, false);
}
);
return bxs;
})()/*drag/drop/paste*/;
const tzOffsetToString = function(off){
const hours = Math.round(off/60), min = Math.round(off % 30);
return ''+(hours + (min ? '.5' : ''));
};
const pad2 = (x)=>('0'+x).substr(-2);
const localTime8601 = function(d){
return [
d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
].join('');
};
/**
Submits the contents of the message input field (if not empty)
and/or the file attachment field to the server. If both are
empty, this is a no-op.
*/
Chat.submitMessage = function f(){
if(!f.spaces){
f.spaces = /\s+$/;
f.markdownContinuation = /\\\s+$/;
}
this.setCurrentView(this.e.viewMessages);
const fd = new FormData();
var msg = this.inputValue().trim();
if(msg && (msg.indexOf('\n')>0 || f.spaces.test(msg))){
/* Cosmetic: trim whitespace from the ends of lines to try to
keep copy/paste from terminals, especially wide ones, from
forcing a horizontal scrollbar on all clients. This breaks
markdown's use of blackslash-space-space for paragraph
continuation, but *not* doing this affects all clients every
time someone pastes in console copy/paste from an affected
platform. We seem to have narrowed to the console pasting
problem to users of tmux. Most consoles don't behave
that way. */
const xmsg = msg.split('\n');
xmsg.forEach(function(line,ndx){
if(!f.markdownContinuation.test(line)){
xmsg[ndx] = line.trimRight();
}
});
msg = xmsg.join('\n');
}
if(msg) fd.set('msg',msg);
const file = BlobXferState.blob || this.e.inputFile.files[0];
if(file) fd.set("file", file);
if( !msg && !file ) return;
const self = this;
fd.set("lmtime", localTime8601(new Date()));
F.fetch("chat-send",{
|
| ︙ | ︙ | |||
900 901 902 903 904 905 906 |
}
}
});
BlobXferState.clear();
Chat.inputValue("").inputFocus();
};
| | > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > | > > > > > > | > > | > > < > > > > > > > > > > > > | > > > > | > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > | | | | | > > > > > | > > | > > > > > | > > > > > | > > > > > | < > > > | | | | > > < > | < < < | < < < < < | | | < > | < > > | > > > > > > > > > > > > > > > > | < > | | | > | < < < < < < | | > > | | | < | > > > | > > > > > > > > > | | | | > | < | > > > > > > > | < | > > > > > > > | < | < > > > > | < > > > > > | < < | < | | | < < > > > > > > > | | > > | | < | | > | | | | > | > > | | > > > > > | > | > > > | > | > > > > | > > > > > | > > | > > | > | < < < < < < > | < | > | | | < > | | > > > | > > | > > | < < | > > > > > | | | | | > | > > > | > > | < > | | | > > > > | > | > > > > > > > > | > > > | 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 |
}
}
});
BlobXferState.clear();
Chat.inputValue("").inputFocus();
};
const inputWidgetKeydown = function f(ev){
if(!f.$toggleCtrl){
f.$toggleCtrl = function(currentMode){
currentMode = !currentMode;
Chat.settings.set('edit-ctrl-send', currentMode);
};
f.$toggleCompact = function(currentMode){
currentMode = !currentMode;
Chat.settings.set('edit-compact-mode', currentMode);
};
}
if(13 !== ev.keyCode) return;
const text = Chat.inputValue().trim();
const ctrlMode = Chat.settings.getBool('edit-ctrl-send', false);
//console.debug("Enter key event:", ctrlMode, ev.ctrlKey, ev.shiftKey, ev);
if(ev.shiftKey){
const compactMode = Chat.settings.getBool('edit-compact-mode', false);
ev.preventDefault();
ev.stopPropagation();
/* Shift-enter will run preview mode UNLESS preview mode is
active AND the input field is empty, in which case it will
switch back to message view. */
if(Chat.e.currentView===Chat.e.viewPreview && !text){
Chat.setCurrentView(Chat.e.viewMessages);
}else if(!text){
f.$toggleCompact(compactMode);
}else{
Chat.e.btnPreview.click();
}
return false;
}
if(ev.ctrlKey && !text && !BlobXferState.blob){
/* Ctrl-enter on empty input field(s) toggles Enter/Ctrl-enter mode */
ev.preventDefault();
ev.stopPropagation();
f.$toggleCtrl(ctrlMode);
return false;
}
if(!ctrlMode && ev.ctrlKey && text){
//console.debug("!ctrlMode && ev.ctrlKey && text.");
/* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
newline, but that is not happening, for unknown reasons
(possibly related to this element being a conteneditable DIV
instead of a textarea). Forcibly appending a newline do the
input area does not work, also for unknown reasons, and would
only be suitable when we're at the end of the input.
Strangely, this approach DOES work for shift-enter, but we
need shift-enter as a hotkey for preview mode.
*/
//return;
// return here "should" cause newline to be added, but that doesn't work
}
if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey/* && ctrlMode*/)){
/* Ship it! */
ev.preventDefault();
ev.stopPropagation();
Chat.submitMessage();
return false;
}
};
Chat.e.inputField.addEventListener('keydown', inputWidgetKeydown, false);
Chat.e.btnSubmit.addEventListener('click',(e)=>{
e.preventDefault();
Chat.submitMessage();
return false;
});
Chat.e.btnAttach.addEventListener(
'click', ()=>Chat.e.inputFile.click(), false);
(function(){/*Set up #chat-button-settings and related bits */
if(window.innerWidth<window.innerHeight){
// Must be set up before config view is...
/* Alignment of 'my' messages: right alignment is conventional
for mobile chat apps but can be difficult to read in wide
windows (desktop/tablet landscape mode), so we default to a
layout based on the apparent "orientation" of the window:
tall vs wide. Can be toggled via settings. */
document.body.classList.add('my-messages-right');
}
const settingsButton = document.querySelector('#chat-button-settings');
const optionsMenu = E1('#chat-config-options');
const cbToggle = function(ev){
ev.preventDefault();
ev.stopPropagation();
Chat.setCurrentView(Chat.e.currentView===Chat.e.viewConfig
? Chat.e.viewMessages : Chat.e.viewConfig);
return false;
};
D.attr(settingsButton, 'role', 'button').addEventListener('click', cbToggle, false);
Chat.e.viewConfig.querySelector('button').addEventListener('click', cbToggle, false);
/** Internal acrobatics to allow certain settings toggles to access
related toggles. */
const namedOptions = {
activeUsers:{
label: "Show active users list",
hint: "List users who have messages in the currently-loaded chat history.",
boolValue: 'active-user-list'
}
};
if(1){
/* Per user request, toggle the list of users on and off if the
legend element is tapped. */
const optAu = namedOptions.activeUsers;
optAu.theLegend = Chat.e.activeUserListWrapper.firstElementChild/*LEGEND*/;
optAu.theList = optAu.theLegend.nextElementSibling/*user list container*/;
optAu.theLegend.addEventListener('click',function(){
D.toggleClass(Chat.e.activeUserListWrapper, 'collapsed');
if(!Chat.e.activeUserListWrapper.classList.contains('collapsed')){
Chat.animate(optAu.theList,'anim-flip-v');
}
}, false);
}/*namedOptions.activeUsers additional setup*/
/* Settings menu entries... the most frequently-needed ones "should"
(arguably) be closer to the start of this list. */
/**
Settings ops structure:
label: string for the UI
boolValue: string (name of Chat.settings setting) or a
function which returns true or false.
select: SELECT element (instead of boolValue)
callback: optional handler to call after setting is modified.
If a setting has a boolValue set, that gets transformed into a
checkbox which toggles the given persistent setting (if
boolValue is a string) AND listens for changes to that setting
fired via Chat.settings.set() so that the checkbox can stay in
sync with external changes to that setting. Various Chat UI
elements stay in sync with the config UI via those settings
events.
*/
const settingsOps = [{
label: "Ctrl-enter to Send",
hint: "When on, only Ctrl-Enter will send messages and Enter adds "+
"blank lines. "+
"When off, both Enter and Ctrl-Enter send. "+
"When the input field has focus, is empty, and preview "+
"mode is NOT active then Ctrl-Enter toggles this setting.",
boolValue: 'edit-ctrl-send'
},{
label: "Compact mode",
hint: "Toggle between a space-saving or more spacious writing area. "+
"When the input field has focus, is empty, and preview mode "+
"is NOT active then Shift-Enter toggles this setting.",
boolValue: 'edit-compact-mode'
},{
label: "Left-align my posts",
hint: "Default alignment of your own messages is selected "
+"based window width/height relationship.",
boolValue: ()=>!document.body.classList.contains('my-messages-right'),
callback: function f(){
document.body.classList[
this.checkbox.checked ? 'remove' : 'add'
]('my-messages-right');
}
},{
label: "Monospace message font",
hint: "Use monospace font for message text?",
boolValue: 'monospace-messages',
callback: function(setting){
document.body.classList[
setting.value ? 'add' : 'remove'
]('monospace-messages');
}
},{
label: "Chat-only mode",
hint: "Toggle the page between normal fossil view and chat-only view.",
boolValue: 'chat-only-mode'
},{
label: "Show images inline",
hint: "Whether to show images inline or as a hyperlink.",
boolValue: 'images-inline'
},namedOptions.activeUsers,{
label: "Timestamps in active users list",
hint: "Whether to show last-message timestamps.",
boolValue: 'active-user-list-timestamps'
}];
/** Set up selection list of notification sounds. */
if(1){
const selectSound = D.select();
D.option(selectSound, "", "(no audio)");
const firstSoundIndex = selectSound.options.length;
F.config.chat.alerts.forEach((a)=>D.option(selectSound, a));
if(true===Chat.settings.getBool('audible-alert')){
/* This setting used to be a plain bool. If we encounter
such a setting, take the first sound in the list. */
selectSound.selectedIndex = firstSoundIndex;
}else{
selectSound.value = Chat.settings.get('audible-alert','<none>');
if(selectSound.selectedIndex<0){
/* Missing file - removed after this setting was
applied. Fall back to the first sound in the list. */
selectSound.selectedIndex = firstSoundIndex;
}
}
Chat.setNewMessageSound(selectSound.value);
settingsOps.push({
hint: "Audio alert. How to enable audio playback is browser-specific!",
select: selectSound,
callback: function(ev){
const v = ev.target.value;
Chat.setNewMessageSound(v);
F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+".");
if(v) setTimeout(()=>Chat.playNewMessageSound(), 0);
}
});
}/*audio notification config*/
settingsOps.push({
label: "Play notification for your own messages.",
hint: "When enabled, the audio notification will be played for all messages, "+
"including your own. When disabled only messages from other users "+
"will trigger a notification.",
boolValue: 'alert-own-messages'
});
/**
Build UI for config options...
*/
settingsOps.forEach(function f(op){
const line = D.addClass(D.div(), 'menu-entry');
const label = op.label
? D.append(D.label(),op.label) : undefined;
const labelWrapper = D.addClass(D.div(), 'label-wrapper');
var hint;
const col0 = D.span();
if(op.hint){
hint = D.append(D.addClass(D.span(),'hint'),op.hint);
}
if(op.hasOwnProperty('select')){
D.append(line, col0, labelWrapper);
D.append(labelWrapper, op.select);
if(hint) D.append(labelWrapper, hint);
if(label) D.append(col0, label);
if(op.callback){
op.select.addEventListener('change', (ev)=>op.callback(ev), false);
}
}else if(op.hasOwnProperty('boolValue')){
if(undefined === f.$id) f.$id = 0;
++f.$id;
if('string' ===typeof op.boolValue){
const key = op.boolValue;
op.boolValue = ()=>Chat.settings.getBool(key);
op.persistentSetting = key;
}
const check = op.checkbox
= D.attr(D.checkbox(1, op.boolValue()),
'aria-label', op.label);
const id = 'cfgopt'+f.$id;
check.checked = op.boolValue();
op.checkbox = check;
D.attr(check, 'id', id);
D.append(line, col0, labelWrapper);
D.append(col0, check);
if(label){
D.attr(label, 'for', id);
D.append(labelWrapper, label);
}
if(hint) D.append(labelWrapper, hint);
}else{
line.addEventListener('click', callback);
D.append(line, col0, labelWrapper);
if(label) D.append(labelWrapper, label);
if(hint) D.append(labelWrapper, hint);
}
D.append(optionsMenu, line);
if(op.persistentSetting){
Chat.settings.addListener(
op.persistentSetting,
function(setting){
if(op.checkbox) op.checkbox.checked = !!setting.value;
else if(op.select) op.select.value = setting.value;
if(op.callback) op.callback(setting);
}
);
if(op.checkbox){
op.checkbox.addEventListener(
'change', function(){
Chat.settings.set(op.persistentSetting, op.checkbox.checked)
}, false);
}
}else if(op.callback && op.checkbox){
op.checkbox.addEventListener('change', (ev)=>op.callback(ev), false);
}
});
})()/*#chat-button-settings setup*/;
(function(){
/* Install default settings... must come after
chat-button-settings setup so that the listeners which that
installs are notified via the properties getting initialized
here. */
Chat.settings.addListener('monospace-messages',function(s){
document.body.classList[s.value ? 'add' : 'remove']('monospace-messages');
})
Chat.settings.addListener('active-user-list',function(s){
Chat.showActiveUserList(s.value);
});
Chat.settings.addListener('active-user-list-timestamps',function(s){
Chat.showActiveUserTimestamps(s.value);
});
Chat.settings.addListener('chat-only-mode',function(s){
Chat.chatOnlyMode(s.value);
});
Chat.settings.addListener('edit-compact-mode',function(s){
Chat.e.inputLine.classList[
s.value ? 'add' : 'remove'
]('compact');
});
Chat.settings.addListener('edit-ctrl-send',function(s){
const label = (s.value ? "Ctrl-" : "")+"Enter submits messages.";
const eInput = Chat.inputElement();
eInput.dataset.placeholder = eInput.dataset.placeholder0 + " " +label;
Chat.e.btnSubmit.title = label;
});
const valueKludges = {
/* Convert certain string-format values to other types... */
"false": false,
"true": true
};
Object.keys(Chat.settings.defaults).forEach(function(k){
var v = Chat.settings.get(k,Chat);
if(Chat===v) v = Chat.settings.defaults[k];
if(valueKludges.hasOwnProperty(v)) v = valueKludges[v];
Chat.settings.set(k,v)
/* fires event listeners so that the Config area checkboxes
get in sync */;
});
})();
(function(){/*set up message preview*/
const btnPreview = Chat.e.btnPreview;
Chat.setPreviewText = function(t){
this.setCurrentView(this.e.viewPreview);
this.e.previewContent.innerHTML = t;
this.e.viewPreview.querySelectorAll('a').forEach(addAnchorTargetBlank);
this.inputFocus();
};
Chat.e.viewPreview.querySelector('#chat-preview-close').
addEventListener('click', ()=>Chat.setCurrentView(Chat.e.viewMessages), false);
let previewPending = false;
const elemsToEnable = [btnPreview, Chat.e.btnSubmit, Chat.e.inputField];
const submit = function(ev){
ev.preventDefault();
ev.stopPropagation();
if(previewPending) return false;
const txt = Chat.inputValue();
if(!txt){
Chat.setPreviewText('');
previewPending = false;
return false;
}
const fd = new FormData();
fd.append('content', txt);
fd.append('filename','chat.md'
/*filename needed for mimetype determination*/);
fd.append('render_mode',F.page.previewModes.wiki);
F.fetch('ajax/preview-text',{
payload: fd,
onload: (html)=>Chat.setPreviewText(html),
onerror: function(e){
F.fetch.onerror(e);
Chat.setPreviewText("ERROR: "+(
e.message || 'Unknown error fetching preview!'
));
},
beforesend: function(){
D.disable(elemsToEnable);
Chat.ajaxStart();
previewPending = true;
Chat.setPreviewText("Loading preview...");
},
aftersend:function(){
previewPending = false;
Chat.ajaxEnd();
D.enable(elemsToEnable);
}
});
return false;
};
btnPreview.addEventListener('click', submit, false);
})()/*message preview setup*/;
/** Callback for poll() to inject new content into the page. jx ==
the response from /chat-poll. If atEnd is true, the message is
appended to the end of the chat list (for loading older
messages), else the beginning (the default). */
const newcontent = function f(jx,atEnd){
if(!f.processPost){
/** Processes chat message m, placing it either the start (if atEnd
is falsy) or end (if atEnd is truthy) of the chat history. atEnd
should only be true when loading older messages. */
f.processPost = function(m,atEnd){
++Chat.totalMessageCount;
if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid;
if(m.xfrom && m.mtime){
const d = new Date(m.mtime);
const uls = Chat.usersLastSeen[m.xfrom];
if(!uls || uls<d){
d.$uColor = m.uclr;
Chat.usersLastSeen[m.xfrom] = d;
}
}
if( m.mdel ){
/* A record deletion notice. */
Chat.deleteMessageElem(m.mdel);
return;
}
if(!Chat._isBatchLoading
&& (Chat.me!==m.xfrom
|| Chat.settings.getBool('alert-own-messages'))){
Chat.playNewMessageSound();
}
const row = new Chat.MessageWidget(m);
Chat.injectMessageElem(row.e.body,atEnd);
if(m.isError){
Chat._gotServerError = m;
}
}/*processPost()*/;
}/*end static init*/
jx.msgs.forEach((m)=>f.processPost(m,atEnd));
Chat.updateActiveUserList();
if('visible'===document.visibilityState){
if(Chat.changesSincePageHidden){
Chat.changesSincePageHidden = 0;
Chat.e.pageTitle.innerText = Chat.pageTitleOrig;
}
}else{
Chat.changesSincePageHidden += jx.msgs.length;
|
| ︙ | ︙ | |||
1139 1140 1141 1142 1143 1144 1145 |
const loadLegend = D.legend("Load...");
const toolbar = Chat.e.loadOlderToolbar = D.attr(
D.fieldset(loadLegend), "id", "load-msg-toolbar"
);
Chat.disableDuringAjax.push(toolbar);
/* Loads the next n oldest messages, or all previous history if n is negative. */
const loadOldMessages = function(n){
| | | | > | 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 |
const loadLegend = D.legend("Load...");
const toolbar = Chat.e.loadOlderToolbar = D.attr(
D.fieldset(loadLegend), "id", "load-msg-toolbar"
);
Chat.disableDuringAjax.push(toolbar);
/* Loads the next n oldest messages, or all previous history if n is negative. */
const loadOldMessages = function(n){
Chat.e.viewMessages.classList.add('loading');
Chat._isBatchLoading = true;
const scrollHt = Chat.e.viewMessages.scrollHeight,
scrollTop = Chat.e.viewMessages.scrollTop;
F.fetch("chat-poll",{
urlParams:{
before: Chat.mnMsg,
n: n
},
responseType: 'json',
onerror:function(err){
Chat.reportErrorAsMessage(err);
Chat._isBatchLoading = false;
},
onload:function(x){
let gotMessages = x.msgs.length;
newcontent(x,true);
Chat._isBatchLoading = false;
Chat.updateActiveUserList();
if(Chat._gotServerError){
Chat._gotServerError = false;
return;
}
if(n<0/*we asked for all history*/
|| 0===gotMessages/*we found no history*/
|| (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
|
| ︙ | ︙ | |||
1180 1181 1182 1183 1184 1185 1186 |
if(ndx>=0) Chat.disableDuringAjax.splice(ndx,1);
Chat.e.loadOlderToolbar.disabled = true;
}
if(gotMessages > 0){
F.toast.message("Loaded "+gotMessages+" older messages.");
/* Return scroll position to where it was when the history load
was requested, per user request */
| | | | | | | 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 |
if(ndx>=0) Chat.disableDuringAjax.splice(ndx,1);
Chat.e.loadOlderToolbar.disabled = true;
}
if(gotMessages > 0){
F.toast.message("Loaded "+gotMessages+" older messages.");
/* Return scroll position to where it was when the history load
was requested, per user request */
Chat.e.viewMessages.scrollTo(
0, Chat.e.viewMessages.scrollHeight - scrollHt + scrollTop
);
}
},
aftersend:function(){
Chat.e.viewMessages.classList.remove('loading');
Chat.ajaxEnd();
}
});
};
const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */;
D.append(toolbar, wrapper);
var btn = D.button("Previous "+Chat.loadMessageCount+" messages");
D.append(wrapper, btn);
btn.addEventListener('click',()=>loadOldMessages(Chat.loadMessageCount));
btn = D.button("All previous messages");
D.append(wrapper, btn);
btn.addEventListener('click',()=>loadOldMessages(-1));
D.append(Chat.e.viewMessages, toolbar);
toolbar.disabled = true /*will be enabled when msg load finishes */;
})()/*end history loading widget setup*/;
const afterFetch = function f(){
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxEnd();
Chat.e.viewMessages.classList.remove('loading');
setTimeout(function(){
Chat.scrollMessagesTo(1);
}, 250);
}
if(Chat._gotServerError && Chat.intervalTimer){
clearInterval(Chat.intervalTimer);
Chat.reportErrorAsMessage(
|
| ︙ | ︙ | |||
1229 1230 1231 1232 1233 1234 1235 |
const poll = async function f(){
if(f.running) return;
f.running = true;
Chat._isBatchLoading = f.isFirstCall;
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxStart();
| | > | > > > > > > > > > > > > | | 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 |
const poll = async function f(){
if(f.running) return;
f.running = true;
Chat._isBatchLoading = f.isFirstCall;
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxStart();
Chat.e.viewMessages.classList.add('loading');
}
F.fetch("chat-poll",{
timeout: 420 * 1000/*FIXME: get the value from the server*/,
urlParams:{
name: Chat.mxMsg
},
responseType: "json",
// Disable the ajax start/end handling for this long-polling op:
beforesend: function(){},
aftersend: function(){},
onerror:function(err){
Chat._isBatchLoading = false;
if(Chat.verboseErrors) console.error(err);
/* ^^^ we don't use Chat.reportError() here b/c the polling
fails exepectedly when it times out, but is then immediately
resumed, and reportError() produces a loud error message. */
afterFetch();
},
onload:function(y){
newcontent(y);
if(Chat._isBatchLoading){
Chat._isBatchLoading = false;
Chat.updateActiveUserList();
}
afterFetch();
}
});
};
poll.isFirstCall = true;
Chat._gotServerError = poll.running = false;
if( window.fossil.config.chat.fromcli ){
Chat.chatOnlyMode(true);
}
Chat.intervalTimer = setInterval(poll, 1000);
if(0){
const flip = (ev)=>Chat.animate(ev.target,'anim-flip-h');
document.querySelectorAll('#chat-buttons-wrapper .cbutton').forEach(function(e){
e.addEventListener('click',flip, false);
});
}
delete ForceResizeKludge.$disabled;
ForceResizeKludge();
Chat.animate.$disabled = false;
setTimeout( ()=>Chat.inputFocus(), 0 );
F.page.chat = Chat/* enables testing the APIs via the dev tools */;
});
|
Changes to src/fossil.page.fileedit.js.
| ︙ | ︙ | |||
722 723 724 725 726 727 728 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
| | > | | | > | 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 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
if(ev.shiftKey && 13 === ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
P.e.taEditor.blur(/*force change event, if needed*/);
P.tabs.switchToTab(P.e.tabs.preview);
if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
P.preview();
}
return false;
}
}, false);
// If we're in the preview tab, have ctrl-enter switch back to the editor.
document.body.addEventListener('keydown',function(ev){
if(ev.shiftKey && 13 === ev.keyCode){
if(currentTab === P.e.tabs.preview){
ev.preventDefault();
ev.stopPropagation();
P.tabs.switchToTab(P.e.tabs.content);
P.e.taEditor.focus(/*doesn't work for client-supplied editor widget!
And it's slow as molasses for long docs, as focus()
forces a document reflow.*/);
return false;
}
}
}, true);
F.connectPagePreviewers(
P.e.tabs.preview.querySelector(
'#btn-preview-refresh'
|
| ︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 |
F.fetch.onerror(e);
callback("Error fetching preview: "+e);
}
});
return this;
};
| < < < < < < < < < < < < < < < < < < < | 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 |
F.fetch.onerror(e);
callback("Error fetching preview: "+e);
}
});
return this;
};
/**
Fetches the content diff based on the contents and settings of
this page's input fields, and updates the UI with the diff view.
Returns this object, noting that the operation is async.
*/
P.diff = function f(sbs){
|
| ︙ | ︙ | |||
1246 1247 1248 1249 1250 1251 1252 |
onload: function(c){
D.parseHtml(D.clearElement(target),[
"<div>Diff <code>[",
self.finfo.checkin,
"]</code> → Local Edits</div>",
c||'No changes.'
].join(''));
| > | | 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 |
onload: function(c){
D.parseHtml(D.clearElement(target),[
"<div>Diff <code>[",
self.finfo.checkin,
"]</code> → Local Edits</div>",
c||'No changes.'
].join(''));
F.diff.setupDiffContextLoad();
if(sbs) P.tweakSbsDiffs();
F.message('Updated diff.');
self.tabs.switchToTab(self.e.tabs.diff);
}
});
return this;
};
|
| ︙ | ︙ |
Changes to src/fossil.page.pikchrshow.js.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
P.e.previewModeToggle,
'\u00a0',
P.e.previewCopyButton,
P.e.previewModeLabel,
P.e.markupAlignWrapper );
////////////////////////////////////////////////////////////
| | | > > > > > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
P.e.previewModeToggle,
'\u00a0',
P.e.previewCopyButton,
P.e.previewModeLabel,
P.e.markupAlignWrapper );
////////////////////////////////////////////////////////////
// Trigger preview on Shift-Enter.
P.e.taContent.addEventListener('keydown',function(ev){
if(ev.shiftKey && 13 === ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
P.preview();
return false;
}
}, false);
////////////////////////////////////////////////////////////
// Setup clipboard-copy of markup/SVG...
F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false);
|
| ︙ | ︙ | |||
383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
this.response.raw;
console.error("svg parsed HTML nodes:",childs);
}
D.append(D.clearElement(preTgt), this.e.taPreviewText);
break;
}
this.e.previewModeLabel.innerText = label;
};
/**
Fetches the preview from the server and updates the preview to
the rendered SVG content or error report.
*/
P.preview = function fp(){
| > | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
this.response.raw;
console.error("svg parsed HTML nodes:",childs);
}
D.append(D.clearElement(preTgt), this.e.taPreviewText);
break;
}
this.e.previewModeLabel.innerText = label;
this.e.taContent.focus(/*not sure why this gets lost on preview!*/);
};
/**
Fetches the preview from the server and updates the preview to
the rendered SVG content or error report.
*/
P.preview = function fp(){
|
| ︙ | ︙ |
Changes to src/fossil.page.wikiedit.js.
| ︙ | ︙ | |||
912 913 914 915 916 917 918 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
| | | | | > | 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 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
if(ev.shiftKey && 13 === ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
P.e.taEditor.blur(/*force change event, if needed*/);
P.tabs.switchToTab(P.e.tabs.preview);
if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
P.preview();
}
}
}, false);
// If we're in the preview tab, have ctrl-enter switch back to the editor.
document.body.addEventListener('keydown',function(ev){
if(ev.shiftKey && 13 === ev.keyCode){
if(currentTab === P.e.tabs.preview){
ev.preventDefault();
ev.stopPropagation();
P.tabs.switchToTab(P.e.tabs.content);
P.e.taEditor.focus(/*doesn't work for client-supplied editor widget!
And it's slow as molasses for long docs, as focus()
forces a document reflow. */);
//console.debug("BODY ctrl-enter");
return false;
}
}
}, true);
F.connectPagePreviewers(
P.e.tabs.preview.querySelector(
'#btn-preview-refresh'
|
| ︙ | ︙ | |||
1494 1495 1496 1497 1498 1499 1500 |
F.fetch.onerror(e);
callback("Error fetching preview: "+e);
}
});
return this;
};
| < < < < < < < < < < < < < < < < < < < | 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 |
F.fetch.onerror(e);
callback("Error fetching preview: "+e);
}
});
return this;
};
/**
Fetches the content diff based on the contents and settings of
this page's input fields, and updates the UI with the diff view.
Returns this object, noting that the operation is async.
*/
P.diff = function f(sbs){
|
| ︙ | ︙ | |||
1540 1541 1542 1543 1544 1545 1546 |
onload: function(c){
D.parseHtml(D.clearElement(target), [
"<div>Diff <code>[",
self.winfo.name,
"]</code> → Local Edits</div>",
c||'No changes.'
].join(''));
| > | | 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 |
onload: function(c){
D.parseHtml(D.clearElement(target), [
"<div>Diff <code>[",
self.winfo.name,
"]</code> → Local Edits</div>",
c||'No changes.'
].join(''));
F.diff.setupDiffContextLoad();
if(sbs) P.tweakSbsDiffs();
F.message('Updated diff.');
self.tabs.switchToTab(self.e.tabs.diff);
}
});
return this;
};
|
| ︙ | ︙ |
Changes to src/fossil.popupwidget.js.
| ︙ | ︙ | |||
353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
when the botton is clicked.
*/
setup: function f(){
if(!f.hasOwnProperty('clickHandler')){
f.clickHandler = function fch(ev){
ev.preventDefault();
if(!fch.popup){
fch.popup = new F.PopupWidget({
cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
refresh: function(){
}
});
fch.popup.e.style.maxWidth = '80%'/*of body*/;
| > | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
when the botton is clicked.
*/
setup: function f(){
if(!f.hasOwnProperty('clickHandler')){
f.clickHandler = function fch(ev){
ev.preventDefault();
ev.stopPropagation();
if(!fch.popup){
fch.popup = new F.PopupWidget({
cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
refresh: function(){
}
});
fch.popup.e.style.maxWidth = '80%'/*of body*/;
|
| ︙ | ︙ | |||
409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
}else{
fch.popup.e.style.removeProperty('min-width');
x -= popupRect.width/2;
}
if(x<0) x = 0;
//console.debug("dimensions",x,y, popupRect, rectBody);
fch.popup.show(x, y);
};
f.foreachElement = function(e){
if(e.classList.contains('processed')) return;
e.classList.add('processed');
e.$helpContent = [];
/* We have to move all child nodes out of the way because we
cannot hide TEXT nodes via CSS (which cannot select TEXT
| > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
}else{
fch.popup.e.style.removeProperty('min-width');
x -= popupRect.width/2;
}
if(x<0) x = 0;
//console.debug("dimensions",x,y, popupRect, rectBody);
fch.popup.show(x, y);
return false;
};
f.foreachElement = function(e){
if(e.classList.contains('processed')) return;
e.classList.add('processed');
e.$helpContent = [];
/* We have to move all child nodes out of the way because we
cannot hide TEXT nodes via CSS (which cannot select TEXT
|
| ︙ | ︙ |
Changes to src/fossil.storage.js.
1 2 |
(function(F){
/**
| | | 1 2 3 4 5 6 7 8 9 10 |
(function(F){
/**
fossil.storage is a basic wrapper around localStorage
or sessionStorage or a dummy proxy object if neither
of those are available.
*/
const tryStorage = function f(obj){
if(!f.key) f.key = 'fossil.access.check';
try{
obj.setItem(f.key, 'f');
|
| ︙ | ︙ |
Changes to src/hook.c.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 | ** the hash of the artifact and continues with a description of the ** interpretation of the artifact. */ #include "config.h" #include "hook.h" /* | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
** the hash of the artifact and continues with a description of the
** interpretation of the artifact.
*/
#include "config.h"
#include "hook.h"
/*
** SETTING: hooks sensitive width=40 block-text
** The "hooks" setting contains JSON that describes all defined
** hooks. The value is an array of objects. Each object describes
** a single hook. Example:
**
**
** {
** "type": "after-receive", // type of hook
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
** command line */
if( g.zSSLIdentity!=0 ){
identityFile = g.zSSLIdentity;
}else{
identityFile = db_get("ssl-identity", 0);
}
if( identityFile!=0 && identityFile[0]!='\0' ){
| | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
** command line */
if( g.zSSLIdentity!=0 ){
identityFile = g.zSSLIdentity;
}else{
identityFile = db_get("ssl-identity", 0);
}
if( identityFile!=0 && identityFile[0]!='\0' ){
if( SSL_CTX_use_certificate_chain_file(sslCtx,identityFile)!=1
|| SSL_CTX_use_PrivateKey_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1
){
fossil_fatal("Could not load SSL identity from %s", identityFile);
}
}
/* Register a callback to tell the user what to do when the server asks
** for a cert */
|
| ︙ | ︙ | |||
395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
ssl_close();
return 1;
}
ssl_one_time_exception(pUrlData, zHash);
prompt_user("remember this exception (y/N)? ", &ans);
cReply = blob_str(&ans)[0];
if( cReply=='y' || cReply=='Y') {
ssl_remember_certificate_exception(pUrlData, zHash);
}
blob_reset(&ans);
}
}
/* Set the Global.zIpAddr variable to the server we are talking to.
| > | 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
ssl_close();
return 1;
}
ssl_one_time_exception(pUrlData, zHash);
prompt_user("remember this exception (y/N)? ", &ans);
cReply = blob_str(&ans)[0];
if( cReply=='y' || cReply=='Y') {
db_open_config(0,0);
ssl_remember_certificate_exception(pUrlData, zHash);
}
blob_reset(&ans);
}
}
/* Set the Global.zIpAddr variable to the server we are talking to.
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
328 329 330 331 332 333 334 | /* ** Append the difference between artifacts to the output */ static void append_diff( const char *zFrom, /* Diff from this artifact */ const char *zTo, /* ... to this artifact */ | | < | > > < | | < < | < < < > > < | < | 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 |
/*
** Append the difference between artifacts to the output
*/
static void append_diff(
const char *zFrom, /* Diff from this artifact */
const char *zTo, /* ... to this artifact */
DiffConfig *pCfg /* The diff configuration */
){
int fromid;
int toid;
Blob from, to;
if( zFrom ){
fromid = uuid_to_rid(zFrom, 0);
content_get(fromid, &from);
pCfg->zLeftHash = zFrom;
}else{
blob_zero(&from);
pCfg->zLeftHash = 0;
}
if( zTo ){
toid = uuid_to_rid(zTo, 0);
content_get(toid, &to);
}else{
blob_zero(&to);
}
if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
text_diff(&from, &to, cgi_output_blob(), pCfg);
pCfg->zLeftHash = 0;
blob_reset(&from);
blob_reset(&to);
}
/*
** Write a line of web-page output that shows changes that have occurred
** to a file between two check-ins.
*/
static void append_file_change_line(
const char *zCkin, /* The checkin on which the change occurs */
const char *zName, /* Name of the file that has changed */
const char *zOld, /* blob.uuid before change. NULL for added files */
const char *zNew, /* blob.uuid after change. NULL for deletes */
const char *zOldName, /* Prior name. NULL if no name change. */
DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
int mperm /* executable or symlink permission for zNew */
){
@ <p>
if( !g.perm.Hyperlink ){
if( zNew==0 ){
@ Deleted %h(zName).
}else if( zOld==0 ){
|
| ︙ | ︙ | |||
395 396 397 398 399 400 401 |
@ %h(zName) became a symlink.
}else{
@ %h(zName) became a regular file.
}
}else{
@ Changes to %h(zName).
}
| | | | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
@ %h(zName) became a symlink.
}else{
@ %h(zName) became a regular file.
}
}else{
@ Changes to %h(zName).
}
if( pCfg ){
append_diff(zOld, zNew, pCfg);
}
}else{
if( zOld && zNew ){
if( fossil_strcmp(zOld, zNew)!=0 ){
@ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
@ %h(zName)</a>
@ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
|
| ︙ | ︙ | |||
430 431 432 433 434 435 436 |
}else if( zOld ){
@ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
}else{
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
| | | | | | | | | | < < < | < | < < | < | < > > | > < | 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 |
}else if( zOld ){
@ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
}else{
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
if( pCfg ){
append_diff(zOld, zNew, pCfg);
}else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
@
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
}
}
@ </p>
}
/*
** Generate javascript to enhance HTML diffs.
*/
void append_diff_javascript(int diffType){
if( diffType==0 ) return;
builtin_fossil_js_bundle_or("diff", NULL);
}
/*
** Construct an appropriate diffFlag for text_diff() based on query
** parameters and the to boolean arguments.
*/
DiffConfig *construct_diff_flags(int diffType, DiffConfig *pCfg){
u64 diffFlags = 0; /* Zero means do not show any diff */
if( diffType>0 ){
int x;
if( diffType==2 ) diffFlags = DIFF_SIDEBYSIDE;
if( P("w") ) diffFlags |= DIFF_IGNORE_ALLWS;
if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
diffFlags |= DIFF_STRIP_EOLCR;
diff_config_init(pCfg, diffFlags);
/* "dc" query parameter determines lines of context */
x = atoi(PD("dc","7"));
if( x>0 ) pCfg->nContext = x;
/* The "noopt" parameter disables diff optimization */
return pCfg;
}else{
diff_config_init(pCfg, 0);
return 0;
}
}
/*
** WEBPAGE: ci_tags
** URL: /ci_tags?name=ARTIFACTID
**
** Show all tags and properties for a given check-in.
|
| ︙ | ︙ | |||
618 619 620 621 622 623 624 |
** or a tag or branch name that identifies the check-in.
*/
void ci_page(void){
Stmt q1, q2, q3;
int rid;
int isLeaf;
int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
| < | | > | 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
** or a tag or branch name that identifies the check-in.
*/
void ci_page(void){
Stmt q1, q2, q3;
int rid;
int isLeaf;
int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
const char *zName; /* Name of the check-in to be displayed */
const char *zUuid; /* Hash of zName, found via blob.uuid */
const char *zParent; /* Hash of the parent check-in (if any) */
const char *zRe; /* regex parameter */
ReCompiled *pRe = 0; /* regex */
const char *zW; /* URL param for ignoring whitespace */
const char *zPage = "vinfo"; /* Page that shows diffs */
const char *zPageHide = "ci"; /* Page that hides diffs */
const char *zBrName; /* Branch name */
DiffConfig DCfg,*pCfg; /* Type of diff */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("vinfo");
zName = P("name");
rid = name_to_rid_www("name");
if( rid==0 ){
|
| ︙ | ︙ | |||
882 883 884 885 886 887 888 |
wiki_render_associated("checkin", zUuid, 0);
}
render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
@ <div class="section">Context</div>
render_checkin_context(rid, 0, 0, 0);
@ <div class="section">Changes</div>
@ <div class="sectionmenu">
| | > | | 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 |
wiki_render_associated("checkin", zUuid, 0);
}
render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
@ <div class="section">Context</div>
render_checkin_context(rid, 0, 0, 0);
@ <div class="section">Changes</div>
@ <div class="sectionmenu">
pCfg = construct_diff_flags(diffType, &DCfg);
DCfg.pRe = pRe;
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=0 ){
@ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
@ Hide Diffs</a>
}
if( diffType!=1 ){
@ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
@ Unified Diffs</a>
|
| ︙ | ︙ | |||
937 938 939 940 941 942 943 |
while( db_step(&q3)==SQLITE_ROW ){
const char *zName = db_column_text(&q3,0);
int mperm = db_column_int(&q3, 1);
const char *zOld = db_column_text(&q3,2);
const char *zNew = db_column_text(&q3,3);
const char *zOldName = db_column_text(&q3, 4);
append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
| | | < | 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 |
while( db_step(&q3)==SQLITE_ROW ){
const char *zName = db_column_text(&q3,0);
int mperm = db_column_int(&q3, 1);
const char *zOld = db_column_text(&q3,2);
const char *zNew = db_column_text(&q3,3);
const char *zOldName = db_column_text(&q3, 4);
append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
pCfg,mperm);
}
db_finalize(&q3);
append_diff_javascript(diffType);
style_finish_page();
}
/*
** WEBPAGE: winfo
** URL: /winfo?name=HASH
**
|
| ︙ | ︙ | |||
1171 1172 1173 1174 1175 1176 1177 |
** inv "Invert". Exchange the roles of from= and to=
**
** Show all differences between two check-ins.
*/
void vdiff_page(void){
int ridFrom, ridTo;
int diffType = 0; /* 0: none, 1: unified, 2: side-by-side */
| < > | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 |
** inv "Invert". Exchange the roles of from= and to=
**
** Show all differences between two check-ins.
*/
void vdiff_page(void){
int ridFrom, ridTo;
int diffType = 0; /* 0: none, 1: unified, 2: side-by-side */
Manifest *pFrom, *pTo;
ManifestFile *pFileFrom, *pFileTo;
const char *zBranch;
const char *zFrom;
const char *zTo;
const char *zRe;
const char *zGlob;
char *zMergeOrigin = 0;
ReCompiled *pRe = 0;
DiffConfig DCfg, *pCfg = 0;
int graphFlags = 0;
Blob qp;
int bInvert = PB("inv");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
login_anonymous_available();
|
| ︙ | ︙ | |||
1233 1234 1235 1236 1237 1238 1239 |
blob_appendf(&qp, "&glob=%T", zGlob);
}
}
if( PB("nc") ){
graphFlags |= TIMELINE_NOCOLOR;
blob_appendf(&qp, "&nc");
}
| | | | 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 |
blob_appendf(&qp, "&glob=%T", zGlob);
}
}
if( PB("nc") ){
graphFlags |= TIMELINE_NOCOLOR;
blob_appendf(&qp, "&nc");
}
pCfg = construct_diff_flags(diffType, &DCfg);
if( DCfg.diffFlags & DIFF_IGNORE_ALLWS ){
blob_appendf(&qp, "&w");
}
style_set_current_feature("vdiff");
if( zBranch==0 ){
style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
}
if( diffType!=0 ){
|
| ︙ | ︙ | |||
1257 1258 1259 1260 1261 1262 1263 |
if( zBranch==0 ){
style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b", diffType, &qp);
}
if( zGlob ){
style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp);
}else{
style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo,
| | | 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 |
if( zBranch==0 ){
style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b", diffType, &qp);
}
if( zGlob ){
style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp);
}else{
style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo,
(DCfg.diffFlags & DIFF_IGNORE_ALLWS)?"&w":"");
}
if( diffType!=0 ){
style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
}
if( zBranch ){
style_header("Changes On Branch %h", zBranch);
}else{
|
| ︙ | ︙ | |||
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 |
}
blob_reset(&qp);
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 = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
}
if( cmp<0 ){
if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){
append_file_change_line(zFrom, pFileFrom->zName,
| > | | | | < | 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 |
}
blob_reset(&qp);
manifest_file_rewind(pFrom);
pFileFrom = manifest_file_next(pFrom, 0);
manifest_file_rewind(pTo);
pFileTo = manifest_file_next(pTo, 0);
DCfg.pRe = pRe;
while( pFileFrom || pFileTo ){
int cmp;
if( pFileFrom==0 ){
cmp = +1;
}else if( pFileTo==0 ){
cmp = -1;
}else{
cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
}
if( cmp<0 ){
if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){
append_file_change_line(zFrom, pFileFrom->zName,
pFileFrom->zUuid, 0, 0, pCfg, 0);
}
pFileFrom = manifest_file_next(pFrom, 0);
}else if( cmp>0 ){
if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){
append_file_change_line(zTo, pFileTo->zName,
0, pFileTo->zUuid, 0, pCfg,
manifest_file_mperm(pFileTo));
}
pFileTo = manifest_file_next(pTo, 0);
}else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
pFileFrom = manifest_file_next(pFrom, 0);
pFileTo = manifest_file_next(pTo, 0);
}else{
if(!zGlob || (sqlite3_strglob(zGlob, pFileFrom->zName)==0
|| sqlite3_strglob(zGlob, pFileTo->zName)==0) ){
append_file_change_line(zFrom, pFileFrom->zName,
pFileFrom->zUuid,
pFileTo->zUuid, 0, pCfg,
manifest_file_mperm(pFileTo));
}
pFileFrom = manifest_file_next(pFrom, 0);
pFileTo = manifest_file_next(pTo, 0);
}
}
manifest_destroy(pFrom);
manifest_destroy(pTo);
append_diff_javascript(diffType);
style_finish_page();
}
#if INTERFACE
/*
** Possible return values from object_description()
*/
|
| ︙ | ︙ | |||
1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 |
char *zV1;
char *zV2;
const char *zRe;
ReCompiled *pRe = 0;
u64 diffFlags;
u32 objdescFlags = 0;
int verbose = PB("verbose");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
diffType = preferred_diff_type();
if( P("from") && P("to") ){
v1 = artifact_from_ci_and_filename("from");
v2 = artifact_from_ci_and_filename("to");
| > | 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 |
char *zV1;
char *zV2;
const char *zRe;
ReCompiled *pRe = 0;
u64 diffFlags;
u32 objdescFlags = 0;
int verbose = PB("verbose");
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
diffType = preferred_diff_type();
if( P("from") && P("to") ){
v1 = artifact_from_ci_and_filename("from");
v2 = artifact_from_ci_and_filename("to");
|
| ︙ | ︙ | |||
1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 |
}
if( v1==0 || v2==0 ) fossil_redirect_home();
zRe = P("regex");
if( zRe ) re_compile(&pRe, zRe, 0);
if( verbose ) objdescFlags |= OBJDESC_DETAIL;
if( isPatch ){
Blob c1, c2, *pOut;
pOut = cgi_output_blob();
cgi_set_content_type("text/plain");
diffFlags = 4;
content_get(v1, &c1);
content_get(v2, &c2);
| > > > | | > | 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 |
}
if( v1==0 || v2==0 ) fossil_redirect_home();
zRe = P("regex");
if( zRe ) re_compile(&pRe, zRe, 0);
if( verbose ) objdescFlags |= OBJDESC_DETAIL;
if( isPatch ){
Blob c1, c2, *pOut;
DiffConfig DCfg;
pOut = cgi_output_blob();
cgi_set_content_type("text/plain");
diffFlags = 4;
content_get(v1, &c1);
content_get(v2, &c2);
diff_config_init(&DCfg, diffFlags);
DCfg.pRe = pRe;
text_diff(&c1, &c2, pOut, &DCfg);
blob_reset(&c1);
blob_reset(&c2);
return;
}
zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
construct_diff_flags(diffType, &DCfg);
DCfg.diffFlags |= DIFF_HTML;
style_set_current_feature("fdiff");
style_header("Diff");
style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
if( diffType==2 ){
style_submenu_element("Unified Diff", "%R/fdiff?v1=%T&v2=%T&diff=1",
P("v1"), P("v2"));
|
| ︙ | ︙ | |||
1815 1816 1817 1818 1819 1820 1821 1822 1823 |
object_description(v1, objdescFlags,0, 0);
@ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
object_description(v2, objdescFlags,0, 0);
}
if( pRe ){
@ <b>Only differences that match regular expression "%h(zRe)"
@ are shown.</b>
}
@ <hr />
| > | | 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 |
object_description(v1, objdescFlags,0, 0);
@ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
object_description(v2, objdescFlags,0, 0);
}
if( pRe ){
@ <b>Only differences that match regular expression "%h(zRe)"
@ are shown.</b>
DCfg.pRe = pRe;
}
@ <hr />
append_diff(zV1, zV2, &DCfg);
append_diff_javascript(diffType);
style_finish_page();
}
/*
** WEBPAGE: raw
** URL: /raw/ARTIFACTID
|
| ︙ | ︙ | |||
1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 |
@ Unknown artifact: "%h(zName)"
return;
}
g.isConst = 1;
deliver_artifact(rid, P("m"));
}
/*
** Generate a verbatim artifact as the result of an HTTP request.
** If zMime is not NULL, use it as the mimetype. If zMime is
** NULL, guess at the mimetype based on the filename
** associated with the artifact.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
@ Unknown artifact: "%h(zName)"
return;
}
g.isConst = 1;
deliver_artifact(rid, P("m"));
}
/*
** WEBPAGE: jchunk hidden
** URL: /jchunk/HASH?from=N&to=M
**
** Return lines of text from a file as a JSON array - one entry in the
** array for each line of text.
**
** **Warning:** This is an internal-use-only interface that is subject to
** change at any moment. External application should not use this interface
** since the application will break when this interface changes, and this
** interface will undoubtedly change.
**
** This page is intended to be used in an XHR from javascript on a
** diff page, to return unseen context to fill in additional context
** when the user clicks on the appropriate button. The response is
** always in JSON form and errors are reported as documented for
** ajax_route_error().
*/
void jchunk_page(void){
int rid = 0;
const char *zName = PD("name", "");
int iFrom = atoi(PD("from","0"));
int iTo = atoi(PD("to","0"));
int ln;
int go = 1;
const char *zSep;
Blob content;
Blob line;
Blob *pOut;
if(0){
ajax_route_error(400, "Just testing client-side error handling.");
return;
}
login_check_credentials();
if( !g.perm.Read ){
ajax_route_error(403, "Access requires Read permissions.");
return;
}
#if 1
/* Re-enable this block once this code is integrated somewhere into
the UI. */
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
if( rid==0 ){
ajax_route_error(404, "Unknown artifact: %h", zName);
return;
}
#else
/* This impl is only to simplify "manual" testing via the JS
console. */
rid = symbolic_name_to_rid(zName, "*");
if( rid==0 ){
ajax_route_error(404, "Unknown artifact: %h", zName);
return;
}else if( rid<0 ){
ajax_route_error(418, "Ambiguous artifact name: %h", zName);
return;
}
#endif
if( iFrom<1 || iTo<iFrom ){
ajax_route_error(500, "Invalid line range from=%d, to=%d.",
iFrom, iTo);
return;
}
content_get(rid, &content);
g.isConst = 1;
cgi_set_content_type("application/json");
ln = 0;
while( go && ln<iFrom ){
go = blob_line(&content, &line);
ln++;
}
pOut = cgi_output_blob();
blob_append(pOut, "[\n", 2);
zSep = 0;
while( go && ln<=iTo ){
if( zSep ) blob_append(pOut, zSep, 2);
blob_trim(&line);
blob_append_json_literal(pOut, blob_buffer(&line), blob_size(&line));
zSep = ",\n";
go = blob_line(&content, &line);
ln++;
}
blob_appendf(pOut,"]\n");
blob_reset(&content);
}
/*
** Generate a verbatim artifact as the result of an HTTP request.
** If zMime is not NULL, use it as the mimetype. If zMime is
** NULL, guess at the mimetype based on the filename
** associated with the artifact.
*/
|
| ︙ | ︙ | |||
2163 2164 2165 2166 2167 2168 2169 |
);
++nSpans;
iStart = iEnd = atoi(&zLn[i++]);
}while( zLn[i] && iStart && iEnd );
}
/*cgi_printf("<!-- ln span count=%d -->", nSpans);*/
cgi_append_content("<table class='numbered-lines'><tbody>"
| | | 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 |
);
++nSpans;
iStart = iEnd = atoi(&zLn[i++]);
}while( zLn[i] && iStart && iEnd );
}
/*cgi_printf("<!-- ln span count=%d -->", nSpans);*/
cgi_append_content("<table class='numbered-lines'><tbody>"
"<tr><td class='line-numbers'><pre>", -1);
iStart = iEnd = 0;
count_lines(z, nZ, &nLine);
for( n=1 ; n<=nLine; ++n ){
const char * zAttr = "";
const char * zId = "";
if(nSpans>0 && iEnd==0){/*Grab the next range of zLn marking*/
db_prepare(&q, "SELECT iStart, iEnd FROM lnos "
|
| ︙ | ︙ | |||
2205 2206 2207 2208 2209 2210 2211 |
iStart = 0;
}else if(n==iEnd){
zAttr = " class='selected-line end'";
iEnd = 0;
}else if( n>iStart && n<iEnd ){
zAttr = " class='selected-line'";
}
| | > | | 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 |
iStart = 0;
}else if(n==iEnd){
zAttr = " class='selected-line end'";
iEnd = 0;
}else if( n>iStart && n<iEnd ){
zAttr = " class='selected-line'";
}
cgi_printf("<span%s%s>%6d</span>\n", zId, zAttr, n)
/* ^^^ explicit \n is necessary for text-mode browsers. */;
}
cgi_append_content("</pre></td><td class='file-content'><pre>",-1);
if(zExt && *zExt){
cgi_printf("<code class='language-%h'>",zExt);
}else{
cgi_append_content("<code>", -1);
}
cgi_printf("%z", htmlize(z, nZ));
CX("</code></pre></td></tr></tbody></table>\n");
|
| ︙ | ︙ | |||
2359 2360 2361 2362 2363 2364 2365 |
url_initialize(&url, g.zPath);
url_add_parameter(&url, "name", zName);
url_add_parameter(&url, "ci", zCI); /* no-op if zCI is NULL */
if( zCI==0 && !isFile ){
/* If there is no ci= query parameter, then prefer to interpret
** name= as a hash for /artifact and /whatis. But for not for /file.
| | | 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 |
url_initialize(&url, g.zPath);
url_add_parameter(&url, "name", zName);
url_add_parameter(&url, "ci", zCI); /* no-op if zCI is NULL */
if( zCI==0 && !isFile ){
/* If there is no ci= query parameter, then prefer to interpret
** name= as a hash for /artifact and /whatis. But for not for /file.
** For /file, a name= without a ci= will prefer to use the default
** "tip" value for ci=. */
rid = name_to_rid(zName);
}
if( rid==0 ){
rid = artifact_from_ci_and_filename(0);
}
|
| ︙ | ︙ | |||
3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 |
@ <div><label>
if( fNewPropagateColor ){
@ <input type="checkbox" name="pclr" checked="checked" />
}else{
@ <input type="checkbox" name="pclr" />
}
@ Propagate color to descendants</label></div>
@ </td></tr>
@ <tr><th align="right" valign="top">Tags:</th>
@ <td valign="top">
@ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) />
@ Add the following new tag name to this check-in:</label>
@ <input type="text" size='15' name="tagname" value="%h(zNewTag)" \
| > > > > > | 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 |
@ <div><label>
if( fNewPropagateColor ){
@ <input type="checkbox" name="pclr" checked="checked" />
}else{
@ <input type="checkbox" name="pclr" />
}
@ Propagate color to descendants</label></div>
@ <div class='font-size-80'>Be aware that fixed background
@ colors will not interact well with all available skins.
@ It is recommended that fossil be allowed to select these
@ colors automatically so that it can take the skin's
@ preferences into account.</div>
@ </td></tr>
@ <tr><th align="right" valign="top">Tags:</th>
@ <td valign="top">
@ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) />
@ Add the following new tag name to this check-in:</label>
@ <input type="text" size='15' name="tagname" value="%h(zNewTag)" \
|
| ︙ | ︙ |
Changes to src/json_diff.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 |
*/
cson_value * json_generate_diff(const char *zFrom, const char *zTo,
int nContext, char fSbs,
char fHtml){
int fromid;
int toid;
int outLen;
Blob from = empty_blob, to = empty_blob, out = empty_blob;
cson_value * rc = NULL;
| > < | > | | 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 |
*/
cson_value * json_generate_diff(const char *zFrom, const char *zTo,
int nContext, char fSbs,
char fHtml){
int fromid;
int toid;
int outLen;
DiffConfig DCfg;
Blob from = empty_blob, to = empty_blob, out = empty_blob;
cson_value * rc = NULL;
int flags = (fSbs ? DIFF_SIDEBYSIDE : 0)
| (fHtml ? DIFF_HTML : 0);
fromid = name_to_typed_rid(zFrom, "*");
if(fromid<=0){
json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
"Could not resolve 'from' ID.");
return NULL;
}
toid = name_to_typed_rid(zTo, "*");
if(toid<=0){
json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
"Could not resolve 'to' ID.");
return NULL;
}
content_get(fromid, &from);
content_get(toid, &to);
blob_zero(&out);
diff_config_init(&DCfg, flags);
text_diff(&from, &to, &out, &DCfg);
blob_reset(&from);
blob_reset(&to);
outLen = blob_size(&out);
if(outLen>=0){
rc = cson_value_new_string(blob_buffer(&out),
(unsigned int)blob_size(&out));
}
|
| ︙ | ︙ |
Changes to src/json_wiki.c.
| ︙ | ︙ | |||
517 518 519 520 521 522 523 | char const * zV2 = NULL; cson_object * pay = NULL; int argPos = g.json.dispatchDepth; int r1 = 0, r2 = 0; Manifest * pW1 = NULL, *pW2 = NULL; Blob w1 = empty_blob, w2 = empty_blob, d = empty_blob; char const * zErrTag = NULL; | | | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
char const * zV2 = NULL;
cson_object * pay = NULL;
int argPos = g.json.dispatchDepth;
int r1 = 0, r2 = 0;
Manifest * pW1 = NULL, *pW2 = NULL;
Blob w1 = empty_blob, w2 = empty_blob, d = empty_blob;
char const * zErrTag = NULL;
DiffConfig DCfg;
char * zUuid = NULL;
if( !g.perm.Hyperlink ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'h' permissions.");
return NULL;
}
|
| ︙ | ︙ | |||
565 566 567 568 569 570 571 |
goto manifest;
}
blob_init(&w1, pW1->zWiki, -1);
blob_zero(&w2);
blob_init(&w2, pW2->zWiki, -1);
blob_zero(&d);
| | | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
goto manifest;
}
blob_init(&w1, pW1->zWiki, -1);
blob_zero(&w2);
blob_init(&w2, pW2->zWiki, -1);
blob_zero(&d);
diff_config_init(&DCfg, DIFF_IGNORE_EOLWS | DIFF_STRIP_EOLCR);
text_diff(&w1, &w2, &d, &DCfg);
blob_reset(&w1);
blob_reset(&w2);
pay = cson_new_object();
zUuid = json_wiki_get_uuid_for_rid( pW1->rid );
cson_object_set(pay, "v1", json_new_string(zUuid) );
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
/*
** Redirect to the page specified by the "g" query parameter.
** Or if there is no "g" query parameter, redirect to the homepage.
*/
static void redirect_to_g(void){
const char *zGoto = P("g");
if( zGoto ){
| | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
/*
** Redirect to the page specified by the "g" query parameter.
** Or if there is no "g" query parameter, redirect to the homepage.
*/
static void redirect_to_g(void){
const char *zGoto = P("g");
if( zGoto ){
cgi_redirectf("%R/%s",zGoto);
}else{
fossil_redirect_home();
}
}
/*
** Return an abbreviated project code. The abbreviation is the first
|
| ︙ | ︙ | |||
547 548 549 550 551 552 553 |
const char *zAnonPw = 0;
const char *zGoto = P("g");
int anonFlag; /* Login as "anonymous" would be useful */
char *zErrMsg = "";
int uid; /* User id logged in user */
char *zSha1Pw;
const char *zIpAddr; /* IP address of requestor */
| < | 547 548 549 550 551 552 553 554 555 556 557 558 559 560 |
const char *zAnonPw = 0;
const char *zGoto = P("g");
int anonFlag; /* Login as "anonymous" would be useful */
char *zErrMsg = "";
int uid; /* User id logged in user */
char *zSha1Pw;
const char *zIpAddr; /* IP address of requestor */
const int noAnon = P("noanon")!=0;
int rememberMe; /* If true, use persistent cookie, else
session cookie. Toggled per
checkbox. */
login_check_credentials();
fossil_redirect_to_https_if_needed(1);
|
| ︙ | ︙ | |||
638 639 640 641 642 643 644 |
@ The password cannot be changed for this type of login.
@ The password is unchanged.
@ </span></p>
;
}
}
zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
| < | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
@ The password cannot be changed for this type of login.
@ The password is unchanged.
@ </span></p>
;
}
}
zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
if(zUsername==0){
/* Initial login page hit. */
rememberMe = 0;
}else{
rememberMe = P("remember")!=0;
}
|
| ︙ | ︙ | |||
706 707 708 709 710 711 712 |
){
form_begin(0, "https:%s/login", g.zBaseURL+5);
}else{
form_begin(0, "%R/login");
}
if( zGoto ){
@ <input type="hidden" name="g" value="%h(zGoto)" />
| < < | 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
){
form_begin(0, "https:%s/login", g.zBaseURL+5);
}else{
form_begin(0, "%R/login");
}
if( zGoto ){
@ <input type="hidden" name="g" value="%h(zGoto)" />
}
if( anonFlag ){
@ <input type="hidden" name="anon" value="1" />
}
if( g.zLogin ){
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
@ <input type="submit" name="out" value="Logout"></p>
|
| ︙ | ︙ | |||
1024 1025 1026 1027 1028 1029 1030 |
if( g.localOpen ) zLogin = db_lget("default-user",0);
if( zLogin!=0 ){
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
}else{
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
}
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
| | | 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 |
if( g.localOpen ) zLogin = db_lget("default-user",0);
if( zLogin!=0 ){
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
}else{
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
}
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
zCap = "sxy";
g.noPswd = 1;
g.isHuman = 1;
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
}
/* Check the login cookie to see if it matches a known valid user.
*/
|
| ︙ | ︙ | |||
1053 1054 1055 1056 1057 1058 1059 |
}
}
}
if( zUser==0 ){
/* Invalid cookie */
}else if( fossil_strcmp(zUser, "anonymous")==0 ){
/* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
| | | 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 |
}
}
}
if( zUser==0 ){
/* Invalid cookie */
}else if( fossil_strcmp(zUser, "anonymous")==0 ){
/* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
** too old and the sha1 hash of TIME/SECRET must match HASH.
** SECRET is the "captcha-secret" value in the repository.
*/
double rTime = atof(zArg);
Blob b;
blob_zero(&b);
blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret",""));
sha1sum_blob(&b, &b);
|
| ︙ | ︙ | |||
1477 1478 1479 1480 1481 1482 1483 |
json_err( FSL_JSON_E_DENIED, NULL, 1 );
fossil_exit(0);
/* NOTREACHED */
assert(0);
}else
#endif /* FOSSIL_ENABLE_JSON */
{
| < | < < < > | | | | 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 |
json_err( FSL_JSON_E_DENIED, NULL, 1 );
fossil_exit(0);
/* NOTREACHED */
assert(0);
}else
#endif /* FOSSIL_ENABLE_JSON */
{
const char *zQS = P("QUERY_STRING");
const char *zPathInfo = PD("PATH_INFO","");
Blob redir;
blob_init(&redir, 0, 0);
if( zPathInfo[0]=='/' ) zPathInfo++; /* skip leading slash */
if( fossil_wants_https(1) ){
blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zPathInfo);
}else{
blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
}
if( zQS && zQS[0] ){
blob_appendf(&redir, "%%3f%T", zQS);
}
if( anonOk ) blob_append(&redir, "&anon", 5);
cgi_redirect(blob_str(&redir));
/* NOTREACHED */
assert(0);
}
}
/*
** Call this routine if the user lacks g.perm.Hyperlink permission. If
** the anonymous user has Hyperlink permission, then paint a mesage
** to inform the user that much more information is available by
** logging in as anonymous.
*/
void login_anonymous_available(void){
if( !g.perm.Hyperlink && g.anon.Hyperlink ){
const char *zUrl = PD("PATH_INFO", "");
@ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
@ Use <a href="%R/login?anon=1&g=%T(zUrl)">anonymous login</a>
@ to enable hyperlinks.</p>
}
}
/*
|
| ︙ | ︙ | |||
1611 1612 1613 1614 1615 1616 1617 |
style_finish_page();
return;
}
zPerms = db_get("default-perms", "u");
/* Prompt the user for email alerts if this repository is configured for
** email alerts and if the default permissions include "7" */
| | | | 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 |
style_finish_page();
return;
}
zPerms = db_get("default-perms", "u");
/* Prompt the user for email alerts if this repository is configured for
** email alerts and if the default permissions include "7" */
canDoAlerts = alert_tables_exist() && (db_int(0,
"SELECT fullcap(%Q) GLOB '*7*'", zPerms
) || db_get_boolean("selfreg-verify",0));
doAlerts = canDoAlerts && atoi(PD("alerts","1"))!=0;
zUserID = PDT("u","");
zPasswd = PDT("p","");
zConfirm = PDT("cp","");
zEAddr = PDT("ea","");
zDName = PDT("dn","");
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
2739 2740 2741 2742 2743 2744 2745 |
process_one_web_page(0, FileGlob, 0);
blob_reset(&g.cgiIn);
} while ( g.fSshClient & CGI_SSH_FOSSIL ||
g.fSshClient & CGI_SSH_COMPAT );
}
/*
| < < | > > > > > > > > > > > > > > > > > > > > > > | | 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 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 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 |
process_one_web_page(0, FileGlob, 0);
blob_reset(&g.cgiIn);
} while ( g.fSshClient & CGI_SSH_FOSSIL ||
g.fSshClient & CGI_SSH_COMPAT );
}
/*
** COMMAND: test-http
**
** Works like the [[http]] command but gives setup permission to all users,
** or whatever permission is described by "--usercap CAP".
**
** This command can used for interactive debugging of web pages. For
** example, one can put a simple HTTP request in a file like this:
**
** echo 'GET /timeline' >request.txt
**
** Then run (in a debugger) a command like this:
**
** fossil test-http --debug <request.txt
**
** This command is also used internally by the "ssh" sync protocol. Some
** special processing to support sync happens when this command is run
** and the SSH_CONNECTION environment variable is set. Use the --test
** option on interactive sessions to avoid that special processing when
** using this command interactively over SSH. A better solution would be
** to use a different command for "ssh" sync, but we cannot do that without
** breaking legacy.
**
** Options:
** --test Do not do special "sync" processing when operating
** over an SSH link.
** --th-trace Trace TH1 execution (for debugging purposes)
** --usercap CAP User capability string (Default: "sxy")
**
*/
void cmd_test_http(void){
const char *zIpAddr; /* IP address of remote client */
const char *zUserCap;
int bTest = 0;
Th_InitTraceLog();
zUserCap = find_option("usercap",0,1);
if( zUserCap==0 ){
g.useLocalauth = 1;
zUserCap = "sxy";
}
bTest = find_option("test",0,0)!=0;
login_set_capabilities(zUserCap, 0);
g.httpIn = stdin;
g.httpOut = stdout;
fossil_binary_mode(g.httpOut);
fossil_binary_mode(g.httpIn);
g.zExtRoot = find_option("extroot",0,1);
find_server_repository(2, 0);
g.cgiOutput = 1;
g.fNoHttpCompress = 1;
g.fullHttpReply = 1;
g.sslNotAvailable = 1; /* Avoid attempts to redirect */
zIpAddr = bTest ? 0 : cgi_ssh_remote_addr(0);
if( zIpAddr && zIpAddr[0] ){
g.fSshClient |= CGI_SSH_CLIENT;
ssh_request_loop(zIpAddr, 0);
}else{
cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
cgi_handle_http_request(0);
process_one_web_page(0, 0, 1);
|
| ︙ | ︙ | |||
2857 2858 2859 2860 2861 2862 2863 | ** such as ".txt" or ".html" or ".jpeg" and do not match the pattern ** "*.fossil*" will be served as static content. With the "ui" command, ** the REPOSITORY can only be a directory if the --notfound option is ** also present. ** ** For the special case REPOSITORY name of "/", the global configuration ** database is consulted for a list of all known repositories. The --repolist | | | > | 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 | ** such as ".txt" or ".html" or ".jpeg" and do not match the pattern ** "*.fossil*" will be served as static content. With the "ui" command, ** the REPOSITORY can only be a directory if the --notfound option is ** also present. ** ** For the special case REPOSITORY name of "/", the global configuration ** database is consulted for a list of all known repositories. The --repolist ** option is implied by this special case. The "fossil ui /" command is ** equivalent to "fossil all ui". To see all repositories owned by "user" ** on machine "remote" via ssh, run "fossil ui user@remote:/". ** ** By default, the "ui" command provides full administrative access without ** having to log in. This can be disabled by turning off the "localauth" ** setting. Automatic login for the "server" command is available if the ** --localauth option is present and the "localauth" setting is off and the ** connection is from localhost. The "ui" command also enables --repolist ** by default. |
| ︙ | ︙ | |||
2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 | ** each JavaScript file. ** bundled One single separate HTTP fetches all ** JavaScript concatenated together. ** Depending on the needs of any given page, inline ** and bundled modes might result in a single ** amalgamated script or several, but both approaches ** result in fewer HTTP requests than the separate mode. ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** --nobrowser Do not automatically launch a web-browser for the ** "fossil ui" command. ** --nocompress Do not compress HTTP replies ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl signal that no SSL connections are available (Always ** set by default for the "ui" command) ** --notfound URL Redirect ** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci" ** -P|--port TCPPORT listen to request on port TCPPORT | > > < | < | 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 |
** each JavaScript file.
** bundled One single separate HTTP fetches all
** JavaScript concatenated together.
** Depending on the needs of any given page, inline
** and bundled modes might result in a single
** amalgamated script or several, but both approaches
** result in fewer HTTP requests than the separate mode.
** --mainmenu FILE Override the mainmenu config setting with the contents
** of the given file.
** --max-latency N Do not let any single HTTP request run for more than N
** seconds (only works on unix)
** --nobrowser Do not automatically launch a web-browser for the
** "fossil ui" command.
** --nocompress Do not compress HTTP replies
** --nojail Drop root privileges but do not enter the chroot jail
** --nossl signal that no SSL connections are available (Always
** set by default for the "ui" command)
** --notfound URL Redirect
** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci"
** -P|--port TCPPORT listen to request on port TCPPORT
** --repolist If REPOSITORY is dir, URL "/" lists repos.
** --scgi Accept SCGI rather than HTTP
** --skin LABEL Use override skin LABEL
** --th-trace trace TH1 execution (for debugging purposes)
** --usepidkey Use saved encryption key from parent process. This is
** only necessary when using SEE on Windows.
**
** See also: [[cgi]], [[http]], [[winsrv]]
*/
void cmd_webserver(void){
int iPort, mxPort; /* Range of TCP ports allowed */
|
| ︙ | ︙ | |||
3070 3071 3072 3073 3074 3075 3076 |
if( zIpAddr==0 ){
zBrowserArg = mprintf("http://localhost:%%d/%s", zInitPage);
}else if( strchr(zIpAddr,':') ){
zBrowserArg = mprintf("http://[%s]:%%d/%s", zIpAddr, zInitPage);
}else{
zBrowserArg = mprintf("http://%s:%%d/%s", zIpAddr, zInitPage);
}
| < < < < | 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 |
if( zIpAddr==0 ){
zBrowserArg = mprintf("http://localhost:%%d/%s", zInitPage);
}else if( strchr(zIpAddr,':') ){
zBrowserArg = mprintf("http://[%s]:%%d/%s", zIpAddr, zInitPage);
}else{
zBrowserArg = mprintf("http://%s:%%d/%s", zIpAddr, zInitPage);
}
zBrowserCmd = mprintf("%s %!$ &", zBrowser, zBrowserArg);
fossil_free(zBrowserArg);
}
if( zRemote ){
/* If a USER@HOST:REPO argument is supplied, then use SSH to run
** "fossil ui --nobrowser" on the remote system and to set up a
** tunnel from the local machine to the remote. */
FILE *sshIn;
|
| ︙ | ︙ | |||
3120 3121 3122 3123 3124 3125 3126 |
zBrowserCmd = 0;
}
}
pclose(sshIn);
fossil_free(zBrowserCmd);
return;
}
| < < > > > > > < < < < | 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 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 |
zBrowserCmd = 0;
}
}
pclose(sshIn);
fossil_free(zBrowserCmd);
return;
}
if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
db_close(1);
/* Start up an HTTP server
*/
#if !defined(_WIN32)
/* Unix implementation */
if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
fossil_fatal("unable to listen on TCP socket %d", iPort);
}
/* For the parent process, the cgi_http_server() command above never
** returns (except in the case of an error). Instead, for each incoming
** client connection, a child process is created, file descriptors 0
** and 1 are bound to that connection, and the child returns.
**
** So, when control reaches this point, we are running as a
** child process, the HTTP or SCGI request is pending on file
** descriptor 0 and the reply should be written to file descriptor 1.
*/
if( zTimeout ){
fossil_set_timeout(atoi(zTimeout));
}else{
fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
}
g.httpIn = stdin;
g.httpOut = stdout;
signal(SIGSEGV, sigsegv_handler);
signal(SIGPIPE, sigpipe_handler);
if( g.fAnyTrace ){
fprintf(stderr, "/***** Subprocess %d *****/\n", getpid());
}
g.cgiOutput = 1;
find_server_repository(2, 0);
if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
allowRepoList = 1;
|
| ︙ | ︙ | |||
3172 3173 3174 3175 3176 3177 3178 |
process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
if( g.fAnyTrace ){
fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
getpid());
}
#else
/* Win32 implementation */
| < < < | 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 |
process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
if( g.fAnyTrace ){
fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
getpid());
}
#else
/* Win32 implementation */
if( allowRepoList ){
flags |= HTTP_SERVER_REPOLIST;
}
if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
}
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
212 213 214 215 216 217 218 | $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/accordion.js \ $(SRCDIR)/alerts/bflat2.wav \ $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ | < > | | | > < > | 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 | $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/accordion.js \ $(SRCDIR)/alerts/bflat2.wav \ $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ $(SRCDIR)/fossil.diff.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.wcontent.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ $(SRCDIR)/sounds/4.wav \ $(SRCDIR)/sounds/5.wav \ $(SRCDIR)/sounds/6.wav \ $(SRCDIR)/sounds/7.wav \ $(SRCDIR)/sounds/8.wav \ $(SRCDIR)/sounds/9.wav \ $(SRCDIR)/sounds/a.wav \ $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ |
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
351 352 353 354 355 356 357 |
if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
md5sum_init();
md5sum_step_text(z, n-35);
zHash = md5sum_finish(0);
if( memcmp(&z[n-33], zHash, 32)==0 ){
return 1;
}else{
| > | > | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
md5sum_init();
md5sum_step_text(z, n-35);
zHash = md5sum_finish(0);
if( memcmp(&z[n-33], zHash, 32)==0 ){
return 1;
}else{
if(pErr!=0){
blob_appendf(pErr, "incorrect Z-card cksum: expected %.32s", zHash);
}
return 2;
}
}
/*
** A structure used for rapid parsing of the Manifest file
*/
|
| ︙ | ︙ | |||
486 487 488 489 490 491 492 |
** if that is not the case for this artifact.
*/
if( !isRepeat ) g.parseCnt[0]++;
z = blob_materialize(pContent);
n = blob_size(pContent);
if( n<=0 || z[n-1]!='\n' ){
blob_reset(pContent);
| > | > > | > | 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 |
** if that is not the case for this artifact.
*/
if( !isRepeat ) g.parseCnt[0]++;
z = blob_materialize(pContent);
n = blob_size(pContent);
if( n<=0 || z[n-1]!='\n' ){
blob_reset(pContent);
if(pErr!=0){
blob_appendf(pErr, "%s", n ? "not terminated with \\n" : "zero-length");
}
return 0;
}
/* Strip off the PGP signature if there is one.
*/
remove_pgp_signature((const char**)&z, &n);
/* 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);
if(pErr!=0){
blob_appendf(pErr, "line 1 not recognized");
}
return 0;
}
/* Then verify the Z-card.
*/
#if 1
/* Disable this ***ONLY*** (ONLY!) when testing hand-written inputs
for card-related syntax errors. */
|
| ︙ | ︙ | |||
1120 1121 1122 1123 1124 1125 1126 |
if( !isRepeat ) g.parseCnt[p->type]++;
return p;
manifest_syntax_error:
{
char *zUuid = rid_to_uuid(rid);
if( zUuid ){
| > | > > | | | | > | 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 |
if( !isRepeat ) g.parseCnt[p->type]++;
return p;
manifest_syntax_error:
{
char *zUuid = rid_to_uuid(rid);
if( zUuid ){
if(pErr!=0){
blob_appendf(pErr, "artifact [%s] ", zUuid);
}
fossil_free(zUuid);
}
}
if(pErr!=0){
if( zErr ){
blob_appendf(pErr, "line %d: %s", lineNo, zErr);
}else{
blob_appendf(pErr, "unknown error on line %d", lineNo);
}
}
md5sum_init();
manifest_destroy(p);
return 0;
}
/*
|
| ︙ | ︙ |
Changes to src/markdown.c.
| ︙ | ︙ | |||
1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 |
int level = 0;
size_t i, end, skip, span_beg, span_size;
if( !size || data[0]!='#' ) return 0;
while( level<size && level<6 && data[level]=='#' ){ level++; }
for(i=level; i<size && (data[i]==' ' || data[i]=='\t'); i++);
span_beg = i;
for(end=i; end<size && data[end]!='\n'; end++);
skip = end;
if( end<=i ) return parse_paragraph(ob, rndr, data, size);
while( end && data[end-1]=='#' ){ end--; }
while( end && (data[end-1]==' ' || data[end-1]=='\t') ){ end--; }
| > | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 |
int level = 0;
size_t i, end, skip, span_beg, span_size;
if( !size || data[0]!='#' ) return 0;
while( level<size && level<6 && data[level]=='#' ){ level++; }
for(i=level; i<size && (data[i]==' ' || data[i]=='\t'); i++);
if ( i == level ) return parse_paragraph(ob, rndr, data, size);
span_beg = i;
for(end=i; end<size && data[end]!='\n'; end++);
skip = end;
if( end<=i ) return parse_paragraph(ob, rndr, data, size);
while( end && data[end-1]=='#' ){ end--; }
while( end && (data[end-1]==' ' || data[end-1]=='\t') ){ end--; }
|
| ︙ | ︙ |
Changes to src/merge3.c.
| ︙ | ︙ | |||
194 195 196 197 198 199 200 201 202 203 204 205 206 207 | 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 */ int useCrLf = 0; blob_zero(pOut); /* Merge results stored in pOut */ /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), ** keep it in the output. This should be secure enough not to cause ** unintended changes to the merged file and consistent with what ** users are using in their source files. | > | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | 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 */ int useCrLf = 0; DiffConfig DCfg; blob_zero(pOut); /* Merge results stored in pOut */ /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), ** keep it in the output. This should be secure enough not to cause ** unintended changes to the merged file and consistent with what ** users are using in their source files. |
| ︙ | ︙ | |||
222 223 224 225 226 227 228 | ** 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. */ | > | | | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
** 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.
*/
diff_config_init(&DCfg, 0);
aC1 = text_diff(pPivot, pV1, 0, &DCfg);
aC2 = text_diff(pPivot, pV2, 0, &DCfg);
if( aC1==0 || aC2==0 ){
free(aC1);
free(aC2);
return -1;
}
blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */
|
| ︙ | ︙ |
Changes to src/mkindex.c.
| ︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
#define CMDFLAG_COMMAND 0x0010 /* A command */
#define CMDFLAG_SETTING 0x0020 /* A setting */
#define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */
#define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */
#define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */
#define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */
#define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */
/**************************************************************************/
/*
** Each entry looks like this:
*/
typedef struct Entry {
int eType; /* CMDFLAG_* values */
| > | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
#define CMDFLAG_COMMAND 0x0010 /* A command */
#define CMDFLAG_SETTING 0x0020 /* A setting */
#define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */
#define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */
#define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */
#define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */
#define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */
#define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */
/**************************************************************************/
/*
** Each entry looks like this:
*/
typedef struct Entry {
int eType; /* CMDFLAG_* values */
|
| ︙ | ︙ | |||
255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
aEntry[nUsed].eType |= CMDFLAG_SENSITIVE;
}else if( j>6 && strncmp(&zLine[i], "width=", 6)==0 ){
aEntry[nUsed].iWidth = atoi(&zLine[i+6]);
}else if( j>8 && strncmp(&zLine[i], "default=", 8)==0 ){
aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8);
}else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){
aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
}else{
fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
zFile, nLine, j, &zLine[i]);
nErr++;
}
}
| > > | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
aEntry[nUsed].eType |= CMDFLAG_SENSITIVE;
}else if( j>6 && strncmp(&zLine[i], "width=", 6)==0 ){
aEntry[nUsed].iWidth = atoi(&zLine[i+6]);
}else if( j>8 && strncmp(&zLine[i], "default=", 8)==0 ){
aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8);
}else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){
aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
}else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
}else{
fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
zFile, nLine, j, &zLine[i]);
nErr++;
}
}
|
| ︙ | ︙ |
Changes to src/name.c.
| ︙ | ︙ | |||
222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
" AND event.type GLOB '%q'"
" ORDER BY event.mtime DESC LIMIT 1"
") LIMIT 1;",
zType, zTag, zTag, zType
);
}
/*
** Convert a symbolic name into a RID. Acceptable forms:
**
** * artifact hash (optionally enclosed in [...])
** * 4-character or larger prefix of a artifact
** * Symbolic Name
| > > > > > > > > | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
" AND event.type GLOB '%q'"
" ORDER BY event.mtime DESC LIMIT 1"
") LIMIT 1;",
zType, zTag, zTag, zType
);
}
/*
** Return true if character "c" is a character that might have been
** accidentally appended to the end of a URL.
*/
static int is_trailing_punct(char c){
return c=='.' || c=='_' || c==')' || c=='>' || c=='!' || c=='?' || c==',';
}
/*
** Convert a symbolic name into a RID. Acceptable forms:
**
** * artifact hash (optionally enclosed in [...])
** * 4-character or larger prefix of a artifact
** * Symbolic Name
|
| ︙ | ︙ | |||
483 484 485 486 487 488 489 490 491 492 493 494 495 496 |
rid = db_int(0,
"SELECT event.objid"
" FROM event"
" WHERE event.objid=%s"
" AND event.type GLOB '%q'", zTag /*safe-for-%s*/, zType);
}
}
}
return rid;
}
/*
** This routine takes a user-entered string and tries to convert it to
** an artifact hash.
| > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
rid = db_int(0,
"SELECT event.objid"
" FROM event"
" WHERE event.objid=%s"
" AND event.type GLOB '%q'", zTag /*safe-for-%s*/, zType);
}
}
return rid;
}
/* If nothing matches and the name ends with punctuation,
** then the name might have originated from a URL in plain text
** that was incorrectly extracted from the text. Try to remove
** the extra punctuation and rerun the match.
*/
if( nTag>4
&& is_trailing_punct(zTag[nTag-1])
&& !is_trailing_punct(zTag[nTag-2])
){
char *zNew = fossil_strndup(zTag, nTag-1);
rid = symbolic_name_to_rid(zNew,zType);
fossil_free(zNew);
}else
if( nTag>5
&& is_trailing_punct(zTag[nTag-1])
&& is_trailing_punct(zTag[nTag-2])
&& !is_trailing_punct(zTag[nTag-3])
){
char *zNew = fossil_strndup(zTag, nTag-2);
rid = symbolic_name_to_rid(zNew,zType);
fossil_free(zNew);
}
return rid;
}
/*
** This routine takes a user-entered string and tries to convert it to
** an artifact hash.
|
| ︙ | ︙ |
Changes to src/patch.c.
| ︙ | ︙ | |||
370 371 372 373 374 375 376 |
** First update the check-out to be at "baseline". Then loop through
** and update all files.
*/
void patch_apply(unsigned mFlags){
Stmt q;
Blob cmd;
| > > | | > > > > > > > > > | > | > > | 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 |
** First update the check-out to be at "baseline". Then loop through
** and update all files.
*/
void patch_apply(unsigned mFlags){
Stmt q;
Blob cmd;
blob_init(&cmd, 0, 0);
if( unsaved_changes(0) ){
if( (mFlags & PATCH_FORCE)==0 ){
fossil_fatal("there are unsaved changes in the current checkout");
}else{
blob_appendf(&cmd, "%$ revert", g.nameOfExe);
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s\n", blob_str(&cmd));
}else{
int rc = fossil_system(blob_str(&cmd));
if( rc ){
fossil_fatal("unable to revert preexisting changes: %s",
blob_str(&cmd));
}
}
blob_reset(&cmd);
}
}
file_chdir(g.zLocalRoot, 0);
db_prepare(&q,
"SELECT patch.cfg.value"
" FROM patch.cfg, localdb.vvar"
" WHERE patch.cfg.key='baseline'"
" AND localdb.vvar.name='checkout-hash'"
" AND patch.cfg.key<>localdb.vvar.name"
|
| ︙ | ︙ | |||
560 561 562 563 564 565 566 |
}
}
db_finalize(&q);
if( blob_size(&cmd)>0 ){
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s", blob_str(&cmd));
}else{
| | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
}
}
db_finalize(&q);
if( blob_size(&cmd)>0 ){
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s", blob_str(&cmd));
}else{
int rc = fossil_unsafe_system(blob_str(&cmd));
if( rc ){
fossil_fatal("unable to add new files:\n%s",
blob_str(&cmd));
}
}
blob_reset(&cmd);
}
|
| ︙ | ︙ | |||
645 646 647 648 649 650 651 652 653 654 655 656 657 |
** a FILE* obtained from popen() into which we write the patch, or from
** which we read the patch, depending on whether this is a push or pull.
*/
static FILE *patch_remote_command(
unsigned mFlags, /* flags */
const char *zThisCmd, /* "push" or "pull" */
const char *zRemoteCmd, /* "apply" or "create" */
const char *zRW /* "w" or "r" */
){
char *zRemote;
char *zDir;
Blob cmd;
FILE *f;
| > > > > > | > > > > | | > < < | < > | 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 |
** a FILE* obtained from popen() into which we write the patch, or from
** which we read the patch, depending on whether this is a push or pull.
*/
static FILE *patch_remote_command(
unsigned mFlags, /* flags */
const char *zThisCmd, /* "push" or "pull" */
const char *zRemoteCmd, /* "apply" or "create" */
const char *zFossilCmd, /* Name of "fossil" on remote system */
const char *zRW /* "w" or "r" */
){
char *zRemote;
char *zDir;
Blob cmd;
FILE *f;
Blob flgs;
char *zForce;
blob_init(&flgs, 0, 0);
if( mFlags & PATCH_FORCE ) blob_appendf(&flgs, " -f");
if( mFlags & PATCH_VERBOSE ) blob_appendf(&flgs, " -v");
if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n");
zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : "";
if( g.argc!=4 ){
usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
}
zRemote = fossil_strdup(g.argv[3]);
zDir = (char*)file_skip_userhost(zRemote);
if( zDir==0 ){
zDir = zRemote;
blob_init(&cmd, 0, 0);
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
}else{
Blob remote;
*(char*)(zDir-1) = 0;
transport_ssh_command(&cmd);
blob_appendf(&cmd, " -T");
blob_append_escaped_arg(&cmd, zRemote, 0);
blob_init(&remote, 0, 0);
if( zFossilCmd==0 ) zFossilCmd = "fossil";
blob_appendf(&remote, "%$ patch %s%s --dir64 %z -",
zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1));
blob_append_escaped_arg(&cmd, blob_str(&remote), 0);
blob_reset(&remote);
}
if( mFlags & PATCH_VERBOSE ){
fossil_print("# %s\n", blob_str(&cmd));
fflush(stdout);
}
f = popen(blob_str(&cmd), zRW);
if( f==0 ){
fossil_fatal("cannot run command: %s", blob_str(&cmd));
}
blob_reset(&cmd);
blob_reset(&flgs);
return f;
}
/*
** Show a diff for the patch currently loaded into database "patch".
*/
static void patch_diff(
unsigned mFlags, /* Patch flags. only -f is allowed */
DiffConfig *pCfg /* Diff options */
){
int nErr = 0;
Stmt q;
int bWebpage = (pCfg->diffFlags & DIFF_WEBPAGE)!=0;
Blob empty;
blob_zero(&empty);
if( (mFlags & PATCH_FORCE)==0 ){
/* Check to ensure that the patch is against the repository that
** we have opened.
**
|
| ︙ | ︙ | |||
736 737 738 739 740 741 742 |
" WHERE key='baseline'");
if( zBaseline ){
fossil_fatal("the baseline of the patch (check-in %S) is not found "
"in the %s repository", zBaseline, g.zRepositoryName);
}
}
}
| | > < | 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 |
" WHERE key='baseline'");
if( zBaseline ){
fossil_fatal("the baseline of the patch (check-in %S) is not found "
"in the %s repository", zBaseline, g.zRepositoryName);
}
}
}
diff_begin(pCfg);
db_prepare(&q,
"SELECT"
" (SELECT blob.rid FROM blob WHERE blob.uuid=chng.hash),"
" pathname," /* 1: new pathname */
" origname," /* 2: original pathname. Null if not renamed */
" delta," /* 3: delta. NULL if deleted. empty is no change */
" hash" /* 4: baseline hash */
" FROM patch.chng"
" ORDER BY pathname"
);
while( db_step(&q)==SQLITE_ROW ){
int rid;
const char *zName;
Blob a, b;
if( db_column_type(&q,0)!=SQLITE_INTEGER
&& db_column_type(&q,4)==SQLITE_TEXT
){
char *zUuid = fossil_strdup(db_column_text(&q,4));
char *zName = fossil_strdup(db_column_text(&q,1));
|
| ︙ | ︙ | |||
775 776 777 778 779 780 781 |
zUuid, zName);
}
}
zName = db_column_text(&q, 1);
rid = db_column_int(&q, 0);
if( db_column_type(&q,3)==SQLITE_NULL ){
| | | < < | < | | < < | < < < | < > | 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 |
zUuid, zName);
}
}
zName = db_column_text(&q, 1);
rid = db_column_int(&q, 0);
if( db_column_type(&q,3)==SQLITE_NULL ){
if( !bWebpage ) fossil_print("DELETE %s\n", zName);
diff_print_index(zName, pCfg, 0);
content_get(rid, &a);
diff_file_mem(&a, &empty, zName, pCfg);
}else if( rid==0 ){
db_ephemeral_blob(&q, 3, &a);
blob_uncompress(&a, &a);
if( !bWebpage ) fossil_print("ADDED %s\n", zName);
diff_print_index(zName, pCfg, 0);
diff_file_mem(&empty, &a, zName, pCfg);
blob_reset(&a);
}else if( db_column_bytes(&q, 3)>0 ){
Blob delta;
db_ephemeral_blob(&q, 3, &delta);
blob_uncompress(&delta, &delta);
content_get(rid, &a);
blob_delta_apply(&a, &delta, &b);
diff_file_mem(&a, &b, zName, pCfg);
blob_reset(&a);
blob_reset(&b);
blob_reset(&delta);
}
}
db_finalize(&q);
diff_end(pCfg, nErr);
if( nErr ) fossil_fatal("abort due to prior errors");
}
/*
** COMMAND: patch
**
|
| ︙ | ︙ | |||
837 838 839 840 841 842 843 | ** ** > fossil patch apply [DIRECTORY] FILENAME ** ** Apply the changes in FILENAME to the check-out at DIRECTORY, or ** in the current directory if DIRECTORY is omitted. Options: ** ** -f|--force Apply the patch even though there are unsaved | | > | 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 | ** ** > fossil patch apply [DIRECTORY] FILENAME ** ** Apply the changes in FILENAME to the check-out at DIRECTORY, or ** in the current directory if DIRECTORY is omitted. Options: ** ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved changes ** are reverted and permanently lost. ** -n|--dryrun Do nothing, but print what would have happened. ** -v|--verbose Extra output explaining what happens. ** ** > fossil patch diff [DIRECTORY] FILENAME ** ** Show a human-readable diff for the patch. All the usual ** diff flags described at "fossil help diff" apply. In addition: |
| ︙ | ︙ | |||
860 861 862 863 864 865 866 | ** a remote machine (using ssh) and apply the patch there. The ** REMOTE-CHECKOUT is in one of the following formats: ** ** * DIRECTORY ** * HOST:DIRECTORY ** * USER@HOST:DIRECTORY ** | | > > > > | > > > > | < < < | < < | | 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 |
** a remote machine (using ssh) and apply the patch there. The
** REMOTE-CHECKOUT is in one of the following formats:
**
** * DIRECTORY
** * HOST:DIRECTORY
** * USER@HOST:DIRECTORY
**
** Command-line options:
**
** -f|--force Apply the patch even though there are unsaved
** changes in the current check-out. Unsaved
** changes will be reverted and then the patch is
** applied.
** --fossilcmd EXE Name of the "fossil" executable on the remote
** -n|--dryrun Do nothing, but print what would have happened.
** -v|--verbose Extra output explaining what happens.
**
**
** > fossil patch pull REMOTE-CHECKOUT
**
** Like "fossil patch push" except that the transfer is from remote
** to local. All the same command-line options apply.
**
** > fossil patch view FILENAME
**
** View a summary of the changes in the binary patch FILENAME.
** Use "fossil patch diff" for detailed patch content.
**
** -v|--verbose Show extra detail about the patch.
**
*/
void patch_cmd(void){
const char *zCmd;
size_t n;
if( g.argc<3 ){
patch_usage:
|
| ︙ | ︙ | |||
913 914 915 916 917 918 919 |
zOut = patch_find_patch_filename("create");
verify_all_options();
db_must_be_within_tree();
patch_create(flags, zOut, stdout);
fossil_free(zOut);
}else
if( strncmp(zCmd, "diff", n)==0 ){
| < < < < > < < < | < < < < < | > | > > | | 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 |
zOut = patch_find_patch_filename("create");
verify_all_options();
db_must_be_within_tree();
patch_create(flags, zOut, stdout);
fossil_free(zOut);
}else
if( strncmp(zCmd, "diff", n)==0 ){
char *zIn;
unsigned flags = 0;
DiffConfig DCfg;
if( find_option("tk",0,0)!=0 ){
db_close(0);
diff_tk("patch diff", 3);
return;
}
diff_options(&DCfg, zCmd[0]=='g', 0);
db_find_and_open_repository(0, 0);
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
verify_all_options();
zIn = patch_find_patch_filename("apply");
patch_attach(zIn, stdin);
patch_diff(flags, &DCfg);
fossil_free(zIn);
}else
if( strncmp(zCmd, "pull", n)==0 ){
FILE *pIn = 0;
unsigned flags = 0;
const char *zFossilCmd = find_option("fossilcmd",0,1);
if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
db_must_be_within_tree();
verify_all_options();
pIn = patch_remote_command(flags & (~PATCH_FORCE),
"pull", "create", zFossilCmd, "r");
if( pIn ){
patch_attach(0, pIn);
pclose(pIn);
patch_apply(flags);
}
}else
if( strncmp(zCmd, "push", n)==0 ){
FILE *pOut = 0;
unsigned flags = 0;
const char *zFossilCmd = find_option("fossilcmd",0,1);
if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
db_must_be_within_tree();
verify_all_options();
pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w");
if( pOut ){
patch_create(0, 0, pOut);
pclose(pOut);
}
}else
if( strncmp(zCmd, "view", n)==0 ){
const char *zIn;
|
| ︙ | ︙ |
Changes to src/pikchrshow.c.
| ︙ | ︙ | |||
212 213 214 215 216 217 218 |
blob_append(pOut, "</div>\n", 7);
}
}else{
isErr = 2;
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
blob_append(pOut, "<pre class='error'>\n", 20);
}
| | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
blob_append(pOut, "</div>\n", 7);
}
}else{
isErr = 2;
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
blob_append(pOut, "<pre class='error'>\n", 20);
}
blob_appendf(pOut, "%h", zOut);
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
blob_append(pOut, "\n</pre>\n", 8);
}
}
fossil_free(zOut);
}
}
|
| ︙ | ︙ | |||
247 248 249 250 251 252 253 |
int pikFlags =
PIKCHR_PROCESS_DIV
| PIKCHR_PROCESS_SRC
| PIKCHR_PROCESS_ERR_PRE;
login_check_credentials();
if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){
| | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
int pikFlags =
PIKCHR_PROCESS_DIV
| PIKCHR_PROCESS_SRC
| PIKCHR_PROCESS_ERR_PRE;
login_check_credentials();
if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){
cgi_redirectf("%R/login?g=pikchrshow");
}
zContent = PD("content",P("p"));
if(P("ajax")!=0){
/* Called from the JS-side preview updater.
TODO: respond with JSON instead.*/
cgi_set_content_type("text/html");
if(zContent && *zContent){
|
| ︙ | ︙ | |||
333 334 335 336 337 338 339 |
"margin-right: 0.5em; vertical-align: middle;"
"}\n");
CX("body.pikchrshow .v-align-middle{"
"vertical-align: middle"
"}\n");
CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n");
} CX("</style>");
| | | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
"margin-right: 0.5em; vertical-align: middle;"
"}\n");
CX("body.pikchrshow .v-align-middle{"
"vertical-align: middle"
"}\n");
CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n");
} CX("</style>");
CX("<div>Input pikchr code and tap Preview (or Shift-Enter) to render "
"it:</div>");
CX("<div id='sbs-wrapper'>"); {
CX("<div id='pikchrshow-form'>"); {
CX("<textarea id='content' name='content' rows='15'>"
"%s</textarea>",zContent/*safe-for-%s*/);
CX("<div id='pikchrshow-controls'>"); {
CX("<button id='pikchr-submit-preview'>Preview</button>");
|
| ︙ | ︙ |
Changes to src/printf.c.
| ︙ | ︙ | |||
956 957 958 959 960 961 962 | ** On windows, transform the output into the current terminal encoding ** if the output is going to the screen. If output is redirected into ** a file, no translation occurs. Switch output mode to binary to ** properly process line-endings, make sure to switch the mode back to ** text when done. ** No translation ever occurs on unix. */ | | < | 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 |
** On windows, transform the output into the current terminal encoding
** if the output is going to the screen. If output is redirected into
** a file, no translation occurs. Switch output mode to binary to
** properly process line-endings, make sure to switch the mode back to
** text when done.
** No translation ever occurs on unix.
*/
void fossil_puts(const char *z, int toStdErr, int n){
FILE* out = (toStdErr ? stderr : stdout);
if( n==0 ) return;
assert( toStdErr==0 || toStdErr==1 );
if( toStdErr==0 ) stdoutAtBOL = (z[n-1]=='\n');
#if defined(_WIN32)
if( fossil_utf8_to_console(z, n, toStdErr) >= 0 ){
return;
}
|
| ︙ | ︙ | |||
982 983 984 985 986 987 988 |
/*
** Force the standard output cursor to move to the beginning
** of a line, if it is not there already.
*/
int fossil_force_newline(void){
if( g.cgiOutput==0 && stdoutAtBOL==0 ){
| | | 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 |
/*
** Force the standard output cursor to move to the beginning
** of a line, if it is not there already.
*/
int fossil_force_newline(void){
if( g.cgiOutput==0 && stdoutAtBOL==0 ){
fossil_puts("\n", 0, 1);
return 1;
}
return 0;
}
/*
** Indicate that the cursor has moved to the start of a line by means
|
| ︙ | ︙ | |||
1007 1008 1009 1010 1011 1012 1013 |
*/
void fossil_print(const char *zFormat, ...){
va_list ap;
va_start(ap, zFormat);
if( g.cgiOutput ){
cgi_vprintf(zFormat, ap);
}else{
| < | < < < | < < | | 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 |
*/
void fossil_print(const char *zFormat, ...){
va_list ap;
va_start(ap, zFormat);
if( g.cgiOutput ){
cgi_vprintf(zFormat, ap);
}else{
vxprintf(0, zFormat, ap);
}
va_end(ap);
}
void fossil_vprint(const char *zFormat, va_list ap){
if( g.cgiOutput ){
cgi_vprintf(zFormat, ap);
}else{
vxprintf(0, zFormat, ap);
}
}
/*
** Print a trace message on standard error.
*/
void fossil_trace(const char *zFormat, ...){
va_list ap;
Blob b;
va_start(ap, zFormat);
b = empty_blob;
vxprintf(&b, zFormat, ap);
fossil_puts(blob_buffer(&b), 1, blob_size(&b));
blob_reset(&b);
va_end(ap);
}
/*
** Write a message to the error log, if the error log filename is
** defined.
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
923 924 925 926 927 928 929 |
bNeedRebuild = db_exists("SELECT 1 FROM private");
delete_private_content();
}
if( !privateOnly ){
db_unprotect(PROTECT_ALL);
db_multi_exec(
"UPDATE user SET pw='';"
| | > > | | | | | | > > > > > > | | 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 |
bNeedRebuild = db_exists("SELECT 1 FROM private");
delete_private_content();
}
if( !privateOnly ){
db_unprotect(PROTECT_ALL);
db_multi_exec(
"UPDATE user SET pw='';"
"DELETE FROM config WHERE name IN"
"(WITH pattern(x) AS (VALUES"
" ('last-sync-*'),"
" ('sync-*'),"
" ('peer-*'),"
" ('login-group-*'),"
" ('skin:*'),"
" ('subrepo:*'),"
" ('http-auth:*'),"
" ('cert:*'),"
" ('syncwith:*'),"
" ('gitpush:*'),"
" ('ckout:*'),"
" ('draft[1-9]-*'),"
" ('baseurl:*')"
") SELECT name FROM config, pattern WHERE name GLOB x);"
);
if( bVerily ){
db_multi_exec(
"DELETE FROM concealed;\n"
"UPDATE rcvfrom SET ipaddr='unknown';\n"
"DROP TABLE IF EXISTS accesslog;\n"
"UPDATE user SET photo=NULL, info='';\n"
|
| ︙ | ︙ |
Deleted src/sbsdiff.js.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to src/security_audit.c.
| ︙ | ︙ | |||
279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
@ Anonymous users can act as moderators for wiki, tickets, or
@ forum posts. This defeats the whole purpose of moderation.
@ <p>Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum"
@ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5")
@ from users "anonymous" and "nobody"
@ on the <a href="setup_ulist">User Configuration</a> page.
}
/* The strict-manifest-syntax setting should be on. */
if( db_get_boolean("strict-manifest-syntax",1)==0 ){
@ <li><p><b>WARNING:</b>
@ The "strict-manifest-syntax" flag is off. This is a security
@ risk. Turn this setting on (its default) to protect the users
@ of this repository.
| > > > > > > > > > > > | 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 |
@ Anonymous users can act as moderators for wiki, tickets, or
@ forum posts. This defeats the whole purpose of moderation.
@ <p>Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum"
@ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5")
@ from users "anonymous" and "nobody"
@ on the <a href="setup_ulist">User Configuration</a> page.
}
/* Check to see if any TH1 scripts are configured to run on a sync
*/
if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'"
" AND length(value)>0") ){
@ <li><p><b>WARNING:</b>
@ TH1 scripts might be configured to run on any sync, push, pull, or
@ clone operation. See the the <a href="%R/xfersetup">/xfersetup</a>
@ page for more information. These TH1 scripts are a potential
@ security concern and so should be carefully audited by a human.
}
/* The strict-manifest-syntax setting should be on. */
if( db_get_boolean("strict-manifest-syntax",1)==0 ){
@ <li><p><b>WARNING:</b>
@ The "strict-manifest-syntax" flag is off. This is a security
@ risk. Turn this setting on (its default) to protect the users
@ of this repository.
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
"Automatic notifications of changes via outbound email");
| < < | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
"Automatic notifications of changes via outbound email");
setup_menu_entry("Transfers", "xfersetup",
"Configure the transfer system for this repository");
}
setup_menu_entry("Skins", "setup_skin",
"Select and/or modify the web interface \"skins\"");
setup_menu_entry("Moderation", "setup_modreq",
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
|
| ︙ | ︙ | |||
682 683 684 685 686 687 688 |
@ </table>
@
@ <p><form action="%R/setup_login_group" method="post"><div>
login_insert_csrf_secret();
@ To leave this login group press
@ <input type="submit" value="Leave Login Group" name="leave">
@ </form></p>
| < < < | 680 681 682 683 684 685 686 687 688 689 690 691 692 693 |
@ </table>
@
@ <p><form action="%R/setup_login_group" method="post"><div>
login_insert_csrf_secret();
@ To leave this login group press
@ <input type="submit" value="Leave Login Group" name="leave">
@ </form></p>
@ <hr /><h2>Implementation Details</h2>
@ <p>The following are fields from the CONFIG table related to login-groups,
@ provided here for instructional and debugging purposes:</p>
@ <table border='1' class='sortable' data-column-types='ttt' \
@ data-init-sort='1'>
@ <thead><tr>
@ <th>Config.Name<th>Config.Value<th>Config.mtime</tr>
|
| ︙ | ︙ |
Changes to src/shell.c.
| ︙ | ︙ | |||
649 650 651 652 653 654 655 |
while( *z ){
if( (0xc0&*(z++))!=0x80 ) n++;
}
return n;
}
/*
| > | > > > | > > > > > > > > > > > < | | < > > > | > | | > > | 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 |
while( *z ){
if( (0xc0&*(z++))!=0x80 ) n++;
}
return n;
}
/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.
** Otherwise return 0.
*/
static FILE * openChrSource(const char *zFile){
#ifdef _WIN32
struct _stat x = {0};
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
/* On Windows, open first, then check the stream nature. This order
** is necessary because _stat() and sibs, when checking a named pipe,
** effectively break the pipe as its supplier sees it. */
FILE *rv = fopen(zFile, "rb");
if( rv==0 ) return 0;
if( _fstat(_fileno(rv), &x) != 0
|| !STAT_CHR_SRC(x.st_mode)){
fclose(rv);
rv = 0;
}
return rv;
#else
struct stat x = {0};
int rc = stat(zFile, &x);
# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode))
if( rc!=0 ) return 0;
if( STAT_CHR_SRC(x.st_mode) ){
return fopen(zFile, "rb");
}else{
return 0;
}
#endif
#undef STAT_CHR_SRC
}
/*
** 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.
**
|
| ︙ | ︙ | |||
851 852 853 854 855 856 857 |
if( quote ){
len += 2;
for(i=0; i<nAppend; i++){
if( zAppend[i]==quote ) len++;
}
}
| | | 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 |
if( quote ){
len += 2;
for(i=0; i<nAppend; i++){
if( zAppend[i]==quote ) len++;
}
}
if( p->z==0 || p->n+len>=p->nAlloc ){
p->nAlloc = p->nAlloc*2 + len + 20;
p->z = realloc(p->z, p->nAlloc);
if( p->z==0 ) shell_out_of_memory();
}
if( quote ){
char *zCsr = p->z+p->n;
|
| ︙ | ︙ | |||
2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 | ** symlink, a text value containing the text of the link. For a ** directory, NULL. ** ** If a non-NULL value is specified for the optional $dir parameter and ** $path is a relative path, then $path is interpreted relative to $dir. ** And the paths returned in the "name" column of the table are also ** relative to directory $dir. */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <stdio.h> #include <string.h> #include <assert.h> | > > > > > | 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 | ** symlink, a text value containing the text of the link. For a ** directory, NULL. ** ** If a non-NULL value is specified for the optional $dir parameter and ** $path is a relative path, then $path is interpreted relative to $dir. ** And the paths returned in the "name" column of the table are also ** relative to directory $dir. ** ** Notes on building this extension for Windows: ** Unless linked statically with the SQLite library, a preprocessor ** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone ** DLL form of this extension for WIN32. See its use below for details. */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <stdio.h> #include <string.h> #include <assert.h> |
| ︙ | ︙ | |||
2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 | fileIntervals.LowPart = pFileTime->dwLowDateTime; fileIntervals.HighPart = pFileTime->dwHighDateTime; return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000; } /* ** This function attempts to normalize the time values found in the stat() ** buffer to UTC. This is necessary on Win32, where the runtime library ** appears to return these values as local times. */ static void statTimesToUtc( const char *zPath, | > > > > > > > > > > > > > > > > | 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 |
fileIntervals.LowPart = pFileTime->dwLowDateTime;
fileIntervals.HighPart = pFileTime->dwHighDateTime;
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
}
#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
# /* To allow a standalone DLL, use this next replacement function: */
# undef sqlite3_win32_utf8_to_unicode
# define sqlite3_win32_utf8_to_unicode utf8_to_utf16
#
LPWSTR utf8_to_utf16(const char *z){
int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0);
LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR));
if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) )
return rv;
sqlite3_free(rv);
return 0;
}
#endif
/*
** This function attempts to normalize the time values found in the stat()
** buffer to UTC. This is necessary on Win32, where the runtime library
** appears to return these values as local times.
*/
static void statTimesToUtc(
const char *zPath,
|
| ︙ | ︙ | |||
3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 |
lsModeFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = fsdirRegister(db);
}
return rc;
}
/************************* End ../ext/misc/fileio.c ********************/
/************************* Begin ../ext/misc/completion.c ******************/
/*
** 2017-07-10
**
** The author disclaims copyright to this source code. In place of
| > > > > > > > > | 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 |
lsModeFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = fsdirRegister(db);
}
return rc;
}
#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
/* To allow a standalone DLL, make test_windirent.c use the same
* redefined SQLite API calls as the above extension code does.
* Just pull in this .c to accomplish this. As a beneficial side
* effect, this extension becomes a single translation unit. */
# include "test_windirent.c"
#endif
/************************* End ../ext/misc/fileio.c ********************/
/************************* Begin ../ext/misc/completion.c ******************/
/*
** 2017-07-10
**
** The author disclaims copyright to this source code. In place of
|
| ︙ | ︙ | |||
5348 5349 5350 5351 5352 5353 5354 |
while( m!=0 && ((m>>32)&0xfff00000)==0 ){
m <<= 1;
e--;
}
e += 1075;
if( e<=0 ){
/* Subnormal */
| > > > | > | 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 |
while( m!=0 && ((m>>32)&0xfff00000)==0 ){
m <<= 1;
e--;
}
e += 1075;
if( e<=0 ){
/* Subnormal */
if( 1-e >= 64 ){
m = 0;
}else{
m >>= 1-e;
}
e = 0;
}else if( e>0x7ff ){
e = 0x7ff;
}
a = m & ((((sqlite3_int64)1)<<52)-1);
a |= e<<52;
if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
|
| ︙ | ︙ | |||
5894 5895 5896 5897 5898 5899 5900 |
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
#ifndef SQLITE_OMIT_VIRTUALTABLE
| | | 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 |
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( sqlite3_libversion_number()<3008012 && pzErrMsg!=0 ){
*pzErrMsg = sqlite3_mprintf(
"generate_series() requires SQLite 3.8.12 or later");
return SQLITE_ERROR;
}
rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0);
#endif
return rc;
|
| ︙ | ︙ | |||
6207 6208 6209 6210 6211 6212 6213 |
}
case RE_OP_ACCEPT: {
rc = 1;
goto re_match_end;
}
case RE_OP_CC_EXC: {
if( c==0 ) break;
| | | | 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 |
}
case RE_OP_ACCEPT: {
rc = 1;
goto re_match_end;
}
case RE_OP_CC_EXC: {
if( c==0 ) break;
/* fall-through */ goto re_op_cc_inc;
}
case RE_OP_CC_INC: re_op_cc_inc: {
int j = 1;
int n = pRe->aArg[x];
int hit = 0;
for(j=1; j>0 && j<n; j++){
if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
if( pRe->aArg[x+j]==c ){
hit = 1;
|
| ︙ | ︙ | |||
6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 | /* typedef sqlite3_int64 i64; */ /* typedef unsigned char u8; */ typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ #define MIN(a,b) ((a)<(b) ? (a) : (b)) #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) | > > > | 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 | /* typedef sqlite3_int64 i64; */ /* typedef unsigned char u8; */ typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ #define MIN(a,b) ((a)<(b) ? (a) : (b)) #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 #endif #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) |
| ︙ | ︙ | |||
7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 |
return (aBuf[1] << 8) + aBuf[0];
}
/*
** Read and return a 32-bit little-endian unsigned integer from buffer aBuf.
*/
static u32 zipfileGetU32(const u8 *aBuf){
return ((u32)(aBuf[3]) << 24)
+ ((u32)(aBuf[2]) << 16)
+ ((u32)(aBuf[1]) << 8)
+ ((u32)(aBuf[0]) << 0);
}
/*
| > | 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 |
return (aBuf[1] << 8) + aBuf[0];
}
/*
** Read and return a 32-bit little-endian unsigned integer from buffer aBuf.
*/
static u32 zipfileGetU32(const u8 *aBuf){
if( aBuf==0 ) return 0;
return ((u32)(aBuf[3]) << 24)
+ ((u32)(aBuf[2]) << 16)
+ ((u32)(aBuf[1]) << 8)
+ ((u32)(aBuf[0]) << 0);
}
/*
|
| ︙ | ︙ | |||
7546 7547 7548 7549 7550 7551 7552 |
ZipfileLFH lfh;
if( pFile ){
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else{
aRead = (u8*)&aBlob[pNew->cds.iOffset];
}
| | | 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 |
ZipfileLFH lfh;
if( pFile ){
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else{
aRead = (u8*)&aBlob[pNew->cds.iOffset];
}
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
if( aBlob && pNew->cds.szCompressed ){
pNew->aData = &pNew->aExtra[nExtra];
memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
}
|
| ︙ | ︙ | |||
7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 |
FILE *pFile, /* Read from this file if aBlob==0 */
ZipfileEOCD *pEOCD /* Object to populate */
){
u8 *aRead = pTab->aBuffer; /* Temporary buffer */
int nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
if( aBlob==0 ){
i64 iOff; /* Offset to read from */
i64 szFile; /* Total size of file in bytes */
fseek(pFile, 0, SEEK_END);
szFile = (i64)ftell(pFile);
if( szFile==0 ){
| > < | 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 |
FILE *pFile, /* Read from this file if aBlob==0 */
ZipfileEOCD *pEOCD /* Object to populate */
){
u8 *aRead = pTab->aBuffer; /* Temporary buffer */
int nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
memset(pEOCD, 0, sizeof(ZipfileEOCD));
if( aBlob==0 ){
i64 iOff; /* Offset to read from */
i64 szFile; /* Total size of file in bytes */
fseek(pFile, 0, SEEK_END);
szFile = (i64)ftell(pFile);
if( szFile==0 ){
return SQLITE_OK;
}
nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE));
iOff = szFile - nRead;
rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg);
}else{
nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE));
|
| ︙ | ︙ | |||
9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 | ** ************************************************************************* */ /* #include "sqlite3expert.h" */ #include <assert.h> #include <string.h> #include <stdio.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* typedef sqlite3_int64 i64; */ /* typedef sqlite3_uint64 u64; */ typedef struct IdxColumn IdxColumn; | > > > > > > > > > > > > > > > > > | 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 | ** ************************************************************************* */ /* #include "sqlite3expert.h" */ #include <assert.h> #include <string.h> #include <stdio.h> #if !defined(SQLITE_AMALGAMATION) #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 #endif #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif #endif /* !defined(SQLITE_AMALGAMATION) */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* typedef sqlite3_int64 i64; */ /* typedef sqlite3_uint64 u64; */ typedef struct IdxColumn IdxColumn; |
| ︙ | ︙ | |||
9925 9926 9927 9928 9929 9930 9931 |
nCol++;
}
idxFinalize(&rc, p1);
if( rc!=SQLITE_OK ){
sqlite3_free(pNew);
pNew = 0;
| | | | 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 |
nCol++;
}
idxFinalize(&rc, p1);
if( rc!=SQLITE_OK ){
sqlite3_free(pNew);
pNew = 0;
}else if( ALWAYS(pNew!=0) ){
pNew->zName = pCsr;
if( ALWAYS(pNew->zName!=0) ) memcpy(pNew->zName, zTab, nTab+1);
}
*ppOut = pNew;
return rc;
}
/*
|
| ︙ | ︙ | |||
10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 |
}
}
idxFinalize(&rc, pIdxList);
*pRc = rc;
return 0;
}
static int idxCreateFromCons(
sqlite3expert *p,
IdxScan *pScan,
IdxConstraint *pEq,
IdxConstraint *pTail
){
| > > > > > > > > > > > > > | 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 |
}
}
idxFinalize(&rc, pIdxList);
*pRc = rc;
return 0;
}
/* Callback for sqlite3_exec() with query with leading count(*) column.
* The first argument is expected to be an int*, referent to be incremented
* if that leading column is not exactly '0'.
*/
static int countNonzeros(void* pCount, int nc,
char* azResults[], char* azColumns[]){
(void)azColumns; /* Suppress unused parameter warning */
if( nc>0 && (azResults[0][0]!='0' || azResults[0][1]!=0) ){
*((int *)pCount) += 1;
}
return 0;
}
static int idxCreateFromCons(
sqlite3expert *p,
IdxScan *pScan,
IdxConstraint *pEq,
IdxConstraint *pTail
){
|
| ︙ | ︙ | |||
10124 10125 10126 10127 10128 10129 10130 |
for(pCons=pTail; pCons; pCons=pCons->pLink){
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
}
if( rc==SQLITE_OK ){
/* Hash the list of columns to come up with a name for the index */
const char *zTable = pScan->pTab->zName;
| > | > > | > | | | > | | > > > > > > > > > > > > > > > > > > | | > > > | > | 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 10237 10238 10239 10240 10241 10242 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 |
for(pCons=pTail; pCons; pCons=pCons->pLink){
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
}
if( rc==SQLITE_OK ){
/* Hash the list of columns to come up with a name for the index */
const char *zTable = pScan->pTab->zName;
int quoteTable = idxIdentifierRequiresQuotes(zTable);
char *zName = 0; /* Index name */
int collisions = 0;
do{
int i;
char *zFind;
for(i=0; zCols[i]; i++){
h += ((h<<3) + zCols[i]);
}
sqlite3_free(zName);
zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
if( zName==0 ) break;
/* Is is unique among table, view and index names? */
zFmt = "SELECT count(*) FROM sqlite_schema WHERE name=%Q"
" AND type in ('index','table','view')";
zFind = sqlite3_mprintf(zFmt, zName);
i = 0;
rc = sqlite3_exec(dbm, zFind, countNonzeros, &i, 0);
assert(rc==SQLITE_OK);
sqlite3_free(zFind);
if( i==0 ){
collisions = 0;
break;
}
++collisions;
}while( collisions<50 && zName!=0 );
if( collisions ){
/* This return means "Gave up trying to find a unique index name." */
rc = SQLITE_BUSY_TIMEOUT;
}else if( zName==0 ){
rc = SQLITE_NOMEM;
}else{
if( quoteTable ){
zFmt = "CREATE INDEX \"%w\" ON \"%w\"(%s)";
}else{
zFmt = "CREATE INDEX %s ON %s(%s)";
}
zIdx = sqlite3_mprintf(zFmt, zName, zTable, zCols);
if( !zIdx ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg);
if( rc!=SQLITE_OK ){
rc = SQLITE_BUSY_TIMEOUT;
}else{
idxHashAdd(&rc, &p->hIdx, zName, zIdx);
}
}
sqlite3_free(zName);
sqlite3_free(zIdx);
}
}
sqlite3_free(zCols);
|
| ︙ | ︙ | |||
11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 11079 11080 |
/* Do trigger processing to collect any extra IdxScan structures */
rc = idxProcessTriggers(p, pzErr);
/* Create candidate indexes within the in-memory database file */
if( rc==SQLITE_OK ){
rc = idxCreateCandidates(p);
}
/* Generate the stat1 data */
if( rc==SQLITE_OK ){
rc = idxPopulateStat1(p, pzErr);
}
| > > > > | 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 11196 11197 |
/* Do trigger processing to collect any extra IdxScan structures */
rc = idxProcessTriggers(p, pzErr);
/* Create candidate indexes within the in-memory database file */
if( rc==SQLITE_OK ){
rc = idxCreateCandidates(p);
}else if ( rc==SQLITE_BUSY_TIMEOUT ){
if( pzErr )
*pzErr = sqlite3_mprintf("Cannot find a unique index name to propose.");
return rc;
}
/* Generate the stat1 data */
if( rc==SQLITE_OK ){
rc = idxPopulateStat1(p, pzErr);
}
|
| ︙ | ︙ | |||
12060 12061 12062 12063 12064 12065 12066 12067 12068 12069 12070 12071 12072 12073 | u8 autoEQPtest; /* autoEQP is in test mode */ u8 autoEQPtrace; /* autoEQP is in trace mode */ u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ FILE *in; /* Read commands from this stream */ | > > | 12177 12178 12179 12180 12181 12182 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 | u8 autoEQPtest; /* autoEQP is in test mode */ u8 autoEQPtrace; /* autoEQP is in trace mode */ u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ FILE *in; /* Read commands from this stream */ |
| ︙ | ︙ | |||
12111 12112 12113 12114 12115 12116 12117 12118 |
OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
#endif
} aAuxDb[5], /* Array of all database connections */
*pAuxDb; /* Currently active database connection */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
| > | | 12230 12231 12232 12233 12234 12235 12236 12237 12238 12239 12240 12241 12242 12243 12244 12245 12246 |
OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
#endif
} aAuxDb[5], /* Array of all database connections */
*pAuxDb; /* Currently active database connection */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
char *zNonce; /* Nonce for temporary safe-mode excapes */
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
};
/* Allowed values for ShellState.autoEQP
*/
#define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */
#define AUTOEQP_on 1 /* Automatic EQP is on */
|
| ︙ | ︙ | |||
12156 12157 12158 12159 12160 12161 12162 | #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ #define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ #define SHFLG_Echo 0x00000040 /* .echo or --echo setting */ | | | 12276 12277 12278 12279 12280 12281 12282 12283 12284 12285 12286 12287 12288 12289 12290 | #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ #define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ #define SHFLG_Echo 0x00000040 /* .echo or --echo setting */ #define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */ #define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */ #define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */ /* ** Macros for testing and setting shellFlgs */ #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) |
| ︙ | ︙ | |||
12247 12248 12249 12250 12251 12252 12253 12254 12255 12256 12257 12258 12259 12260 |
sqlite3_value **apVal
){
ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
(void)nVal;
utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
sqlite3_result_value(pCtx, apVal[0]);
}
/*
** SQL function: edit(VALUE)
** edit(VALUE,EDITOR)
**
** These steps:
**
| > > > > > > > > > > > > > > > > > > > > > | 12367 12368 12369 12370 12371 12372 12373 12374 12375 12376 12377 12378 12379 12380 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 12391 12392 12393 12394 12395 12396 12397 12398 12399 12400 12401 |
sqlite3_value **apVal
){
ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
(void)nVal;
utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
sqlite3_result_value(pCtx, apVal[0]);
}
/*
** If in safe mode, print an error message described by the arguments
** and exit immediately.
*/
static void failIfSafeMode(
ShellState *p,
const char *zErrMsg,
...
){
if( p->bSafeMode ){
va_list ap;
char *zMsg;
va_start(ap, zErrMsg);
zMsg = sqlite3_vmprintf(zErrMsg, ap);
va_end(ap);
raw_printf(stderr, "line %d: ", p->lineno);
utf8_printf(stderr, "%s\n", zMsg);
exit(1);
}
}
/*
** SQL function: edit(VALUE)
** edit(VALUE,EDITOR)
**
** These steps:
**
|
| ︙ | ︙ | |||
12368 12369 12370 12371 12372 12373 12374 |
}
if( bBin ){
sqlite3_result_blob64(context, p, sz, sqlite3_free);
}else{
sqlite3_int64 i, j;
if( hasCRNL ){
/* If the original contains \r\n then do no conversions back to \n */
| < | 12509 12510 12511 12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 |
}
if( bBin ){
sqlite3_result_blob64(context, p, sz, sqlite3_free);
}else{
sqlite3_int64 i, j;
if( hasCRNL ){
/* If the original contains \r\n then do no conversions back to \n */
}else{
/* If the file did not originally contain \r\n then convert any new
** \r\n back into \n */
for(i=j=0; i<sz; i++){
if( p[i]=='\r' && p[i+1]=='\n' ) i++;
p[j++] = p[i];
}
|
| ︙ | ︙ | |||
12725 12726 12727 12728 12729 12730 12731 12732 12733 12734 12735 12736 12737 12738 |
return TRUE;
}
return FALSE;
}
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
/*
** When the ".auth ON" is set, the following authorizer callback is
** invoked. It always returns SQLITE_OK.
*/
static int shellAuth(
void *pClientData,
int op,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 12865 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 12892 12893 12894 12895 12896 12897 12898 12899 12900 12901 12902 12903 12904 12905 12906 12907 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 |
return TRUE;
}
return FALSE;
}
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
/*
** This authorizer runs in safe mode.
*/
static int safeModeAuth(
void *pClientData,
int op,
const char *zA1,
const char *zA2,
const char *zA3,
const char *zA4
){
ShellState *p = (ShellState*)pClientData;
static const char *azProhibitedFunctions[] = {
"edit",
"fts3_tokenizer",
"load_extension",
"readfile",
"writefile",
"zipfile",
"zipfile_cds",
};
UNUSED_PARAMETER(zA2);
UNUSED_PARAMETER(zA3);
UNUSED_PARAMETER(zA4);
switch( op ){
case SQLITE_ATTACH: {
failIfSafeMode(p, "cannot run ATTACH in safe mode");
break;
}
case SQLITE_FUNCTION: {
int i;
for(i=0; i<ArraySize(azProhibitedFunctions); i++){
if( sqlite3_stricmp(zA1, azProhibitedFunctions[i])==0 ){
failIfSafeMode(p, "cannot use the %s() function in safe mode",
azProhibitedFunctions[i]);
}
}
break;
}
}
return SQLITE_OK;
}
/*
** When the ".auth ON" is set, the following authorizer callback is
** invoked. It always returns SQLITE_OK.
*/
static int shellAuth(
void *pClientData,
int op,
|
| ︙ | ︙ | |||
12767 12768 12769 12770 12771 12772 12773 12774 12775 12776 12777 12778 12779 12780 |
if( az[i] ){
output_c_string(p->out, az[i]);
}else{
raw_printf(p->out, "NULL");
}
}
raw_printf(p->out, "\n");
return SQLITE_OK;
}
#endif
/*
** Print a schema statement. Part of MODE_Semi and MODE_Pretty output.
**
| > | 12950 12951 12952 12953 12954 12955 12956 12957 12958 12959 12960 12961 12962 12963 12964 |
if( az[i] ){
output_c_string(p->out, az[i]);
}else{
raw_printf(p->out, "NULL");
}
}
raw_printf(p->out, "\n");
if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
return SQLITE_OK;
}
#endif
/*
** Print a schema statement. Part of MODE_Semi and MODE_Pretty output.
**
|
| ︙ | ︙ | |||
14053 14054 14055 14056 14057 14058 14059 |
if( azData==0 ) shell_out_of_memory();
}
nRow++;
for(i=0; i<nColumn; i++){
z = (const char*)sqlite3_column_text(pStmt,i);
azData[nRow*nColumn + i] = z ? strdup(z) : 0;
}
| | | | 14237 14238 14239 14240 14241 14242 14243 14244 14245 14246 14247 14248 14249 14250 14251 14252 14253 |
if( azData==0 ) shell_out_of_memory();
}
nRow++;
for(i=0; i<nColumn; i++){
z = (const char*)sqlite3_column_text(pStmt,i);
azData[nRow*nColumn + i] = z ? strdup(z) : 0;
}
}while( sqlite3_step(pStmt)==SQLITE_ROW );
if( nColumn>p->nWidth ){
p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int));
if( p->colWidth==0 ) shell_out_of_memory();
for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0;
p->nWidth = nColumn;
p->actualWidth = &p->colWidth[nColumn];
}
memset(p->actualWidth, 0, nColumn*sizeof(int));
for(i=0; i<nColumn; i++){
|
| ︙ | ︙ | |||
14197 14198 14199 14200 14201 14202 14203 |
rc = sqlite3_step(pStmt);
/* if we have a result set... */
if( SQLITE_ROW == rc ){
/* allocate space for col name ptr, value ptr, and type */
int nCol = sqlite3_column_count(pStmt);
void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
if( !pData ){
| | | 14381 14382 14383 14384 14385 14386 14387 14388 14389 14390 14391 14392 14393 14394 14395 |
rc = sqlite3_step(pStmt);
/* if we have a result set... */
if( SQLITE_ROW == rc ){
/* allocate space for col name ptr, value ptr, and type */
int nCol = sqlite3_column_count(pStmt);
void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
if( !pData ){
shell_out_of_memory();
}else{
char **azCols = (char **)pData; /* Names of result columns */
char **azVals = &azCols[nCol]; /* Results */
int *aiTypes = (int *)&azVals[nCol]; /* Result types */
int i, x;
assert(sizeof(int) <= sizeof(char *));
/* save off ptrs to column names */
|
| ︙ | ︙ | |||
14940 14941 14942 14943 14944 14945 14946 14947 14948 14949 14950 14951 14952 14953 | " line One value per line", " list Values delimited by \"|\"", " markdown Markdown table format", " quote Escape answers as for SQL", " table ASCII-art table", " tabs Tab-separated values", " tcl TCL list elements", ".nullvalue STRING Use STRING in place of NULL values", ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", " --bom Put a UTF8 byte-order mark at the beginning", " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet (same as \".excel\")", #ifdef SQLITE_DEBUG | > | 15124 15125 15126 15127 15128 15129 15130 15131 15132 15133 15134 15135 15136 15137 15138 | " line One value per line", " list Values delimited by \"|\"", " markdown Markdown table format", " quote Escape answers as for SQL", " table ASCII-art table", " tabs Tab-separated values", " tcl TCL list elements", ".nonce STRING Disable safe mode for one command if the nonce matches", ".nullvalue STRING Use STRING in place of NULL values", ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", " --bom Put a UTF8 byte-order mark at the beginning", " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet (same as \".excel\")", #ifdef SQLITE_DEBUG |
| ︙ | ︙ | |||
15337 15338 15339 15340 15341 15342 15343 |
break;
}
rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
k = iOffset+j;
| | | 15522 15523 15524 15525 15526 15527 15528 15529 15530 15531 15532 15533 15534 15535 15536 |
break;
}
rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
k = iOffset+j;
if( k+16<=n && k>=0 ){
int ii;
for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
}
}
}
*pnData = n;
if( in!=p->in ){
|
| ︙ | ︙ | |||
15654 15655 15656 15657 15658 15659 15660 15661 15662 15663 15664 15665 15666 15667 |
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
}
}
#endif
}
}
/*
** Attempt to close the databaes connection. Report errors.
*/
void close_db(sqlite3 *db){
int rc = sqlite3_close(db);
| > > > | 15839 15840 15841 15842 15843 15844 15845 15846 15847 15848 15849 15850 15851 15852 15853 15854 15855 |
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
}
}
#endif
}
if( p->bSafeModePersist && p->db!=0 ){
sqlite3_set_authorizer(p->db, safeModeAuth, p);
}
}
/*
** Attempt to close the databaes connection. Report errors.
*/
void close_db(sqlite3 *db){
int rc = sqlite3_close(db);
|
| ︙ | ︙ | |||
16251 16252 16253 16254 16255 16256 16257 |
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
utf8_printf(stderr, "Error: (%d) %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
zQuery);
goto end_schema_xfer;
}
| | | 16439 16440 16441 16442 16443 16444 16445 16446 16447 16448 16449 16450 16451 16452 16453 |
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
utf8_printf(stderr, "Error: (%d) %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
zQuery);
goto end_schema_xfer;
}
while( sqlite3_step(pQuery)==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
printf("%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
|
| ︙ | ︙ | |||
16639 16640 16641 16642 16643 16644 16645 |
#endif
}
p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix);
}else{
p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
}
if( p->zTempFile==0 ){
| | < | 16827 16828 16829 16830 16831 16832 16833 16834 16835 16836 16837 16838 16839 16840 16841 |
#endif
}
p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix);
}else{
p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
}
if( p->zTempFile==0 ){
shell_out_of_memory();
}
}
/*
** The implementation of SQL scalar function fkey_collate_clause(), used
** by the ".lint fkey-indexes" command. This scalar function is always
|
| ︙ | ︙ | |||
18417 18418 18419 18420 18421 18422 18423 18424 18425 18426 18427 18428 18429 18430 18431 18432 18433 18434 18435 18436 18437 18438 18439 18440 18441 18442 18443 18444 18445 18446 18447 18448 18449 18450 18451 18452 18453 |
raw_printf(stderr, "Usage: .auth ON|OFF\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
if( booleanValue(azArg[1]) ){
sqlite3_set_authorizer(p->db, shellAuth, p);
}else{
sqlite3_set_authorizer(p->db, 0, 0);
}
}else
#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
open_db(p, 0);
rc = arDotCommand(p, 0, azArg, nArg);
}else
#endif
if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
){
const char *zDestFile = 0;
const char *zDb = 0;
sqlite3 *pDest;
sqlite3_backup *pBackup;
int j;
int bAsync = 0;
const char *zVfs = 0;
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
if( strcmp(z, "-append")==0 ){
zVfs = "apndvfs";
}else
| > > > > | 18604 18605 18606 18607 18608 18609 18610 18611 18612 18613 18614 18615 18616 18617 18618 18619 18620 18621 18622 18623 18624 18625 18626 18627 18628 18629 18630 18631 18632 18633 18634 18635 18636 18637 18638 18639 18640 18641 18642 18643 18644 |
raw_printf(stderr, "Usage: .auth ON|OFF\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
if( booleanValue(azArg[1]) ){
sqlite3_set_authorizer(p->db, shellAuth, p);
}else if( p->bSafeModePersist ){
sqlite3_set_authorizer(p->db, safeModeAuth, p);
}else{
sqlite3_set_authorizer(p->db, 0, 0);
}
}else
#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
open_db(p, 0);
failIfSafeMode(p, "cannot run .archive in safe mode");
rc = arDotCommand(p, 0, azArg, nArg);
}else
#endif
if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
){
const char *zDestFile = 0;
const char *zDb = 0;
sqlite3 *pDest;
sqlite3_backup *pBackup;
int j;
int bAsync = 0;
const char *zVfs = 0;
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
if( strcmp(z, "-append")==0 ){
zVfs = "apndvfs";
}else
|
| ︙ | ︙ | |||
18528 18529 18530 18531 18532 18533 18534 18535 18536 18537 18538 18539 18540 18541 |
** routine named test_breakpoint().
*/
if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
test_breakpoint();
}else
if( c=='c' && strcmp(azArg[0],"cd")==0 ){
if( nArg==2 ){
#if defined(_WIN32) || defined(WIN32)
wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
rc = !SetCurrentDirectoryW(z);
sqlite3_free(z);
#else
rc = chdir(azArg[1]);
| > | 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 |
** routine named test_breakpoint().
*/
if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
test_breakpoint();
}else
if( c=='c' && strcmp(azArg[0],"cd")==0 ){
failIfSafeMode(p, "cannot run .cd in safe mode");
if( nArg==2 ){
#if defined(_WIN32) || defined(WIN32)
wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
rc = !SetCurrentDirectoryW(z);
sqlite3_free(z);
#else
rc = chdir(azArg[1]);
|
| ︙ | ︙ | |||
18581 18582 18583 18584 18585 18586 18587 18588 18589 18590 18591 18592 18593 18594 |
utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase);
p->nCheck++;
}
sqlite3_free(zRes);
}else
if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
if( nArg==2 ){
tryToClone(p, azArg[1]);
}else{
raw_printf(stderr, "Usage: .clone FILENAME\n");
rc = 1;
}
}else
| > | 18773 18774 18775 18776 18777 18778 18779 18780 18781 18782 18783 18784 18785 18786 18787 |
utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase);
p->nCheck++;
}
sqlite3_free(zRes);
}else
if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
failIfSafeMode(p, "cannot run .clone in safe mode");
if( nArg==2 ){
tryToClone(p, azArg[1]);
}else{
raw_printf(stderr, "Usage: .clone FILENAME\n");
rc = 1;
}
}else
|
| ︙ | ︙ | |||
19142 19143 19144 19145 19146 19147 19148 19149 19150 19151 19152 19153 19154 19155 |
char *zSql; /* An SQL statement */
ImportCtx sCtx; /* Reader context */
char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
int eVerbose = 0; /* Larger for more console output */
int nSkip = 0; /* Initial lines to skip */
int useOutputMode = 1; /* Use output mode to determine separators */
memset(&sCtx, 0, sizeof(sCtx));
if( p->mode==MODE_Ascii ){
xRead = ascii_read_one_field;
}else{
xRead = csv_read_one_field;
}
for(i=1; i<nArg; i++){
| > | 19335 19336 19337 19338 19339 19340 19341 19342 19343 19344 19345 19346 19347 19348 19349 |
char *zSql; /* An SQL statement */
ImportCtx sCtx; /* Reader context */
char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
int eVerbose = 0; /* Larger for more console output */
int nSkip = 0; /* Initial lines to skip */
int useOutputMode = 1; /* Use output mode to determine separators */
failIfSafeMode(p, "cannot run .import in safe mode");
memset(&sCtx, 0, sizeof(sCtx));
if( p->mode==MODE_Ascii ){
xRead = ascii_read_one_field;
}else{
xRead = csv_read_one_field;
}
for(i=1; i<nArg; i++){
|
| ︙ | ︙ | |||
19448 19449 19450 19451 19452 19453 19454 |
isWO = sqlite3_column_int(pStmt, 1);
}
sqlite3_finalize(pStmt);
zSql = sqlite3_mprintf("PRAGMA index_xinfo='%q'", azArg[1]);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
i = 0;
| | | 19642 19643 19644 19645 19646 19647 19648 19649 19650 19651 19652 19653 19654 19655 19656 |
isWO = sqlite3_column_int(pStmt, 1);
}
sqlite3_finalize(pStmt);
zSql = sqlite3_mprintf("PRAGMA index_xinfo='%q'", azArg[1]);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
i = 0;
while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
char zLabel[20];
const char *zCol = (const char*)sqlite3_column_text(pStmt,2);
i++;
if( zCol==0 ){
if( sqlite3_column_int(pStmt,1)==-1 ){
zCol = "_ROWID_";
}else{
|
| ︙ | ︙ | |||
19593 19594 19595 19596 19597 19598 19599 19600 19601 19602 19603 19604 19605 19606 19607 19608 19609 19610 19611 19612 19613 19614 19615 19616 19617 19618 19619 19620 19621 19622 19623 19624 |
lintDotCommand(p, azArg, nArg);
}else
#ifndef SQLITE_OMIT_LOAD_EXTENSION
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
if( nArg<2 ){
raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
rc = 1;
goto meta_command_exit;
}
zFile = azArg[1];
zProc = nArg>=3 ? azArg[2] : 0;
open_db(p, 0);
rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
if( rc!=SQLITE_OK ){
utf8_printf(stderr, "Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}
}else
#endif
if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
if( nArg!=2 ){
raw_printf(stderr, "Usage: .log FILENAME\n");
rc = 1;
}else{
const char *zFile = azArg[1];
output_file_close(p->pLog);
p->pLog = output_file_open(zFile, 0);
| > > | 19787 19788 19789 19790 19791 19792 19793 19794 19795 19796 19797 19798 19799 19800 19801 19802 19803 19804 19805 19806 19807 19808 19809 19810 19811 19812 19813 19814 19815 19816 19817 19818 19819 19820 |
lintDotCommand(p, azArg, nArg);
}else
#ifndef SQLITE_OMIT_LOAD_EXTENSION
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
failIfSafeMode(p, "cannot run .load in safe mode");
if( nArg<2 ){
raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
rc = 1;
goto meta_command_exit;
}
zFile = azArg[1];
zProc = nArg>=3 ? azArg[2] : 0;
open_db(p, 0);
rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
if( rc!=SQLITE_OK ){
utf8_printf(stderr, "Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}
}else
#endif
if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
failIfSafeMode(p, "cannot run .log in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .log FILENAME\n");
rc = 1;
}else{
const char *zFile = azArg[1];
output_file_close(p->pLog);
p->pLog = output_file_open(zFile, 0);
|
| ︙ | ︙ | |||
19680 19681 19682 19683 19684 19685 19686 19687 19688 19689 19690 19691 19692 19693 |
raw_printf(stderr, "Error: mode should be one of: "
"ascii box column csv html insert json line list markdown "
"quote table tabs tcl\n");
rc = 1;
}
p->cMode = p->mode;
}else
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
"%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]);
}else{
raw_printf(stderr, "Usage: .nullvalue STRING\n");
| > > > > > > > > > > > > > > | 19876 19877 19878 19879 19880 19881 19882 19883 19884 19885 19886 19887 19888 19889 19890 19891 19892 19893 19894 19895 19896 19897 19898 19899 19900 19901 19902 19903 |
raw_printf(stderr, "Error: mode should be one of: "
"ascii box column csv html insert json line list markdown "
"quote table tabs tcl\n");
rc = 1;
}
p->cMode = p->mode;
}else
if( c=='n' && strcmp(azArg[0], "nonce")==0 ){
if( nArg!=2 ){
raw_printf(stderr, "Usage: .nonce NONCE\n");
rc = 1;
}else if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){
raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]);
exit(1);
}else{
p->bSafeMode = 0;
return 0; /* Return immediately to bypass the safe mode reset
** at the end of this procedure */
}
}else
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
"%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]);
}else{
raw_printf(stderr, "Usage: .nullvalue STRING\n");
|
| ︙ | ︙ | |||
19772 19773 19774 19775 19776 19777 19778 |
goto meta_command_exit;
}else{
zNewFilename = sqlite3_mprintf("%s", z);
}
}
/* If a filename is specified, try to open it first */
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
| | > > > > > > > | 19982 19983 19984 19985 19986 19987 19988 19989 19990 19991 19992 19993 19994 19995 19996 19997 19998 19999 20000 20001 20002 20003 |
goto meta_command_exit;
}else{
zNewFilename = sqlite3_mprintf("%s", z);
}
}
/* If a filename is specified, try to open it first */
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename);
if( p->bSafeMode
&& p->openMode!=SHELL_OPEN_HEXDB
&& zNewFilename
&& strcmp(zNewFilename,":memory:")!=0
){
failIfSafeMode(p, "cannot open disk-based database files in safe mode");
}
p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
}else{
p->pAuxDb->zFreeOnClose = zNewFilename;
|
| ︙ | ︙ | |||
19800 19801 19802 19803 19804 19805 19806 19807 19808 19809 19810 19811 19812 19813 |
char *zFile = 0;
int bTxtMode = 0;
int i;
int eMode = 0;
int bBOM = 0;
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
if( c=='e' ){
eMode = 'x';
bOnce = 2;
}else if( strncmp(azArg[0],"once",n)==0 ){
bOnce = 1;
}
for(i=1; i<nArg; i++){
| > | 20017 20018 20019 20020 20021 20022 20023 20024 20025 20026 20027 20028 20029 20030 20031 |
char *zFile = 0;
int bTxtMode = 0;
int i;
int eMode = 0;
int bBOM = 0;
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( c=='e' ){
eMode = 'x';
bOnce = 2;
}else if( strncmp(azArg[0],"once",n)==0 ){
bOnce = 1;
}
for(i=1; i<nArg; i++){
|
| ︙ | ︙ | |||
19929 19930 19931 19932 19933 19934 19935 |
}
sqlite3_finalize(pStmt);
pStmt = 0;
if( len ){
rx = sqlite3_prepare_v2(p->db,
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
| | | 20147 20148 20149 20150 20151 20152 20153 20154 20155 20156 20157 20158 20159 20160 20161 |
}
sqlite3_finalize(pStmt);
pStmt = 0;
if( len ){
rx = sqlite3_prepare_v2(p->db,
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0),
sqlite3_column_text(pStmt,1));
}
sqlite3_finalize(pStmt);
}
}else
|
| ︙ | ︙ | |||
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 |
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if( nArg!=2 ){
raw_printf(stderr, "Usage: .read FILE\n");
rc = 1;
goto meta_command_exit;
}
if( azArg[1][0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
raw_printf(stderr, "Error: pipes are not supported in this OS\n");
rc = 1;
p->out = stdout;
#else
p->in = popen(azArg[1]+1, "r");
if( p->in==0 ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
pclose(p->in);
}
#endif
| > | > | 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 |
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
int savedLineno = p->lineno;
failIfSafeMode(p, "cannot run .read in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .read FILE\n");
rc = 1;
goto meta_command_exit;
}
if( azArg[1][0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
raw_printf(stderr, "Error: pipes are not supported in this OS\n");
rc = 1;
p->out = stdout;
#else
p->in = popen(azArg[1]+1, "r");
if( p->in==0 ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
pclose(p->in);
}
#endif
}else if( (p->in = openChrSource(azArg[1]))==0 ){
utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
fclose(p->in);
}
p->in = inSaved;
p->lineno = savedLineno;
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){
const char *zSrcFile;
const char *zDb;
sqlite3 *pSrc;
sqlite3_backup *pBackup;
int nTimeout = 0;
failIfSafeMode(p, "cannot run .restore in safe mode");
if( nArg==2 ){
zSrcFile = azArg[1];
zDb = "main";
}else if( nArg==3 ){
zSrcFile = azArg[2];
zDb = azArg[1];
}else{
|
| ︙ | ︙ | |||
20359 20360 20361 20362 20363 20364 20365 20366 20367 20368 20369 20370 20371 20372 |
/* .session changeset FILE
** .session patchset FILE
** Write a changeset or patchset into a file. The file is overwritten.
*/
if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
FILE *out = 0;
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb");
if( out==0 ){
utf8_printf(stderr, "ERROR: cannot open \"%s\" for writing\n",
azCmd[1]);
}else{
| > | 20579 20580 20581 20582 20583 20584 20585 20586 20587 20588 20589 20590 20591 20592 20593 |
/* .session changeset FILE
** .session patchset FILE
** Write a changeset or patchset into a file. The file is overwritten.
*/
if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
FILE *out = 0;
failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]);
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb");
if( out==0 ){
utf8_printf(stderr, "ERROR: cannot open \"%s\" for writing\n",
azCmd[1]);
}else{
|
| ︙ | ︙ | |||
20773 20774 20775 20776 20777 20778 20779 20780 20781 20782 20783 20784 20785 20786 |
#ifndef SQLITE_NOHAVE_SYSTEM
if( c=='s'
&& (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
){
char *zCmd;
int i, x;
if( nArg<2 ){
raw_printf(stderr, "Usage: .system COMMAND\n");
rc = 1;
goto meta_command_exit;
}
zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
for(i=2; i<nArg; i++){
| > | 20994 20995 20996 20997 20998 20999 21000 21001 21002 21003 21004 21005 21006 21007 21008 |
#ifndef SQLITE_NOHAVE_SYSTEM
if( c=='s'
&& (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
){
char *zCmd;
int i, x;
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( nArg<2 ){
raw_printf(stderr, "Usage: .system COMMAND\n");
rc = 1;
goto meta_command_exit;
}
zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
for(i=2; i<nArg; i++){
|
| ︙ | ︙ | |||
20898 20899 20900 20901 20902 20903 20904 |
" AND name LIKE ?1", 0);
}else{
appendText(&s," WHERE type='index'"
" AND tbl_name LIKE ?1", 0);
}
}
rc = sqlite3_finalize(pStmt);
| > | | > | 21120 21121 21122 21123 21124 21125 21126 21127 21128 21129 21130 21131 21132 21133 21134 21135 21136 21137 |
" AND name LIKE ?1", 0);
}else{
appendText(&s," WHERE type='index'"
" AND tbl_name LIKE ?1", 0);
}
}
rc = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ){
appendText(&s, " ORDER BY 1", 0);
rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
}
freeText(&s);
if( rc ) return shellDatabaseError(p->db);
/* Run the SQL statement prepared by the above block. Store the results
** as an array of nul-terminated strings in azResult[]. */
nRow = nAlloc = 0;
azResult = 0;
|
| ︙ | ︙ | |||
21425 21426 21427 21428 21429 21430 21431 |
sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x);
}else
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
int j;
assert( nArg<=ArraySize(azArg) );
p->nWidth = nArg-1;
| | > | < < < > | < | > > > > | > > > > > < > > > > | > > > > | | > > > > > > > > > > > > > | > | | > > | < > | > > > > > > > | > > > > > > > > | > > | < > > > | > | | > > | > > > | > > > | | | | < | < | < > | > | 21649 21650 21651 21652 21653 21654 21655 21656 21657 21658 21659 21660 21661 21662 21663 21664 21665 21666 21667 21668 21669 21670 21671 21672 21673 21674 21675 21676 21677 21678 21679 21680 21681 21682 21683 21684 21685 21686 21687 21688 21689 21690 21691 21692 21693 21694 21695 21696 21697 21698 21699 21700 21701 21702 21703 21704 21705 21706 21707 21708 21709 21710 21711 21712 21713 21714 21715 21716 21717 21718 21719 21720 21721 21722 21723 21724 21725 21726 21727 21728 21729 21730 21731 21732 21733 21734 21735 21736 21737 21738 21739 21740 21741 21742 21743 21744 21745 21746 21747 21748 21749 21750 21751 21752 21753 21754 21755 21756 21757 21758 21759 21760 21761 21762 21763 21764 21765 21766 21767 21768 21769 21770 21771 21772 21773 21774 21775 21776 21777 21778 21779 21780 21781 21782 21783 21784 21785 21786 21787 21788 21789 21790 |
sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x);
}else
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
int j;
assert( nArg<=ArraySize(azArg) );
p->nWidth = nArg-1;
p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
for(j=1; j<nArg; j++){
p->colWidth[j-1] = (int)integerValue(azArg[j]);
}
}else
{
utf8_printf(stderr, "Error: unknown command or invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[0]);
rc = 1;
}
meta_command_exit:
if( p->outCount ){
p->outCount--;
if( p->outCount==0 ) output_reset(p);
}
p->bSafeMode = p->bSafeModePersist;
return rc;
}
/* Line scan result and intermediate states (supporting scan resumption)
*/
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif
typedef enum {
QSS_HasDark = 1<<CHAR_BIT, QSS_EndingSemi = 2<<CHAR_BIT,
QSS_CharMask = (1<<CHAR_BIT)-1, QSS_ScanMask = 3<<CHAR_BIT,
QSS_Start = 0
} QuickScanState;
#define QSS_SETV(qss, newst) ((newst) | ((qss) & QSS_ScanMask))
#define QSS_INPLAIN(qss) (((qss)&QSS_CharMask)==QSS_Start)
#define QSS_PLAINWHITE(qss) (((qss)&~QSS_EndingSemi)==QSS_Start)
#define QSS_PLAINDARK(qss) (((qss)&~QSS_EndingSemi)==QSS_HasDark)
#define QSS_SEMITERM(qss) (((qss)&~QSS_HasDark)==QSS_EndingSemi)
/*
** Scan line for classification to guide shell's handling.
** The scan is resumable for subsequent lines when prior
** return values are passed as the 2nd argument.
*/
static QuickScanState quickscan(char *zLine, QuickScanState qss){
char cin;
char cWait = (char)qss; /* intentional narrowing loss */
if( cWait==0 ){
PlainScan:
assert( cWait==0 );
while( (cin = *zLine++)!=0 ){
if( IsSpace(cin) )
continue;
switch (cin){
case '-':
if( *zLine!='-' )
break;
while((cin = *++zLine)!=0 )
if( cin=='\n')
goto PlainScan;
return qss;
case ';':
qss |= QSS_EndingSemi;
continue;
case '/':
if( *zLine=='*' ){
++zLine;
cWait = '*';
qss = QSS_SETV(qss, cWait);
goto TermScan;
}
break;
case '[':
cin = ']';
/* fall thru */
case '`': case '\'': case '"':
cWait = cin;
qss = QSS_HasDark | cWait;
goto TermScan;
default:
break;
}
qss = (qss & ~QSS_EndingSemi) | QSS_HasDark;
}
}else{
TermScan:
while( (cin = *zLine++)!=0 ){
if( cin==cWait ){
switch( cWait ){
case '*':
if( *zLine != '/' )
continue;
++zLine;
cWait = 0;
qss = QSS_SETV(qss, 0);
goto PlainScan;
case '`': case '\'': case '"':
if(*zLine==cWait){
++zLine;
continue;
}
/* fall thru */
case ']':
cWait = 0;
qss = QSS_SETV(qss, 0);
goto PlainScan;
default: assert(0);
}
}
}
}
return qss;
}
/*
** 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 line_is_command_terminator(char *zLine){
while( IsSpace(zLine[0]) ){ zLine++; };
if( zLine[0]=='/' )
zLine += 1; /* Oracle */
else if ( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o' )
zLine += 2; /* SQL Server */
else
return 0;
return quickscan(zLine,QSS_Start)==QSS_Start;
}
/*
** We need a default sqlite3_complete() implementation to use in case
** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes
** any arbitrary text is a complete SQL statement. This is not very
** user-friendly, but it does seem to work.
|
| ︙ | ︙ | |||
21552 21553 21554 21555 21556 21557 21558 |
sqlite3_free(zErrMsg);
zErrMsg = 0;
}else{
utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
}
return 1;
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
| > > | > < > > > > > > > | | > > > > < | < > | < < | > > | > | | 21833 21834 21835 21836 21837 21838 21839 21840 21841 21842 21843 21844 21845 21846 21847 21848 21849 21850 21851 21852 21853 21854 21855 21856 21857 21858 21859 21860 21861 21862 21863 21864 21865 21866 21867 21868 21869 21870 21871 21872 21873 21874 21875 21876 21877 21878 21879 21880 21881 21882 21883 21884 21885 21886 21887 21888 21889 21890 21891 21892 21893 21894 21895 21896 21897 21898 21899 21900 21901 21902 21903 21904 21905 21906 21907 21908 21909 21910 21911 21912 21913 21914 21915 21916 21917 21918 21919 21920 21921 21922 21923 21924 21925 21926 21927 21928 21929 21930 21931 21932 21933 21934 21935 21936 21937 21938 21939 21940 21941 21942 21943 21944 21945 21946 21947 21948 21949 21950 21951 21952 21953 21954 |
sqlite3_free(zErrMsg);
zErrMsg = 0;
}else{
utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
}
return 1;
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
char zLineBuf[2000];
sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
"changes: %lld total_changes: %lld",
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
raw_printf(p->out, "%s\n", zLineBuf);
}
return 0;
}
/*
** 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(ShellState *p){
char *zLine = 0; /* A single input line */
char *zSql = 0; /* Accumulated SQL text */
int nLine; /* Length of current line */
int nSql = 0; /* Bytes of zSql[] used */
int nAlloc = 0; /* Allocated zSql[] space */
int rc; /* Error code */
int errCnt = 0; /* Number of errors seen */
int startline = 0; /* Line number for start of current input */
QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */
p->lineno = 0;
while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){
fflush(p->out);
zLine = one_input_line(p->in, zLine, nSql>0);
if( zLine==0 ){
/* End of input */
if( p->in==0 && stdin_is_interactive ) printf("\n");
break;
}
if( seenInterrupt ){
if( p->in!=0 ) break;
seenInterrupt = 0;
}
p->lineno++;
if( QSS_INPLAIN(qss)
&& line_is_command_terminator(zLine)
&& line_is_complete(zSql, nSql) ){
memcpy(zLine,";",2);
}
qss = quickscan(zLine, qss);
if( QSS_PLAINWHITE(qss) && nSql==0 ){
if( ShellHasFlag(p, SHFLG_Echo) )
printf("%s\n", zLine);
/* Just swallow single-line whitespace */
qss = QSS_Start;
continue;
}
if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
if( zLine[0]=='.' ){
rc = do_meta_command(zLine, p);
if( rc==2 ){ /* exit requested */
break;
}else if( rc ){
errCnt++;
}
}
qss = QSS_Start;
continue;
}
/* No single-line dispositions remain; accumulate line(s). */
nLine = strlen30(zLine);
if( nSql+nLine+2>=nAlloc ){
/* Grow buffer by half-again increments when big. */
nAlloc = nSql+(nSql>>1)+nLine+100;
zSql = realloc(zSql, nAlloc);
if( zSql==0 ) shell_out_of_memory();
}
if( nSql==0 ){
int i;
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
assert( nAlloc>0 && zSql!=0 );
memcpy(zSql, zLine+i, nLine+1-i);
startline = p->lineno;
nSql = nLine-i;
}else{
zSql[nSql++] = '\n';
memcpy(zSql+nSql, zLine, nLine+1);
nSql += nLine;
}
if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){
errCnt += runOneSqlLine(p, zSql, p->in, startline);
nSql = 0;
if( p->outCount ){
output_reset(p);
p->outCount = 0;
}else{
clearTempFile(p);
}
p->bSafeMode = p->bSafeModePersist;
qss = QSS_Start;
}else if( nSql && QSS_PLAINWHITE(qss) ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql);
nSql = 0;
qss = QSS_Start;
}
}
if( nSql && QSS_PLAINDARK(qss) ){
errCnt += runOneSqlLine(p, zSql, p->in, startline);
}
free(zSql);
free(zLine);
return errCnt>0;
}
|
| ︙ | ︙ | |||
21807 21808 21809 21810 21811 21812 21813 21814 21815 21816 21817 21818 21819 21820 21821 21822 21823 21824 | " -memtrace trace all memory allocations and deallocations\n" " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -separator SEP set output column separator. Default: '|'\n" #ifdef SQLITE_ENABLE_SORTER_REFERENCES " -sorterref SIZE sorter references threshold size\n" #endif " -stats print memory stats before each finalize\n" " -table set output mode to 'table'\n" " -tabs set output mode to 'tabs'\n" | > > | 22101 22102 22103 22104 22105 22106 22107 22108 22109 22110 22111 22112 22113 22114 22115 22116 22117 22118 22119 22120 | " -memtrace trace all memory allocations and deallocations\n" " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" " -nonce STRING set the safe-mode escape nonce\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -safe enable safe-mode\n" " -separator SEP set output column separator. Default: '|'\n" #ifdef SQLITE_ENABLE_SORTER_REFERENCES " -sorterref SIZE sorter references threshold size\n" #endif " -stats print memory stats before each finalize\n" " -table set output mode to 'table'\n" " -tabs set output mode to 'tabs'\n" |
| ︙ | ︙ | |||
22096 22097 22098 22099 22100 22101 22102 22103 22104 22105 22106 22107 22108 22109 |
int n, sz;
sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
if( sz<0 ) sz = 0;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
if( n<0 ) n = 0;
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
#ifdef SQLITE_ENABLE_VFSTRACE
}else if( strcmp(z,"-vfstrace")==0 ){
extern int vfstrace_register(
const char *zTraceName,
const char *zOldVfsName,
int (*xOut)(const char*,void*),
void *pOutArg,
| > > > > > > > > | 22392 22393 22394 22395 22396 22397 22398 22399 22400 22401 22402 22403 22404 22405 22406 22407 22408 22409 22410 22411 22412 22413 |
int n, sz;
sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
if( sz<0 ) sz = 0;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
if( n<0 ) n = 0;
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
}else if( strcmp(z,"-threadsafe")==0 ){
int n;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
switch( n ){
case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
}
#ifdef SQLITE_ENABLE_VFSTRACE
}else if( strcmp(z,"-vfstrace")==0 ){
extern int vfstrace_register(
const char *zTraceName,
const char *zOldVfsName,
int (*xOut)(const char*,void*),
void *pOutArg,
|
| ︙ | ︙ | |||
22148 22149 22150 22151 22152 22153 22154 22155 22156 22157 22158 22159 22160 22161 |
** command, so ignore them */
break;
#endif
}else if( strcmp(z, "-memtrace")==0 ){
sqlite3MemTraceActivate(stderr);
}else if( strcmp(z,"-bail")==0 ){
bail_on_error = 1;
}
}
verify_uninitialized();
#ifdef SQLITE_SHELL_INIT_PROC
{
| > > > > > | 22452 22453 22454 22455 22456 22457 22458 22459 22460 22461 22462 22463 22464 22465 22466 22467 22468 22469 22470 |
** command, so ignore them */
break;
#endif
}else if( strcmp(z, "-memtrace")==0 ){
sqlite3MemTraceActivate(stderr);
}else if( strcmp(z,"-bail")==0 ){
bail_on_error = 1;
}else if( strcmp(z,"-nonce")==0 ){
free(data.zNonce);
data.zNonce = strdup(argv[++i]);
}else if( strcmp(z,"-safe")==0 ){
/* no-op - catch this on the second pass */
}
}
verify_uninitialized();
#ifdef SQLITE_SHELL_INIT_PROC
{
|
| ︙ | ︙ | |||
22274 22275 22276 22277 22278 22279 22280 |
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-nullvalue")==0 ){
sqlite3_snprintf(sizeof(data.nullValue), data.nullValue,
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-header")==0 ){
data.showHeader = 1;
| > | > | 22583 22584 22585 22586 22587 22588 22589 22590 22591 22592 22593 22594 22595 22596 22597 22598 22599 22600 |
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-nullvalue")==0 ){
sqlite3_snprintf(sizeof(data.nullValue), data.nullValue,
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-header")==0 ){
data.showHeader = 1;
ShellSetFlag(&data, SHFLG_HeaderSet);
}else if( strcmp(z,"-noheader")==0 ){
data.showHeader = 0;
ShellSetFlag(&data, SHFLG_HeaderSet);
}else if( strcmp(z,"-echo")==0 ){
ShellSetFlag(&data, SHFLG_Echo);
}else if( strcmp(z,"-eqp")==0 ){
data.autoEQP = AUTOEQP_on;
}else if( strcmp(z,"-eqpfull")==0 ){
data.autoEQP = AUTOEQP_full;
}else if( strcmp(z,"-stats")==0 ){
|
| ︙ | ︙ | |||
22308 22309 22310 22311 22312 22313 22314 22315 22316 22317 22318 22319 22320 22321 |
stdin_is_interactive = 0;
}else if( strcmp(z,"-heap")==0 ){
i++;
}else if( strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
}else if( strcmp(z,"-memtrace")==0 ){
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( strcmp(z,"-sorterref")==0 ){
i++;
| > > > > | 22619 22620 22621 22622 22623 22624 22625 22626 22627 22628 22629 22630 22631 22632 22633 22634 22635 22636 |
stdin_is_interactive = 0;
}else if( strcmp(z,"-heap")==0 ){
i++;
}else if( strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( strcmp(z,"-threadsafe")==0 ){
i+=2;
}else if( strcmp(z,"-nonce")==0 ){
i += 2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
}else if( strcmp(z,"-memtrace")==0 ){
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( strcmp(z,"-sorterref")==0 ){
i++;
|
| ︙ | ︙ | |||
22366 22367 22368 22369 22370 22371 22372 22373 22374 22375 22376 22377 22378 22379 |
arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
}else{
arDotCommand(&data, 1, argv+i, argc-i);
}
readStdin = 0;
break;
#endif
}else{
utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
raw_printf(stderr,"Use -help for a list of options.\n");
return 1;
}
data.cMode = data.mode;
}
| > > | 22681 22682 22683 22684 22685 22686 22687 22688 22689 22690 22691 22692 22693 22694 22695 22696 |
arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
}else{
arDotCommand(&data, 1, argv+i, argc-i);
}
readStdin = 0;
break;
#endif
}else if( strcmp(z,"-safe")==0 ){
data.bSafeMode = data.bSafeModePersist = 1;
}else{
utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
raw_printf(stderr,"Use -help for a list of options.\n");
return 1;
}
data.cMode = data.mode;
}
|
| ︙ | ︙ | |||
22468 22469 22470 22471 22472 22473 22474 22475 22476 22477 22478 22479 | data.doXdgOpen = 0; clearTempFile(&data); #if !SQLITE_SHELL_IS_UTF8 for(i=0; i<argcToFree; i++) free(argvToFree[i]); free(argvToFree); #endif free(data.colWidth); /* Clear the global data structure so that valgrind will detect memory ** leaks */ memset(&data, 0, sizeof(data)); return rc; } | > | 22785 22786 22787 22788 22789 22790 22791 22792 22793 22794 22795 22796 22797 | data.doXdgOpen = 0; clearTempFile(&data); #if !SQLITE_SHELL_IS_UTF8 for(i=0; i<argcToFree; i++) free(argvToFree[i]); free(argvToFree); #endif free(data.colWidth); free(data.zNonce); /* Clear the global data structure so that valgrind will detect memory ** leaks */ memset(&data, 0, sizeof(data)); return rc; } |
Changes to src/skins.c.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
** be used (rank 3, above), then returns 0.
*/
char *skin_use_alternative(const char *zName, int rank){
static int currentRank = 5;
int i;
Blob err = BLOB_INITIALIZER;
if(rank > currentRank) return 0;
if( zName && 1==rank && strchr(zName, '/')!=0 ){
zAltSkinDir = fossil_strdup(zName);
return 0;
}
if( zName && sqlite3_strglob("draft[1-9]", zName)==0 ){
skin_use_draft(zName[5] - '0');
return 0;
| > | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
** be used (rank 3, above), then returns 0.
*/
char *skin_use_alternative(const char *zName, int rank){
static int currentRank = 5;
int i;
Blob err = BLOB_INITIALIZER;
if(rank > currentRank) return 0;
currentRank = rank;
if( zName && 1==rank && strchr(zName, '/')!=0 ){
zAltSkinDir = fossil_strdup(zName);
return 0;
}
if( zName && sqlite3_strglob("draft[1-9]", zName)==0 ){
skin_use_draft(zName[5] - '0');
return 0;
|
| ︙ | ︙ | |||
864 865 866 867 868 869 870 |
}
@ <hr />
@ Baseline: \
skin_emit_skin_selector("basis", zBasis, zDraft);
@ <input type="submit" name="diff" value="Unified Diff" />
@ <input type="submit" name="sbsdiff" value="Side-by-Side Diff" />
if( P("diff")!=0 || P("sbsdiff")!=0 ){
| < > > > | > | | > | < | 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 |
}
@ <hr />
@ Baseline: \
skin_emit_skin_selector("basis", zBasis, zDraft);
@ <input type="submit" name="diff" value="Unified Diff" />
@ <input type="submit" name="sbsdiff" value="Side-by-Side Diff" />
if( P("diff")!=0 || P("sbsdiff")!=0 ){
Blob from, to, out;
DiffConfig DCfg;
construct_diff_flags(1, &DCfg);
DCfg.diffFlags |= DIFF_STRIP_EOLCR;
if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE;
blob_init(&to, zContent, -1);
blob_init(&from, skin_file_content(zBasis, zFile), -1);
blob_zero(&out);
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
text_diff(&from, &to, &out, &DCfg);
@ %s(blob_str(&out))
}else{
DCfg.diffFlags |= DIFF_LINENO;
text_diff(&from, &to, &out, &DCfg);
@ <pre class="udiff">
@ %s(blob_str(&out))
@ </pre>
}
blob_reset(&from);
blob_reset(&to);
blob_reset(&out);
|
| ︙ | ︙ |
Changes to src/sqlite3.c.
| ︙ | ︙ | |||
450 451 452 453 454 455 456 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.37.0" #define SQLITE_VERSION_NUMBER 3037000 | | | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.37.0" #define SQLITE_VERSION_NUMBER 3037000 #define SQLITE_SOURCE_ID "2021-10-06 10:36:56 566e6974892ebd3d3de8d77b24655257a5efe14434c553e1a25fc680b201b336" /* ** 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 |
| ︙ | ︙ | |||
863 864 865 866 867 868 869 870 871 872 873 874 875 876 | #define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) #define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) #define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) #define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) | > | 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 | #define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) #define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) #define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) #define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) #define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) |
| ︙ | ︙ | |||
13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 | */ #ifndef NDEBUG # define VVA_ONLY(X) X #else # define VVA_ONLY(X) #endif /* ** The ALWAYS and NEVER macros surround boolean expressions which ** are intended to always be true or false, respectively. Such ** expressions could be omitted from the code completely. But they ** are included in a few cases in order to enhance the resilience ** of SQLite to unexpected behavior - to make the code "self-healing" ** or "ductile" rather than being "brittle" and crashing at the first ** hint of unplanned behavior. ** ** In other words, ALWAYS and NEVER are added for defensive code. ** ** When doing coverage testing ALWAYS and NEVER are hard-coded to ** be true and false so that the unreachable code they specify will ** not be counted as untested code. */ | > > > > > > > > | | 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 | */ #ifndef NDEBUG # define VVA_ONLY(X) X #else # define VVA_ONLY(X) #endif /* ** Disable ALWAYS() and NEVER() (make them pass-throughs) for coverage ** and mutation testing */ #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 #endif /* ** The ALWAYS and NEVER macros surround boolean expressions which ** are intended to always be true or false, respectively. Such ** expressions could be omitted from the code completely. But they ** are included in a few cases in order to enhance the resilience ** of SQLite to unexpected behavior - to make the code "self-healing" ** or "ductile" rather than being "brittle" and crashing at the first ** hint of unplanned behavior. ** ** In other words, ALWAYS and NEVER are added for defensive code. ** ** When doing coverage testing ALWAYS and NEVER are hard-coded to ** be true and false so that the unreachable code they specify will ** not be counted as untested code. */ #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) |
| ︙ | ︙ | |||
13300 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 | /* ** SQLITE_ENABLE_EXPLAIN_COMMENTS is incompatible with SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_OMIT_EXPLAIN # undef SQLITE_ENABLE_EXPLAIN_COMMENTS #endif /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() ** macros to verify that we have tested SQLite for large-file support. */ #define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) | > > > > > > > | 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 | /* ** SQLITE_ENABLE_EXPLAIN_COMMENTS is incompatible with SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_OMIT_EXPLAIN # undef SQLITE_ENABLE_EXPLAIN_COMMENTS #endif /* ** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE */ #if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) # define SQLITE_OMIT_ALTERTABLE #endif /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() ** macros to verify that we have tested SQLite for large-file support. */ #define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) |
| ︙ | ︙ | |||
13444 13445 13446 13447 13448 13449 13450 | #define TK_IF 18 #define TK_NOT 19 #define TK_EXISTS 20 #define TK_TEMP 21 #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 | < | > | 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 | #define TK_IF 18 #define TK_NOT 19 #define TK_EXISTS 20 #define TK_TEMP 21 #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 #define TK_COMMA 25 #define TK_WITHOUT 26 #define TK_ABORT 27 #define TK_ACTION 28 #define TK_AFTER 29 #define TK_ANALYZE 30 #define TK_ASC 31 #define TK_ATTACH 32 #define TK_BEFORE 33 |
| ︙ | ︙ | |||
15107 15108 15109 15110 15111 15112 15113 | #define OP_Yield 14 /* jump */ #define OP_MustBeInt 15 /* jump */ #define OP_Jump 16 /* jump */ #define OP_Once 17 /* jump */ #define OP_If 18 /* jump */ #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ #define OP_IfNot 20 /* jump */ | > | | | | | | | | | | | | | | | | | | | | | < > | | | | < > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | < < | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 15123 15124 15125 15126 15127 15128 15129 15130 15131 15132 15133 15134 15135 15136 15137 15138 15139 15140 15141 15142 15143 15144 15145 15146 15147 15148 15149 15150 15151 15152 15153 15154 15155 15156 15157 15158 15159 15160 15161 15162 15163 15164 15165 15166 15167 15168 15169 15170 15171 15172 15173 15174 15175 15176 15177 15178 15179 15180 15181 15182 15183 15184 15185 15186 15187 15188 15189 15190 15191 15192 15193 15194 15195 15196 15197 15198 15199 15200 15201 15202 15203 15204 15205 15206 15207 15208 15209 15210 15211 15212 15213 15214 15215 15216 15217 15218 15219 15220 15221 15222 15223 15224 15225 15226 15227 15228 15229 15230 15231 15232 15233 15234 15235 15236 15237 15238 15239 15240 15241 15242 15243 15244 15245 15246 15247 15248 15249 15250 15251 15252 15253 15254 15255 15256 15257 15258 15259 15260 15261 15262 15263 15264 15265 15266 15267 15268 15269 15270 15271 15272 15273 15274 15275 15276 15277 15278 15279 15280 15281 15282 15283 15284 15285 15286 15287 15288 15289 15290 15291 15292 15293 15294 15295 15296 15297 15298 15299 15300 15301 15302 15303 15304 15305 15306 15307 15308 15309 15310 15311 15312 15313 15314 15315 15316 15317 15318 15319 15320 15321 15322 15323 15324 15325 15326 15327 15328 15329 15330 15331 15332 15333 15334 15335 15336 15337 15338 15339 15340 |
#define OP_Yield 14 /* jump */
#define OP_MustBeInt 15 /* jump */
#define OP_Jump 16 /* jump */
#define OP_Once 17 /* jump */
#define OP_If 18 /* jump */
#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */
#define OP_IfNot 20 /* jump */
#define OP_IsNullOrType 21 /* jump, synopsis: if typeof(r[P1]) IN (P3,5) goto P2 */
#define OP_IfNullRow 22 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
#define OP_SeekLT 23 /* jump, synopsis: key=r[P3@P4] */
#define OP_SeekLE 24 /* jump, synopsis: key=r[P3@P4] */
#define OP_SeekGE 25 /* jump, synopsis: key=r[P3@P4] */
#define OP_SeekGT 26 /* jump, synopsis: key=r[P3@P4] */
#define OP_IfNotOpen 27 /* jump, synopsis: if( !csr[P1] ) goto P2 */
#define OP_IfNoHope 28 /* jump, synopsis: key=r[P3@P4] */
#define OP_NoConflict 29 /* jump, synopsis: key=r[P3@P4] */
#define OP_NotFound 30 /* jump, synopsis: key=r[P3@P4] */
#define OP_Found 31 /* jump, synopsis: key=r[P3@P4] */
#define OP_SeekRowid 32 /* jump, synopsis: intkey=r[P3] */
#define OP_NotExists 33 /* jump, synopsis: intkey=r[P3] */
#define OP_Last 34 /* jump */
#define OP_IfSmaller 35 /* jump */
#define OP_SorterSort 36 /* jump */
#define OP_Sort 37 /* jump */
#define OP_Rewind 38 /* jump */
#define OP_IdxLE 39 /* jump, synopsis: key=r[P3@P4] */
#define OP_IdxGT 40 /* jump, synopsis: key=r[P3@P4] */
#define OP_IdxLT 41 /* jump, synopsis: key=r[P3@P4] */
#define OP_IdxGE 42 /* jump, synopsis: key=r[P3@P4] */
#define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
#define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
#define OP_RowSetRead 45 /* jump, synopsis: r[P3]=rowset(P1) */
#define OP_RowSetTest 46 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
#define OP_Program 47 /* jump */
#define OP_FkIfZero 48 /* jump, synopsis: if fkctr[P1]==0 goto P2 */
#define OP_IfPos 49 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
#define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */
#define OP_Eq 53 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */
#define OP_Gt 54 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */
#define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */
#define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]<r[P1] */
#define OP_Ge 57 /* jump, same as TK_GE, synopsis: IF r[P3]>=r[P1] */
#define OP_ElseEq 58 /* jump, same as TK_ESCAPE */
#define OP_IfNotZero 59 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
#define OP_DecrJumpZero 60 /* jump, synopsis: if (--r[P1])==0 goto P2 */
#define OP_IncrVacuum 61 /* jump */
#define OP_VNext 62 /* jump */
#define OP_Init 63 /* jump, synopsis: Start at P2 */
#define OP_PureFunc 64 /* synopsis: r[P3]=func(r[P2@NP]) */
#define OP_Function 65 /* synopsis: r[P3]=func(r[P2@NP]) */
#define OP_Return 66
#define OP_EndCoroutine 67
#define OP_HaltIfNull 68 /* synopsis: if r[P3]=null halt */
#define OP_Halt 69
#define OP_Integer 70 /* synopsis: r[P2]=P1 */
#define OP_Int64 71 /* synopsis: r[P2]=P4 */
#define OP_String 72 /* synopsis: r[P2]='P4' (len=P1) */
#define OP_Null 73 /* synopsis: r[P2..P3]=NULL */
#define OP_SoftNull 74 /* synopsis: r[P1]=NULL */
#define OP_Blob 75 /* synopsis: r[P2]=P4 (len=P1) */
#define OP_Variable 76 /* synopsis: r[P2]=parameter(P1,P4) */
#define OP_Move 77 /* synopsis: r[P2@P3]=r[P1@P3] */
#define OP_Copy 78 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
#define OP_SCopy 79 /* synopsis: r[P2]=r[P1] */
#define OP_IntCopy 80 /* synopsis: r[P2]=r[P1] */
#define OP_ChngCntRow 81 /* synopsis: output=r[P1] */
#define OP_ResultRow 82 /* synopsis: output=r[P1@P2] */
#define OP_CollSeq 83
#define OP_AddImm 84 /* synopsis: r[P1]=r[P1]+P2 */
#define OP_RealAffinity 85
#define OP_Cast 86 /* synopsis: affinity(r[P1]) */
#define OP_Permutation 87
#define OP_Compare 88 /* synopsis: r[P1@P3] <-> r[P2@P3] */
#define OP_IsTrue 89 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */
#define OP_ZeroOrNull 90 /* synopsis: r[P2] = 0 OR NULL */
#define OP_Offset 91 /* synopsis: r[P3] = sqlite_offset(P1) */
#define OP_Column 92 /* synopsis: r[P3]=PX */
#define OP_TypeCheck 93 /* synopsis: typecheck(r[P1@P2]) */
#define OP_Affinity 94 /* synopsis: affinity(r[P1@P2]) */
#define OP_MakeRecord 95 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
#define OP_Count 96 /* synopsis: r[P2]=count() */
#define OP_ReadCookie 97
#define OP_SetCookie 98
#define OP_ReopenIdx 99 /* synopsis: root=P2 iDb=P3 */
#define OP_OpenRead 100 /* synopsis: root=P2 iDb=P3 */
#define OP_OpenWrite 101 /* synopsis: root=P2 iDb=P3 */
#define OP_BitAnd 102 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
#define OP_BitOr 103 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
#define OP_ShiftLeft 104 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
#define OP_ShiftRight 105 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
#define OP_Add 106 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
#define OP_Subtract 107 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
#define OP_Multiply 108 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
#define OP_Divide 109 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
#define OP_Remainder 110 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
#define OP_Concat 111 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
#define OP_OpenDup 112
#define OP_BitNot 113 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
#define OP_OpenAutoindex 114 /* synopsis: nColumn=P2 */
#define OP_OpenEphemeral 115 /* synopsis: nColumn=P2 */
#define OP_String8 116 /* same as TK_STRING, synopsis: r[P2]='P4' */
#define OP_SorterOpen 117
#define OP_SequenceTest 118 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
#define OP_OpenPseudo 119 /* synopsis: P3 columns in r[P2] */
#define OP_Close 120
#define OP_ColumnsUsed 121
#define OP_SeekScan 122 /* synopsis: Scan-ahead up to P1 rows */
#define OP_SeekHit 123 /* synopsis: set P2<=seekHit<=P3 */
#define OP_Sequence 124 /* synopsis: r[P2]=cursor[P1].ctr++ */
#define OP_NewRowid 125 /* synopsis: r[P2]=rowid */
#define OP_Insert 126 /* synopsis: intkey=r[P3] data=r[P2] */
#define OP_RowCell 127
#define OP_Delete 128
#define OP_ResetCount 129
#define OP_SorterCompare 130 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
#define OP_SorterData 131 /* synopsis: r[P2]=data */
#define OP_RowData 132 /* synopsis: r[P2]=data */
#define OP_Rowid 133 /* synopsis: r[P2]=rowid */
#define OP_NullRow 134
#define OP_SeekEnd 135
#define OP_IdxInsert 136 /* synopsis: key=r[P2] */
#define OP_SorterInsert 137 /* synopsis: key=r[P2] */
#define OP_IdxDelete 138 /* synopsis: key=r[P2@P3] */
#define OP_DeferredSeek 139 /* synopsis: Move P3 to P1.rowid if needed */
#define OP_IdxRowid 140 /* synopsis: r[P2]=rowid */
#define OP_FinishSeek 141
#define OP_Destroy 142
#define OP_Clear 143
#define OP_ResetSorter 144
#define OP_CreateBtree 145 /* synopsis: r[P2]=root iDb=P1 flags=P3 */
#define OP_SqlExec 146
#define OP_ParseSchema 147
#define OP_LoadAnalysis 148
#define OP_DropTable 149
#define OP_DropIndex 150
#define OP_DropTrigger 151
#define OP_Real 152 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
#define OP_IntegrityCk 153
#define OP_RowSetAdd 154 /* synopsis: rowset(P1)=r[P2] */
#define OP_Param 155
#define OP_FkCounter 156 /* synopsis: fkctr[P1]+=P2 */
#define OP_MemMax 157 /* synopsis: r[P1]=max(r[P1],r[P2]) */
#define OP_OffsetLimit 158 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
#define OP_AggInverse 159 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */
#define OP_AggStep 160 /* synopsis: accum=r[P3] step(r[P2@P5]) */
#define OP_AggStep1 161 /* synopsis: accum=r[P3] step(r[P2@P5]) */
#define OP_AggValue 162 /* synopsis: r[P3]=value N=P2 */
#define OP_AggFinal 163 /* synopsis: accum=r[P1] N=P2 */
#define OP_Expire 164
#define OP_CursorLock 165
#define OP_CursorUnlock 166
#define OP_TableLock 167 /* synopsis: iDb=P1 root=P2 write=P3 */
#define OP_VBegin 168
#define OP_VCreate 169
#define OP_VDestroy 170
#define OP_VOpen 171
#define OP_VColumn 172 /* synopsis: r[P3]=vcolumn(P2) */
#define OP_VRename 173
#define OP_Pagecount 174
#define OP_MaxPgcnt 175
#define OP_Trace 176
#define OP_CursorHint 177
#define OP_ReleaseReg 178 /* synopsis: release r[P1@P2] mask P3 */
#define OP_Noop 179
#define OP_Explain 180
#define OP_Abortable 181
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
** are encoded into bitvectors as follows:
*/
#define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */
#define OPFLG_IN1 0x02 /* in1: P1 is an input */
#define OPFLG_IN2 0x04 /* in2: P2 is an input */
#define OPFLG_IN3 0x08 /* in3: P3 is an input */
#define OPFLG_OUT2 0x10 /* out2: P2 is an output */
#define OPFLG_OUT3 0x20 /* out3: P3 is an output */
#define OPFLG_INITIALIZER {\
/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x10,\
/* 8 */ 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03,\
/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x03, 0x01, 0x09,\
/* 24 */ 0x09, 0x09, 0x09, 0x01, 0x09, 0x09, 0x09, 0x09,\
/* 32 */ 0x09, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\
/* 40 */ 0x01, 0x01, 0x01, 0x26, 0x26, 0x23, 0x0b, 0x01,\
/* 48 */ 0x01, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x01, 0x01, 0x01,\
/* 64 */ 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10,\
/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\
/* 80 */ 0x10, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00,\
/* 88 */ 0x00, 0x12, 0x1e, 0x20, 0x00, 0x00, 0x00, 0x00,\
/* 96 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x26,\
/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\
/* 112 */ 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\
/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,\
/* 136 */ 0x04, 0x04, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,\
/* 144 */ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 152 */ 0x10, 0x00, 0x06, 0x10, 0x00, 0x04, 0x1a, 0x00,\
/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
/* 176 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
/* The resolve3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode. The smaller the maximum
** JUMP opcode the better, so the mkopcodeh.tcl script that
** generated this include file strives to group all JUMP opcodes
** together near the beginning of the list.
*/
#define SQLITE_MX_JUMP_OPCODE 63 /* Maximum JUMP opcode */
/************** End of opcodes.h *********************************************/
/************** Continuing where we left off in vdbe.h ***********************/
/*
** Additional non-public SQLITE_PREPARE_* flags
*/
|
| ︙ | ︙ | |||
16385 16386 16387 16388 16389 16390 16391 16392 | signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ u8 mTrace; /* zero or more SQLITE_TRACE flags */ u8 noSharedCache; /* True if no shared-cache backends */ u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ int nextPagesize; /* Pagesize after VACUUM if >0 */ | > < | | 16403 16404 16405 16406 16407 16408 16409 16410 16411 16412 16413 16414 16415 16416 16417 16418 16419 16420 16421 16422 16423 16424 16425 16426 16427 16428 16429 16430 |
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
u8 suppressErr; /* Do not issue error messages if true */
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
u8 mTrace; /* zero or more SQLITE_TRACE flags */
u8 noSharedCache; /* True if no shared-cache backends */
u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */
u8 eOpenState; /* Current condition of the connection */
int nextPagesize; /* Pagesize after VACUUM if >0 */
i64 nChange; /* Value returned by sqlite3_changes() */
i64 nTotalChange; /* Value returned by sqlite3_total_changes() */
int aLimit[SQLITE_N_LIMIT]; /* Limits */
int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
struct sqlite3InitInfo { /* Information used during initialization */
Pgno newTnum; /* Rootpage of table being initialized */
u8 iDb; /* Which db file is being initialized */
u8 busy; /* TRUE if currently initializing */
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
unsigned imposterTable : 1; /* Building an imposter table */
unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
const char **azInit; /* "type", "name", and "tbl_name" columns */
} init;
int nVdbeActive; /* Number of VDBEs currently running */
int nVdbeRead; /* Number of active VDBEs that read or write */
int nVdbeWrite; /* Number of active VDBEs that read and write */
int nVdbeExec; /* Number of nested calls to VdbeExec() */
int nVDestroy; /* Number of active OP_VDestroy operations */
int nExtension; /* Number of loaded extensions */
|
| ︙ | ︙ | |||
16613 16614 16615 16616 16617 16618 16619 | /* ** Return true if it OK to factor constant expressions into the initialization ** code. The argument is a Parse object for the code generator. */ #define ConstFactorOk(P) ((P)->okConstFactor) | < | | < > | | | | | | | 16631 16632 16633 16634 16635 16636 16637 16638 16639 16640 16641 16642 16643 16644 16645 16646 16647 16648 16649 16650 16651 16652 16653 16654 | /* ** Return true if it OK to factor constant expressions into the initialization ** code. The argument is a Parse object for the code generator. */ #define ConstFactorOk(P) ((P)->okConstFactor) /* Possible values for the sqlite3.eOpenState field. ** The numbers are randomly selected such that a minimum of three bits must ** change to convert any number to another or to zero */ #define SQLITE_STATE_OPEN 0x76 /* Database is open */ #define SQLITE_STATE_CLOSED 0xce /* Database is closed */ #define SQLITE_STATE_SICK 0xba /* Error and awaiting close */ #define SQLITE_STATE_BUSY 0x6d /* Database currently in use */ #define SQLITE_STATE_ERROR 0xd5 /* An SQLITE_MISUSE error occurred */ #define SQLITE_STATE_ZOMBIE 0xa7 /* Close with last statement close */ /* ** Each SQL function is defined by an instance of the following ** structure. For global built-in functions (ex: substr(), max(), count()) ** a pointer to this structure is held in the sqlite3BuiltinFunctions object. ** For per-connection application-defined functions, a pointer to this ** structure is held in the db->aHash hash table. |
| ︙ | ︙ | |||
16892 16893 16894 16895 16896 16897 16898 |
** is only included if the COLFLAG_HASTYPE bit of colFlags is set and the
** collating sequence name is only included if the COLFLAG_HASCOLL bit is
** set.
*/
struct Column {
char *zCnName; /* Name of this column */
unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */
| | | > | | | | | | | 16909 16910 16911 16912 16913 16914 16915 16916 16917 16918 16919 16920 16921 16922 16923 16924 16925 16926 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 16941 16942 16943 16944 16945 |
** is only included if the COLFLAG_HASTYPE bit of colFlags is set and the
** collating sequence name is only included if the COLFLAG_HASCOLL bit is
** set.
*/
struct Column {
char *zCnName; /* Name of this column */
unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */
unsigned eCType :4; /* One of the standard types */
char affinity; /* One of the SQLITE_AFF_... values */
u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */
u8 hName; /* Column name hash for faster lookup */
u16 iDflt; /* 1-based index of DEFAULT. 0 means "none" */
u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
/* Allowed values for Column.eCType.
**
** Values must match entries in the global constant arrays
** sqlite3StdTypeLen[] and sqlite3StdType[]. Each value is one more
** than the offset into these arrays for the corresponding name.
** Adjust the SQLITE_N_STDTYPE value if adding or removing entries.
*/
#define COLTYPE_CUSTOM 0 /* Type appended to zName */
#define COLTYPE_ANY 1
#define COLTYPE_BLOB 2
#define COLTYPE_INT 3
#define COLTYPE_INTEGER 4
#define COLTYPE_REAL 5
#define COLTYPE_TEXT 6
#define SQLITE_N_STDTYPE 6 /* Number of standard types */
/* Allowed values for Column.colFlags.
**
** Constraints:
** TF_HasVirtual == COLFLAG_VIRTUAL
** TF_HasStored == COLFLAG_STORED
** TF_HasHidden == COLFLAG_HIDDEN
|
| ︙ | ︙ | |||
17137 17138 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 | #define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ #define TF_Shadow 0x00001000 /* True for a shadow table */ #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ #define TF_Ephemeral 0x00004000 /* An ephemeral table */ #define TF_Eponymous 0x00008000 /* An eponymous virtual table */ /* ** Allowed values for Table.eTabType */ #define TABTYP_NORM 0 /* Ordinary table */ #define TABTYP_VTAB 1 /* Virtual table */ #define TABTYP_VIEW 2 /* A view */ | > | 17155 17156 17157 17158 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 | #define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ #define TF_Shadow 0x00001000 /* True for a shadow table */ #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ #define TF_Ephemeral 0x00004000 /* An ephemeral table */ #define TF_Eponymous 0x00008000 /* An eponymous virtual table */ #define TF_Strict 0x00010000 /* STRICT mode */ /* ** Allowed values for Table.eTabType */ #define TABTYP_NORM 0 /* Ordinary table */ #define TABTYP_VTAB 1 /* Virtual table */ #define TABTYP_VIEW 2 /* A view */ |
| ︙ | ︙ | |||
18961 18962 18963 18964 18965 18966 18967 | #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*); SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p); SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*); SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin); | | | 18980 18981 18982 18983 18984 18985 18986 18987 18988 18989 18990 18991 18992 18993 18994 | #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*); SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p); SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*); SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin); SQLITE_PRIVATE int sqlite3WindowCompare(const Parse*, const Window*, const Window*, int); SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Select*); SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int); SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*); SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*); SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p); SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p); SQLITE_PRIVATE void sqlite3WindowFunctions(void); |
| ︙ | ︙ | |||
19093 19094 19095 19096 19097 19098 19099 | SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, u64); SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3*,const char*,const char*); SQLITE_PRIVATE void *sqlite3Realloc(void*, u64); SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64); SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64); SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*); SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*); | | | | 19112 19113 19114 19115 19116 19117 19118 19119 19120 19121 19122 19123 19124 19125 19126 19127 | SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, u64); SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3*,const char*,const char*); SQLITE_PRIVATE void *sqlite3Realloc(void*, u64); SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64); SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64); SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*); SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*); SQLITE_PRIVATE int sqlite3MallocSize(const void*); SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, const void*); SQLITE_PRIVATE void *sqlite3PageMalloc(int); SQLITE_PRIVATE void sqlite3PageFree(void*); SQLITE_PRIVATE void sqlite3MemSetDefault(void); #ifndef SQLITE_UNTESTABLE SQLITE_PRIVATE void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); #endif SQLITE_PRIVATE int sqlite3HeapNearlyFull(void); |
| ︙ | ︙ | |||
19230 19231 19232 19233 19234 19235 19236 | SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*); SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*); SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); | | | | | 19249 19250 19251 19252 19253 19254 19255 19256 19257 19258 19259 19260 19261 19262 19263 19264 19265 19266 19267 19268 19269 19270 19271 19272 19273 | SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*); SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*); SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*); SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int); SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32); |
| ︙ | ︙ | |||
19288 19289 19290 19291 19292 19293 19294 | SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token,Token); SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int); SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int); SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*); SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*); SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); SQLITE_PRIVATE void sqlite3AddGenerated(Parse*,Expr*,Token*); | | | 19307 19308 19309 19310 19311 19312 19313 19314 19315 19316 19317 19318 19319 19320 19321 |
SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token,Token);
SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int);
SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*);
SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*);
SQLITE_PRIVATE void sqlite3AddGenerated(Parse*,Expr*,Token*);
SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u32,Select*);
SQLITE_PRIVATE void sqlite3AddReturning(Parse*,ExprList*);
SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
#define sqlite3CodecQueryParameters(A,B,C) 0
SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
#ifdef SQLITE_UNTESTABLE
|
| ︙ | ︙ | |||
19423 19424 19425 19426 19427 19428 19429 | SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *); SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*,Expr*); SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); | | | | | | | 19442 19443 19444 19445 19446 19447 19448 19449 19450 19451 19452 19453 19454 19455 19456 19457 19458 19459 19460 | SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *); SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*,Expr*); SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, const Token*); SQLITE_PRIVATE int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int); SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*,Expr*,int); SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList*,const ExprList*, int); SQLITE_PRIVATE int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int); SQLITE_PRIVATE void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*); |
| ︙ | ︙ | |||
19458 19459 19460 19461 19462 19463 19464 | SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); #endif | | | 19477 19478 19479 19480 19481 19482 19483 19484 19485 19486 19487 19488 19489 19490 19491 |
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
#ifdef SQLITE_ENABLE_CURSOR_HINTS
SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
#endif
SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
|
| ︙ | ︙ | |||
19483 19484 19485 19486 19487 19488 19489 | SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*); SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int); SQLITE_PRIVATE void sqlite3MultiWrite(Parse*); SQLITE_PRIVATE void sqlite3MayAbort(Parse*); SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*); SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*); | | | | | | | 19502 19503 19504 19505 19506 19507 19508 19509 19510 19511 19512 19513 19514 19515 19516 19517 19518 19519 19520 | SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*); SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int); SQLITE_PRIVATE void sqlite3MultiWrite(Parse*); SQLITE_PRIVATE void sqlite3MayAbort(Parse*); SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*); SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*); SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,const Expr*,int); SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,const ExprList*,int); SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,const SrcList*,int); SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*); SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int); SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); |
| ︙ | ︙ | |||
19625 19626 19627 19628 19629 19630 19631 | #define putVarint sqlite3PutVarint SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*); SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int); SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); | | | 19644 19645 19646 19647 19648 19649 19650 19651 19652 19653 19654 19655 19656 19657 19658 | #define putVarint sqlite3PutVarint SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*); SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int); SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table*,int); SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr); SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*); SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); SQLITE_PRIVATE void sqlite3Error(sqlite3*,int); SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*); SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int); |
| ︙ | ︙ | |||
19654 19655 19656 19657 19658 19659 19660 | SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); SQLITE_PRIVATE int sqlite3IsBinary(const CollSeq*); SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8); SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); | | | | 19673 19674 19675 19676 19677 19678 19679 19680 19681 19682 19683 19684 19685 19686 19687 19688 | SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); SQLITE_PRIVATE int sqlite3IsBinary(const CollSeq*); SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8); SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(const Parse *pParse, Expr*, const Token*, int); SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(const Parse*,Expr*,const char*); SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*); SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr*); SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*); SQLITE_PRIVATE int sqlite3CheckObjectName(Parse*, const char*,const char*,const char*); SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, i64); SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64); |
| ︙ | ︙ | |||
19686 19687 19688 19689 19690 19691 19692 | #ifndef SQLITE_UNTESTABLE SQLITE_PRIVATE void sqlite3ResultIntReal(sqlite3_context*); #endif SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); #endif | | > | 19705 19706 19707 19708 19709 19710 19711 19712 19713 19714 19715 19716 19717 19718 19719 19720 19721 19722 19723 19724 19725 19726 | #ifndef SQLITE_UNTESTABLE SQLITE_PRIVATE void sqlite3ResultIntReal(sqlite3_context*); #endif SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); #endif SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, const Expr *, u8, u8, sqlite3_value **); SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[]; SQLITE_PRIVATE const char sqlite3StrBINARY[]; SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[]; SQLITE_PRIVATE const char sqlite3StdTypeAffinity[]; SQLITE_PRIVATE const char sqlite3StdTypeMap[]; SQLITE_PRIVATE const char *sqlite3StdType[]; SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; SQLITE_PRIVATE const unsigned char *sqlite3aLTb; SQLITE_PRIVATE const unsigned char *sqlite3aEQb; SQLITE_PRIVATE const unsigned char *sqlite3aGTb; SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; |
| ︙ | ︙ | |||
19737 19738 19739 19740 19741 19742 19743 | SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*); SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *); | | | | | 19757 19758 19759 19760 19761 19762 19763 19764 19765 19766 19767 19768 19769 19770 19771 19772 19773 | SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*); SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *); SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse*, SrcList*, const Token*); SQLITE_PRIVATE const void *sqlite3RenameTokenMap(Parse*, const void*, const Token*); SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, const void *pTo, const void *pFrom); SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*); SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*); SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*); SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*); SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*); SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*); |
| ︙ | ︙ | |||
19783 19784 19785 19786 19787 19788 19789 19790 19791 19792 19793 19794 19795 19796 | SQLITE_PRIVATE void sqlite3OomFault(sqlite3*); SQLITE_PRIVATE void sqlite3OomClear(sqlite3*); SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int); SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *); SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); #ifndef SQLITE_OMIT_SUBQUERY | > > | 19803 19804 19805 19806 19807 19808 19809 19810 19811 19812 19813 19814 19815 19816 19817 19818 | SQLITE_PRIVATE void sqlite3OomFault(sqlite3*); SQLITE_PRIVATE void sqlite3OomClear(sqlite3*); SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum*, u8); SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*); SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int); SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *); SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); #ifndef SQLITE_OMIT_SUBQUERY |
| ︙ | ︙ | |||
19835 19836 19837 19838 19839 19840 19841 | #endif #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*); #endif #ifdef SQLITE_OMIT_VIRTUALTABLE | | | 19857 19858 19859 19860 19861 19862 19863 19864 19865 19866 19867 19868 19869 19870 19871 | #endif #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*); #endif #ifdef SQLITE_OMIT_VIRTUALTABLE # define sqlite3VtabClear(D,T) # define sqlite3VtabSync(X,Y) SQLITE_OK # define sqlite3VtabRollback(X) # define sqlite3VtabCommit(X) # define sqlite3VtabInSync(db) 0 # define sqlite3VtabLock(X) # define sqlite3VtabUnlock(X) # define sqlite3VtabModuleUnref(D,X) |
| ︙ | ︙ | |||
20014 20015 20016 20017 20018 20019 20020 | #endif SQLITE_PRIVATE int sqlite3JournalIsInMemory(sqlite3_file *p); SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *); SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 | | | 20036 20037 20038 20039 20040 20041 20042 20043 20044 20045 20046 20047 20048 20049 20050 | #endif SQLITE_PRIVATE int sqlite3JournalIsInMemory(sqlite3_file *p); SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *); SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *); SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); #else #define sqlite3SelectExprHeight(x) 0 #define sqlite3ExprCheckHeight(x,y) #endif SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*); |
| ︙ | ︙ | |||
20111 20112 20113 20114 20115 20116 20117 | #if defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST) SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3*); #endif #if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST) SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*); #endif | | | | 20133 20134 20135 20136 20137 20138 20139 20140 20141 20142 20143 20144 20145 20146 20147 20148 | #if defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST) SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3*); #endif #if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST) SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*); #endif SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr); SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr); SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr*, int); SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int); SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse*, Expr*); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt); #endif |
| ︙ | ︙ | |||
21462 21463 21464 21465 21466 21467 21468 21469 | ** Name of the default collating sequence */ SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY"; /* ** Standard typenames. These names must match the COLTYPE_* definitions. ** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. */ | > > > > > > > > > > > > | > > > > > > > > > > | 21484 21485 21486 21487 21488 21489 21490 21491 21492 21493 21494 21495 21496 21497 21498 21499 21500 21501 21502 21503 21504 21505 21506 21507 21508 21509 21510 21511 21512 21513 21514 21515 21516 21517 21518 21519 21520 21521 21522 21523 21524 21525 21526 21527 21528 21529 |
** Name of the default collating sequence
*/
SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY";
/*
** Standard typenames. These names must match the COLTYPE_* definitions.
** Adjust the SQLITE_N_STDTYPE value if adding or removing entries.
**
** sqlite3StdType[] The actual names of the datatypes.
**
** sqlite3StdTypeLen[] The length (in bytes) of each entry
** in sqlite3StdType[].
**
** sqlite3StdTypeAffinity[] The affinity associated with each entry
** in sqlite3StdType[].
**
** sqlite3StdTypeMap[] The type value (as returned from
** sqlite3_column_type() or sqlite3_value_type())
** for each entry in sqlite3StdType[].
*/
SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 };
SQLITE_PRIVATE const char sqlite3StdTypeAffinity[] = {
SQLITE_AFF_NUMERIC,
SQLITE_AFF_BLOB,
SQLITE_AFF_INTEGER,
SQLITE_AFF_INTEGER,
SQLITE_AFF_REAL,
SQLITE_AFF_TEXT
};
SQLITE_PRIVATE const char sqlite3StdTypeMap[] = {
0,
SQLITE_BLOB,
SQLITE_INTEGER,
SQLITE_INTEGER,
SQLITE_FLOAT,
SQLITE_TEXT
};
SQLITE_PRIVATE const char *sqlite3StdType[] = {
"ANY",
"BLOB",
"INT",
"INTEGER",
"REAL",
"TEXT"
};
|
| ︙ | ︙ | |||
23519 23520 23521 23522 23523 23524 23525 |
*/
static void strftimeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
DateTime x;
| < < > | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < | < | | < | > | | > > > | | | | < | | | > > > | | | | | | | | | | | | | < | | < | | | | | < | | | > > > | > > > | | | < | | > | > > | > | | | | | | | > > > > | > > < > | < | 23563 23564 23565 23566 23567 23568 23569 23570 23571 23572 23573 23574 23575 23576 23577 23578 23579 23580 23581 23582 23583 23584 23585 23586 23587 23588 23589 23590 23591 23592 23593 23594 23595 23596 23597 23598 23599 23600 23601 23602 23603 23604 23605 23606 23607 23608 23609 23610 23611 23612 23613 23614 23615 23616 23617 23618 23619 23620 23621 23622 23623 23624 23625 23626 23627 23628 23629 23630 23631 23632 23633 23634 23635 23636 23637 23638 23639 23640 23641 23642 23643 23644 23645 23646 23647 23648 23649 23650 23651 23652 23653 23654 23655 23656 23657 23658 23659 23660 23661 23662 23663 23664 23665 23666 23667 23668 23669 23670 |
*/
static void strftimeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
DateTime x;
size_t i,j;
sqlite3 *db;
const char *zFmt;
sqlite3_str sRes;
if( argc==0 ) return;
zFmt = (const char*)sqlite3_value_text(argv[0]);
if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return;
db = sqlite3_context_db_handle(context);
sqlite3StrAccumInit(&sRes, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
computeJD(&x);
computeYMD_HMS(&x);
for(i=j=0; zFmt[i]; i++){
if( zFmt[i]!='%' ) continue;
if( j<i ) sqlite3_str_append(&sRes, zFmt+j, (int)(i-j));
i++;
j = i + 1;
switch( zFmt[i] ){
case 'd': {
sqlite3_str_appendf(&sRes, "%02d", x.D);
break;
}
case 'f': {
double s = x.s;
if( s>59.999 ) s = 59.999;
sqlite3_str_appendf(&sRes, "%06.3f", s);
break;
}
case 'H': {
sqlite3_str_appendf(&sRes, "%02d", x.h);
break;
}
case 'W': /* Fall thru */
case 'j': {
int nDay; /* Number of days since 1st day of year */
DateTime y = x;
y.validJD = 0;
y.M = 1;
y.D = 1;
computeJD(&y);
nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
if( zFmt[i]=='W' ){
int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
wd = (int)(((x.iJD+43200000)/86400000)%7);
sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7);
}else{
sqlite3_str_appendf(&sRes,"%03d",nDay+1);
}
break;
}
case 'J': {
sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0);
break;
}
case 'm': {
sqlite3_str_appendf(&sRes,"%02d",x.M);
break;
}
case 'M': {
sqlite3_str_appendf(&sRes,"%02d",x.m);
break;
}
case 's': {
i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000);
sqlite3_str_appendf(&sRes,"%lld",iS);
break;
}
case 'S': {
sqlite3_str_appendf(&sRes,"%02d",(int)x.s);
break;
}
case 'w': {
sqlite3_str_appendchar(&sRes, 1,
(char)(((x.iJD+129600000)/86400000) % 7) + '0');
break;
}
case 'Y': {
sqlite3_str_appendf(&sRes,"%04d",x.Y);
break;
}
case '%': {
sqlite3_str_appendchar(&sRes, 1, '%');
break;
}
default: {
sqlite3_str_reset(&sRes);
return;
}
}
}
if( j<i ) sqlite3_str_append(&sRes, zFmt+j, (int)(i-j));
sqlite3ResultStrAccum(context, &sRes);
}
/*
** current_time()
**
** This function returns the same value as time('now').
*/
|
| ︙ | ︙ | |||
23924 23925 23926 23927 23928 23929 23930 23931 23932 23933 23934 23935 23936 23937 |
}
SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){
int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize;
return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
}
SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
return id->pMethods->xDeviceCharacteristics(id);
}
#ifndef SQLITE_OMIT_WAL
SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){
return id->pMethods->xShmLock(id, offset, n, flags);
}
SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id){
| > | 23937 23938 23939 23940 23941 23942 23943 23944 23945 23946 23947 23948 23949 23950 23951 |
}
SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){
int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize;
return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
}
SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
if( NEVER(id->pMethods==0) ) return 0;
return id->pMethods->xDeviceCharacteristics(id);
}
#ifndef SQLITE_OMIT_WAL
SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){
return id->pMethods->xShmLock(id, offset, n, flags);
}
SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id){
|
| ︙ | ︙ | |||
28241 28242 28243 28244 28245 28246 28247 | return sqlite3Malloc(n); } /* ** TRUE if p is a lookaside memory allocation from db */ #ifndef SQLITE_OMIT_LOOKASIDE | | | | | | | 28255 28256 28257 28258 28259 28260 28261 28262 28263 28264 28265 28266 28267 28268 28269 28270 28271 28272 28273 28274 28275 28276 28277 28278 28279 28280 28281 28282 28283 28284 28285 28286 28287 28288 28289 28290 28291 |
return sqlite3Malloc(n);
}
/*
** TRUE if p is a lookaside memory allocation from db
*/
#ifndef SQLITE_OMIT_LOOKASIDE
static int isLookaside(sqlite3 *db, const void *p){
return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd);
}
#else
#define isLookaside(A,B) 0
#endif
/*
** Return the size of a memory allocation previously obtained from
** sqlite3Malloc() or sqlite3_malloc().
*/
SQLITE_PRIVATE int sqlite3MallocSize(const void *p){
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
return sqlite3GlobalConfig.m.xSize((void*)p);
}
static int lookasideMallocSize(sqlite3 *db, const void *p){
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
return p<db->lookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL;
#else
return db->lookaside.szTrue;
#endif
}
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, const void *p){
assert( p!=0 );
#ifdef SQLITE_DEBUG
if( db==0 || !isLookaside(db,p) ){
if( db==0 ){
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
}else{
|
| ︙ | ︙ | |||
28290 28291 28292 28293 28294 28295 28296 |
#endif
if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
assert( sqlite3_mutex_held(db->mutex) );
return db->lookaside.szTrue;
}
}
}
| | | 28304 28305 28306 28307 28308 28309 28310 28311 28312 28313 28314 28315 28316 28317 28318 |
#endif
if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
assert( sqlite3_mutex_held(db->mutex) );
return db->lookaside.szTrue;
}
}
}
return sqlite3GlobalConfig.m.xSize((void*)p);
}
SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
return p ? sqlite3GlobalConfig.m.xSize(p) : 0;
}
|
| ︙ | ︙ | |||
28900 28901 28902 28903 28904 28905 28906 | return (char)digit; } #endif /* SQLITE_OMIT_FLOATING_POINT */ /* ** Set the StrAccum object to an error mode. */ | | | 28914 28915 28916 28917 28918 28919 28920 28921 28922 28923 28924 28925 28926 28927 28928 |
return (char)digit;
}
#endif /* SQLITE_OMIT_FLOATING_POINT */
/*
** Set the StrAccum object to an error mode.
*/
SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum *p, u8 eError){
assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
p->accError = eError;
if( p->mxAlloc ) sqlite3_str_reset(p);
if( eError==SQLITE_TOOBIG ) sqlite3ErrorToParser(p->db, eError);
}
/*
|
| ︙ | ︙ | |||
28936 28937 28938 28939 28940 28941 28942 |
** SQL from requesting large allocations using the precision or width
** field of the printf() function.
*/
static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){
char *z;
if( pAccum->accError ) return 0;
if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){
| | | | 28950 28951 28952 28953 28954 28955 28956 28957 28958 28959 28960 28961 28962 28963 28964 28965 28966 28967 28968 28969 |
** SQL from requesting large allocations using the precision or width
** field of the printf() function.
*/
static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){
char *z;
if( pAccum->accError ) return 0;
if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){
sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG);
return 0;
}
z = sqlite3DbMallocRaw(pAccum->db, n);
if( z==0 ){
sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM);
}
return z;
}
/*
** On machines with a small stack size, you can redefine the
** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
|
| ︙ | ︙ | |||
29680 29681 29682 29683 29684 29685 29686 |
assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
if( p->accError ){
testcase(p->accError==SQLITE_TOOBIG);
testcase(p->accError==SQLITE_NOMEM);
return 0;
}
if( p->mxAlloc==0 ){
| | | | | 29694 29695 29696 29697 29698 29699 29700 29701 29702 29703 29704 29705 29706 29707 29708 29709 29710 29711 29712 29713 29714 29715 29716 29717 29718 29719 29720 29721 29722 29723 29724 29725 29726 29727 29728 29729 29730 29731 29732 29733 29734 29735 29736 29737 29738 29739 |
assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
if( p->accError ){
testcase(p->accError==SQLITE_TOOBIG);
testcase(p->accError==SQLITE_NOMEM);
return 0;
}
if( p->mxAlloc==0 ){
sqlite3StrAccumSetError(p, SQLITE_TOOBIG);
return p->nAlloc - p->nChar - 1;
}else{
char *zOld = isMalloced(p) ? p->zText : 0;
i64 szNew = p->nChar;
szNew += (sqlite3_int64)N + 1;
if( szNew+p->nChar<=p->mxAlloc ){
/* Force exponential buffer size growth as long as it does not overflow,
** to avoid having to call this routine too often */
szNew += p->nChar;
}
if( szNew > p->mxAlloc ){
sqlite3_str_reset(p);
sqlite3StrAccumSetError(p, SQLITE_TOOBIG);
return 0;
}else{
p->nAlloc = (int)szNew;
}
if( p->db ){
zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
}else{
zNew = sqlite3Realloc(zOld, p->nAlloc);
}
if( zNew ){
assert( p->zText!=0 || p->nChar==0 );
if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
p->zText = zNew;
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
sqlite3_str_reset(p);
sqlite3StrAccumSetError(p, SQLITE_NOMEM);
return 0;
}
}
return N;
}
/*
|
| ︙ | ︙ | |||
29784 29785 29786 29787 29788 29789 29790 |
char *zText;
assert( p->mxAlloc>0 && !isMalloced(p) );
zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
if( zText ){
memcpy(zText, p->zText, p->nChar+1);
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
| | > > > > > > > > > > > > > > > > | 29798 29799 29800 29801 29802 29803 29804 29805 29806 29807 29808 29809 29810 29811 29812 29813 29814 29815 29816 29817 29818 29819 29820 29821 29822 29823 29824 29825 29826 29827 29828 29829 29830 29831 29832 29833 29834 29835 29836 29837 29838 29839 29840 29841 |
char *zText;
assert( p->mxAlloc>0 && !isMalloced(p) );
zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
if( zText ){
memcpy(zText, p->zText, p->nChar+1);
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
sqlite3StrAccumSetError(p, SQLITE_NOMEM);
}
p->zText = zText;
return zText;
}
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
if( p->zText ){
p->zText[p->nChar] = 0;
if( p->mxAlloc>0 && !isMalloced(p) ){
return strAccumFinishRealloc(p);
}
}
return p->zText;
}
/*
** Use the content of the StrAccum passed as the second argument
** as the result of an SQL function.
*/
SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context *pCtx, StrAccum *p){
if( p->accError ){
sqlite3_result_error_code(pCtx, p->accError);
sqlite3_str_reset(p);
}else if( isMalloced(p) ){
sqlite3_result_text(pCtx, p->zText, p->nChar, SQLITE_DYNAMIC);
}else{
sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC);
sqlite3_str_reset(p);
}
}
/*
** This singleton is an sqlite3_str object that is returned if
** sqlite3_malloc() fails to provide space for a real one. This
** sqlite3_str object accepts no new text and always returns
** an SQLITE_NOMEM error.
*/
|
| ︙ | ︙ | |||
31894 31895 31896 31897 31898 31899 31900 31901 31902 31903 31904 |
return xCallback ? xCallback(iTest) : SQLITE_OK;
}
#endif
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Return true if the floating point value is Not a Number (NaN).
*/
SQLITE_PRIVATE int sqlite3IsNaN(double x){
u64 y;
memcpy(&y,&x,sizeof(y));
| > > > > > > > > > > | | 31924 31925 31926 31927 31928 31929 31930 31931 31932 31933 31934 31935 31936 31937 31938 31939 31940 31941 31942 31943 31944 31945 31946 31947 31948 31949 31950 31951 31952 |
return xCallback ? xCallback(iTest) : SQLITE_OK;
}
#endif
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Return true if the floating point value is Not a Number (NaN).
**
** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN.
** Otherwise, we have our own implementation that works on most systems.
*/
SQLITE_PRIVATE int sqlite3IsNaN(double x){
int rc; /* The value return */
#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN
u64 y;
memcpy(&y,&x,sizeof(y));
rc = IsNaN(y);
#else
rc = isnan(x);
#endif /* HAVE_ISNAN */
testcase( rc );
return rc;
}
#endif /* SQLITE_OMIT_FLOATING_POINT */
/*
** Compute a string length that is limited to what can be stored in
** lower 30 bits of a 32-bit signed integer.
**
|
| ︙ | ︙ | |||
31925 31926 31927 31928 31929 31930 31931 |
**
** The column type is an extra string stored after the zero-terminator on
** the column name if and only if the COLFLAG_HASTYPE flag is set.
*/
SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){
if( pCol->colFlags & COLFLAG_HASTYPE ){
return pCol->zCnName + strlen(pCol->zCnName) + 1;
| | | | | 31965 31966 31967 31968 31969 31970 31971 31972 31973 31974 31975 31976 31977 31978 31979 31980 31981 |
**
** The column type is an extra string stored after the zero-terminator on
** the column name if and only if the COLFLAG_HASTYPE flag is set.
*/
SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){
if( pCol->colFlags & COLFLAG_HASTYPE ){
return pCol->zCnName + strlen(pCol->zCnName) + 1;
}else if( pCol->eCType ){
assert( pCol->eCType<=SQLITE_N_STDTYPE );
return (char*)sqlite3StdType[pCol->eCType-1];
}else{
return zDflt;
}
}
/*
** Helper function for sqlite3Error() - called rarely. Broken out into
|
| ︙ | ︙ | |||
33233 33234 33235 33236 33237 33238 33239 |
**
** sqlite3SafetyCheckOk() requires that the db pointer be valid for
** use. sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to
** open properly and is not fit for general use but which can be
** used as an argument to sqlite3_errmsg() or sqlite3_close().
*/
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){
| | | | | | | | | | 33273 33274 33275 33276 33277 33278 33279 33280 33281 33282 33283 33284 33285 33286 33287 33288 33289 33290 33291 33292 33293 33294 33295 33296 33297 33298 33299 33300 33301 33302 33303 33304 33305 33306 33307 33308 |
**
** sqlite3SafetyCheckOk() requires that the db pointer be valid for
** use. sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to
** open properly and is not fit for general use but which can be
** used as an argument to sqlite3_errmsg() or sqlite3_close().
*/
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){
u8 eOpenState;
if( db==0 ){
logBadConnection("NULL");
return 0;
}
eOpenState = db->eOpenState;
if( eOpenState!=SQLITE_STATE_OPEN ){
if( sqlite3SafetyCheckSickOrOk(db) ){
testcase( sqlite3GlobalConfig.xLog!=0 );
logBadConnection("unopened");
}
return 0;
}else{
return 1;
}
}
SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
u8 eOpenState;
eOpenState = db->eOpenState;
if( eOpenState!=SQLITE_STATE_SICK &&
eOpenState!=SQLITE_STATE_OPEN &&
eOpenState!=SQLITE_STATE_BUSY ){
testcase( sqlite3GlobalConfig.xLog!=0 );
logBadConnection("invalid");
return 0;
}else{
return 1;
}
}
|
| ︙ | ︙ | |||
33873 33874 33875 33876 33877 33878 33879 |
/* 14 */ "Yield" OpHelp(""),
/* 15 */ "MustBeInt" OpHelp(""),
/* 16 */ "Jump" OpHelp(""),
/* 17 */ "Once" OpHelp(""),
/* 18 */ "If" OpHelp(""),
/* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
/* 20 */ "IfNot" OpHelp(""),
| > | | | | | | | | | | | | | | | | | | | | | < > | | | | < > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | < < | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | 33913 33914 33915 33916 33917 33918 33919 33920 33921 33922 33923 33924 33925 33926 33927 33928 33929 33930 33931 33932 33933 33934 33935 33936 33937 33938 33939 33940 33941 33942 33943 33944 33945 33946 33947 33948 33949 33950 33951 33952 33953 33954 33955 33956 33957 33958 33959 33960 33961 33962 33963 33964 33965 33966 33967 33968 33969 33970 33971 33972 33973 33974 33975 33976 33977 33978 33979 33980 33981 33982 33983 33984 33985 33986 33987 33988 33989 33990 33991 33992 33993 33994 33995 33996 33997 33998 33999 34000 34001 34002 34003 34004 34005 34006 34007 34008 34009 34010 34011 34012 34013 34014 34015 34016 34017 34018 34019 34020 34021 34022 34023 34024 34025 34026 34027 34028 34029 34030 34031 34032 34033 34034 34035 34036 34037 34038 34039 34040 34041 34042 34043 34044 34045 34046 34047 34048 34049 34050 34051 34052 34053 34054 34055 34056 34057 34058 34059 34060 34061 34062 34063 34064 34065 34066 34067 34068 34069 34070 34071 34072 34073 34074 34075 34076 34077 34078 34079 34080 34081 34082 34083 34084 34085 34086 34087 |
/* 14 */ "Yield" OpHelp(""),
/* 15 */ "MustBeInt" OpHelp(""),
/* 16 */ "Jump" OpHelp(""),
/* 17 */ "Once" OpHelp(""),
/* 18 */ "If" OpHelp(""),
/* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
/* 20 */ "IfNot" OpHelp(""),
/* 21 */ "IsNullOrType" OpHelp("if typeof(r[P1]) IN (P3,5) goto P2"),
/* 22 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"),
/* 23 */ "SeekLT" OpHelp("key=r[P3@P4]"),
/* 24 */ "SeekLE" OpHelp("key=r[P3@P4]"),
/* 25 */ "SeekGE" OpHelp("key=r[P3@P4]"),
/* 26 */ "SeekGT" OpHelp("key=r[P3@P4]"),
/* 27 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"),
/* 28 */ "IfNoHope" OpHelp("key=r[P3@P4]"),
/* 29 */ "NoConflict" OpHelp("key=r[P3@P4]"),
/* 30 */ "NotFound" OpHelp("key=r[P3@P4]"),
/* 31 */ "Found" OpHelp("key=r[P3@P4]"),
/* 32 */ "SeekRowid" OpHelp("intkey=r[P3]"),
/* 33 */ "NotExists" OpHelp("intkey=r[P3]"),
/* 34 */ "Last" OpHelp(""),
/* 35 */ "IfSmaller" OpHelp(""),
/* 36 */ "SorterSort" OpHelp(""),
/* 37 */ "Sort" OpHelp(""),
/* 38 */ "Rewind" OpHelp(""),
/* 39 */ "IdxLE" OpHelp("key=r[P3@P4]"),
/* 40 */ "IdxGT" OpHelp("key=r[P3@P4]"),
/* 41 */ "IdxLT" OpHelp("key=r[P3@P4]"),
/* 42 */ "IdxGE" OpHelp("key=r[P3@P4]"),
/* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
/* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
/* 45 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
/* 46 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
/* 47 */ "Program" OpHelp(""),
/* 48 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
/* 49 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
/* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
/* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
/* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"),
/* 53 */ "Eq" OpHelp("IF r[P3]==r[P1]"),
/* 54 */ "Gt" OpHelp("IF r[P3]>r[P1]"),
/* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"),
/* 56 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
/* 57 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
/* 58 */ "ElseEq" OpHelp(""),
/* 59 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
/* 60 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
/* 61 */ "IncrVacuum" OpHelp(""),
/* 62 */ "VNext" OpHelp(""),
/* 63 */ "Init" OpHelp("Start at P2"),
/* 64 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"),
/* 65 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"),
/* 66 */ "Return" OpHelp(""),
/* 67 */ "EndCoroutine" OpHelp(""),
/* 68 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
/* 69 */ "Halt" OpHelp(""),
/* 70 */ "Integer" OpHelp("r[P2]=P1"),
/* 71 */ "Int64" OpHelp("r[P2]=P4"),
/* 72 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
/* 73 */ "Null" OpHelp("r[P2..P3]=NULL"),
/* 74 */ "SoftNull" OpHelp("r[P1]=NULL"),
/* 75 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
/* 76 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
/* 77 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
/* 78 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
/* 79 */ "SCopy" OpHelp("r[P2]=r[P1]"),
/* 80 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
/* 81 */ "ChngCntRow" OpHelp("output=r[P1]"),
/* 82 */ "ResultRow" OpHelp("output=r[P1@P2]"),
/* 83 */ "CollSeq" OpHelp(""),
/* 84 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
/* 85 */ "RealAffinity" OpHelp(""),
/* 86 */ "Cast" OpHelp("affinity(r[P1])"),
/* 87 */ "Permutation" OpHelp(""),
/* 88 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
/* 89 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"),
/* 90 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"),
/* 91 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"),
/* 92 */ "Column" OpHelp("r[P3]=PX"),
/* 93 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"),
/* 94 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
/* 95 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
/* 96 */ "Count" OpHelp("r[P2]=count()"),
/* 97 */ "ReadCookie" OpHelp(""),
/* 98 */ "SetCookie" OpHelp(""),
/* 99 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
/* 100 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
/* 101 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
/* 102 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
/* 103 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
/* 104 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
/* 105 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
/* 106 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
/* 107 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
/* 108 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
/* 109 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
/* 110 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
/* 111 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
/* 112 */ "OpenDup" OpHelp(""),
/* 113 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
/* 114 */ "OpenAutoindex" OpHelp("nColumn=P2"),
/* 115 */ "OpenEphemeral" OpHelp("nColumn=P2"),
/* 116 */ "String8" OpHelp("r[P2]='P4'"),
/* 117 */ "SorterOpen" OpHelp(""),
/* 118 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
/* 119 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
/* 120 */ "Close" OpHelp(""),
/* 121 */ "ColumnsUsed" OpHelp(""),
/* 122 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"),
/* 123 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"),
/* 124 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
/* 125 */ "NewRowid" OpHelp("r[P2]=rowid"),
/* 126 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
/* 127 */ "RowCell" OpHelp(""),
/* 128 */ "Delete" OpHelp(""),
/* 129 */ "ResetCount" OpHelp(""),
/* 130 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
/* 131 */ "SorterData" OpHelp("r[P2]=data"),
/* 132 */ "RowData" OpHelp("r[P2]=data"),
/* 133 */ "Rowid" OpHelp("r[P2]=rowid"),
/* 134 */ "NullRow" OpHelp(""),
/* 135 */ "SeekEnd" OpHelp(""),
/* 136 */ "IdxInsert" OpHelp("key=r[P2]"),
/* 137 */ "SorterInsert" OpHelp("key=r[P2]"),
/* 138 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
/* 139 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"),
/* 140 */ "IdxRowid" OpHelp("r[P2]=rowid"),
/* 141 */ "FinishSeek" OpHelp(""),
/* 142 */ "Destroy" OpHelp(""),
/* 143 */ "Clear" OpHelp(""),
/* 144 */ "ResetSorter" OpHelp(""),
/* 145 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"),
/* 146 */ "SqlExec" OpHelp(""),
/* 147 */ "ParseSchema" OpHelp(""),
/* 148 */ "LoadAnalysis" OpHelp(""),
/* 149 */ "DropTable" OpHelp(""),
/* 150 */ "DropIndex" OpHelp(""),
/* 151 */ "DropTrigger" OpHelp(""),
/* 152 */ "Real" OpHelp("r[P2]=P4"),
/* 153 */ "IntegrityCk" OpHelp(""),
/* 154 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
/* 155 */ "Param" OpHelp(""),
/* 156 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
/* 157 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
/* 158 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
/* 159 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"),
/* 160 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
/* 161 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"),
/* 162 */ "AggValue" OpHelp("r[P3]=value N=P2"),
/* 163 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
/* 164 */ "Expire" OpHelp(""),
/* 165 */ "CursorLock" OpHelp(""),
/* 166 */ "CursorUnlock" OpHelp(""),
/* 167 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
/* 168 */ "VBegin" OpHelp(""),
/* 169 */ "VCreate" OpHelp(""),
/* 170 */ "VDestroy" OpHelp(""),
/* 171 */ "VOpen" OpHelp(""),
/* 172 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
/* 173 */ "VRename" OpHelp(""),
/* 174 */ "Pagecount" OpHelp(""),
/* 175 */ "MaxPgcnt" OpHelp(""),
/* 176 */ "Trace" OpHelp(""),
/* 177 */ "CursorHint" OpHelp(""),
/* 178 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"),
/* 179 */ "Noop" OpHelp(""),
/* 180 */ "Explain" OpHelp(""),
/* 181 */ "Abortable" OpHelp(""),
};
return azName[i];
}
#endif
/************** End of opcodes.c *********************************************/
/************** Begin file os_unix.c *****************************************/
|
| ︙ | ︙ | |||
48815 48816 48817 48818 48819 48820 48821 |
){
MemFile *pFile = (MemFile*)pFd;
MemStore *p = 0;
int szName;
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFd, flags, pOutFlags);
}
| | | 48857 48858 48859 48860 48861 48862 48863 48864 48865 48866 48867 48868 48869 48870 48871 |
){
MemFile *pFile = (MemFile*)pFd;
MemStore *p = 0;
int szName;
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFd, flags, pOutFlags);
}
memset(pFile, 0, sizeof(*pFile));
szName = sqlite3Strlen30(zName);
if( szName>1 && zName[0]=='/' ){
int i;
#ifndef SQLITE_MUTEX_OMIT
sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
#endif
sqlite3_mutex_enter(pVfsMutex);
|
| ︙ | ︙ | |||
49540 49541 49542 49543 49544 49545 49546 | if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; /* NULL pBitvec tests */ sqlite3BitvecSet(0, 1); sqlite3BitvecClear(0, 1, pTmpSpace); /* Run the program */ | | | 49582 49583 49584 49585 49586 49587 49588 49589 49590 49591 49592 49593 49594 49595 49596 |
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
/* NULL pBitvec tests */
sqlite3BitvecSet(0, 1);
sqlite3BitvecClear(0, 1, pTmpSpace);
/* Run the program */
pc = i = 0;
while( (op = aOp[pc])!=0 ){
switch( op ){
case 1:
case 2:
case 5: {
nx = 4;
i = aOp[pc+2] - 1;
|
| ︙ | ︙ | |||
49844 49845 49846 49847 49848 49849 49850 49851 49852 49853 49854 |
*/
static int numberOfCachePages(PCache *p){
if( p->szCache>=0 ){
/* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the
** suggested cache size is set to N. */
return p->szCache;
}else{
/* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the
** number of cache pages is adjusted to be a number of pages that would
** use approximately abs(N*1024) bytes of memory based on the current
** page size. */
| > | > > | 49886 49887 49888 49889 49890 49891 49892 49893 49894 49895 49896 49897 49898 49899 49900 49901 49902 49903 49904 49905 49906 49907 |
*/
static int numberOfCachePages(PCache *p){
if( p->szCache>=0 ){
/* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the
** suggested cache size is set to N. */
return p->szCache;
}else{
i64 n;
/* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the
** number of cache pages is adjusted to be a number of pages that would
** use approximately abs(N*1024) bytes of memory based on the current
** page size. */
n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
if( n>1000000000 ) n = 1000000000;
return (int)n;
}
}
/*************************************************** General Interfaces ******
**
** Initialize and shutdown the page cache subsystem. Neither of these
** functions are threadsafe.
|
| ︙ | ︙ | |||
51303 51304 51305 51306 51307 51308 51309 51310 51311 51312 |
/*
** Implementation of the sqlite3_pcache.xCachesize method.
**
** Configure the cache_size limit for a cache.
*/
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
PCache1 *pCache = (PCache1 *)p;
if( pCache->bPurgeable ){
PGroup *pGroup = pCache->pGroup;
pcache1EnterMutex(pGroup);
| > > > > > > | | | | 51348 51349 51350 51351 51352 51353 51354 51355 51356 51357 51358 51359 51360 51361 51362 51363 51364 51365 51366 51367 51368 51369 51370 51371 51372 51373 51374 51375 51376 51377 51378 51379 51380 51381 51382 51383 51384 51385 51386 51387 51388 51389 |
/*
** Implementation of the sqlite3_pcache.xCachesize method.
**
** Configure the cache_size limit for a cache.
*/
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
PCache1 *pCache = (PCache1 *)p;
u32 n;
assert( nMax>=0 );
if( pCache->bPurgeable ){
PGroup *pGroup = pCache->pGroup;
pcache1EnterMutex(pGroup);
n = (u32)nMax;
if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){
n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax;
}
pGroup->nMaxPage += (n - pCache->nMax);
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->nMax = n;
pCache->n90pct = pCache->nMax*9/10;
pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup);
}
}
/*
** Implementation of the sqlite3_pcache.xShrink method.
**
** Free up as much memory as possible.
*/
static void pcache1Shrink(sqlite3_pcache *p){
PCache1 *pCache = (PCache1*)p;
if( pCache->bPurgeable ){
PGroup *pGroup = pCache->pGroup;
unsigned int savedMaxPage;
pcache1EnterMutex(pGroup);
savedMaxPage = pGroup->nMaxPage;
pGroup->nMaxPage = 0;
pcache1EnforceMaxPage(pCache);
pGroup->nMaxPage = savedMaxPage;
pcache1LeaveMutex(pGroup);
}
|
| ︙ | ︙ | |||
53110 53111 53112 53113 53114 53115 53116 | ** 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 */ | < > | 53161 53162 53163 53164 53165 53166 53167 53168 53169 53170 53171 53172 53173 53174 53175 53176 | ** 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 */ Pgno mxPgno; /* Maximum allowed size of the database */ i64 pageSize; /* Number of bytes in a page */ 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 */ int aStat[4]; /* Total cache hits, misses, writes, spills */ #ifdef SQLITE_TEST |
| ︙ | ︙ | |||
55455 55456 55457 55458 55459 55460 55461 55462 55463 55464 55465 55466 55467 55468 |
**
** This is an unconditional update. See also the pager_incr_changecounter()
** routine which only updates the change-counter if the update is actually
** needed, as determined by the pPager->changeCountDone state variable.
*/
static void pager_write_changecounter(PgHdr *pPg){
u32 change_counter;
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
put32bits(((char*)pPg->pData)+24, change_counter);
/* Also store the SQLite version number in bytes 96..99 and in
** bytes 92..95 store the change counter for which the version number
| > | 55506 55507 55508 55509 55510 55511 55512 55513 55514 55515 55516 55517 55518 55519 55520 |
**
** This is an unconditional update. See also the pager_incr_changecounter()
** routine which only updates the change-counter if the update is actually
** needed, as determined by the pPager->changeCountDone state variable.
*/
static void pager_write_changecounter(PgHdr *pPg){
u32 change_counter;
if( NEVER(pPg==0) ) return;
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
put32bits(((char*)pPg->pData)+24, change_counter);
/* Also store the SQLite version number in bytes 96..99 and in
** bytes 92..95 store the change counter for which the version number
|
| ︙ | ︙ | |||
57289 57290 57291 57292 57293 57294 57295 57296 57297 57298 57299 57300 57301 57302 |
sqlite3FileSuffix3(zFilename, pPager->zWal);
pPtr = (u8*)(pPager->zWal + sqlite3Strlen30(pPager->zWal)+1);
#endif
}else{
pPager->zWal = 0;
}
#endif
if( nPathname ) sqlite3DbFree(0, zPathname);
pPager->pVfs = pVfs;
pPager->vfsFlags = vfsFlags;
/* Open the pager file.
*/
| > | 57341 57342 57343 57344 57345 57346 57347 57348 57349 57350 57351 57352 57353 57354 57355 |
sqlite3FileSuffix3(zFilename, pPager->zWal);
pPtr = (u8*)(pPager->zWal + sqlite3Strlen30(pPager->zWal)+1);
#endif
}else{
pPager->zWal = 0;
}
#endif
(void)pPtr; /* Suppress warning about unused pPtr value */
if( nPathname ) sqlite3DbFree(0, zPathname);
pPager->pVfs = pVfs;
pPager->vfsFlags = vfsFlags;
/* Open the pager file.
*/
|
| ︙ | ︙ | |||
59168 59169 59170 59171 59172 59173 59174 |
#endif
/*
** Return the approximate number of bytes of memory currently
** used by the pager and its associated cache.
*/
SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager *pPager){
| | | | 59221 59222 59223 59224 59225 59226 59227 59228 59229 59230 59231 59232 59233 59234 59235 59236 |
#endif
/*
** Return the approximate number of bytes of memory currently
** used by the pager and its associated cache.
*/
SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager *pPager){
int perPageSize = pPager->pageSize + pPager->nExtra
+ (int)(sizeof(PgHdr) + 5*sizeof(void*));
return perPageSize*sqlite3PcachePagecount(pPager->pPCache)
+ sqlite3MallocSize(pPager)
+ pPager->pageSize;
}
/*
** Return the number of references to the specified page.
|
| ︙ | ︙ | |||
59363 59364 59365 59366 59367 59368 59369 |
*/
nNew = iSavepoint + (( op==SAVEPOINT_RELEASE ) ? 0 : 1);
for(ii=nNew; ii<pPager->nSavepoint; ii++){
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
}
pPager->nSavepoint = nNew;
| < | > | | 59416 59417 59418 59419 59420 59421 59422 59423 59424 59425 59426 59427 59428 59429 59430 59431 59432 59433 59434 59435 59436 59437 |
*/
nNew = iSavepoint + (( op==SAVEPOINT_RELEASE ) ? 0 : 1);
for(ii=nNew; ii<pPager->nSavepoint; ii++){
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
}
pPager->nSavepoint = nNew;
/* Truncate the sub-journal so that it only includes the parts
** that are still in use. */
if( op==SAVEPOINT_RELEASE ){
PagerSavepoint *pRel = &pPager->aSavepoint[nNew];
if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){
/* Only truncate if it is an in-memory sub-journal. */
if( sqlite3JournalIsInMemory(pPager->sjfd) ){
i64 sz = (pPager->pageSize+4)*(i64)pRel->iSubRec;
rc = sqlite3OsTruncate(pPager->sjfd, sz);
assert( rc==SQLITE_OK );
}
pPager->nSubRec = pRel->iSubRec;
}
}
/* Else this is a rollback operation, playback the specified savepoint.
|
| ︙ | ︙ | |||
68431 68432 68433 68434 68435 68436 68437 |
** is returned if we run out of memory.
*/
static int lockBtree(BtShared *pBt){
int rc; /* Result code from subfunctions */
MemPage *pPage1; /* Page 1 of the database file */
u32 nPage; /* Number of pages in the database */
u32 nPageFile = 0; /* Number of pages in the database file */
| < | | 68484 68485 68486 68487 68488 68489 68490 68491 68492 68493 68494 68495 68496 68497 68498 68499 68500 68501 68502 68503 68504 68505 68506 68507 68508 68509 |
** is returned if we run out of memory.
*/
static int lockBtree(BtShared *pBt){
int rc; /* Result code from subfunctions */
MemPage *pPage1; /* Page 1 of the database file */
u32 nPage; /* Number of pages in the database */
u32 nPageFile = 0; /* Number of pages in the database file */
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pBt->pPage1==0 );
rc = sqlite3PagerSharedLock(pBt->pPager);
if( rc!=SQLITE_OK ) return rc;
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 = get4byte(28+(u8*)pPage1->aData);
sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile);
if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
nPage = nPageFile;
}
if( (pBt->db->flags & SQLITE_ResetDatabase)!=0 ){
nPage = 0;
}
|
| ︙ | ︙ | |||
68478 68479 68480 68481 68482 68483 68484 |
if( page1[18]>2 ){
pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>2 ){
goto page1_init_failed;
}
| | | 68530 68531 68532 68533 68534 68535 68536 68537 68538 68539 68540 68541 68542 68543 68544 |
if( page1[18]>2 ){
pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>2 ){
goto page1_init_failed;
}
/* If the read version is set to 2, this database should be accessed
** in WAL mode. If the log is not already open, open it now. Then
** return SQLITE_OK and return without populating BtShared.pPage1.
** The caller detects this and calls this function again. This is
** required as the version of page 1 currently in the page1 buffer
** may not be the latest version - there may be a newer one in the log
** file.
*/
|
| ︙ | ︙ | |||
70816 70817 70818 70819 70820 70821 70822 |
*pRes = 0;
rc = sqlite3BtreeNext(pCur, 0);
if( rc==SQLITE_OK ){
getCellInfo(pCur);
if( pCur->info.nKey==intKey ){
return SQLITE_OK;
}
| | < < | 70868 70869 70870 70871 70872 70873 70874 70875 70876 70877 70878 70879 70880 70881 70882 |
*pRes = 0;
rc = sqlite3BtreeNext(pCur, 0);
if( rc==SQLITE_OK ){
getCellInfo(pCur);
if( pCur->info.nKey==intKey ){
return SQLITE_OK;
}
}else if( rc!=SQLITE_DONE ){
return rc;
}
}
}
}
#ifdef SQLITE_DEBUG
|
| ︙ | ︙ | |||
72663 72664 72665 72666 72667 72668 72669 72670 72671 72672 72673 72674 72675 72676 |
int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
assert( nCell>=nTail );
nCell -= nTail;
}
pData = &aData[get2byteNotZero(&aData[hdr+5])];
if( pData<pBegin ) goto editpage_fail;
/* Add cells to the start of the page */
if( iNew<iOld ){
int nAdd = MIN(nNew,iOld-iNew);
assert( (iOld-iNew)<nNew || nCell==0 || CORRUPT_DB );
assert( nAdd>=0 );
pCellptr = pPg->aCellIdx;
| > | 72713 72714 72715 72716 72717 72718 72719 72720 72721 72722 72723 72724 72725 72726 72727 |
int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
assert( nCell>=nTail );
nCell -= nTail;
}
pData = &aData[get2byteNotZero(&aData[hdr+5])];
if( pData<pBegin ) goto editpage_fail;
if( NEVER(pData>pPg->aDataEnd) ) goto editpage_fail;
/* Add cells to the start of the page */
if( iNew<iOld ){
int nAdd = MIN(nNew,iOld-iNew);
assert( (iOld-iNew)<nNew || nCell==0 || CORRUPT_DB );
assert( nAdd>=0 );
pCellptr = pPg->aCellIdx;
|
| ︙ | ︙ | |||
73024 73025 73026 73027 73028 73029 73030 | int szNew[NB+2]; /* Combined size of cells placed on i-th page */ u8 *aSpace1; /* Space for copies of dividers cells */ Pgno pgno; /* Temp var to store a page number in */ u8 abDone[NB+2]; /* True after i'th new page is populated */ Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */ Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */ u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */ | | | < | 73075 73076 73077 73078 73079 73080 73081 73082 73083 73084 73085 73086 73087 73088 73089 73090 73091 73092 | int szNew[NB+2]; /* Combined size of cells placed on i-th page */ u8 *aSpace1; /* Space for copies of dividers cells */ Pgno pgno; /* Temp var to store a page number in */ u8 abDone[NB+2]; /* True after i'th new page is populated */ Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */ Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */ u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */ CellArray b; /* Parsed information on cells being balanced */ memset(abDone, 0, sizeof(abDone)); memset(&b, 0, sizeof(b)); pBt = pParent->pBt; assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); /* At this point pParent may have at most one overflow cell. And if ** this overflow cell is present, it must be the cell with ** index iParentIdx. This scenario comes about when this function |
| ︙ | ︙ | |||
73839 73840 73841 73842 73843 73844 73845 | return SQLITE_OK; } /* ** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid ** on the same B-tree as pCur. ** | | | 73889 73890 73891 73892 73893 73894 73895 73896 73897 73898 73899 73900 73901 73902 73903 |
return SQLITE_OK;
}
/*
** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid
** on the same B-tree as pCur.
**
** This can occur if a database is corrupt with two or more SQL tables
** pointing to the same b-tree. If an insert occurs on one SQL table
** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL
** table linked to the same b-tree. If the secondary insert causes a
** rebalance, that can change content out from under the cursor on the
** first SQL table, violating invariants on the first insert.
*/
static int anotherValidCursor(BtCursor *pCur){
|
| ︙ | ︙ | |||
74068 74069 74070 74071 74072 74073 74074 |
assert( iOffset>=0 );
ovflPgno = get4byte(pCur->info.pPayload + iOffset);
pBt = pPage->pBt;
ovflPageSize = pBt->usableSize - 4;
do{
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
if( rc ) return rc;
| | | 74118 74119 74120 74121 74122 74123 74124 74125 74126 74127 74128 74129 74130 74131 74132 |
assert( iOffset>=0 );
ovflPgno = get4byte(pCur->info.pPayload + iOffset);
pBt = pPage->pBt;
ovflPageSize = pBt->usableSize - 4;
do{
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
if( rc ) return rc;
if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){
rc = SQLITE_CORRUPT_BKPT;
}else{
if( iOffset+ovflPageSize<(u32)nTotal ){
ovflPgno = get4byte(pPage->aData);
}else{
ovflPageSize = nTotal - iOffset;
}
|
| ︙ | ︙ | |||
74490 74491 74492 74493 74494 74495 74496 |
ovflIn = get4byte(aIn);
aIn += 4;
nIn = pSrc->pBt->usableSize - 4;
}
}
}while( rc==SQLITE_OK && nOut>0 );
| | | 74540 74541 74542 74543 74544 74545 74546 74547 74548 74549 74550 74551 74552 74553 74554 |
ovflIn = get4byte(aIn);
aIn += 4;
nIn = pSrc->pBt->usableSize - 4;
}
}
}while( rc==SQLITE_OK && nOut>0 );
if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){
Pgno pgnoNew;
MemPage *pNew = 0;
rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
put4byte(pPgnoOut, pgnoNew);
if( ISAUTOVACUUM && pPageOut ){
ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
}
|
| ︙ | ︙ | |||
77164 77165 77166 77167 77168 77169 77170 77171 77172 77173 77174 77175 77176 77177 |
** SQLITE_NOMEM may be returned if a malloc() fails during conversion
** between formats.
*/
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
#ifndef SQLITE_OMIT_UTF16
int rc;
#endif
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|| desiredEnc==SQLITE_UTF16BE );
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
return SQLITE_OK;
}
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
| > | 77214 77215 77216 77217 77218 77219 77220 77221 77222 77223 77224 77225 77226 77227 77228 |
** SQLITE_NOMEM may be returned if a malloc() fails during conversion
** between formats.
*/
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
#ifndef SQLITE_OMIT_UTF16
int rc;
#endif
assert( pMem!=0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|| desiredEnc==SQLITE_UTF16BE );
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
return SQLITE_OK;
}
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
| ︙ | ︙ | |||
77296 77297 77298 77299 77300 77301 77302 77303 77304 77305 77306 77307 77308 77309 |
/*
** Change pMem so that its MEM_Str or MEM_Blob value is stored in
** MEM.zMalloc, where it can be safely written.
**
** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
*/
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
int rc = vdbeMemAddTerminator(pMem);
if( rc ) return rc;
| > | 77347 77348 77349 77350 77351 77352 77353 77354 77355 77356 77357 77358 77359 77360 77361 |
/*
** Change pMem so that its MEM_Str or MEM_Blob value is stored in
** MEM.zMalloc, where it can be safely written.
**
** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
*/
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
int rc = vdbeMemAddTerminator(pMem);
if( rc ) return rc;
|
| ︙ | ︙ | |||
77320 77321 77322 77323 77324 77325 77326 77327 77328 77329 77330 77331 77332 77333 |
/*
** If the given Mem* has a zero-filled tail, turn it into an ordinary
** blob stored in dynamically allocated space.
*/
#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
int nByte;
assert( pMem->flags & MEM_Zero );
assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) );
testcase( sqlite3_value_nochange(pMem) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
/* Set nByte to the number of bytes required to store the expanded blob. */
| > | 77372 77373 77374 77375 77376 77377 77378 77379 77380 77381 77382 77383 77384 77385 77386 |
/*
** If the given Mem* has a zero-filled tail, turn it into an ordinary
** blob stored in dynamically allocated space.
*/
#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
int nByte;
assert( pMem!=0 );
assert( pMem->flags & MEM_Zero );
assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) );
testcase( sqlite3_value_nochange(pMem) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
/* Set nByte to the number of bytes required to store the expanded blob. */
|
| ︙ | ︙ | |||
77347 77348 77349 77350 77351 77352 77353 77354 77355 77356 77357 77358 77359 77360 |
}
#endif
/*
** Make sure the given Mem is \u0000 terminated.
*/
SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) );
testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 );
if( (pMem->flags & (MEM_Term|MEM_Str))!=MEM_Str ){
return SQLITE_OK; /* Nothing to do */
}else{
return vdbeMemAddTerminator(pMem);
| > | 77400 77401 77402 77403 77404 77405 77406 77407 77408 77409 77410 77411 77412 77413 77414 |
}
#endif
/*
** Make sure the given Mem is \u0000 terminated.
*/
SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) );
testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 );
if( (pMem->flags & (MEM_Term|MEM_Str))!=MEM_Str ){
return SQLITE_OK; /* Nothing to do */
}else{
return vdbeMemAddTerminator(pMem);
|
| ︙ | ︙ | |||
77374 77375 77376 77377 77378 77379 77380 77381 77382 77383 77384 77385 77386 77387 |
** sqlite3_value_text()), or for ensuring that values to be used as btree
** keys are strings. In the former case a NULL pointer is returned the
** user and the latter is an internal programming error.
*/
SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
const int nByte = 32;
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !(pMem->flags&MEM_Zero) );
assert( !(pMem->flags&(MEM_Str|MEM_Blob)) );
assert( pMem->flags&(MEM_Int|MEM_Real|MEM_IntReal) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
| > | 77428 77429 77430 77431 77432 77433 77434 77435 77436 77437 77438 77439 77440 77441 77442 |
** sqlite3_value_text()), or for ensuring that values to be used as btree
** keys are strings. In the former case a NULL pointer is returned the
** user and the latter is an internal programming error.
*/
SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
const int nByte = 32;
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !(pMem->flags&MEM_Zero) );
assert( !(pMem->flags&(MEM_Str|MEM_Blob)) );
assert( pMem->flags&(MEM_Int|MEM_Real|MEM_IntReal) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
| ︙ | ︙ | |||
77409 77410 77411 77412 77413 77414 77415 77416 77417 77418 77419 77420 77421 77422 |
** Return SQLITE_ERROR if the finalizer reports an error. SQLITE_OK
** otherwise.
*/
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
sqlite3_context ctx;
Mem t;
assert( pFunc!=0 );
assert( pFunc->xFinalize!=0 );
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));
memset(&t, 0, sizeof(t));
t.flags = MEM_Null;
t.db = pMem->db;
| > | 77464 77465 77466 77467 77468 77469 77470 77471 77472 77473 77474 77475 77476 77477 77478 |
** Return SQLITE_ERROR if the finalizer reports an error. SQLITE_OK
** otherwise.
*/
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
sqlite3_context ctx;
Mem t;
assert( pFunc!=0 );
assert( pMem!=0 );
assert( pFunc->xFinalize!=0 );
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));
memset(&t, 0, sizeof(t));
t.flags = MEM_Null;
t.db = pMem->db;
|
| ︙ | ︙ | |||
77559 77560 77561 77562 77563 77564 77565 77566 77567 77568 77569 77570 77571 77572 |
static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){
i64 value = 0;
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
return value;
}
SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){
int flags;
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
flags = pMem->flags;
if( flags & (MEM_Int|MEM_IntReal) ){
testcase( flags & MEM_IntReal );
return pMem->u.i;
}else if( flags & MEM_Real ){
| > | 77615 77616 77617 77618 77619 77620 77621 77622 77623 77624 77625 77626 77627 77628 77629 |
static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){
i64 value = 0;
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
return value;
}
SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){
int flags;
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
flags = pMem->flags;
if( flags & (MEM_Int|MEM_IntReal) ){
testcase( flags & MEM_IntReal );
return pMem->u.i;
}else if( flags & MEM_Real ){
|
| ︙ | ︙ | |||
77587 77588 77589 77590 77591 77592 77593 77594 77595 77596 77597 77598 77599 77600 |
static SQLITE_NOINLINE double memRealValue(Mem *pMem){
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
double val = (double)0;
sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc);
return val;
}
SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
if( pMem->flags & MEM_Real ){
return pMem->u.r;
}else if( pMem->flags & (MEM_Int|MEM_IntReal) ){
testcase( pMem->flags & MEM_IntReal );
return (double)pMem->u.i;
| > | 77644 77645 77646 77647 77648 77649 77650 77651 77652 77653 77654 77655 77656 77657 77658 |
static SQLITE_NOINLINE double memRealValue(Mem *pMem){
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
double val = (double)0;
sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc);
return val;
}
SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
if( pMem->flags & MEM_Real ){
return pMem->u.r;
}else if( pMem->flags & (MEM_Int|MEM_IntReal) ){
testcase( pMem->flags & MEM_IntReal );
return (double)pMem->u.i;
|
| ︙ | ︙ | |||
77619 77620 77621 77622 77623 77624 77625 77626 77627 77628 77629 77630 77631 77632 |
/*
** The MEM structure is already a MEM_Real. Try to also make it a
** MEM_Int if we can.
*/
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
i64 ix;
assert( pMem->flags & MEM_Real );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
ix = doubleToInt64(pMem->u.r);
| > | 77677 77678 77679 77680 77681 77682 77683 77684 77685 77686 77687 77688 77689 77690 77691 |
/*
** The MEM structure is already a MEM_Real. Try to also make it a
** MEM_Int if we can.
*/
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
i64 ix;
assert( pMem!=0 );
assert( pMem->flags & MEM_Real );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
ix = doubleToInt64(pMem->u.r);
|
| ︙ | ︙ | |||
77646 77647 77648 77649 77650 77651 77652 77653 77654 77655 77656 77657 77658 77659 77660 77661 77662 77663 77664 77665 77666 77667 77668 77669 77670 77671 77672 77673 |
}
}
/*
** Convert pMem to type integer. Invalidate any prior representations.
*/
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
pMem->u.i = sqlite3VdbeIntValue(pMem);
MemSetTypeFlag(pMem, MEM_Int);
return SQLITE_OK;
}
/*
** Convert pMem so that it is of type MEM_Real.
** Invalidate any prior representations.
*/
SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
pMem->u.r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
return SQLITE_OK;
}
| > > | 77705 77706 77707 77708 77709 77710 77711 77712 77713 77714 77715 77716 77717 77718 77719 77720 77721 77722 77723 77724 77725 77726 77727 77728 77729 77730 77731 77732 77733 77734 |
}
}
/*
** Convert pMem to type integer. Invalidate any prior representations.
*/
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
pMem->u.i = sqlite3VdbeIntValue(pMem);
MemSetTypeFlag(pMem, MEM_Int);
return SQLITE_OK;
}
/*
** Convert pMem so that it is of type MEM_Real.
** Invalidate any prior representations.
*/
SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
pMem->u.r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
77693 77694 77695 77696 77697 77698 77699 77700 77701 77702 77703 77704 77705 77706 |
** 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){
testcase( pMem->flags & MEM_Int );
testcase( pMem->flags & MEM_Real );
testcase( pMem->flags & MEM_IntReal );
testcase( pMem->flags & MEM_Null );
if( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))==0 ){
int rc;
sqlite3_int64 ix;
| > | 77754 77755 77756 77757 77758 77759 77760 77761 77762 77763 77764 77765 77766 77767 77768 |
** 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){
assert( pMem!=0 );
testcase( pMem->flags & MEM_Int );
testcase( pMem->flags & MEM_Real );
testcase( pMem->flags & MEM_IntReal );
testcase( pMem->flags & MEM_Null );
if( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))==0 ){
int rc;
sqlite3_int64 ix;
|
| ︙ | ︙ | |||
78044 78045 78046 78047 78048 78049 78050 78051 78052 78053 78054 78055 78056 78057 |
u8 enc, /* Encoding of z. 0 for BLOBs */
void (*xDel)(void*) /* Destructor function */
){
i64 nByte = n; /* New value for pMem->n */
int iLimit; /* Maximum allowed string or blob size */
u16 flags = 0; /* New value for pMem->flags */
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
/* If z is a NULL pointer, set pMem to contain an SQL NULL. */
if( !z ){
sqlite3VdbeMemSetNull(pMem);
return SQLITE_OK;
| > | 78106 78107 78108 78109 78110 78111 78112 78113 78114 78115 78116 78117 78118 78119 78120 |
u8 enc, /* Encoding of z. 0 for BLOBs */
void (*xDel)(void*) /* Destructor function */
){
i64 nByte = n; /* New value for pMem->n */
int iLimit; /* Maximum allowed string or blob size */
u16 flags = 0; /* New value for pMem->flags */
assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
/* If z is a NULL pointer, set pMem to contain an SQL NULL. */
if( !z ){
sqlite3VdbeMemSetNull(pMem);
return SQLITE_OK;
|
| ︙ | ︙ | |||
78352 78353 78354 78355 78356 78357 78358 | ** If the conditions above are not met, this function returns SQLITE_OK ** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to ** NULL and an SQLite error code returned. */ #ifdef SQLITE_ENABLE_STAT4 static int valueFromFunction( sqlite3 *db, /* The database connection */ | | | 78415 78416 78417 78418 78419 78420 78421 78422 78423 78424 78425 78426 78427 78428 78429 |
** If the conditions above are not met, this function returns SQLITE_OK
** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to
** NULL and an SQLite error code returned.
*/
#ifdef SQLITE_ENABLE_STAT4
static int valueFromFunction(
sqlite3 *db, /* The database connection */
const Expr *p, /* The expression to evaluate */
u8 enc, /* Encoding to use */
u8 aff, /* Affinity to use */
sqlite3_value **ppVal, /* Write the new value here */
struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
){
sqlite3_context ctx; /* Context object for function invocation */
sqlite3_value **apVal = 0; /* Function arguments */
|
| ︙ | ︙ | |||
78446 78447 78448 78449 78450 78451 78452 | ** If pCtx is NULL and an error occurs after the sqlite3_value object ** has been allocated, it is freed before returning. Or, if pCtx is not ** NULL, it is assumed that the caller will free any allocated object ** in all cases. */ static int valueFromExpr( sqlite3 *db, /* The database connection */ | | | 78509 78510 78511 78512 78513 78514 78515 78516 78517 78518 78519 78520 78521 78522 78523 |
** If pCtx is NULL and an error occurs after the sqlite3_value object
** has been allocated, it is freed before returning. Or, if pCtx is not
** NULL, it is assumed that the caller will free any allocated object
** in all cases.
*/
static int valueFromExpr(
sqlite3 *db, /* The database connection */
const Expr *pExpr, /* The expression to evaluate */
u8 enc, /* Encoding to use */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal, /* Write the new value here */
struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
){
int op;
char *zVal = 0;
|
| ︙ | ︙ | |||
78601 78602 78603 78604 78605 78606 78607 | ** be converted directly into a value, then the value is allocated and ** a pointer written to *ppVal. The caller is responsible for deallocating ** the value by passing it to sqlite3ValueFree() later on. If the expression ** cannot be converted to a value, then *ppVal is set to NULL. */ SQLITE_PRIVATE int sqlite3ValueFromExpr( sqlite3 *db, /* The database connection */ | | | 78664 78665 78666 78667 78668 78669 78670 78671 78672 78673 78674 78675 78676 78677 78678 |
** be converted directly into a value, then the value is allocated and
** a pointer written to *ppVal. The caller is responsible for deallocating
** the value by passing it to sqlite3ValueFree() later on. If the expression
** cannot be converted to a value, then *ppVal is set to NULL.
*/
SQLITE_PRIVATE int sqlite3ValueFromExpr(
sqlite3 *db, /* The database connection */
const Expr *pExpr, /* The expression to evaluate */
u8 enc, /* Encoding to use */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal /* Write the new value here */
){
return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0;
}
|
| ︙ | ︙ | |||
79117 79118 79119 79120 79121 79122 79123 79124 79125 79126 79127 79128 79129 79130 79131 79132 |
i = p->nOp;
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( op>=0 && op<0xff );
if( p->nOpAlloc<=i ){
return growOp3(p, op, p1, p2, p3);
}
p->nOp++;
pOp = &p->aOp[i];
pOp->opcode = (u8)op;
pOp->p5 = 0;
pOp->p1 = p1;
pOp->p2 = p2;
pOp->p3 = p3;
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
| > > | 79180 79181 79182 79183 79184 79185 79186 79187 79188 79189 79190 79191 79192 79193 79194 79195 79196 79197 |
i = p->nOp;
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( op>=0 && op<0xff );
if( p->nOpAlloc<=i ){
return growOp3(p, op, p1, p2, p3);
}
assert( p->aOp!=0 );
p->nOp++;
pOp = &p->aOp[i];
assert( pOp!=0 );
pOp->opcode = (u8)op;
pOp->p5 = 0;
pOp->p1 = p1;
pOp->p2 = p2;
pOp->p3 = p3;
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
|
| ︙ | ︙ | |||
80361 80362 80363 80364 80365 80366 80367 |
sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
zOpName = sqlite3OpcodeName(pOp->opcode);
nOpName = sqlite3Strlen30(zOpName);
if( zOpName[nOpName+1] ){
int seenCom = 0;
char c;
| | | 80426 80427 80428 80429 80430 80431 80432 80433 80434 80435 80436 80437 80438 80439 80440 |
sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
zOpName = sqlite3OpcodeName(pOp->opcode);
nOpName = sqlite3Strlen30(zOpName);
if( zOpName[nOpName+1] ){
int seenCom = 0;
char c;
zSynopsis = zOpName + nOpName + 1;
if( strncmp(zSynopsis,"IF ",3)==0 ){
sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3);
zSynopsis = zAlt;
}
for(ii=0; (c = zSynopsis[ii])!=0; ii++){
if( c=='P' ){
c = zSynopsis[++ii];
|
| ︙ | ︙ | |||
81881 81882 81883 81884 81885 81886 81887 | #endif /* ** This routine is called the when a VDBE tries to halt. If the VDBE ** has made changes and is in autocommit mode, then commit those ** changes. If a rollback is needed, then do the rollback. ** | | | | | 81946 81947 81948 81949 81950 81951 81952 81953 81954 81955 81956 81957 81958 81959 81960 81961 81962 |
#endif
/*
** This routine is called the when a VDBE tries to halt. If the VDBE
** has made changes and is in autocommit mode, then commit those
** changes. If a rollback is needed, then do the rollback.
**
** This routine is the only way to move the sqlite3eOpenState of a VM from
** SQLITE_STATE_RUN to SQLITE_STATE_HALT. It is harmless to
** call this on a VM that is in the SQLITE_STATE_HALT state.
**
** Return an error code. If the commit could not complete because of
** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it
** means the close did not happen and needs to be repeated.
*/
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
int rc; /* Used to store transient return codes */
|
| ︙ | ︙ | |||
86265 86266 86267 86268 86269 86270 86271 | int nToken; /* Length of the parameter token */ int i; /* Loop counter */ Mem *pVar; /* Value of a host parameter */ StrAccum out; /* Accumulate the output here */ #ifndef SQLITE_OMIT_UTF16 Mem utf8; /* Used to convert UTF16 into UTF8 for display */ #endif | < | < | 86330 86331 86332 86333 86334 86335 86336 86337 86338 86339 86340 86341 86342 86343 86344 86345 86346 |
int nToken; /* Length of the parameter token */
int i; /* Loop counter */
Mem *pVar; /* Value of a host parameter */
StrAccum out; /* Accumulate the output here */
#ifndef SQLITE_OMIT_UTF16
Mem utf8; /* Used to convert UTF16 into UTF8 for display */
#endif
db = p->db;
sqlite3StrAccumInit(&out, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
if( db->nVdbeExec>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
sqlite3_str_append(&out, "-- ", 3);
assert( (zRawSql - zStart) > 0 );
sqlite3_str_append(&out, zStart, (int)(zRawSql-zStart));
|
| ︙ | ︙ | |||
87049 87050 87051 87052 87053 87054 87055 87056 87057 87058 87059 87060 87061 87062 |
return out2PrereleaseWithClear(pOut);
}else{
pOut->flags = MEM_Int;
return pOut;
}
}
/*
** Execute as much of a VDBE program as we can.
** This is the core of sqlite3_step().
*/
SQLITE_PRIVATE int sqlite3VdbeExec(
Vdbe *p /* The VDBE */
| > > > > > > > > > > > > > | 87112 87113 87114 87115 87116 87117 87118 87119 87120 87121 87122 87123 87124 87125 87126 87127 87128 87129 87130 87131 87132 87133 87134 87135 87136 87137 87138 |
return out2PrereleaseWithClear(pOut);
}else{
pOut->flags = MEM_Int;
return pOut;
}
}
/*
** Return the symbolic name for the data type of a pMem
*/
static const char *vdbeMemTypeName(Mem *pMem){
static const char *azTypes[] = {
/* SQLITE_INTEGER */ "INT",
/* SQLITE_FLOAT */ "REAL",
/* SQLITE_TEXT */ "TEXT",
/* SQLITE_BLOB */ "BLOB",
/* SQLITE_NULL */ "NULL"
};
return azTypes[sqlite3_value_type(pMem)-1];
}
/*
** Execute as much of a VDBE program as we can.
** This is the core of sqlite3_step().
*/
SQLITE_PRIVATE int sqlite3VdbeExec(
Vdbe *p /* The VDBE */
|
| ︙ | ︙ | |||
88879 88880 88881 88882 88883 88884 88885 88886 88887 88888 88889 88890 88891 88892 |
pIn1 = &aMem[pOp->p1];
VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2);
if( (pIn1->flags & MEM_Null)!=0 ){
goto jump_to_p2;
}
break;
}
/* Opcode: ZeroOrNull P1 P2 P3 * *
** Synopsis: r[P2] = 0 OR NULL
**
** If all both registers P1 and P3 are NOT NULL, then store a zero in
** register P2. If either registers P1 or P3 are NULL then put
** a NULL in register P2.
| > > > > > > > > > > > > > > > > | 88955 88956 88957 88958 88959 88960 88961 88962 88963 88964 88965 88966 88967 88968 88969 88970 88971 88972 88973 88974 88975 88976 88977 88978 88979 88980 88981 88982 88983 88984 |
pIn1 = &aMem[pOp->p1];
VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2);
if( (pIn1->flags & MEM_Null)!=0 ){
goto jump_to_p2;
}
break;
}
/* Opcode: IsNullOrType P1 P2 P3 * *
** Synopsis: if typeof(r[P1]) IN (P3,5) goto P2
**
** Jump to P2 if the value in register P1 is NULL or has a datatype P3.
** P3 is an integer which should be one of SQLITE_INTEGER, SQLITE_FLOAT,
** SQLITE_BLOB, SQLITE_NULL, or SQLITE_TEXT.
*/
case OP_IsNullOrType: { /* jump, in1 */
int doTheJump;
pIn1 = &aMem[pOp->p1];
doTheJump = (pIn1->flags & MEM_Null)!=0 || sqlite3_value_type(pIn1)==pOp->p3;
VdbeBranchTaken( doTheJump, 2);
if( doTheJump ) goto jump_to_p2;
break;
}
/* Opcode: ZeroOrNull P1 P2 P3 * *
** Synopsis: r[P2] = 0 OR NULL
**
** If all both registers P1 and P3 are NOT NULL, then store a zero in
** register P2. If either registers P1 or P3 are NULL then put
** a NULL in register P2.
|
| ︙ | ︙ | |||
89247 89248 89249 89250 89251 89252 89253 89254 89255 89256 89257 89258 89259 89260 |
pOp = &aOp[aOp[0].p3-1];
break;
}else{
rc = SQLITE_CORRUPT_BKPT;
goto abort_due_to_error;
}
}
/* Opcode: Affinity P1 P2 * P4 *
** Synopsis: affinity(r[P1@P2])
**
** Apply affinities to a range of P2 registers starting with P1.
**
** P4 is a string that is P2 characters long. The N-th character of the
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 89339 89340 89341 89342 89343 89344 89345 89346 89347 89348 89349 89350 89351 89352 89353 89354 89355 89356 89357 89358 89359 89360 89361 89362 89363 89364 89365 89366 89367 89368 89369 89370 89371 89372 89373 89374 89375 89376 89377 89378 89379 89380 89381 89382 89383 89384 89385 89386 89387 89388 89389 89390 89391 89392 89393 89394 89395 89396 89397 89398 89399 89400 89401 89402 89403 89404 89405 89406 89407 89408 89409 89410 89411 89412 89413 89414 89415 89416 89417 89418 89419 89420 89421 89422 89423 89424 89425 89426 89427 89428 89429 89430 89431 89432 89433 89434 89435 89436 89437 89438 89439 89440 89441 89442 89443 89444 89445 89446 |
pOp = &aOp[aOp[0].p3-1];
break;
}else{
rc = SQLITE_CORRUPT_BKPT;
goto abort_due_to_error;
}
}
/* Opcode: TypeCheck P1 P2 * P4 *
** Synopsis: typecheck(r[P1@P2])
**
** Apply affinities to the range of P2 registers beginning with P1.
** Take the affinities from the Table object in P4. If any value
** cannot be coerced into the correct type, then raise an error.
**
** This opcode is similar to OP_Affinity except that this opcode
** forces the register type to the Table column type. This is used
** to implement "strict affinity".
**
** Preconditions:
**
** <ul>
** <li> P2 should be the number of non-virtual columns in the
** table of P4.
** <li> Table P4 should be a STRICT table.
** </ul>
**
** If any precondition is false, an assertion fault occurs.
*/
case OP_TypeCheck: {
Table *pTab;
Column *aCol;
int i;
assert( pOp->p4type==P4_TABLE );
pTab = pOp->p4.pTab;
assert( pTab->tabFlags & TF_Strict );
assert( pTab->nNVCol==pOp->p2 );
aCol = pTab->aCol;
pIn1 = &aMem[pOp->p1];
for(i=0; i<pTab->nCol; i++){
if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue;
assert( pIn1 < &aMem[pOp->p1+pOp->p2] );
applyAffinity(pIn1, aCol[i].affinity, encoding);
if( (pIn1->flags & MEM_Null)==0 ){
switch( aCol[i].eCType ){
case COLTYPE_BLOB: {
if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error;
break;
}
case COLTYPE_INTEGER:
case COLTYPE_INT: {
if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error;
break;
}
case COLTYPE_TEXT: {
if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error;
break;
}
case COLTYPE_REAL: {
if( pIn1->flags & MEM_Int ){
/* When applying REAL affinity, if the result is still an MEM_Int
** that will fit in 6 bytes, then change the type to MEM_IntReal
** so that we keep the high-resolution integer value but know that
** the type really wants to be REAL. */
testcase( pIn1->u.i==140737488355328LL );
testcase( pIn1->u.i==140737488355327LL );
testcase( pIn1->u.i==-140737488355328LL );
testcase( pIn1->u.i==-140737488355329LL );
if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){
pIn1->flags |= MEM_IntReal;
pIn1->flags &= ~MEM_Int;
}else{
pIn1->u.r = (double)pIn1->u.i;
pIn1->flags |= MEM_Real;
pIn1->flags &= ~MEM_Int;
}
}else if( (pIn1->flags & MEM_Real)==0 ){
goto vdbe_type_error;
}
break;
}
default: {
/* COLTYPE_ANY. Accept anything. */
break;
}
}
}
REGISTER_TRACE((int)(pIn1-aMem), pIn1);
pIn1++;
}
assert( pIn1 == &aMem[pOp->p1+pOp->p2] );
break;
vdbe_type_error:
sqlite3VdbeError(p, "cannot store %s value in %s column %s.%s",
vdbeMemTypeName(pIn1), sqlite3StdType[aCol[i].eCType-1],
pTab->zName, aCol[i].zCnName);
rc = SQLITE_CONSTRAINT_DATATYPE;
goto abort_due_to_error;
}
/* Opcode: Affinity P1 P2 * P4 *
** Synopsis: affinity(r[P1@P2])
**
** Apply affinities to a range of P2 registers starting with P1.
**
** P4 is a string that is P2 characters long. The N-th character of the
|
| ︙ | ︙ | |||
91511 91512 91513 91514 91515 91516 91517 |
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
zDb = db->aDb[pC->iDb].zDbSName;
pTab = pOp->p4.pTab;
assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) );
}else{
pTab = 0;
| | | 91697 91698 91699 91700 91701 91702 91703 91704 91705 91706 91707 91708 91709 91710 91711 |
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
zDb = db->aDb[pC->iDb].zDbSName;
pTab = pOp->p4.pTab;
assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) );
}else{
pTab = 0;
zDb = 0;
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/* Invoke the pre-update hook, if any */
if( pTab ){
if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){
sqlite3VdbePreUpdateHook(p,pC,SQLITE_INSERT,zDb,pTab,x.nKey,pOp->p2,-1);
|
| ︙ | ︙ | |||
91664 91665 91666 91667 91668 91669 91670 |
assert( pOp->p4.pTab!=0 );
zDb = db->aDb[pC->iDb].zDbSName;
pTab = pOp->p4.pTab;
if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){
pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor);
}
}else{
| | | > | | 91850 91851 91852 91853 91854 91855 91856 91857 91858 91859 91860 91861 91862 91863 91864 91865 91866 91867 91868 91869 91870 91871 |
assert( pOp->p4.pTab!=0 );
zDb = db->aDb[pC->iDb].zDbSName;
pTab = pOp->p4.pTab;
if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){
pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor);
}
}else{
zDb = 0;
pTab = 0;
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/* Invoke the pre-update-hook if required. */
assert( db->xPreUpdateCallback==0 || pTab==pOp->p4.pTab );
if( db->xPreUpdateCallback && pTab ){
assert( !(opflags & OPFLAG_ISUPDATE)
|| HasRowid(pTab)==0
|| (aMem[pOp->p3].flags & MEM_Int)
);
sqlite3VdbePreUpdateHook(p, pC,
(opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
zDb, pTab, pC->movetoTarget,
|
| ︙ | ︙ | |||
91711 91712 91713 91714 91715 91716 91717 |
pC->cacheStatus = CACHE_STALE;
pC->seekResult = 0;
if( rc ) goto abort_due_to_error;
/* Invoke the update-hook if required. */
if( opflags & OPFLAG_NCHANGE ){
p->nChange++;
| | | 91898 91899 91900 91901 91902 91903 91904 91905 91906 91907 91908 91909 91910 91911 91912 |
pC->cacheStatus = CACHE_STALE;
pC->seekResult = 0;
if( rc ) goto abort_due_to_error;
/* Invoke the update-hook if required. */
if( opflags & OPFLAG_NCHANGE ){
p->nChange++;
if( db->xUpdateCallback && ALWAYS(pTab!=0) && HasRowid(pTab) ){
db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
pC->movetoTarget);
assert( pC->iDb>=0 );
}
}
break;
|
| ︙ | ︙ | |||
92298 92299 92300 92301 92302 92303 92304 | ** index opened by cursor P1. ** ** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error ** if no matching index entry is found. This happens when running ** an UPDATE or DELETE statement and the index entry to be updated ** or deleted is not found. For some uses of IdxDelete ** (example: the EXCEPT operator) it does not matter that no matching | | > | 92485 92486 92487 92488 92489 92490 92491 92492 92493 92494 92495 92496 92497 92498 92499 92500 |
** index opened by cursor P1.
**
** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error
** if no matching index entry is found. This happens when running
** an UPDATE or DELETE statement and the index entry to be updated
** or deleted is not found. For some uses of IdxDelete
** (example: the EXCEPT operator) it does not matter that no matching
** entry is found. For those cases, P5 is zero. Also, do not raise
** this (self-correcting and non-critical) error if in writable_schema mode.
*/
case OP_IdxDelete: {
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
UnpackedRecord r;
|
| ︙ | ︙ | |||
92324 92325 92326 92327 92328 92329 92330 |
r.default_rc = 0;
r.aMem = &aMem[pOp->p2];
rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
if( rc ) goto abort_due_to_error;
if( res==0 ){
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
if( rc ) goto abort_due_to_error;
| | | 92512 92513 92514 92515 92516 92517 92518 92519 92520 92521 92522 92523 92524 92525 92526 |
r.default_rc = 0;
r.aMem = &aMem[pOp->p2];
rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
if( rc ) goto abort_due_to_error;
if( res==0 ){
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
if( rc ) goto abort_due_to_error;
}else if( pOp->p5 && !sqlite3WritableSchema(db) ){
rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
goto abort_due_to_error;
}
assert( pC->deferredMoveto==0 );
pC->cacheStatus = CACHE_STALE;
pC->seekResult = 0;
break;
|
| ︙ | ︙ | |||
93989 93990 93991 93992 93993 93994 93995 | /* Grab the index number and argc parameters */ assert( (pQuery->flags&MEM_Int)!=0 && pArgc->flags==MEM_Int ); nArg = (int)pArgc->u.i; iQuery = (int)pQuery->u.i; /* Invoke the xFilter method */ | < | 94177 94178 94179 94180 94181 94182 94183 94184 94185 94186 94187 94188 94189 94190 |
/* Grab the index number and argc parameters */
assert( (pQuery->flags&MEM_Int)!=0 && pArgc->flags==MEM_Int );
nArg = (int)pArgc->u.i;
iQuery = (int)pQuery->u.i;
/* Invoke the xFilter method */
apArg = p->apArg;
for(i = 0; i<nArg; i++){
apArg[i] = &pArgc[i+1];
}
rc = pModule->xFilter(pVCur, iQuery, pOp->p4.z, nArg, apArg);
sqlite3VtabImportErrmsg(p, pVtab);
if( rc ) goto abort_due_to_error;
|
| ︙ | ︙ | |||
94079 94080 94081 94082 94083 94084 94085 |
*/
case OP_VNext: { /* jump */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
VdbeCursor *pCur;
| < | 94266 94267 94268 94269 94270 94271 94272 94273 94274 94275 94276 94277 94278 94279 |
*/
case OP_VNext: { /* jump */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
VdbeCursor *pCur;
pCur = p->apCsr[pOp->p1];
assert( pCur->eCurType==CURTYPE_VTAB );
if( pCur->nullRow ){
break;
}
pVtab = pCur->uc.pVCur->pVtab;
pModule = pVtab->pModule;
|
| ︙ | ︙ | |||
94621 94622 94623 94624 94625 94626 94627 94628 94629 94630 94631 94632 94633 94634 |
abort_due_to_error:
if( db->mallocFailed ){
rc = SQLITE_NOMEM_BKPT;
}else if( rc==SQLITE_IOERR_CORRUPTFS ){
rc = SQLITE_CORRUPT_BKPT;
}
assert( rc );
if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
}
p->rc = rc;
sqlite3SystemError(db, rc);
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(rc, "statement aborts at %d: [%s] %s",
| > > > > > | 94807 94808 94809 94810 94811 94812 94813 94814 94815 94816 94817 94818 94819 94820 94821 94822 94823 94824 94825 |
abort_due_to_error:
if( db->mallocFailed ){
rc = SQLITE_NOMEM_BKPT;
}else if( rc==SQLITE_IOERR_CORRUPTFS ){
rc = SQLITE_CORRUPT_BKPT;
}
assert( rc );
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeTrace ){
printf("ABORT-due-to-error. rc=%d\n", rc);
}
#endif
if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
}
p->rc = rc;
sqlite3SystemError(db, rc);
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(rc, "statement aborts at %d: [%s] %s",
|
| ︙ | ︙ | |||
100212 100213 100214 100215 100216 100217 100218 |
testcase( pExpr->op==TK_IN );
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
int nRef = pNC->nRef;
testcase( pNC->ncFlags & NC_IsCheck );
testcase( pNC->ncFlags & NC_PartIdx );
testcase( pNC->ncFlags & NC_IdxExpr );
testcase( pNC->ncFlags & NC_GenCol );
| > | < > | > | 100403 100404 100405 100406 100407 100408 100409 100410 100411 100412 100413 100414 100415 100416 100417 100418 100419 100420 100421 |
testcase( pExpr->op==TK_IN );
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
int nRef = pNC->nRef;
testcase( pNC->ncFlags & NC_IsCheck );
testcase( pNC->ncFlags & NC_PartIdx );
testcase( pNC->ncFlags & NC_IdxExpr );
testcase( pNC->ncFlags & NC_GenCol );
if( pNC->ncFlags & NC_SelfRef ){
notValidImpl(pParse, pNC, "subqueries", pExpr);
}else{
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
}
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
pNC->ncFlags |= NC_VarSelect;
}
}
break;
|
| ︙ | ︙ | |||
101142 101143 101144 101145 101146 101147 101148 | /* Forward declarations */ static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int); static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); /* ** Return the affinity character for a single column of a table. */ | | | 101335 101336 101337 101338 101339 101340 101341 101342 101343 101344 101345 101346 101347 101348 101349 |
/* Forward declarations */
static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int);
static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree);
/*
** Return the affinity character for a single column of a table.
*/
SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table *pTab, int iCol){
assert( iCol<pTab->nCol );
return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
}
/*
** Return the 'affinity' of the expression pExpr if any.
**
|
| ︙ | ︙ | |||
101213 101214 101215 101216 101217 101218 101219 | ** sequence named by pToken. Return a pointer to a new Expr node that ** implements the COLLATE operator. ** ** If a memory allocation error occurs, that fact is recorded in pParse->db ** and the pExpr parameter is returned unchanged. */ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken( | | | > > > > | 101406 101407 101408 101409 101410 101411 101412 101413 101414 101415 101416 101417 101418 101419 101420 101421 101422 101423 101424 101425 101426 101427 101428 101429 101430 101431 101432 101433 101434 101435 101436 101437 101438 101439 |
** sequence named by pToken. Return a pointer to a new Expr node that
** implements the COLLATE operator.
**
** If a memory allocation error occurs, that fact is recorded in pParse->db
** and the pExpr parameter is returned unchanged.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
const Parse *pParse, /* Parsing context */
Expr *pExpr, /* Add the "COLLATE" clause to this expression */
const Token *pCollName, /* Name of collating sequence */
int dequote /* True to dequote pCollName */
){
if( pCollName->n>0 ){
Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
if( pNew ){
pNew->pLeft = pExpr;
pNew->flags |= EP_Collate|EP_Skip;
pExpr = pNew;
}
}
return pExpr;
}
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(
const Parse *pParse, /* Parsing context */
Expr *pExpr, /* Add the "COLLATE" clause to this expression */
const char *zC /* The collating sequence name */
){
Token s;
assert( zC!=0 );
sqlite3TokenInit(&s, (char*)zC);
return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
}
/*
|
| ︙ | ︙ | |||
101530 101531 101532 101533 101534 101535 101536 | ** ** A vector is defined as any expression that results in two or more ** columns of result. Every TK_VECTOR node is an vector because the ** parser will not generate a TK_VECTOR with fewer than two entries. ** But a TK_SELECT might be either a vector or a scalar. It is only ** considered a vector if it has two or more result columns. */ | | | | 101727 101728 101729 101730 101731 101732 101733 101734 101735 101736 101737 101738 101739 101740 101741 101742 101743 101744 101745 101746 101747 101748 101749 101750 101751 |
**
** A vector is defined as any expression that results in two or more
** columns of result. Every TK_VECTOR node is an vector because the
** parser will not generate a TK_VECTOR with fewer than two entries.
** But a TK_SELECT might be either a vector or a scalar. It is only
** considered a vector if it has two or more result columns.
*/
SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr){
return sqlite3ExprVectorSize(pExpr)>1;
}
/*
** If the expression passed as the only argument is of type TK_VECTOR
** return the number of expressions in the vector. Or, if the expression
** is a sub-select, return the number of columns in the sub-select. For
** any other type of expression, return 1.
*/
SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr){
u8 op = pExpr->op;
if( op==TK_REGISTER ) op = pExpr->op2;
if( op==TK_VECTOR ){
return pExpr->x.pList->nExpr;
}else if( op==TK_SELECT ){
return pExpr->x.pSelect->pEList->nExpr;
}else{
|
| ︙ | ︙ | |||
101633 101634 101635 101636 101637 101638 101639 |
pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0);
if( pRet ){
pRet->iTable = nField;
pRet->iColumn = iField;
pRet->pLeft = pVector;
}
}else{
| | > > > > > > > > < | 101830 101831 101832 101833 101834 101835 101836 101837 101838 101839 101840 101841 101842 101843 101844 101845 101846 101847 101848 101849 101850 101851 101852 101853 |
pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0);
if( pRet ){
pRet->iTable = nField;
pRet->iColumn = iField;
pRet->pLeft = pVector;
}
}else{
if( pVector->op==TK_VECTOR ){
Expr **ppVector = &pVector->x.pList->a[iField].pExpr;
pVector = *ppVector;
if( IN_RENAME_OBJECT ){
/* This must be a vector UPDATE inside a trigger */
*ppVector = 0;
return pVector;
}
}
pRet = sqlite3ExprDup(pParse->db, pVector, 0);
}
return pRet;
}
/*
** If expression pExpr is of type TK_SELECT, generate code to evaluate
** it. Return the register in which the result is stored (or, if the
|
| ︙ | ︙ | |||
101828 101829 101830 101831 101832 101833 101834 | ** of any expression tree referenced by the structure passed as the ** first argument. ** ** If this maximum height is greater than the current value pointed ** to by pnHeight, the second parameter, then set *pnHeight to that ** value. */ | | | | | | 102032 102033 102034 102035 102036 102037 102038 102039 102040 102041 102042 102043 102044 102045 102046 102047 102048 102049 102050 102051 102052 102053 102054 102055 102056 102057 102058 102059 102060 102061 102062 |
** of any expression tree referenced by the structure passed as the
** first argument.
**
** If this maximum height is greater than the current value pointed
** to by pnHeight, the second parameter, then set *pnHeight to that
** value.
*/
static void heightOfExpr(const Expr *p, int *pnHeight){
if( p ){
if( p->nHeight>*pnHeight ){
*pnHeight = p->nHeight;
}
}
}
static void heightOfExprList(const ExprList *p, int *pnHeight){
if( p ){
int i;
for(i=0; i<p->nExpr; i++){
heightOfExpr(p->a[i].pExpr, pnHeight);
}
}
}
static void heightOfSelect(const Select *pSelect, int *pnHeight){
const Select *p;
for(p=pSelect; p; p=p->pPrior){
heightOfExpr(p->pWhere, pnHeight);
heightOfExpr(p->pHaving, pnHeight);
heightOfExpr(p->pLimit, pnHeight);
heightOfExprList(p->pEList, pnHeight);
heightOfExprList(p->pGroupBy, pnHeight);
heightOfExprList(p->pOrderBy, pnHeight);
|
| ︙ | ︙ | |||
101896 101897 101898 101899 101900 101901 101902 | sqlite3ExprCheckHeight(pParse, p->nHeight); } /* ** Return the maximum height of any expression tree referenced ** by the select statement passed as an argument. */ | | | 102100 102101 102102 102103 102104 102105 102106 102107 102108 102109 102110 102111 102112 102113 102114 |
sqlite3ExprCheckHeight(pParse, p->nHeight);
}
/*
** Return the maximum height of any expression tree referenced
** by the select statement passed as an argument.
*/
SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *p){
int nHeight = 0;
heightOfSelect(p, &nHeight);
return nHeight;
}
#else /* ABOVE: Height enforcement enabled. BELOW: Height enforcement off */
/*
** Propagate all EP_Propagate flags from the Expr.x.pList into
|
| ︙ | ︙ | |||
102149 102150 102151 102152 102153 102154 102155 | /* ** Construct a new expression node for a function with multiple ** arguments. */ SQLITE_PRIVATE Expr *sqlite3ExprFunction( Parse *pParse, /* Parsing context */ ExprList *pList, /* Argument list */ | | | 102353 102354 102355 102356 102357 102358 102359 102360 102361 102362 102363 102364 102365 102366 102367 |
/*
** Construct a new expression node for a function with multiple
** arguments.
*/
SQLITE_PRIVATE Expr *sqlite3ExprFunction(
Parse *pParse, /* Parsing context */
ExprList *pList, /* Argument list */
const Token *pToken, /* Name of the function */
int eDistinct /* SF_Distinct or SF_ALL or 0 */
){
Expr *pNew;
sqlite3 *db = pParse->db;
assert( pToken );
pNew = sqlite3ExprAlloc(db, TK_FUNCTION, pToken, 1);
if( pNew==0 ){
|
| ︙ | ︙ | |||
102187 102188 102189 102190 102191 102192 102193 | ** SQLITE_FUNC_UNSAFE - Usable if TRUSTED_SCHEMA or from ** top-level SQL ** ** If the function is not usable, create an error. */ SQLITE_PRIVATE void sqlite3ExprFunctionUsable( Parse *pParse, /* Parsing and code generating context */ | | | | 102391 102392 102393 102394 102395 102396 102397 102398 102399 102400 102401 102402 102403 102404 102405 102406 |
** SQLITE_FUNC_UNSAFE - Usable if TRUSTED_SCHEMA or from
** top-level SQL
**
** If the function is not usable, create an error.
*/
SQLITE_PRIVATE void sqlite3ExprFunctionUsable(
Parse *pParse, /* Parsing and code generating context */
const Expr *pExpr, /* The function invocation */
const FuncDef *pDef /* The function being invoked */
){
assert( !IN_RENAME_OBJECT );
assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 );
if( ExprHasProperty(pExpr, EP_FromDDL) ){
if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0
|| (pParse->db->flags & SQLITE_TrustedSchema)==0
){
|
| ︙ | ︙ | |||
102368 102369 102370 102371 102372 102373 102374 | } /* ** Return the number of bytes allocated for the expression structure ** passed as the first argument. This is always one of EXPR_FULLSIZE, ** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. */ | | | 102572 102573 102574 102575 102576 102577 102578 102579 102580 102581 102582 102583 102584 102585 102586 |
}
/*
** Return the number of bytes allocated for the expression structure
** passed as the first argument. This is always one of EXPR_FULLSIZE,
** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE.
*/
static int exprStructSize(const Expr *p){
if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE;
if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE;
return EXPR_FULLSIZE;
}
/*
** The dupedExpr*Size() routines each return the number of bytes required
|
| ︙ | ︙ | |||
102408 102409 102410 102411 102412 102413 102414 | ** later parts of the Expr object and that extra information might get chopped ** off if the expression is reduced. Note also that it does not work to ** make an EXPRDUP_REDUCE copy of a reduced expression. It is only legal ** to reduce a pristine expression tree from the parser. The implementation ** of dupedExprStructSize() contain multiple assert() statements that attempt ** to enforce this constraint. */ | | | 102612 102613 102614 102615 102616 102617 102618 102619 102620 102621 102622 102623 102624 102625 102626 |
** later parts of the Expr object and that extra information might get chopped
** off if the expression is reduced. Note also that it does not work to
** make an EXPRDUP_REDUCE copy of a reduced expression. It is only legal
** to reduce a pristine expression tree from the parser. The implementation
** of dupedExprStructSize() contain multiple assert() statements that attempt
** to enforce this constraint.
*/
static int dupedExprStructSize(const Expr *p, int flags){
int nSize;
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
assert( EXPR_FULLSIZE<=0xfff );
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
if( 0==flags || p->op==TK_SELECT_COLUMN
#ifndef SQLITE_OMIT_WINDOWFUNC
|| ExprHasProperty(p, EP_WinFunc)
|
| ︙ | ︙ | |||
102439 102440 102441 102442 102443 102444 102445 | } /* ** This function returns the space in bytes required to store the copy ** of the Expr structure and a copy of the Expr.u.zToken string (if that ** string is defined.) */ | | | | | 102643 102644 102645 102646 102647 102648 102649 102650 102651 102652 102653 102654 102655 102656 102657 102658 102659 102660 102661 102662 102663 102664 102665 102666 102667 102668 102669 102670 102671 102672 102673 102674 102675 102676 102677 102678 102679 102680 102681 102682 102683 102684 102685 102686 102687 102688 102689 102690 102691 102692 102693 102694 102695 102696 102697 |
}
/*
** This function returns the space in bytes required to store the copy
** of the Expr structure and a copy of the Expr.u.zToken string (if that
** string is defined.)
*/
static int dupedExprNodeSize(const Expr *p, int flags){
int nByte = dupedExprStructSize(p, flags) & 0xfff;
if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
nByte += sqlite3Strlen30NN(p->u.zToken)+1;
}
return ROUND8(nByte);
}
/*
** Return the number of bytes required to create a duplicate of the
** expression passed as the first argument. The second argument is a
** mask containing EXPRDUP_XXX flags.
**
** The value returned includes space to create a copy of the Expr struct
** itself and the buffer referred to by Expr.u.zToken, if any.
**
** If the EXPRDUP_REDUCE flag is set, then the return value includes
** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
** and Expr.pRight variables (but not for any structures pointed to or
** descended from the Expr.x.pList or Expr.x.pSelect variables).
*/
static int dupedExprSize(const Expr *p, int flags){
int nByte = 0;
if( p ){
nByte = dupedExprNodeSize(p, flags);
if( flags&EXPRDUP_REDUCE ){
nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags);
}
}
return nByte;
}
/*
** This function is similar to sqlite3ExprDup(), except that if pzBuffer
** is not NULL then *pzBuffer is assumed to point to a buffer large enough
** to store the copy of expression p, the copies of p->u.zToken
** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
** if any. Before returning, *pzBuffer is set to the first byte past the
** portion of the buffer copied into by this function.
*/
static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
Expr *pNew; /* Value to return */
u8 *zAlloc; /* Memory space from which to build Expr object */
u32 staticFlag; /* EP_Static if space not obtained from malloc */
assert( db!=0 );
assert( p );
assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE );
|
| ︙ | ︙ | |||
102660 102661 102662 102663 102664 102665 102666 | ** Any tables that the SrcList might point to are not duplicated. ** ** The flags parameter contains a combination of the EXPRDUP_XXX flags. ** If the EXPRDUP_REDUCE flag is set, then the structure returned is a ** truncated version of the usual Expr structure that will be stored as ** part of the in-memory representation of the database schema. */ | | | | > | 102864 102865 102866 102867 102868 102869 102870 102871 102872 102873 102874 102875 102876 102877 102878 102879 102880 102881 102882 102883 102884 102885 |
** Any tables that the SrcList might point to are not duplicated.
**
** The flags parameter contains a combination of the EXPRDUP_XXX flags.
** If the EXPRDUP_REDUCE flag is set, then the structure returned is a
** truncated version of the usual Expr structure that will be stored as
** part of the in-memory representation of the database schema.
*/
SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, const Expr *p, int flags){
assert( flags==0 || flags==EXPRDUP_REDUCE );
return p ? exprDup(db, p, flags, 0) : 0;
}
SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int flags){
ExprList *pNew;
struct ExprList_item *pItem;
const struct ExprList_item *pOldItem;
int i;
Expr *pPriorSelectColOld = 0;
Expr *pPriorSelectColNew = 0;
assert( db!=0 );
if( p==0 ) return 0;
pNew = sqlite3DbMallocRawNN(db, sqlite3DbMallocSize(db, p));
if( pNew==0 ) return 0;
|
| ︙ | ︙ | |||
102718 102719 102720 102721 102722 102723 102724 | ** If cursors, triggers, views and subqueries are all omitted from ** the build, then none of the following routines, except for ** sqlite3SelectDup(), can be called. sqlite3SelectDup() is sometimes ** called with a NULL argument. */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ || !defined(SQLITE_OMIT_SUBQUERY) | | | | 102923 102924 102925 102926 102927 102928 102929 102930 102931 102932 102933 102934 102935 102936 102937 102938 102939 102940 102941 102942 102943 102944 102945 102946 102947 102948 102949 |
** If cursors, triggers, views and subqueries are all omitted from
** the build, then none of the following routines, except for
** sqlite3SelectDup(), can be called. sqlite3SelectDup() is sometimes
** called with a NULL argument.
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
|| !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
SrcList *pNew;
int i;
int nByte;
assert( db!=0 );
if( p==0 ) return 0;
nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
pNew = sqlite3DbMallocRawNN(db, nByte );
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; i<p->nSrc; i++){
SrcItem *pNewItem = &pNew->a[i];
const SrcItem *pOldItem = &p->a[i];
Table *pTab;
pNewItem->pSchema = pOldItem->pSchema;
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
pNewItem->fg = pOldItem->fg;
pNewItem->iCursor = pOldItem->iCursor;
|
| ︙ | ︙ | |||
102762 102763 102764 102765 102766 102767 102768 |
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
pNewItem->colUsed = pOldItem->colUsed;
}
return pNew;
}
| | | 102967 102968 102969 102970 102971 102972 102973 102974 102975 102976 102977 102978 102979 102980 102981 |
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
pNewItem->colUsed = pOldItem->colUsed;
}
return pNew;
}
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
IdList *pNew;
int i;
assert( db!=0 );
if( p==0 ) return 0;
pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
pNew->nId = p->nId;
|
| ︙ | ︙ | |||
102786 102787 102788 102789 102790 102791 102792 |
struct IdList_item *pNewItem = &pNew->a[i];
struct IdList_item *pOldItem = &p->a[i];
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->idx = pOldItem->idx;
}
return pNew;
}
| | | | 102991 102992 102993 102994 102995 102996 102997 102998 102999 103000 103001 103002 103003 103004 103005 103006 103007 103008 103009 |
struct IdList_item *pNewItem = &pNew->a[i];
struct IdList_item *pOldItem = &p->a[i];
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->idx = pOldItem->idx;
}
return pNew;
}
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int flags){
Select *pRet = 0;
Select *pNext = 0;
Select **pp = &pRet;
const Select *p;
assert( db!=0 );
for(p=pDup; p; p=p->pPrior){
Select *pNew = sqlite3DbMallocRawNN(db, sizeof(*p) );
if( pNew==0 ) break;
pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags);
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
|
| ︙ | ︙ | |||
103031 103032 103033 103034 103035 103036 103037 | ** pList might be NULL following an OOM error. But pName should never be ** NULL. If a memory allocation fails, the pParse->db->mallocFailed flag ** is set. */ SQLITE_PRIVATE void sqlite3ExprListSetName( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to add the span. */ | | | | 103236 103237 103238 103239 103240 103241 103242 103243 103244 103245 103246 103247 103248 103249 103250 103251 103252 103253 103254 103255 103256 103257 103258 103259 103260 103261 103262 103263 103264 103265 103266 103267 103268 |
** pList might be NULL following an OOM error. But pName should never be
** NULL. If a memory allocation fails, the pParse->db->mallocFailed flag
** is set.
*/
SQLITE_PRIVATE void sqlite3ExprListSetName(
Parse *pParse, /* Parsing context */
ExprList *pList, /* List to which to add the span. */
const Token *pName, /* Name to be added */
int dequote /* True to cause the name to be dequoted */
){
assert( pList!=0 || pParse->db->mallocFailed!=0 );
assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 );
if( pList ){
struct ExprList_item *pItem;
assert( pList->nExpr>0 );
pItem = &pList->a[pList->nExpr-1];
assert( pItem->zEName==0 );
assert( pItem->eEName==ENAME_NAME );
pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
if( dequote ){
/* If dequote==0, then pName->z does not point to part of a DDL
** statement handled by the parser. And so no token need be added
** to the token-map. */
sqlite3Dequote(pItem->zEName);
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName);
}
}
}
}
/*
** Set the ExprList.a[].zSpan element of the most recently added item
|
| ︙ | ︙ | |||
103477 103478 103479 103480 103481 103482 103483 | /* ** If the expression p codes a constant integer that is small enough ** to fit in a 32-bit integer, return 1 and put the value of the integer ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ | | | | | 103682 103683 103684 103685 103686 103687 103688 103689 103690 103691 103692 103693 103694 103695 103696 103697 103698 103699 103700 103701 103702 103703 103704 103705 103706 103707 103708 103709 103710 103711 103712 103713 103714 103715 103716 103717 |
/*
** If the expression p codes a constant integer that is small enough
** to fit in a 32-bit integer, return 1 and put the value of the integer
** in *pValue. If the expression is not an integer or if it is too big
** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged.
*/
SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue){
int rc = 0;
if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */
/* If an expression is an integer literal that fits in a signed 32-bit
** integer, then the EP_IntValue flag will have already been set */
assert( p->op!=TK_INTEGER || (p->flags & EP_IntValue)!=0
|| sqlite3GetInt32(p->u.zToken, &rc)==0 );
if( p->flags & EP_IntValue ){
*pValue = p->u.iValue;
return 1;
}
switch( p->op ){
case TK_UPLUS: {
rc = sqlite3ExprIsInteger(p->pLeft, pValue);
break;
}
case TK_UMINUS: {
int v = 0;
if( sqlite3ExprIsInteger(p->pLeft, &v) ){
assert( ((unsigned int)v)!=0x80000000 );
*pValue = -v;
rc = 1;
}
break;
}
default: break;
}
|
| ︙ | ︙ | |||
103610 103611 103612 103613 103614 103615 103616 | ** pX is the RHS of an IN operator. If pX is a SELECT statement ** that can be simplified to a direct table access, then return ** a pointer to the SELECT statement. If pX is not a SELECT statement, ** or if the SELECT statement needs to be manifested into a transient ** table, then return NULL. */ #ifndef SQLITE_OMIT_SUBQUERY | | | 103815 103816 103817 103818 103819 103820 103821 103822 103823 103824 103825 103826 103827 103828 103829 |
** pX is the RHS of an IN operator. If pX is a SELECT statement
** that can be simplified to a direct table access, then return
** a pointer to the SELECT statement. If pX is not a SELECT statement,
** or if the SELECT statement needs to be manifested into a transient
** table, then return NULL.
*/
#ifndef SQLITE_OMIT_SUBQUERY
static Select *isCandidateForInOpt(const Expr *pX){
Select *p;
SrcList *pSrc;
ExprList *pEList;
Table *pTab;
int i;
if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */
if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */
|
| ︙ | ︙ | |||
103988 103989 103990 103991 103992 103993 103994 | ** Argument pExpr is an (?, ?...) IN(...) expression. This ** function allocates and returns a nul-terminated string containing ** the affinities to be used for each column of the comparison. ** ** It is the responsibility of the caller to ensure that the returned ** string is eventually freed using sqlite3DbFree(). */ | | | 104193 104194 104195 104196 104197 104198 104199 104200 104201 104202 104203 104204 104205 104206 104207 |
** Argument pExpr is an (?, ?...) IN(...) expression. This
** function allocates and returns a nul-terminated string containing
** the affinities to be used for each column of the comparison.
**
** It is the responsibility of the caller to ensure that the returned
** string is eventually freed using sqlite3DbFree().
*/
static char *exprINAffinity(Parse *pParse, const Expr *pExpr){
Expr *pLeft = pExpr->pLeft;
int nVal = sqlite3ExprVectorSize(pLeft);
Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0;
char *zRet;
assert( pExpr->op==TK_IN );
zRet = sqlite3DbMallocRaw(pParse->db, nVal+1);
|
| ︙ | ︙ | |||
104987 104988 104989 104990 104991 104992 104993 104994 104995 104996 104997 104998 104999 105000 |
break;
}
/***********************************************************************
** Test-only SQL functions that are only usable if enabled
** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS
*/
case INLINEFUNC_expr_compare: {
/* Compare two expressions using sqlite3ExprCompare() */
assert( nFarg==2 );
sqlite3VdbeAddOp2(v, OP_Integer,
sqlite3ExprCompare(0,pFarg->a[0].pExpr, pFarg->a[1].pExpr,-1),
target);
break;
| > | 105192 105193 105194 105195 105196 105197 105198 105199 105200 105201 105202 105203 105204 105205 105206 |
break;
}
/***********************************************************************
** Test-only SQL functions that are only usable if enabled
** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS
*/
#if !defined(SQLITE_UNTESTABLE)
case INLINEFUNC_expr_compare: {
/* Compare two expressions using sqlite3ExprCompare() */
assert( nFarg==2 );
sqlite3VdbeAddOp2(v, OP_Integer,
sqlite3ExprCompare(0,pFarg->a[0].pExpr, pFarg->a[1].pExpr,-1),
target);
break;
|
| ︙ | ︙ | |||
105020 105021 105022 105023 105024 105025 105026 |
target);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
break;
}
| < | | 105226 105227 105228 105229 105230 105231 105232 105233 105234 105235 105236 105237 105238 105239 105240 105241 105242 105243 105244 105245 105246 105247 105248 105249 105250 105251 105252 105253 |
target);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
break;
}
case INLINEFUNC_affinity: {
/* The AFFINITY() function evaluates to a string that describes
** the type affinity of the argument. This is used for testing of
** the SQLite type logic.
*/
const char *azAff[] = { "blob", "text", "numeric", "integer", "real" };
char aff;
assert( nFarg==1 );
aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
sqlite3VdbeLoadString(v, target,
(aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
break;
}
#endif /* !defined(SQLITE_UNTESTABLE) */
}
return target;
}
/*
** Generate code into the current Vdbe to evaluate the given
|
| ︙ | ︙ | |||
105508 105509 105510 105511 105512 105513 105514 |
}
#endif
if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
| | | 105713 105714 105715 105716 105717 105718 105719 105720 105721 105722 105723 105724 105725 105726 105727 |
}
#endif
if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
if( (pDef->funcFlags & SQLITE_FUNC_OFFSET)!=0 && ALWAYS(pFarg!=0) ){
Expr *pArg = pFarg->a[0].pExpr;
if( pArg->op==TK_COLUMN ){
sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
}else
|
| ︙ | ︙ | |||
105907 105908 105909 105910 105911 105912 105913 |
assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
assert( target>0 && target<=pParse->nMem );
assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
if( pParse->pVdbe==0 ) return;
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
if( inReg!=target ){
u8 op;
| | | 106112 106113 106114 106115 106116 106117 106118 106119 106120 106121 106122 106123 106124 106125 106126 |
assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
assert( target>0 && target<=pParse->nMem );
assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
if( pParse->pVdbe==0 ) return;
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
if( inReg!=target ){
u8 op;
if( ALWAYS(pExpr) && ExprHasProperty(pExpr,EP_Subquery) ){
op = OP_Copy;
}else{
op = OP_SCopy;
}
sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);
}
}
|
| ︙ | ︙ | |||
106445 106446 106447 106448 106449 106450 106451 | ** to re-prepare each time a new value is bound to variable pVar. ** ** Additionally, if pExpr is a simple SQL value and the value is the ** same as that currently bound to variable pVar, non-zero is returned. ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ | | > > > > | 106650 106651 106652 106653 106654 106655 106656 106657 106658 106659 106660 106661 106662 106663 106664 106665 106666 106667 106668 |
** to re-prepare each time a new value is bound to variable pVar.
**
** Additionally, if pExpr is a simple SQL value and the value is the
** same as that currently bound to variable pVar, non-zero is returned.
** Otherwise, if the values are not the same or if pExpr is not a simple
** SQL value, zero is returned.
*/
static int exprCompareVariable(
const Parse *pParse,
const Expr *pVar,
const Expr *pExpr
){
int res = 0;
int iVar;
sqlite3_value *pL, *pR = 0;
sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
if( pR ){
iVar = pVar->iColumn;
|
| ︙ | ︙ | |||
106497 106498 106499 106500 106501 106502 106503 | ** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in ** pParse->pReprepare can be matched against literals in pB. The ** pParse->pVdbe->expmask bitmask is updated for each variable referenced. ** If pParse is NULL (the normal case) then any TK_VARIABLE term in ** Argument pParse should normally be NULL. If it is not NULL and pA or ** pB causes a return value of 2. */ | | > > > > > | 106706 106707 106708 106709 106710 106711 106712 106713 106714 106715 106716 106717 106718 106719 106720 106721 106722 106723 106724 106725 |
** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in
** pParse->pReprepare can be matched against literals in pB. The
** pParse->pVdbe->expmask bitmask is updated for each variable referenced.
** If pParse is NULL (the normal case) then any TK_VARIABLE term in
** Argument pParse should normally be NULL. If it is not NULL and pA or
** pB causes a return value of 2.
*/
SQLITE_PRIVATE int sqlite3ExprCompare(
const Parse *pParse,
const Expr *pA,
const Expr *pB,
int iTab
){
u32 combinedFlags;
if( pA==0 || pB==0 ){
return pB==pA ? 0 : 2;
}
if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){
return 0;
}
|
| ︙ | ︙ | |||
106581 106582 106583 106584 106585 106586 106587 | ** only consequence will be disabled optimizations. But this routine ** must never return 0 if the two ExprList objects are different, or ** a malfunction will result. ** ** Two NULL pointers are considered to be the same. But a NULL pointer ** always differs from a non-NULL pointer. */ | | | | | | | 106795 106796 106797 106798 106799 106800 106801 106802 106803 106804 106805 106806 106807 106808 106809 106810 106811 106812 106813 106814 106815 106816 106817 106818 106819 106820 106821 106822 106823 106824 106825 106826 106827 106828 106829 106830 106831 106832 106833 106834 106835 106836 106837 106838 106839 106840 106841 106842 106843 106844 |
** only consequence will be disabled optimizations. But this routine
** must never return 0 if the two ExprList objects are different, or
** a malfunction will result.
**
** Two NULL pointers are considered to be the same. But a NULL pointer
** always differs from a non-NULL pointer.
*/
SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){
int i;
if( pA==0 && pB==0 ) return 0;
if( pA==0 || pB==0 ) return 1;
if( pA->nExpr!=pB->nExpr ) return 1;
for(i=0; i<pA->nExpr; i++){
int res;
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1;
if( (res = sqlite3ExprCompare(0, pExprA, pExprB, iTab)) ) return res;
}
return 0;
}
/*
** Like sqlite3ExprCompare() except COLLATE operators at the top-level
** are ignored.
*/
SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){
return sqlite3ExprCompare(0,
sqlite3ExprSkipCollateAndLikely(pA),
sqlite3ExprSkipCollateAndLikely(pB),
iTab);
}
/*
** Return non-zero if Expr p can only be true if pNN is not NULL.
**
** Or if seenNot is true, return non-zero if Expr p can only be
** non-NULL if pNN is not NULL
*/
static int exprImpliesNotNull(
const Parse *pParse,/* Parsing context */
const Expr *p, /* The expression to be checked */
const Expr *pNN, /* The expression that is NOT NULL */
int iTab, /* Table being evaluated */
int seenNot /* Return true only if p can be any non-NULL value */
){
assert( p );
assert( pNN );
if( sqlite3ExprCompare(pParse, p, pNN, iTab)==0 ){
return pNN->op!=TK_NULL;
|
| ︙ | ︙ | |||
106709 106710 106711 106712 106713 106714 106715 | ** modified to record which bound variables are referenced. If pParse ** is NULL, then false will be returned if pE1 contains any bound variables. ** ** When in doubt, return false. Returning true might give a performance ** improvement. Returning false might cause a performance reduction, but ** it will always give the correct answer and is hence always safe. */ | | > > > > > | 106923 106924 106925 106926 106927 106928 106929 106930 106931 106932 106933 106934 106935 106936 106937 106938 106939 106940 106941 106942 |
** modified to record which bound variables are referenced. If pParse
** is NULL, then false will be returned if pE1 contains any bound variables.
**
** When in doubt, return false. Returning true might give a performance
** improvement. Returning false might cause a performance reduction, but
** it will always give the correct answer and is hence always safe.
*/
SQLITE_PRIVATE int sqlite3ExprImpliesExpr(
const Parse *pParse,
const Expr *pE1,
const Expr *pE2,
int iTab
){
if( sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){
return 1;
}
if( pE2->op==TK_OR
&& (sqlite3ExprImpliesExpr(pParse, pE1, pE2->pLeft, iTab)
|| sqlite3ExprImpliesExpr(pParse, pE1, pE2->pRight, iTab) )
){
|
| ︙ | ︙ | |||
107998 107999 108000 108001 108002 108003 108004 |
if( !zNew ) goto exit_rename_column;
assert( pNew->n>0 );
bQuote = sqlite3Isquote(pNew->z[0]);
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
| | < | 108217 108218 108219 108220 108221 108222 108223 108224 108225 108226 108227 108228 108229 108230 108231 |
if( !zNew ) goto exit_rename_column;
assert( pNew->n>0 );
bQuote = sqlite3Isquote(pNew->z[0]);
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
" AND (type != 'index' OR tbl_name = %Q)",
zDb,
zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
pTab->zName
);
sqlite3NestedParse(pParse,
"UPDATE temp." DFLT_SCHEMA_TABLE " SET "
|
| ︙ | ︙ | |||
108041 108042 108043 108044 108045 108046 108047 |
** routine is used to keep the mapping current.
**
** After the parse finishes, renameTokenFind() routine can be used
** to look up the actual token value that created some element in
** the parse tree.
*/
struct RenameToken {
| | | 108259 108260 108261 108262 108263 108264 108265 108266 108267 108268 108269 108270 108271 108272 108273 |
** routine is used to keep the mapping current.
**
** After the parse finishes, renameTokenFind() routine can be used
** to look up the actual token value that created some element in
** the parse tree.
*/
struct RenameToken {
const void *p; /* Parse tree element created by token t */
Token t; /* The token that created parse tree element p */
RenameToken *pNext; /* Next is a list of all RenameToken objects */
};
/*
** The context of an ALTER TABLE RENAME COLUMN operation that gets passed
** down into the Walker.
|
| ︙ | ︙ | |||
108083 108084 108085 108086 108087 108088 108089 | ** ** sqlite3_free(x); ** if( x==y ) ... ** ** Technically, as x no longer points into a valid object or to the byte ** following a valid object, it may not be used in comparison operations. */ | | | | 108301 108302 108303 108304 108305 108306 108307 108308 108309 108310 108311 108312 108313 108314 108315 108316 108317 |
**
** sqlite3_free(x);
** if( x==y ) ...
**
** Technically, as x no longer points into a valid object or to the byte
** following a valid object, it may not be used in comparison operations.
*/
static void renameTokenCheckAll(Parse *pParse, const void *pPtr){
if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){
const RenameToken *p;
u8 i = 0;
for(p=pParse->pRename; p; p=p->pNext){
if( p->p ){
assert( p->p!=pPtr );
i += *(u8*)(p->p);
}
}
|
| ︙ | ︙ | |||
108111 108112 108113 108114 108115 108116 108117 | ** to the list of RenameToken objects currently being built up ** in pParse->pRename. ** ** The pPtr argument is returned so that this routine can be used ** with tail recursion in tokenExpr() routine, for a small performance ** improvement. */ | | > > > > | 108329 108330 108331 108332 108333 108334 108335 108336 108337 108338 108339 108340 108341 108342 108343 108344 108345 108346 108347 |
** to the list of RenameToken objects currently being built up
** in pParse->pRename.
**
** The pPtr argument is returned so that this routine can be used
** with tail recursion in tokenExpr() routine, for a small performance
** improvement.
*/
SQLITE_PRIVATE const void *sqlite3RenameTokenMap(
Parse *pParse,
const void *pPtr,
const Token *pToken
){
RenameToken *pNew;
assert( pPtr || pParse->db->mallocFailed );
renameTokenCheckAll(pParse, pPtr);
if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
if( pNew ){
pNew->p = pPtr;
|
| ︙ | ︙ | |||
108133 108134 108135 108136 108137 108138 108139 | } /* ** It is assumed that there is already a RenameToken object associated ** with parse tree element pFrom. This function remaps the associated token ** to parse tree element pTo. */ | | | > | 108355 108356 108357 108358 108359 108360 108361 108362 108363 108364 108365 108366 108367 108368 108369 108370 108371 108372 108373 108374 108375 108376 108377 108378 108379 108380 108381 108382 108383 108384 108385 108386 |
}
/*
** It is assumed that there is already a RenameToken object associated
** with parse tree element pFrom. This function remaps the associated token
** to parse tree element pTo.
*/
SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){
RenameToken *p;
renameTokenCheckAll(pParse, pTo);
for(p=pParse->pRename; p; p=p->pNext){
if( p->p==pFrom ){
p->p = pTo;
break;
}
}
}
/*
** Walker callback used by sqlite3RenameExprUnmap().
*/
static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){
Parse *pParse = pWalker->pParse;
sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr);
sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab);
return WRC_Continue;
}
/*
** Iterate through the Select objects that are part of WITH clauses attached
** to select statement pSelect.
*/
|
| ︙ | ︙ | |||
108179 108180 108181 108182 108183 108184 108185 108186 108187 108188 108189 108190 108191 108192 108193 108194 108195 108196 108197 108198 108199 |
}
for(i=0; i<pWith->nCte; i++){
Select *p = pWith->a[i].pSelect;
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC);
sqlite3WalkSelect(pWalker, p);
sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols);
}
if( pCopy && pParse->pWith==pCopy ){
pParse->pWith = pCopy->pOuter;
}
}
}
/*
** Unmap all tokens in the IdList object passed as the second argument.
*/
static void unmapColumnIdlistNames(
Parse *pParse,
| > | | | < < | | 108402 108403 108404 108405 108406 108407 108408 108409 108410 108411 108412 108413 108414 108415 108416 108417 108418 108419 108420 108421 108422 108423 108424 108425 108426 108427 108428 108429 108430 108431 108432 108433 108434 108435 108436 108437 108438 108439 108440 108441 108442 108443 108444 108445 108446 108447 108448 108449 108450 108451 108452 108453 108454 108455 108456 108457 108458 108459 108460 108461 108462 108463 |
}
for(i=0; i<pWith->nCte; i++){
Select *p = pWith->a[i].pSelect;
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC);
if( sNC.pParse->db->mallocFailed ) return;
sqlite3WalkSelect(pWalker, p);
sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols);
}
if( pCopy && pParse->pWith==pCopy ){
pParse->pWith = pCopy->pOuter;
}
}
}
/*
** Unmap all tokens in the IdList object passed as the second argument.
*/
static void unmapColumnIdlistNames(
Parse *pParse,
const IdList *pIdList
){
if( pIdList ){
int ii;
for(ii=0; ii<pIdList->nId; ii++){
sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName);
}
}
}
/*
** Walker callback used by sqlite3RenameExprUnmap().
*/
static int renameUnmapSelectCb(Walker *pWalker, Select *p){
Parse *pParse = pWalker->pParse;
int i;
if( pParse->nErr ) return WRC_Abort;
if( NEVER(p->selFlags & (SF_View|SF_CopyCte)) ){
return WRC_Prune;
}
if( ALWAYS(p->pEList) ){
ExprList *pList = p->pEList;
for(i=0; i<pList->nExpr; i++){
if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){
sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName);
}
}
}
if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */
SrcList *pSrc = p->pSrc;
for(i=0; i<pSrc->nSrc; i++){
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
sqlite3WalkExpr(pWalker, pSrc->a[i].pOn);
unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
}
}
renameWalkWith(pWalker, p);
return WRC_Continue;
}
|
| ︙ | ︙ | |||
108295 108296 108297 108298 108299 108300 108301 | ** If the second argument passed to this function is not NULL and a matching ** RenameToken object is found, remove it from the Parse object and add it to ** the list maintained by the RenameCtx object. */ static RenameToken *renameTokenFind( Parse *pParse, struct RenameCtx *pCtx, | | | 108517 108518 108519 108520 108521 108522 108523 108524 108525 108526 108527 108528 108529 108530 108531 |
** If the second argument passed to this function is not NULL and a matching
** RenameToken object is found, remove it from the Parse object and add it to
** the list maintained by the RenameCtx object.
*/
static RenameToken *renameTokenFind(
Parse *pParse,
struct RenameCtx *pCtx,
const void *pPtr
){
RenameToken **pp;
if( NEVER(pPtr==0) ){
return 0;
}
for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
if( (*pp)->p==pPtr ){
|
| ︙ | ︙ | |||
108414 108415 108416 108417 108418 108419 108420 | ** pEList->a[i].zName) that matches the string in zOld, extract the ** corresponding rename-token from Parse object pParse and add it ** to the RenameCtx pCtx. */ static void renameColumnElistNames( Parse *pParse, RenameCtx *pCtx, | | | | | | | | 108636 108637 108638 108639 108640 108641 108642 108643 108644 108645 108646 108647 108648 108649 108650 108651 108652 108653 108654 108655 108656 108657 108658 108659 108660 108661 108662 108663 108664 108665 108666 108667 108668 108669 108670 108671 108672 108673 108674 108675 108676 108677 108678 108679 108680 108681 108682 108683 |
** pEList->a[i].zName) that matches the string in zOld, extract the
** corresponding rename-token from Parse object pParse and add it
** to the RenameCtx pCtx.
*/
static void renameColumnElistNames(
Parse *pParse,
RenameCtx *pCtx,
const ExprList *pEList,
const char *zOld
){
if( pEList ){
int i;
for(i=0; i<pEList->nExpr; i++){
const char *zName = pEList->a[i].zEName;
if( ALWAYS(pEList->a[i].eEName==ENAME_NAME)
&& ALWAYS(zName!=0)
&& 0==sqlite3_stricmp(zName, zOld)
){
renameTokenFind(pParse, pCtx, (const void*)zName);
}
}
}
}
/*
** For each name in the the id-list pIdList (i.e. each pIdList->a[i].zName)
** that matches the string in zOld, extract the corresponding rename-token
** from Parse object pParse and add it to the RenameCtx pCtx.
*/
static void renameColumnIdlistNames(
Parse *pParse,
RenameCtx *pCtx,
const IdList *pIdList,
const char *zOld
){
if( pIdList ){
int i;
for(i=0; i<pIdList->nId; i++){
const char *zName = pIdList->a[i].zName;
if( 0==sqlite3_stricmp(zName, zOld) ){
renameTokenFind(pParse, pCtx, (const void*)zName);
}
}
}
}
/*
|
| ︙ | ︙ | |||
108849 108850 108851 108852 108853 108854 108855 |
sParse.rc = SQLITE_OK;
sqlite3SelectPrep(&sParse, pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
if( rc==SQLITE_OK ){
sqlite3WalkSelect(&sWalker, pSelect);
}
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
| | | 109071 109072 109073 109074 109075 109076 109077 109078 109079 109080 109081 109082 109083 109084 109085 |
sParse.rc = SQLITE_OK;
sqlite3SelectPrep(&sParse, pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
if( rc==SQLITE_OK ){
sqlite3WalkSelect(&sWalker, pSelect);
}
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
}else if( IsOrdinaryTable(sParse.pNewTable) ){
/* A regular table */
int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
FKey *pFKey;
sCtx.pTab = sParse.pNewTable;
if( bFKOnly==0 ){
if( iCol<sParse.pNewTable->nCol ){
renameTokenFind(
|
| ︙ | ︙ | |||
109147 109148 109149 109150 109151 109152 109153 |
}
return;
}
static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){
| | | 109369 109370 109371 109372 109373 109374 109375 109376 109377 109378 109379 109380 109381 109382 109383 |
}
return;
}
static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){
renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr);
}
return WRC_Continue;
}
/*
** The implementation of an SQL scalar function that rewrites DDL statements
** so that any string literals that use double-quotes are modified so that
|
| ︙ | ︙ | |||
109417 109418 109419 109420 109421 109422 109423 | ** This function is called by the parser upon parsing an ** ** ALTER TABLE pSrc DROP COLUMN pName ** ** statement. Argument pSrc contains the possibly qualified name of the ** table being edited, and token pName the name of the column to drop. */ | | | 109639 109640 109641 109642 109643 109644 109645 109646 109647 109648 109649 109650 109651 109652 109653 |
** This function is called by the parser upon parsing an
**
** ALTER TABLE pSrc DROP COLUMN pName
**
** statement. Argument pSrc contains the possibly qualified name of the
** table being edited, and token pName the name of the column to drop.
*/
SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){
sqlite3 *db = pParse->db; /* Database handle */
Table *pTab; /* Table to modify */
int iDb; /* Index of db containing pTab in aDb[] */
const char *zDb; /* Database containing pTab ("main" etc.) */
char *zCol = 0; /* Name of column to drop */
int iCol; /* Index of column zCol in pTab->aCol[] */
|
| ︙ | ︙ | |||
110002 110003 110004 110005 110006 110007 110008 |
#ifdef SQLITE_ENABLE_STAT4
if( mxSample ){
n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
+ sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
}
#endif
| < | 110224 110225 110226 110227 110228 110229 110230 110231 110232 110233 110234 110235 110236 110237 |
#ifdef SQLITE_ENABLE_STAT4
if( mxSample ){
n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
+ sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
}
#endif
p = sqlite3DbMallocZero(db, n);
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
p->db = db;
|
| ︙ | ︙ | |||
110421 110422 110423 110424 110425 110426 110427 |
** * "WHERE a=? AND b=?" matches 2 rows.
**
** If D is the count of distinct values and K is the total number of
** rows, then each estimate is computed as:
**
** I = (K+D-1)/D
*/
| | | < < < < < | | < | < < | < > > | < < < < < < < | | < | < < > | < < | 110642 110643 110644 110645 110646 110647 110648 110649 110650 110651 110652 110653 110654 110655 110656 110657 110658 110659 110660 110661 110662 110663 110664 110665 110666 110667 110668 110669 110670 110671 110672 110673 110674 110675 110676 110677 110678 110679 110680 110681 110682 110683 110684 110685 110686 110687 110688 110689 110690 110691 110692 110693 110694 110695 110696 110697 110698 110699 110700 110701 110702 110703 110704 110705 |
** * "WHERE a=? AND b=?" matches 2 rows.
**
** If D is the count of distinct values and K is the total number of
** rows, then each estimate is computed as:
**
** I = (K+D-1)/D
*/
sqlite3_str sStat; /* Text of the constructed "stat" line */
int i; /* Loop counter */
sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100);
sqlite3_str_appendf(&sStat, "%llu",
p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
for(i=0; i<p->nKeyCol; i++){
u64 nDistinct = p->current.anDLt[i] + 1;
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
sqlite3_str_appendf(&sStat, " %llu", iVal);
assert( p->current.anEq[i] );
}
sqlite3ResultStrAccum(context, &sStat);
}
#ifdef SQLITE_ENABLE_STAT4
else if( eCall==STAT_GET_ROWID ){
if( p->iGet<0 ){
samplePushPrevious(p, 0);
p->iGet = 0;
}
if( p->iGet<p->nSample ){
StatSample *pS = p->a + p->iGet;
if( pS->nRowid==0 ){
sqlite3_result_int64(context, pS->u.iRowid);
}else{
sqlite3_result_blob(context, pS->u.aRowid, pS->nRowid,
SQLITE_TRANSIENT);
}
}
}else{
tRowcnt *aCnt = 0;
sqlite3_str sStat;
int i;
assert( p->iGet<p->nSample );
switch( eCall ){
case STAT_GET_NEQ: aCnt = p->a[p->iGet].anEq; break;
case STAT_GET_NLT: aCnt = p->a[p->iGet].anLt; break;
default: {
aCnt = p->a[p->iGet].anDLt;
p->iGet++;
break;
}
}
sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100);
for(i=0; i<p->nCol; i++){
sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]);
}
if( sStat.nChar ) sStat.nChar--;
sqlite3ResultStrAccum(context, &sStat);
}
#endif /* SQLITE_ENABLE_STAT4 */
#ifndef SQLITE_DEBUG
UNUSED_PARAMETER( argc );
#endif
}
static const FuncDef statGetFuncdef = {
|
| ︙ | ︙ | |||
111409 111410 111411 111412 111413 111414 111415 111416 111417 |
/*
** Load content from the sqlite_stat4 table into
** the Index.aSample[] arrays of all indices.
*/
static int loadStat4(sqlite3 *db, const char *zDb){
int rc = SQLITE_OK; /* Result codes from subroutines */
assert( db->lookaside.bDisable );
| > | > > | 111612 111613 111614 111615 111616 111617 111618 111619 111620 111621 111622 111623 111624 111625 111626 111627 111628 111629 111630 111631 |
/*
** Load content from the sqlite_stat4 table into
** the Index.aSample[] arrays of all indices.
*/
static int loadStat4(sqlite3 *db, const char *zDb){
int rc = SQLITE_OK; /* Result codes from subroutines */
const Table *pStat4;
assert( db->lookaside.bDisable );
if( (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0
&& IsOrdinaryTable(pStat4)
){
rc = loadStatTbl(db,
"SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
zDb
);
}
return rc;
|
| ︙ | ︙ | |||
111448 111449 111450 111451 111452 111453 111454 111455 111456 111457 111458 111459 111460 111461 |
*/
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
analysisInfo sInfo;
HashElem *i;
char *zSql;
int rc = SQLITE_OK;
Schema *pSchema = db->aDb[iDb].pSchema;
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
/* Clear any prior statistics */
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(i=sqliteHashFirst(&pSchema->tblHash); i; i=sqliteHashNext(i)){
| > | 111654 111655 111656 111657 111658 111659 111660 111661 111662 111663 111664 111665 111666 111667 111668 |
*/
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
analysisInfo sInfo;
HashElem *i;
char *zSql;
int rc = SQLITE_OK;
Schema *pSchema = db->aDb[iDb].pSchema;
const Table *pStat1;
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
/* Clear any prior statistics */
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(i=sqliteHashFirst(&pSchema->tblHash); i; i=sqliteHashNext(i)){
|
| ︙ | ︙ | |||
111470 111471 111472 111473 111474 111475 111476 |
pIdx->aSample = 0;
#endif
}
/* Load new statistics out of the sqlite_stat1 table */
sInfo.db = db;
sInfo.zDatabase = db->aDb[iDb].zDbSName;
| | > > | 111677 111678 111679 111680 111681 111682 111683 111684 111685 111686 111687 111688 111689 111690 111691 111692 111693 |
pIdx->aSample = 0;
#endif
}
/* Load new statistics out of the sqlite_stat1 table */
sInfo.db = db;
sInfo.zDatabase = db->aDb[iDb].zDbSName;
if( (pStat1 = sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase))
&& IsOrdinaryTable(pStat1)
){
zSql = sqlite3MPrintf(db,
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
if( zSql==0 ){
rc = SQLITE_NOMEM_BKPT;
}else{
rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
sqlite3DbFree(db, zSql);
|
| ︙ | ︙ | |||
111861 111862 111863 111864 111865 111866 111867 | int regArgs; if( pParse->nErr ) goto attach_end; memset(&sName, 0, sizeof(NameContext)); sName.pParse = pParse; if( | | | | | 112070 112071 112072 112073 112074 112075 112076 112077 112078 112079 112080 112081 112082 112083 112084 112085 112086 |
int regArgs;
if( pParse->nErr ) goto attach_end;
memset(&sName, 0, sizeof(NameContext));
sName.pParse = pParse;
if(
SQLITE_OK!=resolveAttachExpr(&sName, pFilename) ||
SQLITE_OK!=resolveAttachExpr(&sName, pDbname) ||
SQLITE_OK!=resolveAttachExpr(&sName, pKey)
){
goto attach_end;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
if( pAuthArg ){
char *zAuthArg;
|
| ︙ | ︙ | |||
113274 113275 113276 113277 113278 113279 113280 | ** Any quotation marks (ex: "name", 'name', [name], or `name`) that ** surround the body of the token are removed. ** ** Tokens are often just pointers into the original SQL text and so ** are not \000 terminated and are not persistent. The returned string ** is \000 terminated and is persistent. */ | | | | 113483 113484 113485 113486 113487 113488 113489 113490 113491 113492 113493 113494 113495 113496 113497 113498 113499 113500 |
** Any quotation marks (ex: "name", 'name', [name], or `name`) that
** surround the body of the token are removed.
**
** Tokens are often just pointers into the original SQL text and so
** are not \000 terminated and are not persistent. The returned string
** is \000 terminated and is persistent.
*/
SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, const Token *pName){
char *zName;
if( pName ){
zName = sqlite3DbStrNDup(db, (const char*)pName->z, pName->n);
sqlite3Dequote(zName);
}else{
zName = 0;
}
return zName;
}
|
| ︙ | ︙ | |||
113940 113941 113942 113943 113944 113945 113946 |
pCol->hName = hName;
sqlite3ColumnPropertiesFromName(p, pCol);
if( sType.n==0 ){
/* If there is no type specified, columns have the default affinity
** 'BLOB' with a default size of 4 bytes. */
pCol->affinity = affinity;
| | | 114149 114150 114151 114152 114153 114154 114155 114156 114157 114158 114159 114160 114161 114162 114163 |
pCol->hName = hName;
sqlite3ColumnPropertiesFromName(p, pCol);
if( sType.n==0 ){
/* If there is no type specified, columns have the default affinity
** 'BLOB' with a default size of 4 bytes. */
pCol->affinity = affinity;
pCol->eCType = eType;
pCol->szEst = szEst;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( affinity==SQLITE_AFF_BLOB ){
if( 4>=sqlite3GlobalConfig.szSorterRef ){
pCol->colFlags |= COLFLAG_SORTERREF;
}
}
|
| ︙ | ︙ | |||
114235 114236 114237 114238 114239 114240 114241 |
}
}
}
}
}
if( nTerm==1
&& pCol
| | | 114444 114445 114446 114447 114448 114449 114450 114451 114452 114453 114454 114455 114456 114457 114458 |
}
}
}
}
}
if( nTerm==1
&& pCol
&& pCol->eCType==COLTYPE_INTEGER
&& sortOrder!=SQLITE_SO_DESC
){
if( IN_RENAME_OBJECT && pList ){
Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[0].pExpr);
sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pCExpr);
}
pTab->iPKey = iCol;
|
| ︙ | ︙ | |||
114707 114708 114709 114710 114711 114712 114713 |
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
/* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables)
*/
if( !db->init.imposterTable ){
for(i=0; i<pTab->nCol; i++){
| | > > | 114916 114917 114918 114919 114920 114921 114922 114923 114924 114925 114926 114927 114928 114929 114930 114931 114932 |
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
/* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables)
*/
if( !db->init.imposterTable ){
for(i=0; i<pTab->nCol; i++){
if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
&& (pTab->aCol[i].notNull==OE_None)
){
pTab->aCol[i].notNull = OE_Abort;
}
}
pTab->tabFlags |= TF_HasNotNull;
}
/* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY
|
| ︙ | ︙ | |||
114940 114941 114942 114943 114944 114945 114946 | ** "CREATE TABLE ... AS SELECT ..." statement. The column names of ** the new table will match the result set of the SELECT. */ SQLITE_PRIVATE void sqlite3EndTable( Parse *pParse, /* Parse context */ Token *pCons, /* The ',' token after the last column defn. */ Token *pEnd, /* The ')' before options in the CREATE TABLE */ | | | 115151 115152 115153 115154 115155 115156 115157 115158 115159 115160 115161 115162 115163 115164 115165 |
** "CREATE TABLE ... AS SELECT ..." statement. The column names of
** the new table will match the result set of the SELECT.
*/
SQLITE_PRIVATE void sqlite3EndTable(
Parse *pParse, /* Parse context */
Token *pCons, /* The ',' token after the last column defn. */
Token *pEnd, /* The ')' before options in the CREATE TABLE */
u32 tabOpts, /* Extra table options. Usually 0. */
Select *pSelect /* Select from a "CREATE ... AS SELECT" */
){
Table *p; /* The new table */
sqlite3 *db = pParse->db; /* The database connection */
int iDb; /* Database in which the table lives */
Index *pIdx; /* An implied index of the table */
|
| ︙ | ︙ | |||
114975 114976 114977 114978 114979 114980 114981 114982 114983 114984 114985 114986 114987 114988 |
if( pSelect ){
sqlite3ErrorMsg(pParse, "");
return;
}
p->tnum = db->init.newTnum;
if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
}
assert( (p->tabFlags & TF_HasPrimaryKey)==0
|| p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 );
assert( (p->tabFlags & TF_HasPrimaryKey)!=0
|| (p->iPKey<0 && sqlite3PrimaryKeyIndex(p)==0) );
/* Special processing for WITHOUT ROWID Tables */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 115186 115187 115188 115189 115190 115191 115192 115193 115194 115195 115196 115197 115198 115199 115200 115201 115202 115203 115204 115205 115206 115207 115208 115209 115210 115211 115212 115213 115214 115215 115216 115217 115218 115219 115220 115221 115222 115223 115224 115225 115226 115227 115228 115229 115230 115231 115232 115233 115234 115235 115236 115237 |
if( pSelect ){
sqlite3ErrorMsg(pParse, "");
return;
}
p->tnum = db->init.newTnum;
if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
}
/* Special processing for tables that include the STRICT keyword:
**
** * Do not allow custom column datatypes. Every column must have
** a datatype that is one of INT, INTEGER, REAL, TEXT, or BLOB.
**
** * If a PRIMARY KEY is defined, other than the INTEGER PRIMARY KEY,
** then all columns of the PRIMARY KEY must have a NOT NULL
** constraint.
*/
if( tabOpts & TF_Strict ){
int ii;
p->tabFlags |= TF_Strict;
for(ii=0; ii<p->nCol; ii++){
Column *pCol = &p->aCol[ii];
if( pCol->eCType==COLTYPE_CUSTOM ){
if( pCol->colFlags & COLFLAG_HASTYPE ){
sqlite3ErrorMsg(pParse,
"unknown datatype for %s.%s: \"%s\"",
p->zName, pCol->zCnName, sqlite3ColumnType(pCol, "")
);
}else{
sqlite3ErrorMsg(pParse, "missing datatype for %s.%s",
p->zName, pCol->zCnName);
}
return;
}else if( pCol->eCType==COLTYPE_ANY ){
pCol->affinity = SQLITE_AFF_BLOB;
}
if( (pCol->colFlags & COLFLAG_PRIMKEY)!=0
&& p->iPKey!=ii
&& pCol->notNull == OE_None
){
pCol->notNull = OE_Abort;
p->tabFlags |= TF_HasNotNull;
}
}
}
assert( (p->tabFlags & TF_HasPrimaryKey)==0
|| p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 );
assert( (p->tabFlags & TF_HasPrimaryKey)!=0
|| (p->iPKey<0 && sqlite3PrimaryKeyIndex(p)==0) );
/* Special processing for WITHOUT ROWID Tables */
|
| ︙ | ︙ | |||
116728 116729 116730 116731 116732 116733 116734 |
exit_create_index:
if( pIndex ) sqlite3FreeIndex(db, pIndex);
if( pTab ){
/* Ensure all REPLACE indexes on pTab are at the end of the pIndex list.
** The list was already ordered when this routine was entered, so at this
** point at most a single index (the newly added index) will be out of
** order. So we have to reorder at most one index. */
| | | 116977 116978 116979 116980 116981 116982 116983 116984 116985 116986 116987 116988 116989 116990 116991 |
exit_create_index:
if( pIndex ) sqlite3FreeIndex(db, pIndex);
if( pTab ){
/* Ensure all REPLACE indexes on pTab are at the end of the pIndex list.
** The list was already ordered when this routine was entered, so at this
** point at most a single index (the newly added index) will be out of
** order. So we have to reorder at most one index. */
Index **ppFrom;
Index *pThis;
for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){
Index *pNext;
if( pThis->onError!=OE_Replace ) continue;
while( (pNext = pThis->pNext)!=0 && pNext->onError!=OE_Replace ){
*ppFrom = pNext;
pThis->pNext = pNext->pNext;
|
| ︙ | ︙ | |||
119995 119996 119997 119998 119999 120000 120001 | ** function. */ sqlite3_result_int64(context, sqlite3_last_insert_rowid(db)); } /* ** Implementation of the changes() SQL function. ** | | | | | 120244 120245 120246 120247 120248 120249 120250 120251 120252 120253 120254 120255 120256 120257 120258 120259 120260 |
** function. */
sqlite3_result_int64(context, sqlite3_last_insert_rowid(db));
}
/*
** Implementation of the changes() SQL function.
**
** IMP: R-32760-32347 The changes() SQL function is a wrapper
** around the sqlite3_changes64() C/C++ function and hence follows the
** same rules for counting changes.
*/
static void changes(
sqlite3_context *context,
int NotUsed,
sqlite3_value **NotUsed2
){
sqlite3 *db = sqlite3_context_db_handle(context);
|
| ︙ | ︙ | |||
120020 120021 120022 120023 120024 120025 120026 |
static void total_changes(
sqlite3_context *context,
int NotUsed,
sqlite3_value **NotUsed2
){
sqlite3 *db = sqlite3_context_db_handle(context);
UNUSED_PARAMETER2(NotUsed, NotUsed2);
| | | | 120269 120270 120271 120272 120273 120274 120275 120276 120277 120278 120279 120280 120281 120282 120283 120284 |
static void total_changes(
sqlite3_context *context,
int NotUsed,
sqlite3_value **NotUsed2
){
sqlite3 *db = sqlite3_context_db_handle(context);
UNUSED_PARAMETER2(NotUsed, NotUsed2);
/* IMP: R-11217-42568 This function is a wrapper around the
** sqlite3_total_changes64() C/C++ interface. */
sqlite3_result_int64(context, sqlite3_total_changes64(db));
}
/*
** A structure defining how to do GLOB-style comparisons.
*/
struct compareInfo {
|
| ︙ | ︙ | |||
121139 121140 121141 121142 121143 121144 121145 121146 121147 121148 121149 121150 121151 121152 |
#endif /* SQLITE_OMIT_WINDOWFUNC */
static void minMaxFinalize(sqlite3_context *context){
minMaxValueFinalize(context, 0);
}
/*
** group_concat(EXPR, ?SEPARATOR?)
*/
static void groupConcatStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zVal;
| > > > > > > > > > > > > > > > > > > > > | | < | | | > | > > > > > > > | | | > > > > | | > > > > > > > > > > > > > > > > > | > > | > > > > > | > > | > < | > | | | | > > > | | > > > < > > | | | | > | > > > | | | < | | < < < | < > | | | > | | 121388 121389 121390 121391 121392 121393 121394 121395 121396 121397 121398 121399 121400 121401 121402 121403 121404 121405 121406 121407 121408 121409 121410 121411 121412 121413 121414 121415 121416 121417 121418 121419 121420 121421 121422 121423 121424 121425 121426 121427 121428 121429 121430 121431 121432 121433 121434 121435 121436 121437 121438 121439 121440 121441 121442 121443 121444 121445 121446 121447 121448 121449 121450 121451 121452 121453 121454 121455 121456 121457 121458 121459 121460 121461 121462 121463 121464 121465 121466 121467 121468 121469 121470 121471 121472 121473 121474 121475 121476 121477 121478 121479 121480 121481 121482 121483 121484 121485 121486 121487 121488 121489 121490 121491 121492 121493 121494 121495 121496 121497 121498 121499 121500 121501 121502 121503 121504 121505 121506 121507 121508 121509 121510 121511 121512 121513 121514 121515 121516 121517 121518 121519 121520 121521 121522 121523 121524 121525 121526 121527 121528 121529 121530 121531 121532 121533 121534 121535 121536 121537 121538 121539 121540 121541 121542 121543 121544 121545 121546 121547 121548 121549 121550 121551 121552 121553 121554 121555 121556 121557 121558 |
#endif /* SQLITE_OMIT_WINDOWFUNC */
static void minMaxFinalize(sqlite3_context *context){
minMaxValueFinalize(context, 0);
}
/*
** group_concat(EXPR, ?SEPARATOR?)
**
** The SEPARATOR goes before the EXPR string. This is tragic. The
** groupConcatInverse() implementation would have been easier if the
** SEPARATOR were appended after EXPR. And the order is undocumented,
** so we could change it, in theory. But the old behavior has been
** around for so long that we dare not, for fear of breaking something.
*/
typedef struct {
StrAccum str; /* The accumulated concatenation */
#ifndef SQLITE_OMIT_WINDOWFUNC
int nAccum; /* Number of strings presently concatenated */
int nFirstSepLength; /* Used to detect separator length change */
/* If pnSepLengths!=0, refs an array of inter-string separator lengths,
** stored as actually incorporated into presently accumulated result.
** (Hence, its slots in use number nAccum-1 between method calls.)
** If pnSepLengths==0, nFirstSepLength is the length used throughout.
*/
int *pnSepLengths;
#endif
} GroupConcatCtx;
static void groupConcatStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zVal;
GroupConcatCtx *pGCC;
const char *zSep;
int nVal, nSep;
assert( argc==1 || argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC));
if( pGCC ){
sqlite3 *db = sqlite3_context_db_handle(context);
int firstTerm = pGCC->str.mxAlloc==0;
pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
if( argc==1 ){
if( !firstTerm ){
sqlite3_str_appendchar(&pGCC->str, 1, ',');
}
#ifndef SQLITE_OMIT_WINDOWFUNC
else{
pGCC->nFirstSepLength = 1;
}
#endif
}else if( !firstTerm ){
zSep = (char*)sqlite3_value_text(argv[1]);
nSep = sqlite3_value_bytes(argv[1]);
if( zSep ){
sqlite3_str_append(&pGCC->str, zSep, nSep);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
else{
nSep = 0;
}
if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){
int *pnsl = pGCC->pnSepLengths;
if( pnsl == 0 ){
/* First separator length variation seen, start tracking them. */
pnsl = (int*)sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int));
if( pnsl!=0 ){
int i = 0, nA = pGCC->nAccum-1;
while( i<nA ) pnsl[i++] = pGCC->nFirstSepLength;
}
}else{
pnsl = (int*)sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int));
}
if( pnsl!=0 ){
if( ALWAYS(pGCC->nAccum>0) ){
pnsl[pGCC->nAccum-1] = nSep;
}
pGCC->pnSepLengths = pnsl;
}else{
sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM);
}
}
#endif
}
#ifndef SQLITE_OMIT_WINDOWFUNC
else{
pGCC->nFirstSepLength = sqlite3_value_bytes(argv[1]);
}
pGCC->nAccum += 1;
#endif
zVal = (char*)sqlite3_value_text(argv[0]);
nVal = sqlite3_value_bytes(argv[0]);
if( zVal ) sqlite3_str_append(&pGCC->str, zVal, nVal);
}
}
#ifndef SQLITE_OMIT_WINDOWFUNC
static void groupConcatInverse(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GroupConcatCtx *pGCC;
assert( argc==1 || argc==2 );
(void)argc; /* Suppress unused parameter warning */
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC));
/* pGCC is always non-NULL since groupConcatStep() will have always
** run frist to initialize it */
if( ALWAYS(pGCC) ){
int nVS = sqlite3_value_bytes(argv[0]);
pGCC->nAccum -= 1;
if( pGCC->pnSepLengths!=0 ){
assert(pGCC->nAccum >= 0);
if( pGCC->nAccum>0 ){
nVS += *pGCC->pnSepLengths;
memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1,
(pGCC->nAccum-1)*sizeof(int));
}
}else{
/* If removing single accumulated string, harmlessly over-do. */
nVS += pGCC->nFirstSepLength;
}
if( nVS>=(int)pGCC->str.nChar ){
pGCC->str.nChar = 0;
}else{
pGCC->str.nChar -= nVS;
memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar);
}
if( pGCC->str.nChar==0 ){
pGCC->str.mxAlloc = 0;
sqlite3_free(pGCC->pnSepLengths);
pGCC->pnSepLengths = 0;
}
}
}
#else
# define groupConcatInverse 0
#endif /* SQLITE_OMIT_WINDOWFUNC */
static void groupConcatFinalize(sqlite3_context *context){
GroupConcatCtx *pGCC
= (GroupConcatCtx*)sqlite3_aggregate_context(context, 0);
if( pGCC ){
sqlite3ResultStrAccum(context, &pGCC->str);
#ifndef SQLITE_OMIT_WINDOWFUNC
sqlite3_free(pGCC->pnSepLengths);
#endif
}
}
#ifndef SQLITE_OMIT_WINDOWFUNC
static void groupConcatValue(sqlite3_context *context){
GroupConcatCtx *pGCC
= (GroupConcatCtx*)sqlite3_aggregate_context(context, 0);
if( pGCC ){
StrAccum *pAccum = &pGCC->str;
if( pAccum->accError==SQLITE_TOOBIG ){
sqlite3_result_error_toobig(context);
}else if( pAccum->accError==SQLITE_NOMEM ){
sqlite3_result_error_nomem(context);
}else{
const char *zText = sqlite3_str_value(pAccum);
sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT);
}
}
}
#else
# define groupConcatValue 0
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
| ︙ | ︙ | |||
121547 121548 121549 121550 121551 121552 121553 121554 121555 121556 |
** FuncDef.pHash elements at start-time. The elements of this array
** are read-only after initialization is complete.
**
** For peak efficiency, put the most frequently used function last.
*/
static FuncDef aBuiltinFunc[] = {
/***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/
TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0),
TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0),
TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0),
| > < | | | 121862 121863 121864 121865 121866 121867 121868 121869 121870 121871 121872 121873 121874 121875 121876 121877 121878 121879 121880 121881 |
** FuncDef.pHash elements at start-time. The elements of this array
** are read-only after initialization is complete.
**
** For peak efficiency, put the most frequently used function last.
*/
static FuncDef aBuiltinFunc[] = {
/***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/
#if !defined(SQLITE_UNTESTABLE)
TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0),
TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0),
TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0),
TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0),
#endif /* !defined(SQLITE_UNTESTABLE) */
/***** Regular functions *****/
#ifdef SQLITE_SOUNDEX
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
SFUNCTION(load_extension, 1, 0, 0, loadExt ),
SFUNCTION(load_extension, 2, 0, 0, loadExt ),
|
| ︙ | ︙ | |||
123284 123285 123286 123287 123288 123289 123290 123291 123292 123293 |
pIdx->zColAff[n] = 0;
}
return pIdx->zColAff;
}
/*
** Compute the affinity string for table pTab, if it has not already been
** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities.
**
| > > > > > > | > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 123599 123600 123601 123602 123603 123604 123605 123606 123607 123608 123609 123610 123611 123612 123613 123614 123615 123616 123617 123618 123619 123620 123621 123622 123623 123624 123625 123626 123627 123628 123629 123630 123631 123632 123633 123634 123635 123636 123637 123638 123639 123640 123641 123642 123643 123644 123645 123646 123647 123648 123649 123650 123651 123652 123653 123654 123655 123656 123657 123658 123659 123660 123661 123662 123663 123664 123665 123666 123667 123668 123669 123670 123671 123672 123673 123674 |
pIdx->zColAff[n] = 0;
}
return pIdx->zColAff;
}
/*
** Make changes to the evolving bytecode to do affinity transformations
** of values that are about to be gathered into a row for table pTab.
**
** For ordinary (legacy, non-strict) tables:
** -----------------------------------------
**
** Compute the affinity string for table pTab, if it has not already been
** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities.
**
** If the affinity string is empty (because it was all SQLITE_AFF_BLOB entries
** which were then optimized out) then this routine becomes a no-op.
**
** Otherwise if iReg>0 then code an OP_Affinity opcode that will set the
** affinities for register iReg and following. Or if iReg==0,
** then just set the P4 operand of the previous opcode (which should be
** an OP_MakeRecord) to the affinity string.
**
** A column affinity string has one character per column:
**
** Character Column affinity
** --------- ---------------
** 'A' BLOB
** 'B' TEXT
** 'C' NUMERIC
** 'D' INTEGER
** 'E' REAL
**
** For STRICT tables:
** ------------------
**
** Generate an appropropriate OP_TypeCheck opcode that will verify the
** datatypes against the column definitions in pTab. If iReg==0, that
** means an OP_MakeRecord opcode has already been generated and should be
** the last opcode generated. The new OP_TypeCheck needs to be inserted
** before the OP_MakeRecord. The new OP_TypeCheck should use the same
** register set as the OP_MakeRecord. If iReg>0 then register iReg is
** the first of a series of registers that will form the new record.
** Apply the type checking to that array of registers.
*/
SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
int i, j;
char *zColAff;
if( pTab->tabFlags & TF_Strict ){
if( iReg==0 ){
/* Move the previous opcode (which should be OP_MakeRecord) forward
** by one slot and insert a new OP_TypeCheck where the current
** OP_MakeRecord is found */
VdbeOp *pPrev;
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
pPrev = sqlite3VdbeGetOp(v, -1);
assert( pPrev!=0 );
assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed );
pPrev->opcode = OP_TypeCheck;
sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3);
}else{
/* Insert an isolated OP_Typecheck */
sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol);
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
}
return;
}
zColAff = pTab->zColAff;
if( zColAff==0 ){
sqlite3 *db = sqlite3VdbeDb(v);
zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
if( !zColAff ){
sqlite3OomFault(db);
return;
}
|
| ︙ | ︙ | |||
123331 123332 123333 123334 123335 123336 123337 123338 123339 123340 123341 123342 123343 123344 |
}
assert( zColAff!=0 );
i = sqlite3Strlen30NN(zColAff);
if( i ){
if( iReg ){
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
}else{
sqlite3VdbeChangeP4(v, -1, zColAff, i);
}
}
}
/*
** Return non-zero if the table pTab in database iDb or any of its indices
| > > | 123686 123687 123688 123689 123690 123691 123692 123693 123694 123695 123696 123697 123698 123699 123700 123701 |
}
assert( zColAff!=0 );
i = sqlite3Strlen30NN(zColAff);
if( i ){
if( iReg ){
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
}else{
assert( sqlite3VdbeGetOp(v, -1)->opcode==OP_MakeRecord
|| sqlite3VdbeDb(v)->mallocFailed );
sqlite3VdbeChangeP4(v, -1, zColAff, i);
}
}
}
/*
** Return non-zero if the table pTab in database iDb or any of its indices
|
| ︙ | ︙ | |||
125969 125970 125971 125972 125973 125974 125975 125976 125977 125978 125979 125980 125981 125982 |
}
if( pDest->nCol!=pSrc->nCol ){
return 0; /* Number of columns must be the same in tab1 and tab2 */
}
if( pDest->iPKey!=pSrc->iPKey ){
return 0; /* Both tables must have the same INTEGER PRIMARY KEY */
}
for(i=0; i<pDest->nCol; i++){
Column *pDestCol = &pDest->aCol[i];
Column *pSrcCol = &pSrc->aCol[i];
#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
if( (db->mDbFlags & DBFLAG_Vacuum)==0
&& (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
){
| > > > | 126326 126327 126328 126329 126330 126331 126332 126333 126334 126335 126336 126337 126338 126339 126340 126341 126342 |
}
if( pDest->nCol!=pSrc->nCol ){
return 0; /* Number of columns must be the same in tab1 and tab2 */
}
if( pDest->iPKey!=pSrc->iPKey ){
return 0; /* Both tables must have the same INTEGER PRIMARY KEY */
}
if( (pDest->tabFlags & TF_Strict)!=0 && (pSrc->tabFlags & TF_Strict)==0 ){
return 0; /* Cannot feed from a non-strict into a strict table */
}
for(i=0; i<pDest->nCol; i++){
Column *pDestCol = &pDest->aCol[i];
Column *pSrcCol = &pSrc->aCol[i];
#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
if( (db->mDbFlags & DBFLAG_Vacuum)==0
&& (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
){
|
| ︙ | ︙ | |||
128005 128006 128007 128008 128009 128010 128011 | #define PragTyp_PAGE_SIZE 31 #define PragTyp_PRAGMA_LIST 32 #define PragTyp_SECURE_DELETE 33 #define PragTyp_SHRINK_MEMORY 34 #define PragTyp_SOFT_HEAP_LIMIT 35 #define PragTyp_SYNCHRONOUS 36 #define PragTyp_TABLE_INFO 37 | > | | | | | | | | 128365 128366 128367 128368 128369 128370 128371 128372 128373 128374 128375 128376 128377 128378 128379 128380 128381 128382 128383 128384 128385 128386 | #define PragTyp_PAGE_SIZE 31 #define PragTyp_PRAGMA_LIST 32 #define PragTyp_SECURE_DELETE 33 #define PragTyp_SHRINK_MEMORY 34 #define PragTyp_SOFT_HEAP_LIMIT 35 #define PragTyp_SYNCHRONOUS 36 #define PragTyp_TABLE_INFO 37 #define PragTyp_TABLE_LIST 38 #define PragTyp_TEMP_STORE 39 #define PragTyp_TEMP_STORE_DIRECTORY 40 #define PragTyp_THREADS 41 #define PragTyp_WAL_AUTOCHECKPOINT 42 #define PragTyp_WAL_CHECKPOINT 43 #define PragTyp_LOCK_STATUS 44 #define PragTyp_STATS 45 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */ #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */ #define PragFlg_ReadOnly 0x08 /* Read-only HEADER_VALUE */ #define PragFlg_Result0 0x10 /* Acts as query when no argument */ |
| ︙ | ︙ | |||
128044 128045 128046 128047 128048 128049 128050 |
/* 9 */ "name",
/* 10 */ "type",
/* 11 */ "notnull",
/* 12 */ "dflt_value",
/* 13 */ "pk",
/* 14 */ "hidden",
/* table_info reuses 8 */
| | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > | | | | | | | | | | | | | 128405 128406 128407 128408 128409 128410 128411 128412 128413 128414 128415 128416 128417 128418 128419 128420 128421 128422 128423 128424 128425 128426 128427 128428 128429 128430 128431 128432 128433 128434 128435 128436 128437 128438 128439 128440 128441 128442 128443 128444 128445 128446 128447 128448 128449 128450 128451 128452 128453 128454 128455 128456 128457 128458 128459 128460 128461 128462 128463 |
/* 9 */ "name",
/* 10 */ "type",
/* 11 */ "notnull",
/* 12 */ "dflt_value",
/* 13 */ "pk",
/* 14 */ "hidden",
/* table_info reuses 8 */
/* 15 */ "schema", /* Used by: table_list */
/* 16 */ "name",
/* 17 */ "type",
/* 18 */ "ncol",
/* 19 */ "wr",
/* 20 */ "strict",
/* 21 */ "seqno", /* Used by: index_xinfo */
/* 22 */ "cid",
/* 23 */ "name",
/* 24 */ "desc",
/* 25 */ "coll",
/* 26 */ "key",
/* 27 */ "name", /* Used by: function_list */
/* 28 */ "builtin",
/* 29 */ "type",
/* 30 */ "enc",
/* 31 */ "narg",
/* 32 */ "flags",
/* 33 */ "tbl", /* Used by: stats */
/* 34 */ "idx",
/* 35 */ "wdth",
/* 36 */ "hght",
/* 37 */ "flgs",
/* 38 */ "seq", /* Used by: index_list */
/* 39 */ "name",
/* 40 */ "unique",
/* 41 */ "origin",
/* 42 */ "partial",
/* 43 */ "table", /* Used by: foreign_key_check */
/* 44 */ "rowid",
/* 45 */ "parent",
/* 46 */ "fkid",
/* index_info reuses 21 */
/* 47 */ "seq", /* Used by: database_list */
/* 48 */ "name",
/* 49 */ "file",
/* 50 */ "busy", /* Used by: wal_checkpoint */
/* 51 */ "log",
/* 52 */ "checkpointed",
/* collation_list reuses 38 */
/* 53 */ "database", /* Used by: lock_status */
/* 54 */ "status",
/* 55 */ "cache_size", /* Used by: default_cache_size */
/* module_list pragma_list reuses 9 */
/* 56 */ "timeout", /* Used by: busy_timeout */
};
/* Definitions of all built-in pragmas */
typedef struct PragmaName {
const char *const zName; /* Name of pragma */
u8 ePragTyp; /* PragTyp_XXX value */
u8 mPragFlg; /* Zero or more PragFlg_XXX values */
|
| ︙ | ︙ | |||
128133 128134 128135 128136 128137 128138 128139 |
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_AutoIndex },
#endif
#endif
{/* zName: */ "busy_timeout",
/* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
/* ePragFlg: */ PragFlg_Result0,
| | | 128500 128501 128502 128503 128504 128505 128506 128507 128508 128509 128510 128511 128512 128513 128514 |
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_AutoIndex },
#endif
#endif
{/* zName: */ "busy_timeout",
/* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
/* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 56, 1,
/* iArg: */ 0 },
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
{/* zName: */ "cache_size",
/* ePragTyp: */ PragTyp_CACHE_SIZE,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
|
| ︙ | ︙ | |||
128172 128173 128174 128175 128176 128177 128178 |
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_CkptFullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "collation_list",
/* ePragTyp: */ PragTyp_COLLATION_LIST,
/* ePragFlg: */ PragFlg_Result0,
| | | 128539 128540 128541 128542 128543 128544 128545 128546 128547 128548 128549 128550 128551 128552 128553 |
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_CkptFullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "collation_list",
/* ePragTyp: */ PragTyp_COLLATION_LIST,
/* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 38, 2,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
{/* zName: */ "compile_options",
/* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
/* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 0, 0,
|
| ︙ | ︙ | |||
128207 128208 128209 128210 128211 128212 128213 |
/* ColNames: */ 0, 0,
/* iArg: */ BTREE_DATA_VERSION },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "database_list",
/* ePragTyp: */ PragTyp_DATABASE_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
| | | | 128574 128575 128576 128577 128578 128579 128580 128581 128582 128583 128584 128585 128586 128587 128588 128589 128590 128591 128592 128593 128594 128595 |
/* ColNames: */ 0, 0,
/* iArg: */ BTREE_DATA_VERSION },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "database_list",
/* ePragTyp: */ PragTyp_DATABASE_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
/* ColNames: */ 47, 3,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
{/* zName: */ "default_cache_size",
/* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
/* ColNames: */ 55, 1,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
{/* zName: */ "defer_foreign_keys",
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
| ︙ | ︙ | |||
128244 128245 128246 128247 128248 128249 128250 |
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
{/* zName: */ "foreign_key_check",
/* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
| | | 128611 128612 128613 128614 128615 128616 128617 128618 128619 128620 128621 128622 128623 128624 128625 |
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
{/* zName: */ "foreign_key_check",
/* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 43, 4,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FOREIGN_KEY)
{/* zName: */ "foreign_key_list",
/* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 0, 8,
|
| ︙ | ︙ | |||
128287 128288 128289 128290 128291 128292 128293 |
/* iArg: */ SQLITE_FullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "function_list",
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
/* ePragFlg: */ PragFlg_Result0,
| | | 128654 128655 128656 128657 128658 128659 128660 128661 128662 128663 128664 128665 128666 128667 128668 |
/* iArg: */ SQLITE_FullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "function_list",
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
/* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 27, 6,
/* iArg: */ 0 },
#endif
#endif
{/* zName: */ "hard_heap_limit",
/* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT,
/* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 0, 0,
|
| ︙ | ︙ | |||
128316 128317 128318 128319 128320 128321 128322 |
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "index_info",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
| | | | | 128683 128684 128685 128686 128687 128688 128689 128690 128691 128692 128693 128694 128695 128696 128697 128698 128699 128700 128701 128702 128703 128704 128705 128706 128707 |
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "index_info",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 21, 3,
/* iArg: */ 0 },
{/* zName: */ "index_list",
/* ePragTyp: */ PragTyp_INDEX_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 38, 5,
/* iArg: */ 0 },
{/* zName: */ "index_xinfo",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 21, 6,
/* iArg: */ 1 },
#endif
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
{/* zName: */ "integrity_check",
/* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 0, 0,
|
| ︙ | ︙ | |||
128366 128367 128368 128369 128370 128371 128372 |
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
{/* zName: */ "lock_status",
/* ePragTyp: */ PragTyp_LOCK_STATUS,
/* ePragFlg: */ PragFlg_Result0,
| | | 128733 128734 128735 128736 128737 128738 128739 128740 128741 128742 128743 128744 128745 128746 128747 |
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
{/* zName: */ "lock_status",
/* ePragTyp: */ PragTyp_LOCK_STATUS,
/* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 53, 2,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
{/* zName: */ "locking_mode",
/* ePragTyp: */ PragTyp_LOCKING_MODE,
/* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq,
/* ColNames: */ 0, 0,
|
| ︙ | ︙ | |||
128505 128506 128507 128508 128509 128510 128511 |
/* iArg: */ SQLITE_SqlTrace },
#endif
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
{/* zName: */ "stats",
/* ePragTyp: */ PragTyp_STATS,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
| | > > > > > | 128872 128873 128874 128875 128876 128877 128878 128879 128880 128881 128882 128883 128884 128885 128886 128887 128888 128889 128890 128891 128892 128893 128894 128895 128896 128897 128898 128899 128900 128901 128902 128903 128904 128905 |
/* iArg: */ SQLITE_SqlTrace },
#endif
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
{/* zName: */ "stats",
/* ePragTyp: */ PragTyp_STATS,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
/* ColNames: */ 33, 5,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
{/* zName: */ "synchronous",
/* ePragTyp: */ PragTyp_SYNCHRONOUS,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "table_info",
/* ePragTyp: */ PragTyp_TABLE_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 8, 6,
/* iArg: */ 0 },
{/* zName: */ "table_list",
/* ePragTyp: */ PragTyp_TABLE_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1,
/* ColNames: */ 15, 6,
/* iArg: */ 0 },
{/* zName: */ "table_xinfo",
/* ePragTyp: */ PragTyp_TABLE_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 8, 7,
/* iArg: */ 1 },
#endif
|
| ︙ | ︙ | |||
128596 128597 128598 128599 128600 128601 128602 |
/* ePragTyp: */ PragTyp_WAL_AUTOCHECKPOINT,
/* ePragFlg: */ 0,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
{/* zName: */ "wal_checkpoint",
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
/* ePragFlg: */ PragFlg_NeedSchema,
| | | | 128968 128969 128970 128971 128972 128973 128974 128975 128976 128977 128978 128979 128980 128981 128982 128983 128984 128985 128986 128987 128988 128989 128990 128991 128992 128993 |
/* ePragTyp: */ PragTyp_WAL_AUTOCHECKPOINT,
/* ePragFlg: */ 0,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
{/* zName: */ "wal_checkpoint",
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
/* ePragFlg: */ PragFlg_NeedSchema,
/* ColNames: */ 50, 3,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{/* zName: */ "writable_schema",
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
/* Number of pragmas: 68 on by default, 78 total. */
/************** End of pragma.h **********************************************/
/************** Continuing where we left off in pragma.c *********************/
/*
** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
|
| ︙ | ︙ | |||
129776 129777 129778 129779 129780 129781 129782 129783 129784 129785 129786 129787 129788 129789 |
k,
isHidden);
}
}
}
break;
#ifdef SQLITE_DEBUG
case PragTyp_STATS: {
Index *pIdx;
HashElem *i;
pParse->nMem = 5;
sqlite3CodeVerifySchema(pParse, iDb);
for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 130148 130149 130150 130151 130152 130153 130154 130155 130156 130157 130158 130159 130160 130161 130162 130163 130164 130165 130166 130167 130168 130169 130170 130171 130172 130173 130174 130175 130176 130177 130178 130179 130180 130181 130182 130183 130184 130185 130186 130187 130188 130189 130190 130191 130192 130193 130194 130195 130196 130197 130198 130199 130200 130201 130202 130203 130204 130205 130206 130207 130208 130209 |
k,
isHidden);
}
}
}
break;
/*
** PRAGMA table_list
**
** Return a single row for each table, virtual table, or view in the
** entire schema.
**
** schema: Name of attached database hold this table
** name: Name of the table itself
** type: "table", "view", "virtual", "shadow"
** ncol: Number of columns
** wr: True for a WITHOUT ROWID table
** strict: True for a STRICT table
*/
case PragTyp_TABLE_LIST: {
int ii;
pParse->nMem = 6;
sqlite3CodeVerifyNamedSchema(pParse, zDb);
for(ii=0; ii<db->nDb; ii++){
HashElem *k;
Hash *pHash;
if( zDb && sqlite3_stricmp(zDb, db->aDb[ii].zDbSName)!=0 ) continue;
pHash = &db->aDb[ii].pSchema->tblHash;
for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k) ){
Table *pTab = sqliteHashData(k);
const char *zType;
if( zRight && sqlite3_stricmp(zRight, pTab->zName)!=0 ) continue;
if( IsView(pTab) ){
zType = "view";
}else if( IsVirtual(pTab) ){
zType = "virtual";
}else if( pTab->tabFlags & TF_Shadow ){
zType = "shadow";
}else{
zType = "table";
}
sqlite3VdbeMultiLoad(v, 1, "sssiii",
db->aDb[ii].zDbSName,
pTab->zName,
zType,
pTab->nCol,
(pTab->tabFlags & TF_WithoutRowid)!=0,
(pTab->tabFlags & TF_Strict)!=0
);
}
}
}
break;
#ifdef SQLITE_DEBUG
case PragTyp_STATS: {
Index *pIdx;
HashElem *i;
pParse->nMem = 5;
sqlite3CodeVerifySchema(pParse, iDb);
for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){
|
| ︙ | ︙ | |||
130235 130236 130237 130238 130239 130240 130241 130242 130243 130244 130245 130246 130247 130248 |
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx, *pPk;
Index *pPrior = 0;
int loopTop;
int iDataCur, iIdxCur;
int r1 = -1;
if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */
if( pObjTab && pObjTab!=pTab ) continue;
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
1, 0, &iDataCur, &iIdxCur);
/* reg[7] counts the number of entries in the table.
| > | 130655 130656 130657 130658 130659 130660 130661 130662 130663 130664 130665 130666 130667 130668 130669 |
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx, *pPk;
Index *pPrior = 0;
int loopTop;
int iDataCur, iIdxCur;
int r1 = -1;
int bStrict;
if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */
if( pObjTab && pObjTab!=pTab ) continue;
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
1, 0, &iDataCur, &iIdxCur);
/* reg[7] counts the number of entries in the table.
|
| ︙ | ︙ | |||
130256 130257 130258 130259 130260 130261 130262 130263 |
assert( sqlite3NoTempsInRange(pParse,1,7+j) );
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
if( !isQuick ){
/* Sanity check on record header decoding */
sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3);
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
}
| > | > > > | | > > | | > > > > > > > > > > > > > > > > > | | > | | > | 130677 130678 130679 130680 130681 130682 130683 130684 130685 130686 130687 130688 130689 130690 130691 130692 130693 130694 130695 130696 130697 130698 130699 130700 130701 130702 130703 130704 130705 130706 130707 130708 130709 130710 130711 130712 130713 130714 130715 130716 130717 130718 130719 130720 130721 130722 130723 130724 130725 130726 130727 130728 130729 130730 130731 130732 |
assert( sqlite3NoTempsInRange(pParse,1,7+j) );
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
if( !isQuick ){
/* Sanity check on record header decoding */
sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3);
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
VdbeComment((v, "(right-most column)"));
}
/* Verify that all NOT NULL columns really are NOT NULL. At the
** same time verify the type of the content of STRICT tables */
bStrict = (pTab->tabFlags & TF_Strict)!=0;
for(j=0; j<pTab->nCol; j++){
char *zErr;
Column *pCol = pTab->aCol + j;
int doError, jmp2;
if( j==pTab->iPKey ) continue;
if( pCol->notNull==0 && !bStrict ) continue;
doError = bStrict ? sqlite3VdbeMakeLabel(pParse) : 0;
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
if( sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
}
if( pCol->notNull ){
jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v);
zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
pCol->zCnName);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
if( bStrict ){
sqlite3VdbeGoto(v, doError);
}else{
integrityCheckResultRow(v);
}
sqlite3VdbeJumpHere(v, jmp2);
}
if( (pTab->tabFlags & TF_Strict)!=0
&& pCol->eCType!=COLTYPE_ANY
){
jmp2 = sqlite3VdbeAddOp3(v, OP_IsNullOrType, 3, 0,
sqlite3StdTypeMap[pCol->eCType-1]);
VdbeCoverage(v);
zErr = sqlite3MPrintf(db, "non-%s value in %s.%s",
sqlite3StdType[pCol->eCType-1],
pTab->zName, pTab->aCol[j].zCnName);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
sqlite3VdbeResolveLabel(v, doError);
integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, jmp2);
}
}
/* Verify CHECK constraints */
if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
ExprList *pCheck = sqlite3ExprListDup(db, pTab->pCheck, 0);
if( db->mallocFailed==0 ){
int addrCkFault = sqlite3VdbeMakeLabel(pParse);
int addrCkOk = sqlite3VdbeMakeLabel(pParse);
|
| ︙ | ︙ | |||
131318 131319 131320 131321 131322 131323 131324 |
|| (db->init.newTnum>pData->mxPage && pData->mxPage>0)
){
if( sqlite3Config.bExtraSchemaChecks ){
corruptSchema(pData, argv, "invalid rootpage");
}
}
db->init.orphanTrigger = 0;
| | > | 131764 131765 131766 131767 131768 131769 131770 131771 131772 131773 131774 131775 131776 131777 131778 131779 131780 131781 131782 131783 131784 131785 131786 131787 131788 131789 131790 131791 131792 131793 131794 131795 131796 131797 |
|| (db->init.newTnum>pData->mxPage && pData->mxPage>0)
){
if( sqlite3Config.bExtraSchemaChecks ){
corruptSchema(pData, argv, "invalid rootpage");
}
}
db->init.orphanTrigger = 0;
db->init.azInit = (const char**)argv;
pStmt = 0;
TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0);
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
db->init.iDb = saved_iDb;
/* assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); */
if( SQLITE_OK!=rc ){
if( db->init.orphanTrigger ){
assert( iDb==1 );
}else{
if( rc > pData->rc ) pData->rc = rc;
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
corruptSchema(pData, argv, sqlite3_errmsg(db));
}
}
}
db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */
sqlite3_finalize(pStmt);
}else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
corruptSchema(pData, argv, 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
** constraint for a CREATE TABLE. The index should have already
|
| ︙ | ︙ | |||
132553 132554 132555 132556 132557 132558 132559 132560 132561 132562 132563 132564 132565 132566 |
assert( pSrc->a[iLeft].pTab );
assert( pSrc->a[iRight].pTab );
pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft);
pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight);
pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
if( pEq && isOuterJoin ){
ExprSetProperty(pEq, EP_FromJoin);
assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(pEq, EP_NoReduce);
pEq->iRightJoinTable = pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
| > > > | 133000 133001 133002 133003 133004 133005 133006 133007 133008 133009 133010 133011 133012 133013 133014 133015 133016 |
assert( pSrc->a[iLeft].pTab );
assert( pSrc->a[iRight].pTab );
pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft);
pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight);
pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
assert( pE2!=0 || pEq==0 ); /* Due to db->mallocFailed test
** in sqlite3DbMallocRawNN() called from
** sqlite3PExpr(). */
if( pEq && isOuterJoin ){
ExprSetProperty(pEq, EP_FromJoin);
assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(pEq, EP_NoReduce);
pEq->iRightJoinTable = pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
|
| ︙ | ︙ | |||
134399 134400 134401 134402 134403 134404 134405 |
if( pCol->zCnName ){
memcpy(&pCol->zCnName[n+1], zType, m+1);
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff;
pColl = sqlite3ExprCollSeq(pParse, p);
| | | 134849 134850 134851 134852 134853 134854 134855 134856 134857 134858 134859 134860 134861 134862 134863 |
if( pCol->zCnName ){
memcpy(&pCol->zCnName[n+1], zType, m+1);
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff;
pColl = sqlite3ExprCollSeq(pParse, p);
if( pColl ){
assert( pTab->pIndex==0 );
sqlite3ColumnSetColl(db, pCol, pColl->zName);
}
}
pTab->szTabRow = 1; /* Any non-zero value works */
}
|
| ︙ | ︙ | |||
134565 134566 134567 134568 134569 134570 134571 |
**
** Space to hold the KeyInfo structure is obtained from malloc. The calling
** function is responsible for ensuring that this structure is eventually
** freed.
*/
static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
ExprList *pOrderBy = p->pOrderBy;
| | | 135015 135016 135017 135018 135019 135020 135021 135022 135023 135024 135025 135026 135027 135028 135029 |
**
** Space to hold the KeyInfo structure is obtained from malloc. The calling
** function is responsible for ensuring that this structure is eventually
** freed.
*/
static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
ExprList *pOrderBy = p->pOrderBy;
int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0;
sqlite3 *db = pParse->db;
KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1);
if( pRet ){
int i;
for(i=0; i<nOrderBy; i++){
struct ExprList_item *pItem = &pOrderBy->a[i];
Expr *pTerm = pItem->pExpr;
|
| ︙ | ︙ | |||
134637 134638 134639 134640 134641 134642 134643 |
Parse *pParse, /* Parsing context */
Select *p, /* The recursive SELECT to be coded */
SelectDest *pDest /* What to do with query results */
){
SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */
int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */
Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
| | | 135087 135088 135089 135090 135091 135092 135093 135094 135095 135096 135097 135098 135099 135100 135101 |
Parse *pParse, /* Parsing context */
Select *p, /* The recursive SELECT to be coded */
SelectDest *pDest /* What to do with query results */
){
SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */
int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */
Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
Select *pSetup; /* The setup query */
Select *pFirstRec; /* Left-most recursive term */
int addrTop; /* Top of the loop */
int addrCont, addrBreak; /* CONTINUE and BREAK addresses */
int iCurrent = 0; /* The Current table */
int regCurrent; /* Register holding Current table */
int iQueue; /* The Queue table */
int iDistinct = 0; /* To ensure unique results if UNION */
|
| ︙ | ︙ | |||
134721 134722 134723 134724 134725 134726 134727 | /* Figure out how many elements of the compound SELECT are part of the ** recursive query. Make sure no recursive elements use aggregate ** functions. Mark the recursive elements as UNION ALL even if they ** are really UNION because the distinctness will be enforced by the ** iDistinct table. pFirstRec is left pointing to the left-most ** recursive term of the CTE. */ | < | 135171 135172 135173 135174 135175 135176 135177 135178 135179 135180 135181 135182 135183 135184 |
/* Figure out how many elements of the compound SELECT are part of the
** recursive query. Make sure no recursive elements use aggregate
** functions. Mark the recursive elements as UNION ALL even if they
** are really UNION because the distinctness will be enforced by the
** iDistinct table. pFirstRec is left pointing to the left-most
** recursive term of the CTE.
*/
for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){
if( pFirstRec->selFlags & SF_Aggregate ){
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
goto end_of_recursive_query;
}
pFirstRec->op = TK_ALL;
if( (pFirstRec->pPrior->selFlags & SF_Recursive)==0 ) break;
|
| ︙ | ︙ | |||
135187 135188 135189 135190 135191 135192 135193 135194 135195 135196 135197 135198 135199 135200 |
int i; /* Loop counter */
KeyInfo *pKeyInfo; /* Collating sequence for the result set */
Select *pLoop; /* For looping through SELECT statements */
CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */
int nCol; /* Number of columns in result set */
assert( p->pNext==0 );
nCol = p->pEList->nExpr;
pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
if( !pKeyInfo ){
rc = SQLITE_NOMEM_BKPT;
goto multi_select_end;
}
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
| > | 135636 135637 135638 135639 135640 135641 135642 135643 135644 135645 135646 135647 135648 135649 135650 |
int i; /* Loop counter */
KeyInfo *pKeyInfo; /* Collating sequence for the result set */
Select *pLoop; /* For looping through SELECT statements */
CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */
int nCol; /* Number of columns in result set */
assert( p->pNext==0 );
assert( p->pEList!=0 );
nCol = p->pEList->nExpr;
pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
if( !pKeyInfo ){
rc = SQLITE_NOMEM_BKPT;
goto multi_select_end;
}
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
|
| ︙ | ︙ | |||
135221 135222 135223 135224 135225 135226 135227 |
}
sqlite3KeyInfoUnref(pKeyInfo);
}
multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
| > > | > > | 135671 135672 135673 135674 135675 135676 135677 135678 135679 135680 135681 135682 135683 135684 135685 135686 135687 135688 135689 |
}
sqlite3KeyInfoUnref(pKeyInfo);
}
multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
if( pDelete ){
sqlite3ParserAddCleanup(pParse,
(void(*)(sqlite3*,void*))sqlite3SelectDelete,
pDelete);
}
return rc;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/*
** Error message for when two or more terms of a compound select have different
** size result sets.
|
| ︙ | ︙ | |||
135534 135535 135536 135537 135538 135539 135540 135541 135542 135543 135544 135545 135546 135547 |
** the ORDER BY clause covers every term of the result set. Add
** terms to the ORDER BY clause as necessary.
*/
if( op!=TK_ALL ){
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
assert( pItem->u.x.iOrderByCol>0 );
if( pItem->u.x.iOrderByCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
if( pNew==0 ) return SQLITE_NOMEM_BKPT;
pNew->flags |= EP_IntValue;
| > | 135988 135989 135990 135991 135992 135993 135994 135995 135996 135997 135998 135999 136000 136001 136002 |
** the ORDER BY clause covers every term of the result set. Add
** terms to the ORDER BY clause as necessary.
*/
if( op!=TK_ALL ){
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
assert( pItem!=0 );
assert( pItem->u.x.iOrderByCol>0 );
if( pItem->u.x.iOrderByCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
if( pNew==0 ) return SQLITE_NOMEM_BKPT;
pNew->flags |= EP_IntValue;
|
| ︙ | ︙ | |||
135560 135561 135562 135563 135564 135565 135566 135567 135568 135569 135570 135571 135572 135573 |
** collation.
*/
aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
aPermute[0] = nOrderBy;
for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
assert( pItem->u.x.iOrderByCol>0 );
assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->u.x.iOrderByCol - 1;
}
pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
}else{
pKeyMerge = 0;
| > | 136015 136016 136017 136018 136019 136020 136021 136022 136023 136024 136025 136026 136027 136028 136029 |
** collation.
*/
aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
aPermute[0] = nOrderBy;
for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
assert( pItem!=0 );
assert( pItem->u.x.iOrderByCol>0 );
assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->u.x.iOrderByCol - 1;
}
pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
}else{
pKeyMerge = 0;
|
| ︙ | ︙ | |||
137698 137699 137700 137701 137702 137703 137704 |
if( (db->flags & SQLITE_EnableView)==0
&& pTab->pSchema!=db->aDb[1].pSchema
){
sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
pTab->zName);
}
pFrom->pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0);
| < > | | 138154 138155 138156 138157 138158 138159 138160 138161 138162 138163 138164 138165 138166 138167 138168 138169 138170 |
if( (db->flags & SQLITE_EnableView)==0
&& pTab->pSchema!=db->aDb[1].pSchema
){
sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
pTab->zName);
}
pFrom->pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( ALWAYS(IsVirtual(pTab))
&& pFrom->fg.fromDDL
&& ALWAYS(pTab->u.vtab.p!=0)
&& pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0)
){
sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"",
pTab->zName);
}
|
| ︙ | ︙ | |||
138789 138790 138791 138792 138793 138794 138795 |
*/
pParse->nHeight += sqlite3SelectExprHeight(p);
/* Make copies of constant WHERE-clause terms in the outer query down
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( OptimizationEnabled(db, SQLITE_PushDown)
| | > | 139245 139246 139247 139248 139249 139250 139251 139252 139253 139254 139255 139256 139257 139258 139259 139260 |
*/
pParse->nHeight += sqlite3SelectExprHeight(p);
/* Make copies of constant WHERE-clause terms in the outer query down
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( OptimizationEnabled(db, SQLITE_PushDown)
&& (pItem->fg.isCte==0
|| (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2))
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
(pItem->fg.jointype & JT_OUTER)!=0)
){
#if SELECTTRACE_ENABLED
if( sqlite3SelectTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
|
| ︙ | ︙ | |||
138850 138851 138852 138853 138854 138855 138856 138857 138858 138859 138860 138861 138862 138863 |
** generated. Invoke the subroutine to compute the materialization,
** the make the pItem->iCursor be a copy of the ephemerial table that
** holds the result of the materialization. */
CteUse *pCteUse = pItem->u2.pCteUse;
sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e);
if( pItem->iCursor!=pCteUse->iCur ){
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur);
}
pSub->nSelectRow = pCteUse->nRowEst;
}else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){
/* This view has already been materialized by a prior entry in
** this same FROM clause. Reuse it. */
if( pPrior->addrFillSub ){
sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
| > | 139307 139308 139309 139310 139311 139312 139313 139314 139315 139316 139317 139318 139319 139320 139321 |
** generated. Invoke the subroutine to compute the materialization,
** the make the pItem->iCursor be a copy of the ephemerial table that
** holds the result of the materialization. */
CteUse *pCteUse = pItem->u2.pCteUse;
sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e);
if( pItem->iCursor!=pCteUse->iCur ){
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur);
VdbeComment((v, "%!S", pItem));
}
pSub->nSelectRow = pCteUse->nRowEst;
}else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){
/* This view has already been materialized by a prior entry in
** this same FROM clause. Reuse it. */
if( pPrior->addrFillSub ){
sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
|
| ︙ | ︙ | |||
143565 143566 143567 143568 143569 143570 143571 |
** reaches zero, call the xDisconnect() method to delete the object.
*/
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
sqlite3 *db = pVTab->db;
assert( db );
assert( pVTab->nRef>0 );
| | > | 144023 144024 144025 144026 144027 144028 144029 144030 144031 144032 144033 144034 144035 144036 144037 144038 |
** reaches zero, call the xDisconnect() method to delete the object.
*/
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
sqlite3 *db = pVTab->db;
assert( db );
assert( pVTab->nRef>0 );
assert( db->eOpenState==SQLITE_STATE_OPEN
|| db->eOpenState==SQLITE_STATE_ZOMBIE );
pVTab->nRef--;
if( pVTab->nRef==0 ){
sqlite3_vtab *p = pVTab->pVtab;
sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
if( p ){
p->pModule->xDisconnect(p);
|
| ︙ | ︙ | |||
144160 144161 144162 144163 144164 144165 144166 144167 144168 144169 144170 144171 144172 144173 144174 144175 144176 144177 144178 144179 144180 144181 144182 144183 144184 144185 144186 144187 144188 144189 144190 144191 144192 |
*/
SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
VtabCtx *pCtx;
int rc = SQLITE_OK;
Table *pTab;
char *zErr = 0;
Parse sParse;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
sqlite3Error(db, SQLITE_MISUSE);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
pTab = pCtx->pTab;
assert( IsVirtual(pTab) );
memset(&sParse, 0, sizeof(sParse));
sParse.eParseMode = PARSE_MODE_DECLARE_VTAB;
sParse.db = db;
sParse.nQueryLoop = 1;
if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr)
&& sParse.pNewTable
&& !db->mallocFailed
&& IsOrdinaryTable(sParse.pNewTable)
){
if( !pTab->aCol ){
| > > > > > > > | 144619 144620 144621 144622 144623 144624 144625 144626 144627 144628 144629 144630 144631 144632 144633 144634 144635 144636 144637 144638 144639 144640 144641 144642 144643 144644 144645 144646 144647 144648 144649 144650 144651 144652 144653 144654 144655 144656 144657 144658 |
*/
SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
VtabCtx *pCtx;
int rc = SQLITE_OK;
Table *pTab;
char *zErr = 0;
Parse sParse;
int initBusy;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
sqlite3Error(db, SQLITE_MISUSE);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
pTab = pCtx->pTab;
assert( IsVirtual(pTab) );
memset(&sParse, 0, sizeof(sParse));
sParse.eParseMode = PARSE_MODE_DECLARE_VTAB;
sParse.db = db;
/* We should never be able to reach this point while loading the
** schema. Nevertheless, defend against that (turn off db->init.busy)
** in case a bug arises. */
assert( db->init.busy==0 );
initBusy = db->init.busy;
db->init.busy = 0;
sParse.nQueryLoop = 1;
if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr)
&& sParse.pNewTable
&& !db->mallocFailed
&& IsOrdinaryTable(sParse.pNewTable)
){
if( !pTab->aCol ){
|
| ︙ | ︙ | |||
144225 144226 144227 144228 144229 144230 144231 144232 144233 144234 144235 144236 144237 144238 |
sParse.eParseMode = PARSE_MODE_NORMAL;
if( sParse.pVdbe ){
sqlite3VdbeFinalize(sParse.pVdbe);
}
sqlite3DeleteTable(db, sParse.pNewTable);
sqlite3ParserReset(&sParse);
assert( (rc&0xff)==rc );
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
| > | 144691 144692 144693 144694 144695 144696 144697 144698 144699 144700 144701 144702 144703 144704 144705 |
sParse.eParseMode = PARSE_MODE_NORMAL;
if( sParse.pVdbe ){
sqlite3VdbeFinalize(sParse.pVdbe);
}
sqlite3DeleteTable(db, sParse.pNewTable);
sqlite3ParserReset(&sParse);
db->init.busy = initBusy;
assert( (rc&0xff)==rc );
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
|
| ︙ | ︙ | |||
146531 146532 146533 146534 146535 146536 146537 |
x.iTabCur = iTabCur;
x.iIdxCur = iIdxCur;
x.pWInfo = pWInfo;
x.db = pWInfo->pParse->db;
for(iIdxCol=0; iIdxCol<pIdx->nColumn; iIdxCol++){
i16 iRef = pIdx->aiColumn[iIdxCol];
if( iRef==XN_EXPR ){
| | | 146998 146999 147000 147001 147002 147003 147004 147005 147006 147007 147008 147009 147010 147011 147012 |
x.iTabCur = iTabCur;
x.iIdxCur = iIdxCur;
x.pWInfo = pWInfo;
x.db = pWInfo->pParse->db;
for(iIdxCol=0; iIdxCol<pIdx->nColumn; iIdxCol++){
i16 iRef = pIdx->aiColumn[iIdxCol];
if( iRef==XN_EXPR ){
assert( aColExpr!=0 && aColExpr->a[iIdxCol].pExpr!=0 );
x.pIdxExpr = aColExpr->a[iIdxCol].pExpr;
if( sqlite3ExprIsConstant(x.pIdxExpr) ) continue;
w.xExprCallback = whereIndexExprTransNode;
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
}else if( iRef>=0
&& (pTab->aCol[iRef].colFlags & COLFLAG_VIRTUAL)!=0
&& ((pTab->aCol[iRef].colFlags & COLFLAG_HASCOLL)==0
|
| ︙ | ︙ | |||
146797 146798 146799 146800 146801 146802 146803 |
iReleaseReg = ++pParse->nMem;
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
addrNxt = pLevel->addrNxt;
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
VdbeCoverage(v);
pLevel->op = OP_Noop;
| < < < | 147264 147265 147266 147267 147268 147269 147270 147271 147272 147273 147274 147275 147276 147277 |
iReleaseReg = ++pParse->nMem;
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
addrNxt = pLevel->addrNxt;
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
VdbeCoverage(v);
pLevel->op = OP_Noop;
}else if( (pLoop->wsFlags & WHERE_IPK)!=0
&& (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
){
/* Case 3: We have an inequality comparison against the ROWID field.
*/
int testOp = OP_Noop;
int start;
|
| ︙ | ︙ | |||
148078 148079 148080 148081 148082 148083 148084 |
** 2019-05-02 https://sqlite.org/src/info/b043a54c3de54b28
** 2019-06-10 https://sqlite.org/src/info/fd76310a5e843e07
** 2019-06-14 https://sqlite.org/src/info/ce8717f0885af975
** 2019-09-03 https://sqlite.org/src/info/0f0428096f17252a
*/
if( pLeft->op!=TK_COLUMN
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
| | | 148542 148543 148544 148545 148546 148547 148548 148549 148550 148551 148552 148553 148554 148555 148556 |
** 2019-05-02 https://sqlite.org/src/info/b043a54c3de54b28
** 2019-06-10 https://sqlite.org/src/info/fd76310a5e843e07
** 2019-06-14 https://sqlite.org/src/info/ce8717f0885af975
** 2019-09-03 https://sqlite.org/src/info/0f0428096f17252a
*/
if( pLeft->op!=TK_COLUMN
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|| (pLeft->y.pTab && IsVirtual(pLeft->y.pTab)) /* Might be numeric */
){
int isNum;
double rDummy;
isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
if( isNum<=0 ){
if( iTo==1 && zNew[0]=='-' ){
isNum = +1;
|
| ︙ | ︙ | |||
149774 149775 149776 149777 149778 149779 149780 |
pColl = sqlite3ExprCompareCollSeq(pParse, pX);
if( pColl==0 ) pColl = pParse->db->pDfltColl;
if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
continue;
}
}
if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0
| | > | 150238 150239 150240 150241 150242 150243 150244 150245 150246 150247 150248 150249 150250 150251 150252 150253 |
pColl = sqlite3ExprCompareCollSeq(pParse, pX);
if( pColl==0 ) pColl = pParse->db->pDfltColl;
if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
continue;
}
}
if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0
&& (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0))
&& pX->op==TK_COLUMN
&& pX->iTable==pScan->aiCur[0]
&& pX->iColumn==pScan->aiColumn[0]
){
testcase( pTerm->eOperator & WO_IS );
continue;
}
pScan->pWC = pWC;
|
| ︙ | ︙ | |||
151456 151457 151458 151459 151460 151461 151462 |
sqlite3DbFree(pWInfo->pParse->db, p);
}
}
/*
** Return TRUE if all of the following are true:
**
| | > | 151921 151922 151923 151924 151925 151926 151927 151928 151929 151930 151931 151932 151933 151934 151935 151936 |
sqlite3DbFree(pWInfo->pParse->db, p);
}
}
/*
** Return TRUE if all of the following are true:
**
** (1) X has the same or lower cost, or returns the same or fewer rows,
** than Y.
** (2) X uses fewer WHERE clause terms than Y
** (3) Every WHERE clause term used by X is also used by Y
** (4) X skips at least as many columns as Y
** (5) If X is a covering index, than Y is too
**
** Conditions (2) and (3) mean that X is a "proper subset" of Y.
** If X is a proper subset of Y then Y is a better choice and ought
|
| ︙ | ︙ | |||
151479 151480 151481 151482 151483 151484 151485 151486 |
const WhereLoop *pX, /* First WhereLoop to compare */
const WhereLoop *pY /* Compare against this WhereLoop */
){
int i, j;
if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
return 0; /* X is not a subset of Y */
}
if( pY->nSkip > pX->nSkip ) return 0;
| > < < < < | | | > > | | | > > | | | 151945 151946 151947 151948 151949 151950 151951 151952 151953 151954 151955 151956 151957 151958 151959 151960 151961 151962 151963 151964 151965 151966 151967 151968 151969 151970 151971 151972 151973 151974 151975 151976 151977 151978 151979 151980 151981 151982 151983 151984 151985 151986 151987 151988 151989 151990 151991 151992 151993 151994 151995 151996 151997 151998 151999 152000 152001 152002 152003 152004 152005 152006 152007 152008 152009 152010 152011 |
const WhereLoop *pX, /* First WhereLoop to compare */
const WhereLoop *pY /* Compare against this WhereLoop */
){
int i, j;
if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
return 0; /* X is not a subset of Y */
}
if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0;
if( pY->nSkip > pX->nSkip ) return 0;
for(i=pX->nLTerm-1; i>=0; i--){
if( pX->aLTerm[i]==0 ) continue;
for(j=pY->nLTerm-1; j>=0; j--){
if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
}
if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
}
if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
&& (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
return 0; /* Constraint (5) */
}
return 1; /* All conditions meet */
}
/*
** Try to adjust the cost and number of output rows of WhereLoop pTemplate
** upwards or downwards so that:
**
** (1) pTemplate costs less than any other WhereLoops that are a proper
** subset of pTemplate
**
** (2) pTemplate costs more than any other WhereLoops for which pTemplate
** is a proper subset.
**
** To say "WhereLoop X is a proper subset of Y" means that X uses fewer
** WHERE clause terms than Y and that every WHERE clause term used by X is
** also used by Y.
*/
static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
if( (pTemplate->wsFlags & WHERE_INDEXED)==0 ) return;
for(; p; p=p->pNextLoop){
if( p->iTab!=pTemplate->iTab ) continue;
if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
if( whereLoopCheaperProperSubset(p, pTemplate) ){
/* Adjust pTemplate cost downward so that it is cheaper than its
** subset p. */
WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n",
pTemplate->rRun, pTemplate->nOut,
MIN(p->rRun, pTemplate->rRun),
MIN(p->nOut - 1, pTemplate->nOut)));
pTemplate->rRun = MIN(p->rRun, pTemplate->rRun);
pTemplate->nOut = MIN(p->nOut - 1, pTemplate->nOut);
}else if( whereLoopCheaperProperSubset(pTemplate, p) ){
/* Adjust pTemplate cost upward so that it is costlier than p since
** pTemplate is a proper subset of p */
WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n",
pTemplate->rRun, pTemplate->nOut,
MAX(p->rRun, pTemplate->rRun),
MAX(p->nOut + 1, pTemplate->nOut)));
pTemplate->rRun = MAX(p->rRun, pTemplate->rRun);
pTemplate->nOut = MAX(p->nOut + 1, pTemplate->nOut);
}
}
}
/*
** Search the list of WhereLoops in *ppPrev looking for one that can be
** replaced by pTemplate.
|
| ︙ | ︙ | |||
153527 153528 153529 153530 153531 153532 153533 |
}
}
}
} /* End the loop over all WhereLoops from outer-most down to inner-most */
if( obSat==obDone ) return (i8)nOrderBy;
if( !isOrderDistinct ){
for(i=nOrderBy-1; i>0; i--){
| | | 153994 153995 153996 153997 153998 153999 154000 154001 154002 154003 154004 154005 154006 154007 154008 |
}
}
}
} /* End the loop over all WhereLoops from outer-most down to inner-most */
if( obSat==obDone ) return (i8)nOrderBy;
if( !isOrderDistinct ){
for(i=nOrderBy-1; i>0; i--){
Bitmask m = ALWAYS(i<BMS) ? MASKBIT(i) - 1 : 0;
if( (obSat&m)==m ) return i;
}
return 0;
}
return -1;
}
|
| ︙ | ︙ | |||
154037 154038 154039 154040 154041 154042 154043 154044 154045 154046 154047 154048 154049 154050 154051 154052 154053 154054 154055 154056 | WhereClause *pWC; WhereTerm *pTerm; WhereLoop *pLoop; int iCur; int j; Table *pTab; Index *pIdx; pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; pTab = pItem->pTab; if( IsVirtual(pTab) ) return 0; if( pItem->fg.isIndexedBy ) return 0; iCur = pItem->iCursor; pWC = &pWInfo->sWC; pLoop = pBuilder->pNew; pLoop->wsFlags = 0; pLoop->nSkip = 0; | > | > | > | 154504 154505 154506 154507 154508 154509 154510 154511 154512 154513 154514 154515 154516 154517 154518 154519 154520 154521 154522 154523 154524 154525 154526 154527 154528 154529 154530 154531 154532 154533 154534 154535 154536 154537 154538 154539 154540 154541 154542 154543 154544 154545 154546 154547 154548 154549 154550 154551 154552 154553 |
WhereClause *pWC;
WhereTerm *pTerm;
WhereLoop *pLoop;
int iCur;
int j;
Table *pTab;
Index *pIdx;
WhereScan scan;
pWInfo = pBuilder->pWInfo;
if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0;
assert( pWInfo->pTabList->nSrc>=1 );
pItem = pWInfo->pTabList->a;
pTab = pItem->pTab;
if( IsVirtual(pTab) ) return 0;
if( pItem->fg.isIndexedBy ) return 0;
iCur = pItem->iCursor;
pWC = &pWInfo->sWC;
pLoop = pBuilder->pNew;
pLoop->wsFlags = 0;
pLoop->nSkip = 0;
pTerm = whereScanInit(&scan, pWC, iCur, -1, WO_EQ|WO_IS, 0);
while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan);
if( pTerm ){
testcase( pTerm->eOperator & WO_IS );
pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
pLoop->aLTerm[0] = pTerm;
pLoop->nLTerm = 1;
pLoop->u.btree.nEq = 1;
/* TUNING: Cost of a rowid lookup is 10 */
pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */
}else{
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int opMask;
assert( pLoop->aLTermSpace==pLoop->aLTerm );
if( !IsUniqueIndex(pIdx)
|| pIdx->pPartIdxWhere!=0
|| pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
) continue;
opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ;
for(j=0; j<pIdx->nKeyCol; j++){
pTerm = whereScanInit(&scan, pWC, iCur, j, opMask, pIdx);
while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan);
if( pTerm==0 ) break;
testcase( pTerm->eOperator & WO_IS );
pLoop->aLTerm[j] = pTerm;
}
if( j!=pIdx->nKeyCol ) continue;
pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED;
if( pIdx->isCovering || (pItem->colUsed & pIdx->colNotIdxed)==0 ){
|
| ︙ | ︙ | |||
154098 154099 154100 154101 154102 154103 154104 154105 154106 154107 154108 154109 154110 154111 154112 154113 |
pLoop->maskSelf = 1; /* sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); */
pWInfo->a[0].iTabCur = iCur;
pWInfo->nRowOut = 1;
if( pWInfo->pOrderBy ) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr;
if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
#ifdef SQLITE_DEBUG
pLoop->cId = '0';
#endif
return 1;
}
return 0;
}
/*
| > > > > > > | 154568 154569 154570 154571 154572 154573 154574 154575 154576 154577 154578 154579 154580 154581 154582 154583 154584 154585 154586 154587 154588 154589 |
pLoop->maskSelf = 1; /* sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); */
pWInfo->a[0].iTabCur = iCur;
pWInfo->nRowOut = 1;
if( pWInfo->pOrderBy ) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr;
if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
if( scan.iEquiv>1 ) pLoop->wsFlags |= WHERE_TRANSCONS;
#ifdef SQLITE_DEBUG
pLoop->cId = '0';
#endif
#ifdef WHERETRACE_ENABLED
if( sqlite3WhereTrace ){
sqlite3DebugPrintf("whereShortCut() used to compute solution\n");
}
#endif
return 1;
}
return 0;
}
/*
|
| ︙ | ︙ | |||
156201 156202 156203 156204 156205 156206 156207 156208 156209 156210 156211 156212 156213 156214 |
pSub = sqlite3SelectNew(
pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
);
SELECTTRACE(1,pParse,pSub,
("New window-function subquery in FROM clause of (%u/%p)\n",
p->selId, p));
p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
if( p->pSrc ){
Table *pTab2;
p->pSrc->a[0].pSelect = pSub;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
pSub->selFlags |= (selFlags & SF_Aggregate);
| > > > | 156677 156678 156679 156680 156681 156682 156683 156684 156685 156686 156687 156688 156689 156690 156691 156692 156693 |
pSub = sqlite3SelectNew(
pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
);
SELECTTRACE(1,pParse,pSub,
("New window-function subquery in FROM clause of (%u/%p)\n",
p->selId, p));
p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside
** of sqlite3DbMallocRawNN() called from
** sqlite3SrcListAppend() */
if( p->pSrc ){
Table *pTab2;
p->pSrc->a[0].pSelect = pSub;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
pSub->selFlags |= (selFlags & SF_Aggregate);
|
| ︙ | ︙ | |||
156477 156478 156479 156480 156481 156482 156483 | } /* ** Return 0 if the two window objects are identical, 1 if they are ** different, or 2 if it cannot be determined if the objects are identical ** or not. Identical window objects can be processed in a single scan. */ | | > > > > > | 156956 156957 156958 156959 156960 156961 156962 156963 156964 156965 156966 156967 156968 156969 156970 156971 156972 156973 156974 156975 |
}
/*
** Return 0 if the two window objects are identical, 1 if they are
** different, or 2 if it cannot be determined if the objects are identical
** or not. Identical window objects can be processed in a single scan.
*/
SQLITE_PRIVATE int sqlite3WindowCompare(
const Parse *pParse,
const Window *p1,
const Window *p2,
int bFilter
){
int res;
if( NEVER(p1==0) || NEVER(p2==0) ) return 1;
if( p1->eFrmType!=p2->eFrmType ) return 1;
if( p1->eStart!=p2->eStart ) return 1;
if( p1->eEnd!=p2->eEnd ) return 1;
if( p1->eExclude!=p2->eExclude ) return 1;
if( sqlite3ExprCompare(pParse, p1->pStart, p2->pStart, -1) ) return 1;
|
| ︙ | ︙ | |||
158472 158473 158474 158475 158476 158477 158478 | #define TK_IF 18 #define TK_NOT 19 #define TK_EXISTS 20 #define TK_TEMP 21 #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 | < | > | 158956 158957 158958 158959 158960 158961 158962 158963 158964 158965 158966 158967 158968 158969 158970 158971 | #define TK_IF 18 #define TK_NOT 19 #define TK_EXISTS 20 #define TK_TEMP 21 #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 #define TK_COMMA 25 #define TK_WITHOUT 26 #define TK_ABORT 27 #define TK_ACTION 28 #define TK_AFTER 29 #define TK_ANALYZE 30 #define TK_ASC 31 #define TK_ATTACH 32 #define TK_BEFORE 33 |
| ︙ | ︙ | |||
158690 158691 158692 158693 158694 158695 158696 | ** YY_MAX_REDUCE Maximum value for reduce actions */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int | | | < | | | | < | | | | > | | | > | > | | | | | | | | | | | | | 159174 159175 159176 159177 159178 159179 159180 159181 159182 159183 159184 159185 159186 159187 159188 159189 159190 159191 159192 159193 159194 159195 159196 159197 159198 159199 159200 159201 159202 159203 159204 159205 159206 159207 159208 159209 159210 159211 159212 159213 159214 159215 159216 159217 159218 159219 159220 159221 159222 159223 159224 159225 159226 159227 159228 159229 159230 159231 159232 159233 159234 159235 159236 159237 159238 |
** YY_MAX_REDUCE Maximum value for reduce actions
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
#define YYNOCODE 318
#define YYACTIONTYPE unsigned short int
#define YYWILDCARD 101
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
With* yy43;
u32 yy51;
int yy64;
struct FrameBound yy81;
struct {int value; int mask;} yy83;
TriggerStep* yy95;
Upsert* yy138;
IdList* yy240;
Cte* yy255;
Select* yy303;
Window* yy375;
u8 yy534;
ExprList* yy562;
struct TrigEvent yy570;
const char* yy600;
SrcList* yy607;
Expr* yy626;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
#endif
#define sqlite3ParserARG_SDECL
#define sqlite3ParserARG_PDECL
#define sqlite3ParserARG_PARAM
#define sqlite3ParserARG_FETCH
#define sqlite3ParserARG_STORE
#define sqlite3ParserCTX_SDECL Parse *pParse;
#define sqlite3ParserCTX_PDECL ,Parse *pParse
#define sqlite3ParserCTX_PARAM ,pParse
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
#define YYNSTATE 572
#define YYNRULE 401
#define YYNRULE_WITH_ACTION 339
#define YYNTOKEN 184
#define YY_MAX_SHIFT 571
#define YY_MIN_SHIFTREDUCE 829
#define YY_MAX_SHIFTREDUCE 1229
#define YY_ERROR_ACTION 1230
#define YY_ACCEPT_ACTION 1231
#define YY_NO_ACTION 1232
#define YY_MIN_REDUCE 1233
#define YY_MAX_REDUCE 1633
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
**
** Applications can choose to define yytestcase() in the %include section
|
| ︙ | ︙ | |||
158806 158807 158808 158809 158810 158811 158812 | ** yy_shift_ofst[] For each state, the offset into yy_action for ** shifting terminals. ** yy_reduce_ofst[] For each state, the offset into yy_action for ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 159291 159292 159293 159294 159295 159296 159297 159298 159299 159300 159301 159302 159303 159304 159305 159306 159307 159308 159309 159310 159311 159312 159313 159314 159315 159316 159317 159318 159319 159320 159321 159322 159323 159324 159325 159326 159327 159328 159329 159330 159331 159332 159333 159334 159335 159336 159337 159338 159339 159340 159341 159342 159343 159344 159345 159346 159347 159348 159349 159350 159351 159352 159353 159354 159355 159356 159357 159358 159359 159360 159361 159362 159363 159364 159365 159366 159367 159368 159369 159370 159371 159372 159373 159374 159375 159376 159377 159378 159379 159380 159381 159382 159383 159384 159385 159386 159387 159388 159389 159390 159391 159392 159393 159394 159395 159396 159397 159398 159399 159400 159401 159402 159403 159404 159405 159406 159407 159408 159409 159410 159411 159412 159413 159414 159415 159416 159417 159418 159419 159420 159421 159422 159423 159424 159425 159426 159427 159428 159429 159430 159431 159432 159433 159434 159435 159436 159437 159438 159439 159440 159441 159442 159443 159444 159445 159446 159447 159448 159449 159450 159451 159452 159453 159454 159455 159456 159457 159458 159459 159460 159461 159462 159463 159464 159465 159466 159467 159468 159469 159470 159471 159472 159473 159474 159475 159476 159477 159478 159479 159480 159481 159482 159483 159484 159485 159486 159487 159488 159489 159490 159491 159492 159493 159494 159495 159496 159497 159498 159499 159500 159501 159502 159503 159504 159505 159506 159507 159508 159509 159510 159511 159512 159513 159514 159515 159516 159517 159518 159519 159520 159521 159522 159523 159524 159525 159526 159527 159528 159529 159530 159531 159532 159533 159534 159535 159536 159537 159538 159539 159540 159541 159542 159543 159544 159545 159546 159547 159548 159549 159550 159551 159552 159553 159554 159555 159556 159557 159558 159559 159560 159561 159562 159563 159564 159565 159566 159567 159568 159569 159570 159571 159572 159573 159574 159575 159576 159577 159578 159579 159580 159581 159582 159583 159584 159585 159586 159587 159588 159589 159590 159591 159592 159593 159594 159595 159596 159597 159598 159599 159600 159601 159602 159603 159604 159605 159606 159607 159608 159609 159610 159611 159612 159613 159614 159615 159616 159617 159618 159619 159620 159621 159622 159623 159624 159625 159626 159627 159628 159629 159630 159631 159632 159633 159634 159635 159636 159637 159638 159639 159640 159641 159642 159643 159644 159645 159646 159647 159648 159649 159650 159651 159652 159653 159654 159655 159656 159657 159658 159659 159660 159661 159662 159663 159664 159665 159666 159667 159668 159669 159670 159671 159672 159673 159674 159675 159676 159677 159678 159679 159680 159681 159682 159683 159684 159685 159686 159687 159688 159689 159690 159691 159692 159693 159694 159695 159696 159697 159698 159699 159700 159701 159702 159703 159704 159705 159706 159707 159708 159709 159710 159711 159712 159713 159714 159715 159716 159717 159718 159719 159720 159721 159722 159723 159724 159725 159726 159727 159728 159729 159730 159731 159732 159733 159734 159735 159736 159737 159738 159739 159740 159741 159742 159743 159744 159745 159746 159747 159748 159749 159750 159751 159752 159753 159754 159755 159756 159757 159758 159759 159760 159761 159762 159763 159764 159765 159766 159767 159768 159769 159770 159771 159772 159773 159774 159775 159776 159777 159778 159779 159780 159781 159782 159783 159784 159785 159786 159787 159788 159789 159790 159791 159792 159793 159794 159795 159796 159797 159798 159799 159800 159801 159802 159803 159804 159805 159806 159807 159808 159809 159810 159811 159812 159813 159814 159815 159816 159817 159818 159819 159820 159821 159822 159823 159824 159825 159826 159827 159828 159829 159830 159831 159832 159833 159834 159835 159836 159837 159838 159839 159840 159841 159842 159843 159844 159845 159846 159847 159848 159849 159850 159851 159852 159853 159854 159855 159856 159857 159858 159859 159860 159861 159862 159863 159864 159865 159866 159867 159868 159869 159870 159871 159872 159873 159874 159875 159876 159877 159878 159879 159880 159881 159882 159883 159884 159885 159886 159887 159888 159889 159890 159891 159892 159893 159894 159895 159896 159897 159898 159899 159900 159901 159902 159903 159904 |
** yy_shift_ofst[] For each state, the offset into yy_action for
** shifting terminals.
** yy_reduce_ofst[] For each state, the offset into yy_action for
** shifting non-terminals after a reduce.
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (2037)
static const YYACTIONTYPE yy_action[] = {
/* 0 */ 564, 115, 112, 220, 169, 199, 115, 112, 220, 564,
/* 10 */ 375, 1266, 564, 376, 564, 270, 1309, 1309, 406, 407,
/* 20 */ 1084, 199, 1513, 41, 41, 515, 489, 521, 558, 558,
/* 30 */ 558, 965, 41, 41, 395, 41, 41, 51, 51, 966,
/* 40 */ 296, 1269, 296, 122, 123, 113, 1207, 1207, 1041, 1044,
/* 50 */ 1034, 1034, 120, 120, 121, 121, 121, 121, 564, 407,
/* 60 */ 275, 275, 275, 275, 1268, 115, 112, 220, 115, 112,
/* 70 */ 220, 1512, 846, 561, 516, 561, 115, 112, 220, 250,
/* 80 */ 217, 71, 71, 122, 123, 113, 1207, 1207, 1041, 1044,
/* 90 */ 1034, 1034, 120, 120, 121, 121, 121, 121, 440, 440,
/* 100 */ 440, 1149, 119, 119, 119, 119, 118, 118, 117, 117,
/* 110 */ 117, 116, 442, 1183, 1149, 116, 442, 1149, 546, 513,
/* 120 */ 1548, 1554, 374, 442, 6, 1183, 1154, 522, 1154, 407,
/* 130 */ 1556, 461, 373, 1554, 535, 99, 463, 332, 121, 121,
/* 140 */ 121, 121, 119, 119, 119, 119, 118, 118, 117, 117,
/* 150 */ 117, 116, 442, 122, 123, 113, 1207, 1207, 1041, 1044,
/* 160 */ 1034, 1034, 120, 120, 121, 121, 121, 121, 1257, 1183,
/* 170 */ 1184, 1185, 243, 1064, 564, 502, 499, 498, 567, 124,
/* 180 */ 567, 1183, 1184, 1185, 474, 497, 119, 119, 119, 119,
/* 190 */ 118, 118, 117, 117, 117, 116, 442, 70, 70, 407,
/* 200 */ 121, 121, 121, 121, 114, 117, 117, 117, 116, 442,
/* 210 */ 1409, 1469, 119, 119, 119, 119, 118, 118, 117, 117,
/* 220 */ 117, 116, 442, 122, 123, 113, 1207, 1207, 1041, 1044,
/* 230 */ 1034, 1034, 120, 120, 121, 121, 121, 121, 407, 1031,
/* 240 */ 1031, 1042, 1045, 81, 382, 541, 378, 80, 119, 119,
/* 250 */ 119, 119, 118, 118, 117, 117, 117, 116, 442, 381,
/* 260 */ 463, 332, 122, 123, 113, 1207, 1207, 1041, 1044, 1034,
/* 270 */ 1034, 120, 120, 121, 121, 121, 121, 262, 215, 512,
/* 280 */ 1424, 422, 119, 119, 119, 119, 118, 118, 117, 117,
/* 290 */ 117, 116, 442, 1231, 1, 1, 571, 2, 1235, 1573,
/* 300 */ 571, 2, 1235, 307, 1149, 141, 1600, 307, 407, 141,
/* 310 */ 1183, 361, 1317, 1035, 866, 531, 1317, 1149, 359, 1567,
/* 320 */ 1149, 119, 119, 119, 119, 118, 118, 117, 117, 117,
/* 330 */ 116, 442, 122, 123, 113, 1207, 1207, 1041, 1044, 1034,
/* 340 */ 1034, 120, 120, 121, 121, 121, 121, 275, 275, 1001,
/* 350 */ 426, 275, 275, 1128, 1627, 1021, 1627, 137, 542, 1541,
/* 360 */ 561, 272, 950, 950, 561, 1423, 1183, 1184, 1185, 1594,
/* 370 */ 866, 1012, 530, 315, 231, 1011, 468, 1276, 231, 119,
/* 380 */ 119, 119, 119, 118, 118, 117, 117, 117, 116, 442,
/* 390 */ 1570, 119, 119, 119, 119, 118, 118, 117, 117, 117,
/* 400 */ 116, 442, 330, 359, 1567, 564, 446, 1011, 1011, 1013,
/* 410 */ 446, 207, 564, 306, 555, 407, 363, 1021, 363, 346,
/* 420 */ 184, 118, 118, 117, 117, 117, 116, 442, 71, 71,
/* 430 */ 439, 438, 1126, 1012, 472, 71, 71, 1011, 205, 122,
/* 440 */ 123, 113, 1207, 1207, 1041, 1044, 1034, 1034, 120, 120,
/* 450 */ 121, 121, 121, 121, 219, 219, 472, 1183, 407, 570,
/* 460 */ 1183, 1235, 503, 1477, 149, 546, 307, 489, 141, 1011,
/* 470 */ 1011, 1013, 546, 140, 545, 1317, 1214, 191, 1214, 950,
/* 480 */ 950, 514, 122, 123, 113, 1207, 1207, 1041, 1044, 1034,
/* 490 */ 1034, 120, 120, 121, 121, 121, 121, 563, 119, 119,
/* 500 */ 119, 119, 118, 118, 117, 117, 117, 116, 442, 283,
/* 510 */ 275, 275, 415, 1183, 1184, 1185, 1183, 1184, 1185, 372,
/* 520 */ 1183, 243, 344, 561, 502, 499, 498, 1539, 407, 1540,
/* 530 */ 1183, 288, 870, 143, 497, 1549, 185, 231, 9, 6,
/* 540 */ 253, 119, 119, 119, 119, 118, 118, 117, 117, 117,
/* 550 */ 116, 442, 122, 123, 113, 1207, 1207, 1041, 1044, 1034,
/* 560 */ 1034, 120, 120, 121, 121, 121, 121, 407, 137, 446,
/* 570 */ 447, 863, 169, 1183, 397, 1204, 1183, 1184, 1185, 931,
/* 580 */ 526, 1001, 98, 339, 564, 342, 1183, 1184, 1185, 306,
/* 590 */ 555, 122, 123, 113, 1207, 1207, 1041, 1044, 1034, 1034,
/* 600 */ 120, 120, 121, 121, 121, 121, 452, 71, 71, 275,
/* 610 */ 275, 119, 119, 119, 119, 118, 118, 117, 117, 117,
/* 620 */ 116, 442, 561, 417, 306, 555, 1183, 1307, 1307, 1183,
/* 630 */ 1184, 1185, 1204, 1149, 330, 458, 318, 407, 363, 470,
/* 640 */ 431, 1167, 32, 541, 527, 350, 1149, 1629, 393, 1149,
/* 650 */ 119, 119, 119, 119, 118, 118, 117, 117, 117, 116,
/* 660 */ 442, 122, 123, 113, 1207, 1207, 1041, 1044, 1034, 1034,
/* 670 */ 120, 120, 121, 121, 121, 121, 407, 199, 472, 1183,
/* 680 */ 1022, 472, 1183, 1184, 1185, 386, 151, 539, 1548, 277,
/* 690 */ 400, 137, 6, 317, 5, 564, 562, 3, 920, 920,
/* 700 */ 122, 123, 113, 1207, 1207, 1041, 1044, 1034, 1034, 120,
/* 710 */ 120, 121, 121, 121, 121, 411, 505, 83, 71, 71,
/* 720 */ 119, 119, 119, 119, 118, 118, 117, 117, 117, 116,
/* 730 */ 442, 1183, 218, 428, 1183, 1183, 1184, 1185, 363, 261,
/* 740 */ 278, 358, 508, 353, 507, 248, 407, 306, 555, 1539,
/* 750 */ 1006, 349, 363, 291, 489, 302, 293, 1542, 281, 119,
/* 760 */ 119, 119, 119, 118, 118, 117, 117, 117, 116, 442,
/* 770 */ 122, 123, 113, 1207, 1207, 1041, 1044, 1034, 1034, 120,
/* 780 */ 120, 121, 121, 121, 121, 407, 148, 1183, 1184, 1185,
/* 790 */ 1183, 1184, 1185, 275, 275, 1304, 1257, 1283, 483, 1476,
/* 800 */ 150, 489, 480, 564, 1187, 1304, 561, 1587, 1255, 122,
/* 810 */ 123, 113, 1207, 1207, 1041, 1044, 1034, 1034, 120, 120,
/* 820 */ 121, 121, 121, 121, 564, 886, 13, 13, 520, 119,
/* 830 */ 119, 119, 119, 118, 118, 117, 117, 117, 116, 442,
/* 840 */ 1183, 420, 417, 564, 269, 269, 1316, 13, 13, 1539,
/* 850 */ 1546, 16, 16, 322, 6, 407, 506, 561, 1089, 1089,
/* 860 */ 486, 1187, 425, 1539, 887, 292, 71, 71, 119, 119,
/* 870 */ 119, 119, 118, 118, 117, 117, 117, 116, 442, 122,
/* 880 */ 123, 113, 1207, 1207, 1041, 1044, 1034, 1034, 120, 120,
/* 890 */ 121, 121, 121, 121, 564, 12, 1183, 1184, 1185, 407,
/* 900 */ 275, 275, 451, 303, 834, 835, 836, 417, 489, 276,
/* 910 */ 276, 1547, 284, 561, 319, 6, 321, 71, 71, 429,
/* 920 */ 451, 450, 561, 952, 101, 113, 1207, 1207, 1041, 1044,
/* 930 */ 1034, 1034, 120, 120, 121, 121, 121, 121, 119, 119,
/* 940 */ 119, 119, 118, 118, 117, 117, 117, 116, 442, 1105,
/* 950 */ 1183, 489, 564, 1312, 437, 455, 478, 564, 246, 245,
/* 960 */ 244, 1409, 1545, 547, 1106, 405, 6, 1544, 196, 1258,
/* 970 */ 413, 6, 105, 462, 103, 71, 71, 286, 564, 1107,
/* 980 */ 13, 13, 119, 119, 119, 119, 118, 118, 117, 117,
/* 990 */ 117, 116, 442, 451, 104, 427, 337, 320, 275, 275,
/* 1000 */ 906, 13, 13, 564, 1482, 1105, 1183, 1184, 1185, 126,
/* 1010 */ 907, 561, 546, 564, 407, 478, 295, 1321, 253, 200,
/* 1020 */ 1106, 548, 1482, 1484, 280, 1409, 55, 55, 1287, 561,
/* 1030 */ 478, 380, 423, 951, 407, 1107, 71, 71, 122, 123,
/* 1040 */ 113, 1207, 1207, 1041, 1044, 1034, 1034, 120, 120, 121,
/* 1050 */ 121, 121, 121, 1204, 407, 287, 552, 309, 122, 123,
/* 1060 */ 113, 1207, 1207, 1041, 1044, 1034, 1034, 120, 120, 121,
/* 1070 */ 121, 121, 121, 441, 1128, 1628, 146, 1628, 122, 111,
/* 1080 */ 113, 1207, 1207, 1041, 1044, 1034, 1034, 120, 120, 121,
/* 1090 */ 121, 121, 121, 404, 403, 1482, 424, 119, 119, 119,
/* 1100 */ 119, 118, 118, 117, 117, 117, 116, 442, 1183, 564,
/* 1110 */ 1204, 544, 1086, 858, 329, 361, 1086, 119, 119, 119,
/* 1120 */ 119, 118, 118, 117, 117, 117, 116, 442, 564, 294,
/* 1130 */ 144, 523, 56, 56, 224, 564, 510, 119, 119, 119,
/* 1140 */ 119, 118, 118, 117, 117, 117, 116, 442, 484, 1409,
/* 1150 */ 537, 15, 15, 1126, 434, 439, 438, 407, 13, 13,
/* 1160 */ 1523, 12, 926, 1211, 1183, 1184, 1185, 925, 1213, 536,
/* 1170 */ 858, 557, 413, 193, 1525, 494, 1212, 448, 1160, 1222,
/* 1180 */ 1183, 564, 123, 113, 1207, 1207, 1041, 1044, 1034, 1034,
/* 1190 */ 120, 120, 121, 121, 121, 121, 1521, 1149, 564, 965,
/* 1200 */ 564, 1214, 247, 1214, 13, 13, 1409, 966, 538, 564,
/* 1210 */ 1149, 108, 556, 1149, 4, 310, 392, 1227, 17, 194,
/* 1220 */ 485, 43, 43, 57, 57, 306, 555, 524, 559, 1160,
/* 1230 */ 464, 564, 44, 44, 392, 1127, 1183, 1184, 1185, 479,
/* 1240 */ 119, 119, 119, 119, 118, 118, 117, 117, 117, 116,
/* 1250 */ 442, 443, 564, 327, 13, 13, 564, 418, 1315, 414,
/* 1260 */ 171, 564, 311, 553, 213, 529, 1253, 564, 517, 543,
/* 1270 */ 412, 108, 556, 137, 4, 58, 58, 435, 314, 59,
/* 1280 */ 59, 274, 217, 549, 60, 60, 349, 476, 559, 1353,
/* 1290 */ 61, 61, 1021, 275, 275, 1228, 213, 564, 106, 106,
/* 1300 */ 8, 275, 275, 275, 275, 107, 561, 443, 566, 565,
/* 1310 */ 564, 443, 1011, 1228, 561, 564, 561, 564, 275, 275,
/* 1320 */ 62, 62, 1352, 553, 247, 456, 564, 98, 110, 306,
/* 1330 */ 555, 561, 564, 45, 45, 405, 1203, 533, 46, 46,
/* 1340 */ 47, 47, 532, 465, 1011, 1011, 1013, 1014, 27, 49,
/* 1350 */ 49, 564, 1021, 405, 469, 50, 50, 564, 106, 106,
/* 1360 */ 305, 564, 84, 204, 405, 107, 564, 443, 566, 565,
/* 1370 */ 405, 564, 1011, 564, 63, 63, 564, 1599, 564, 895,
/* 1380 */ 64, 64, 457, 477, 65, 65, 147, 96, 38, 14,
/* 1390 */ 14, 1528, 412, 564, 66, 66, 128, 128, 926, 67,
/* 1400 */ 67, 52, 52, 925, 1011, 1011, 1013, 1014, 27, 1572,
/* 1410 */ 1171, 445, 208, 1123, 279, 394, 68, 68, 228, 390,
/* 1420 */ 390, 389, 264, 387, 1171, 445, 843, 877, 279, 108,
/* 1430 */ 556, 453, 4, 390, 390, 389, 264, 387, 564, 225,
/* 1440 */ 843, 313, 328, 1003, 98, 252, 559, 544, 471, 312,
/* 1450 */ 252, 564, 208, 225, 564, 313, 473, 30, 252, 279,
/* 1460 */ 466, 69, 69, 312, 390, 390, 389, 264, 387, 443,
/* 1470 */ 333, 843, 98, 564, 53, 53, 323, 157, 157, 227,
/* 1480 */ 495, 553, 249, 289, 225, 564, 313, 162, 31, 1501,
/* 1490 */ 135, 564, 1500, 227, 312, 533, 158, 158, 885, 884,
/* 1500 */ 534, 162, 873, 301, 135, 564, 481, 226, 76, 76,
/* 1510 */ 1021, 347, 1071, 98, 54, 54, 106, 106, 1067, 564,
/* 1520 */ 249, 226, 519, 107, 227, 443, 566, 565, 72, 72,
/* 1530 */ 1011, 334, 162, 564, 230, 135, 108, 556, 959, 4,
/* 1540 */ 252, 408, 129, 129, 564, 1349, 306, 555, 564, 923,
/* 1550 */ 564, 110, 226, 559, 564, 408, 73, 73, 564, 873,
/* 1560 */ 306, 555, 1011, 1011, 1013, 1014, 27, 130, 130, 1071,
/* 1570 */ 449, 131, 131, 127, 127, 357, 443, 156, 156, 892,
/* 1580 */ 893, 155, 155, 338, 449, 356, 408, 564, 553, 968,
/* 1590 */ 969, 306, 555, 1015, 341, 564, 108, 556, 564, 4,
/* 1600 */ 1132, 1286, 533, 564, 856, 343, 145, 532, 345, 1300,
/* 1610 */ 136, 136, 1083, 559, 1083, 449, 564, 1021, 134, 134,
/* 1620 */ 1284, 132, 132, 106, 106, 1285, 133, 133, 564, 352,
/* 1630 */ 107, 564, 443, 566, 565, 1340, 443, 1011, 362, 75,
/* 1640 */ 75, 1082, 564, 1082, 564, 924, 1561, 110, 553, 551,
/* 1650 */ 1015, 77, 77, 1361, 74, 74, 1408, 1336, 1347, 550,
/* 1660 */ 1414, 1265, 1256, 1244, 1243, 42, 42, 48, 48, 1011,
/* 1670 */ 1011, 1013, 1014, 27, 1245, 1580, 490, 1021, 267, 202,
/* 1680 */ 1333, 365, 11, 106, 106, 930, 367, 210, 369, 391,
/* 1690 */ 107, 1395, 443, 566, 565, 223, 1390, 1011, 500, 454,
/* 1700 */ 282, 1400, 285, 108, 556, 214, 4, 325, 1383, 1283,
/* 1710 */ 475, 355, 1473, 1583, 1472, 1399, 371, 1222, 326, 398,
/* 1720 */ 559, 290, 331, 197, 100, 556, 209, 4, 198, 1011,
/* 1730 */ 1011, 1013, 1014, 27, 385, 256, 1520, 1518, 554, 1219,
/* 1740 */ 416, 559, 83, 443, 173, 206, 182, 221, 459, 167,
/* 1750 */ 177, 460, 175, 493, 233, 553, 79, 178, 1396, 179,
/* 1760 */ 35, 180, 96, 1402, 443, 396, 36, 467, 1478, 1401,
/* 1770 */ 482, 237, 1404, 399, 82, 186, 553, 1467, 89, 488,
/* 1780 */ 190, 268, 239, 491, 1021, 340, 240, 401, 1246, 1489,
/* 1790 */ 106, 106, 336, 509, 1294, 241, 1303, 107, 430, 443,
/* 1800 */ 566, 565, 1302, 91, 1011, 1021, 1598, 1301, 1273, 215,
/* 1810 */ 1597, 106, 106, 402, 877, 432, 354, 1272, 107, 1271,
/* 1820 */ 443, 566, 565, 1596, 1566, 1011, 1293, 433, 518, 299,
/* 1830 */ 300, 360, 95, 525, 1344, 364, 1011, 1011, 1013, 1014,
/* 1840 */ 27, 254, 255, 1552, 436, 1551, 125, 544, 10, 379,
/* 1850 */ 1326, 1453, 102, 97, 1345, 528, 304, 1011, 1011, 1013,
/* 1860 */ 1014, 27, 366, 377, 1343, 1342, 368, 370, 1325, 384,
/* 1870 */ 201, 383, 34, 1368, 1367, 568, 1177, 266, 263, 265,
/* 1880 */ 1505, 159, 569, 1241, 1236, 1506, 160, 142, 1504, 1503,
/* 1890 */ 297, 211, 830, 161, 212, 78, 444, 203, 308, 222,
/* 1900 */ 1081, 139, 1079, 316, 174, 163, 1203, 229, 176, 909,
/* 1910 */ 324, 232, 1095, 181, 409, 410, 172, 164, 165, 419,
/* 1920 */ 183, 85, 86, 421, 166, 87, 88, 1098, 1094, 234,
/* 1930 */ 235, 152, 18, 236, 335, 1087, 1216, 252, 187, 487,
/* 1940 */ 238, 188, 37, 845, 492, 356, 242, 496, 351, 501,
/* 1950 */ 189, 90, 19, 504, 348, 20, 875, 92, 298, 168,
/* 1960 */ 888, 153, 93, 511, 94, 1165, 154, 1047, 1134, 39,
/* 1970 */ 216, 1133, 271, 273, 958, 192, 953, 110, 1151, 1155,
/* 1980 */ 251, 7, 21, 1159, 1139, 22, 1153, 33, 23, 24,
/* 1990 */ 25, 540, 1158, 195, 98, 1062, 26, 1048, 1046, 1050,
/* 2000 */ 1104, 1051, 1103, 257, 258, 28, 40, 1173, 1016, 857,
/* 2010 */ 109, 29, 560, 388, 138, 1172, 259, 170, 260, 1232,
/* 2020 */ 1232, 919, 1232, 1232, 1232, 1232, 1232, 1232, 1232, 1232,
/* 2030 */ 1232, 1232, 1589, 1232, 1232, 1232, 1588,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 192, 273, 274, 275, 192, 192, 273, 274, 275, 192,
/* 10 */ 218, 215, 192, 218, 192, 212, 234, 235, 205, 19,
/* 20 */ 11, 192, 294, 215, 216, 203, 192, 203, 209, 210,
/* 30 */ 211, 31, 215, 216, 205, 215, 216, 215, 216, 39,
/* 40 */ 227, 215, 229, 43, 44, 45, 46, 47, 48, 49,
/* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 192, 19,
/* 60 */ 238, 239, 238, 239, 215, 273, 274, 275, 273, 274,
/* 70 */ 275, 237, 21, 251, 252, 251, 273, 274, 275, 255,
/* 80 */ 256, 215, 216, 43, 44, 45, 46, 47, 48, 49,
/* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 209, 210,
/* 100 */ 211, 76, 102, 103, 104, 105, 106, 107, 108, 109,
/* 110 */ 110, 111, 112, 59, 89, 111, 112, 92, 252, 307,
/* 120 */ 308, 313, 314, 112, 312, 59, 86, 261, 88, 19,
/* 130 */ 313, 80, 315, 313, 314, 25, 127, 128, 54, 55,
/* 140 */ 56, 57, 102, 103, 104, 105, 106, 107, 108, 109,
/* 150 */ 110, 111, 112, 43, 44, 45, 46, 47, 48, 49,
/* 160 */ 50, 51, 52, 53, 54, 55, 56, 57, 192, 115,
/* 170 */ 116, 117, 118, 122, 192, 121, 122, 123, 202, 69,
/* 180 */ 204, 115, 116, 117, 192, 131, 102, 103, 104, 105,
/* 190 */ 106, 107, 108, 109, 110, 111, 112, 215, 216, 19,
/* 200 */ 54, 55, 56, 57, 58, 108, 109, 110, 111, 112,
/* 210 */ 192, 160, 102, 103, 104, 105, 106, 107, 108, 109,
/* 220 */ 110, 111, 112, 43, 44, 45, 46, 47, 48, 49,
/* 230 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 46,
/* 240 */ 47, 48, 49, 24, 248, 192, 250, 67, 102, 103,
/* 250 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 277,
/* 260 */ 127, 128, 43, 44, 45, 46, 47, 48, 49, 50,
/* 270 */ 51, 52, 53, 54, 55, 56, 57, 26, 164, 165,
/* 280 */ 272, 263, 102, 103, 104, 105, 106, 107, 108, 109,
/* 290 */ 110, 111, 112, 184, 185, 186, 187, 188, 189, 186,
/* 300 */ 187, 188, 189, 194, 76, 196, 229, 194, 19, 196,
/* 310 */ 59, 192, 203, 120, 59, 87, 203, 89, 310, 311,
/* 320 */ 92, 102, 103, 104, 105, 106, 107, 108, 109, 110,
/* 330 */ 111, 112, 43, 44, 45, 46, 47, 48, 49, 50,
/* 340 */ 51, 52, 53, 54, 55, 56, 57, 238, 239, 73,
/* 350 */ 231, 238, 239, 22, 23, 100, 25, 81, 305, 306,
/* 360 */ 251, 23, 25, 25, 251, 272, 115, 116, 117, 214,
/* 370 */ 115, 116, 144, 192, 265, 120, 114, 222, 265, 102,
/* 380 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
/* 390 */ 192, 102, 103, 104, 105, 106, 107, 108, 109, 110,
/* 400 */ 111, 112, 126, 310, 311, 192, 297, 152, 153, 154,
/* 410 */ 297, 149, 192, 137, 138, 19, 192, 100, 192, 23,
/* 420 */ 22, 106, 107, 108, 109, 110, 111, 112, 215, 216,
/* 430 */ 106, 107, 101, 116, 192, 215, 216, 120, 149, 43,
/* 440 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
/* 450 */ 54, 55, 56, 57, 117, 117, 192, 59, 19, 187,
/* 460 */ 59, 189, 23, 282, 240, 252, 194, 192, 196, 152,
/* 470 */ 153, 154, 252, 72, 261, 203, 152, 25, 154, 142,
/* 480 */ 142, 261, 43, 44, 45, 46, 47, 48, 49, 50,
/* 490 */ 51, 52, 53, 54, 55, 56, 57, 192, 102, 103,
/* 500 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 267,
/* 510 */ 238, 239, 237, 115, 116, 117, 115, 116, 117, 192,
/* 520 */ 59, 118, 16, 251, 121, 122, 123, 303, 19, 303,
/* 530 */ 59, 267, 23, 72, 131, 308, 22, 265, 22, 312,
/* 540 */ 24, 102, 103, 104, 105, 106, 107, 108, 109, 110,
/* 550 */ 111, 112, 43, 44, 45, 46, 47, 48, 49, 50,
/* 560 */ 51, 52, 53, 54, 55, 56, 57, 19, 81, 297,
/* 570 */ 295, 23, 192, 59, 203, 59, 115, 116, 117, 108,
/* 580 */ 192, 73, 25, 77, 192, 79, 115, 116, 117, 137,
/* 590 */ 138, 43, 44, 45, 46, 47, 48, 49, 50, 51,
/* 600 */ 52, 53, 54, 55, 56, 57, 119, 215, 216, 238,
/* 610 */ 239, 102, 103, 104, 105, 106, 107, 108, 109, 110,
/* 620 */ 111, 112, 251, 192, 137, 138, 59, 234, 235, 115,
/* 630 */ 116, 117, 116, 76, 126, 127, 128, 19, 192, 268,
/* 640 */ 19, 23, 22, 192, 252, 24, 89, 300, 301, 92,
/* 650 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
/* 660 */ 112, 43, 44, 45, 46, 47, 48, 49, 50, 51,
/* 670 */ 52, 53, 54, 55, 56, 57, 19, 192, 192, 59,
/* 680 */ 23, 192, 115, 116, 117, 200, 240, 307, 308, 22,
/* 690 */ 205, 81, 312, 262, 22, 192, 133, 22, 135, 136,
/* 700 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
/* 710 */ 53, 54, 55, 56, 57, 197, 95, 150, 215, 216,
/* 720 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
/* 730 */ 112, 59, 192, 112, 59, 115, 116, 117, 192, 118,
/* 740 */ 119, 120, 121, 122, 123, 124, 19, 137, 138, 303,
/* 750 */ 23, 130, 192, 267, 192, 252, 267, 306, 203, 102,
/* 760 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
/* 770 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
/* 780 */ 53, 54, 55, 56, 57, 19, 240, 115, 116, 117,
/* 790 */ 115, 116, 117, 238, 239, 222, 192, 224, 280, 237,
/* 800 */ 240, 192, 284, 192, 59, 232, 251, 140, 204, 43,
/* 810 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
/* 820 */ 54, 55, 56, 57, 192, 35, 215, 216, 192, 102,
/* 830 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
/* 840 */ 59, 230, 192, 192, 238, 239, 237, 215, 216, 303,
/* 850 */ 308, 215, 216, 16, 312, 19, 66, 251, 126, 127,
/* 860 */ 128, 116, 230, 303, 74, 203, 215, 216, 102, 103,
/* 870 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 43,
/* 880 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
/* 890 */ 54, 55, 56, 57, 192, 212, 115, 116, 117, 19,
/* 900 */ 238, 239, 192, 252, 7, 8, 9, 192, 192, 238,
/* 910 */ 239, 308, 262, 251, 77, 312, 79, 215, 216, 129,
/* 920 */ 210, 211, 251, 142, 158, 45, 46, 47, 48, 49,
/* 930 */ 50, 51, 52, 53, 54, 55, 56, 57, 102, 103,
/* 940 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 12,
/* 950 */ 59, 192, 192, 237, 252, 243, 192, 192, 126, 127,
/* 960 */ 128, 192, 308, 203, 27, 253, 312, 308, 285, 207,
/* 970 */ 208, 312, 157, 290, 159, 215, 216, 262, 192, 42,
/* 980 */ 215, 216, 102, 103, 104, 105, 106, 107, 108, 109,
/* 990 */ 110, 111, 112, 283, 158, 230, 237, 160, 238, 239,
/* 1000 */ 63, 215, 216, 192, 192, 12, 115, 116, 117, 22,
/* 1010 */ 73, 251, 252, 192, 19, 192, 230, 239, 24, 24,
/* 1020 */ 27, 261, 210, 211, 99, 192, 215, 216, 225, 251,
/* 1030 */ 192, 192, 263, 142, 19, 42, 215, 216, 43, 44,
/* 1040 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
/* 1050 */ 55, 56, 57, 59, 19, 291, 63, 132, 43, 44,
/* 1060 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
/* 1070 */ 55, 56, 57, 252, 22, 23, 22, 25, 43, 44,
/* 1080 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
/* 1090 */ 55, 56, 57, 106, 107, 283, 263, 102, 103, 104,
/* 1100 */ 105, 106, 107, 108, 109, 110, 111, 112, 59, 192,
/* 1110 */ 116, 144, 29, 59, 291, 192, 33, 102, 103, 104,
/* 1120 */ 105, 106, 107, 108, 109, 110, 111, 112, 192, 291,
/* 1130 */ 163, 19, 215, 216, 15, 192, 108, 102, 103, 104,
/* 1140 */ 105, 106, 107, 108, 109, 110, 111, 112, 65, 192,
/* 1150 */ 66, 215, 216, 101, 231, 106, 107, 19, 215, 216,
/* 1160 */ 192, 212, 134, 114, 115, 116, 117, 139, 119, 85,
/* 1170 */ 116, 207, 208, 230, 192, 19, 127, 192, 94, 60,
/* 1180 */ 59, 192, 44, 45, 46, 47, 48, 49, 50, 51,
/* 1190 */ 52, 53, 54, 55, 56, 57, 192, 76, 192, 31,
/* 1200 */ 192, 152, 46, 154, 215, 216, 192, 39, 87, 192,
/* 1210 */ 89, 19, 20, 92, 22, 192, 22, 23, 22, 230,
/* 1220 */ 263, 215, 216, 215, 216, 137, 138, 115, 36, 145,
/* 1230 */ 128, 192, 215, 216, 22, 23, 115, 116, 117, 290,
/* 1240 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
/* 1250 */ 112, 59, 192, 151, 215, 216, 192, 61, 203, 298,
/* 1260 */ 299, 192, 192, 71, 25, 144, 203, 192, 203, 230,
/* 1270 */ 114, 19, 20, 81, 22, 215, 216, 263, 192, 215,
/* 1280 */ 216, 255, 256, 203, 215, 216, 130, 19, 36, 192,
/* 1290 */ 215, 216, 100, 238, 239, 101, 25, 192, 106, 107,
/* 1300 */ 48, 238, 239, 238, 239, 113, 251, 115, 116, 117,
/* 1310 */ 192, 59, 120, 101, 251, 192, 251, 192, 238, 239,
/* 1320 */ 215, 216, 192, 71, 46, 243, 192, 25, 25, 137,
/* 1330 */ 138, 251, 192, 215, 216, 253, 25, 85, 215, 216,
/* 1340 */ 215, 216, 90, 243, 152, 153, 154, 155, 156, 215,
/* 1350 */ 216, 192, 100, 253, 243, 215, 216, 192, 106, 107,
/* 1360 */ 243, 192, 148, 149, 253, 113, 192, 115, 116, 117,
/* 1370 */ 253, 192, 120, 192, 215, 216, 192, 23, 192, 25,
/* 1380 */ 215, 216, 192, 115, 215, 216, 22, 148, 24, 215,
/* 1390 */ 216, 192, 114, 192, 215, 216, 215, 216, 134, 215,
/* 1400 */ 216, 215, 216, 139, 152, 153, 154, 155, 156, 0,
/* 1410 */ 1, 2, 141, 23, 5, 25, 215, 216, 24, 10,
/* 1420 */ 11, 12, 13, 14, 1, 2, 17, 125, 5, 19,
/* 1430 */ 20, 268, 22, 10, 11, 12, 13, 14, 192, 30,
/* 1440 */ 17, 32, 23, 23, 25, 25, 36, 144, 23, 40,
/* 1450 */ 25, 192, 141, 30, 192, 32, 23, 22, 25, 5,
/* 1460 */ 128, 215, 216, 40, 10, 11, 12, 13, 14, 59,
/* 1470 */ 23, 17, 25, 192, 215, 216, 192, 215, 216, 70,
/* 1480 */ 23, 71, 25, 151, 30, 192, 32, 78, 53, 192,
/* 1490 */ 81, 192, 192, 70, 40, 85, 215, 216, 119, 120,
/* 1500 */ 90, 78, 59, 254, 81, 192, 192, 98, 215, 216,
/* 1510 */ 100, 23, 59, 25, 215, 216, 106, 107, 23, 192,
/* 1520 */ 25, 98, 19, 113, 70, 115, 116, 117, 215, 216,
/* 1530 */ 120, 192, 78, 192, 140, 81, 19, 20, 23, 22,
/* 1540 */ 25, 132, 215, 216, 192, 192, 137, 138, 192, 23,
/* 1550 */ 192, 25, 98, 36, 192, 132, 215, 216, 192, 116,
/* 1560 */ 137, 138, 152, 153, 154, 155, 156, 215, 216, 116,
/* 1570 */ 161, 215, 216, 215, 216, 120, 59, 215, 216, 7,
/* 1580 */ 8, 215, 216, 192, 161, 130, 132, 192, 71, 83,
/* 1590 */ 84, 137, 138, 59, 192, 192, 19, 20, 192, 22,
/* 1600 */ 97, 225, 85, 192, 23, 192, 25, 90, 192, 192,
/* 1610 */ 215, 216, 152, 36, 154, 161, 192, 100, 215, 216,
/* 1620 */ 192, 215, 216, 106, 107, 225, 215, 216, 192, 192,
/* 1630 */ 113, 192, 115, 116, 117, 257, 59, 120, 192, 215,
/* 1640 */ 216, 152, 192, 154, 192, 23, 317, 25, 71, 235,
/* 1650 */ 116, 215, 216, 192, 215, 216, 192, 192, 192, 192,
/* 1660 */ 192, 192, 192, 192, 192, 215, 216, 215, 216, 152,
/* 1670 */ 153, 154, 155, 156, 192, 192, 287, 100, 286, 241,
/* 1680 */ 254, 254, 242, 106, 107, 108, 254, 213, 254, 190,
/* 1690 */ 113, 270, 115, 116, 117, 296, 266, 120, 219, 258,
/* 1700 */ 244, 270, 258, 19, 20, 228, 22, 292, 266, 224,
/* 1710 */ 292, 218, 218, 195, 218, 270, 258, 60, 245, 270,
/* 1720 */ 36, 245, 244, 248, 19, 20, 242, 22, 248, 152,
/* 1730 */ 153, 154, 155, 156, 244, 140, 199, 199, 279, 38,
/* 1740 */ 199, 36, 150, 59, 296, 149, 22, 296, 18, 43,
/* 1750 */ 236, 199, 233, 18, 198, 71, 293, 236, 271, 236,
/* 1760 */ 269, 236, 148, 271, 59, 245, 269, 245, 282, 271,
/* 1770 */ 199, 198, 233, 245, 293, 233, 71, 245, 157, 62,
/* 1780 */ 22, 199, 198, 220, 100, 199, 198, 220, 199, 289,
/* 1790 */ 106, 107, 288, 114, 226, 198, 217, 113, 64, 115,
/* 1800 */ 116, 117, 217, 22, 120, 100, 223, 217, 217, 164,
/* 1810 */ 223, 106, 107, 220, 125, 24, 217, 219, 113, 217,
/* 1820 */ 115, 116, 117, 217, 311, 120, 226, 112, 304, 281,
/* 1830 */ 281, 220, 114, 143, 260, 259, 152, 153, 154, 155,
/* 1840 */ 156, 199, 91, 316, 82, 316, 147, 144, 22, 199,
/* 1850 */ 249, 276, 157, 146, 260, 145, 278, 152, 153, 154,
/* 1860 */ 155, 156, 259, 248, 260, 260, 259, 259, 249, 245,
/* 1870 */ 247, 246, 25, 264, 264, 201, 13, 6, 193, 193,
/* 1880 */ 212, 206, 191, 191, 191, 212, 206, 221, 212, 212,
/* 1890 */ 221, 213, 4, 206, 213, 212, 3, 22, 162, 15,
/* 1900 */ 23, 16, 23, 138, 150, 129, 25, 24, 141, 20,
/* 1910 */ 16, 143, 1, 141, 302, 302, 299, 129, 129, 61,
/* 1920 */ 150, 53, 53, 37, 129, 53, 53, 115, 1, 34,
/* 1930 */ 140, 5, 22, 114, 160, 68, 75, 25, 68, 41,
/* 1940 */ 140, 114, 24, 20, 19, 130, 124, 67, 24, 67,
/* 1950 */ 22, 22, 22, 96, 23, 22, 59, 22, 67, 37,
/* 1960 */ 28, 23, 148, 22, 25, 23, 23, 23, 23, 22,
/* 1970 */ 140, 97, 23, 23, 115, 22, 142, 25, 88, 75,
/* 1980 */ 34, 44, 34, 75, 23, 34, 86, 22, 34, 34,
/* 1990 */ 34, 24, 93, 25, 25, 23, 34, 23, 23, 23,
/* 2000 */ 23, 11, 23, 25, 22, 22, 22, 1, 23, 23,
/* 2010 */ 22, 22, 25, 15, 23, 1, 140, 25, 140, 318,
/* 2020 */ 318, 134, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2030 */ 318, 318, 140, 318, 318, 318, 140, 318, 318, 318,
/* 2040 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2050 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2060 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2070 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2080 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2090 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2100 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2110 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2120 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2130 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2140 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2150 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2160 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2170 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2180 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2190 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2200 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2210 */ 318, 318, 318, 318, 318, 318, 318, 318, 318, 318,
/* 2220 */ 318,
};
#define YY_SHIFT_COUNT (571)
#define YY_SHIFT_MIN (0)
#define YY_SHIFT_MAX (2014)
static const unsigned short int yy_shift_ofst[] = {
/* 0 */ 1423, 1409, 1454, 1192, 1192, 610, 1252, 1410, 1517, 1684,
/* 10 */ 1684, 1684, 276, 0, 0, 180, 1015, 1684, 1684, 1684,
/* 20 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684,
/* 30 */ 1049, 1049, 1121, 1121, 54, 487, 610, 610, 610, 610,
/* 40 */ 610, 40, 110, 219, 289, 396, 439, 509, 548, 618,
/* 50 */ 657, 727, 766, 836, 995, 1015, 1015, 1015, 1015, 1015,
/* 60 */ 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015,
/* 70 */ 1015, 1015, 1015, 1035, 1015, 1138, 880, 880, 1577, 1684,
/* 80 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684,
/* 90 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684,
/* 100 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684,
/* 110 */ 1684, 1684, 1684, 1705, 1684, 1684, 1684, 1684, 1684, 1684,
/* 120 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 146, 84, 84,
/* 130 */ 84, 84, 84, 277, 315, 401, 97, 461, 251, 66,
/* 140 */ 66, 51, 1156, 66, 66, 324, 324, 66, 452, 452,
/* 150 */ 452, 452, 133, 114, 114, 4, 11, 2037, 2037, 621,
/* 160 */ 621, 621, 567, 398, 398, 398, 398, 937, 937, 228,
/* 170 */ 251, 331, 1052, 66, 66, 66, 66, 66, 66, 66,
/* 180 */ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
/* 190 */ 66, 66, 66, 557, 557, 66, 9, 25, 25, 745,
/* 200 */ 745, 967, 1088, 2037, 2037, 2037, 2037, 2037, 2037, 2037,
/* 210 */ 255, 317, 317, 514, 403, 620, 471, 672, 781, 891,
/* 220 */ 675, 66, 66, 66, 66, 66, 66, 66, 66, 66,
/* 230 */ 66, 508, 66, 66, 66, 66, 66, 66, 66, 66,
/* 240 */ 66, 66, 66, 66, 790, 790, 790, 66, 66, 66,
/* 250 */ 338, 66, 66, 66, 516, 1084, 66, 66, 993, 66,
/* 260 */ 66, 66, 66, 66, 66, 66, 66, 732, 1083, 563,
/* 270 */ 994, 994, 994, 994, 337, 563, 563, 1028, 987, 897,
/* 280 */ 1119, 262, 1214, 1271, 1112, 1214, 1112, 1268, 1239, 262,
/* 290 */ 262, 1239, 262, 1271, 1268, 1302, 1354, 1278, 1168, 1168,
/* 300 */ 1168, 1112, 1303, 1303, 815, 1311, 1264, 1364, 1657, 1657,
/* 310 */ 1595, 1595, 1701, 1701, 1595, 1592, 1596, 1724, 1706, 1730,
/* 320 */ 1730, 1730, 1730, 1595, 1735, 1614, 1596, 1596, 1614, 1724,
/* 330 */ 1706, 1614, 1706, 1614, 1595, 1735, 1621, 1717, 1595, 1735,
/* 340 */ 1758, 1595, 1735, 1595, 1735, 1758, 1679, 1679, 1679, 1734,
/* 350 */ 1781, 1781, 1758, 1679, 1689, 1679, 1734, 1679, 1679, 1645,
/* 360 */ 1791, 1715, 1715, 1758, 1690, 1718, 1690, 1718, 1690, 1718,
/* 370 */ 1690, 1718, 1595, 1751, 1751, 1762, 1762, 1699, 1703, 1826,
/* 380 */ 1595, 1695, 1699, 1707, 1710, 1614, 1847, 1863, 1863, 1871,
/* 390 */ 1871, 1871, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037,
/* 400 */ 2037, 2037, 2037, 2037, 2037, 2037, 2037, 193, 837, 1194,
/* 410 */ 1212, 506, 832, 1054, 1390, 925, 1435, 1394, 1102, 1332,
/* 420 */ 1419, 1196, 1420, 1425, 1433, 1447, 1457, 1488, 1443, 1379,
/* 430 */ 1572, 1455, 1503, 1453, 1495, 1515, 1506, 1526, 1460, 1489,
/* 440 */ 1581, 1622, 1534, 667, 1888, 1893, 1875, 1736, 1884, 1885,
/* 450 */ 1877, 1879, 1765, 1754, 1776, 1881, 1881, 1883, 1767, 1889,
/* 460 */ 1768, 1894, 1911, 1772, 1788, 1881, 1789, 1858, 1886, 1881,
/* 470 */ 1770, 1868, 1869, 1872, 1873, 1795, 1812, 1895, 1790, 1927,
/* 480 */ 1926, 1910, 1819, 1774, 1867, 1912, 1870, 1861, 1898, 1800,
/* 490 */ 1827, 1918, 1923, 1925, 1815, 1822, 1928, 1880, 1929, 1930,
/* 500 */ 1931, 1933, 1882, 1897, 1924, 1857, 1932, 1935, 1891, 1922,
/* 510 */ 1938, 1814, 1941, 1942, 1943, 1944, 1939, 1945, 1947, 1874,
/* 520 */ 1830, 1949, 1950, 1859, 1946, 1953, 1834, 1952, 1948, 1951,
/* 530 */ 1954, 1955, 1890, 1904, 1900, 1937, 1908, 1899, 1956, 1961,
/* 540 */ 1965, 1967, 1968, 1969, 1962, 1972, 1952, 1974, 1975, 1976,
/* 550 */ 1977, 1978, 1979, 1982, 1990, 1983, 1984, 1985, 1986, 1988,
/* 560 */ 1989, 1987, 1887, 1876, 1878, 1892, 1896, 1992, 1991, 1998,
/* 570 */ 2006, 2014,
};
#define YY_REDUCE_COUNT (406)
#define YY_REDUCE_MIN (-272)
#define YY_REDUCE_MAX (1693)
static const short yy_reduce_ofst[] = {
/* 0 */ 109, 113, 272, 760, -178, -176, -192, -183, -180, -134,
/* 10 */ 213, 220, 371, -208, -205, -272, -197, 611, 632, 765,
/* 20 */ 786, 392, 943, 989, 503, 651, 1039, -18, 702, 821,
/* 30 */ 710, 812, -188, 380, -187, 555, 662, 1055, 1063, 1065,
/* 40 */ 1080, -267, -267, -267, -267, -267, -267, -267, -267, -267,
/* 50 */ -267, -267, -267, -267, -267, -267, -267, -267, -267, -267,
/* 60 */ -267, -267, -267, -267, -267, -267, -267, -267, -267, -267,
/* 70 */ -267, -267, -267, -267, -267, -267, -267, -267, 636, 811,
/* 80 */ 917, 936, 1006, 1008, 1017, 1060, 1064, 1069, 1075, 1105,
/* 90 */ 1118, 1123, 1125, 1134, 1140, 1159, 1165, 1169, 1174, 1179,
/* 100 */ 1181, 1184, 1186, 1201, 1246, 1259, 1262, 1281, 1293, 1299,
/* 110 */ 1313, 1327, 1341, 1352, 1356, 1358, 1362, 1366, 1395, 1403,
/* 120 */ 1406, 1411, 1424, 1436, 1439, 1450, 1452, -267, -267, -267,
/* 130 */ -267, -267, -267, -267, -267, 224, -267, 446, -24, 275,
/* 140 */ 546, 518, 573, 560, 53, -181, -111, 485, 606, 671,
/* 150 */ 606, 671, 683, 8, 93, -267, -267, -267, -267, 155,
/* 160 */ 155, 155, 181, 242, 264, 486, 489, -218, 393, 227,
/* 170 */ 604, 347, 347, -171, 431, 650, 715, -166, 562, 609,
/* 180 */ 716, 764, 18, 823, 769, 833, 838, 957, 759, 119,
/* 190 */ 923, 226, 1014, 542, 603, 451, 949, 654, 659, 762,
/* 200 */ 964, -4, 778, 961, 712, 1082, 1100, 1111, 1026, 1117,
/* 210 */ -204, -174, -151, -8, 77, 198, 305, 327, 388, 540,
/* 220 */ 839, 968, 982, 985, 1004, 1023, 1070, 1086, 1097, 1130,
/* 230 */ 1190, 1163, 1199, 1284, 1297, 1300, 1314, 1339, 1353, 1391,
/* 240 */ 1402, 1413, 1416, 1417, 803, 1376, 1400, 1428, 1437, 1446,
/* 250 */ 1378, 1461, 1464, 1465, 1249, 1329, 1466, 1467, 1414, 1468,
/* 260 */ 305, 1469, 1470, 1471, 1472, 1482, 1483, 1389, 1392, 1438,
/* 270 */ 1426, 1427, 1432, 1434, 1378, 1438, 1438, 1440, 1474, 1499,
/* 280 */ 1399, 1421, 1430, 1456, 1441, 1442, 1444, 1415, 1473, 1431,
/* 290 */ 1445, 1476, 1449, 1478, 1418, 1479, 1477, 1485, 1493, 1494,
/* 300 */ 1496, 1458, 1475, 1480, 1459, 1490, 1484, 1518, 1448, 1451,
/* 310 */ 1537, 1538, 1463, 1481, 1541, 1486, 1487, 1491, 1519, 1514,
/* 320 */ 1521, 1523, 1525, 1552, 1556, 1520, 1492, 1498, 1522, 1497,
/* 330 */ 1539, 1528, 1542, 1532, 1571, 1573, 1500, 1504, 1582, 1584,
/* 340 */ 1563, 1586, 1588, 1589, 1597, 1567, 1579, 1585, 1590, 1568,
/* 350 */ 1583, 1587, 1593, 1591, 1598, 1599, 1600, 1602, 1606, 1513,
/* 360 */ 1524, 1548, 1549, 1611, 1574, 1576, 1594, 1603, 1604, 1607,
/* 370 */ 1605, 1608, 1642, 1527, 1529, 1609, 1610, 1601, 1615, 1575,
/* 380 */ 1650, 1578, 1619, 1623, 1625, 1624, 1674, 1685, 1686, 1691,
/* 390 */ 1692, 1693, 1612, 1613, 1617, 1675, 1668, 1673, 1676, 1677,
/* 400 */ 1680, 1666, 1669, 1678, 1681, 1683, 1687,
};
static const YYACTIONTYPE yy_default[] = {
/* 0 */ 1633, 1633, 1633, 1462, 1230, 1341, 1230, 1230, 1230, 1462,
/* 10 */ 1462, 1462, 1230, 1371, 1371, 1515, 1263, 1230, 1230, 1230,
/* 20 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1461, 1230, 1230,
/* 30 */ 1230, 1230, 1550, 1550, 1230, 1230, 1230, 1230, 1230, 1230,
/* 40 */ 1230, 1230, 1380, 1230, 1387, 1230, 1230, 1230, 1230, 1230,
/* 50 */ 1463, 1464, 1230, 1230, 1230, 1514, 1516, 1479, 1394, 1393,
/* 60 */ 1392, 1391, 1497, 1358, 1385, 1378, 1382, 1457, 1458, 1456,
/* 70 */ 1460, 1464, 1463, 1230, 1381, 1428, 1442, 1427, 1230, 1230,
/* 80 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 90 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 100 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 110 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 120 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1436, 1441, 1447,
/* 130 */ 1440, 1437, 1430, 1429, 1431, 1230, 1432, 1230, 1254, 1230,
/* 140 */ 1230, 1251, 1305, 1230, 1230, 1230, 1230, 1230, 1534, 1533,
/* 150 */ 1230, 1230, 1263, 1422, 1421, 1433, 1434, 1444, 1443, 1522,
/* 160 */ 1586, 1585, 1480, 1230, 1230, 1230, 1230, 1230, 1230, 1550,
/* 170 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 180 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 190 */ 1230, 1230, 1230, 1550, 1550, 1230, 1263, 1550, 1550, 1259,
/* 200 */ 1259, 1365, 1230, 1529, 1332, 1332, 1332, 1332, 1341, 1332,
/* 210 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 220 */ 1230, 1230, 1230, 1230, 1230, 1519, 1517, 1230, 1230, 1230,
/* 230 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 240 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 250 */ 1230, 1230, 1230, 1230, 1337, 1230, 1230, 1230, 1230, 1230,
/* 260 */ 1230, 1230, 1230, 1230, 1230, 1230, 1579, 1230, 1492, 1319,
/* 270 */ 1337, 1337, 1337, 1337, 1339, 1320, 1318, 1331, 1264, 1237,
/* 280 */ 1625, 1397, 1386, 1338, 1360, 1386, 1360, 1622, 1384, 1397,
/* 290 */ 1397, 1384, 1397, 1338, 1622, 1280, 1602, 1275, 1371, 1371,
/* 300 */ 1371, 1360, 1365, 1365, 1459, 1338, 1331, 1230, 1625, 1625,
/* 310 */ 1346, 1346, 1624, 1624, 1346, 1480, 1609, 1406, 1308, 1314,
/* 320 */ 1314, 1314, 1314, 1346, 1248, 1384, 1609, 1609, 1384, 1406,
/* 330 */ 1308, 1384, 1308, 1384, 1346, 1248, 1496, 1619, 1346, 1248,
/* 340 */ 1470, 1346, 1248, 1346, 1248, 1470, 1306, 1306, 1306, 1295,
/* 350 */ 1230, 1230, 1470, 1306, 1280, 1306, 1295, 1306, 1306, 1568,
/* 360 */ 1230, 1474, 1474, 1470, 1364, 1359, 1364, 1359, 1364, 1359,
/* 370 */ 1364, 1359, 1346, 1560, 1560, 1374, 1374, 1379, 1365, 1465,
/* 380 */ 1346, 1230, 1379, 1377, 1375, 1384, 1298, 1582, 1582, 1578,
/* 390 */ 1578, 1578, 1630, 1630, 1529, 1595, 1263, 1263, 1263, 1263,
/* 400 */ 1595, 1282, 1282, 1264, 1264, 1263, 1595, 1230, 1230, 1230,
/* 410 */ 1230, 1230, 1230, 1590, 1230, 1524, 1481, 1350, 1230, 1230,
/* 420 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 430 */ 1230, 1230, 1535, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 440 */ 1230, 1230, 1230, 1411, 1230, 1233, 1526, 1230, 1230, 1230,
/* 450 */ 1230, 1230, 1230, 1230, 1230, 1388, 1389, 1351, 1230, 1230,
/* 460 */ 1230, 1230, 1230, 1230, 1230, 1403, 1230, 1230, 1230, 1398,
/* 470 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1621, 1230,
/* 480 */ 1230, 1230, 1230, 1230, 1230, 1495, 1494, 1230, 1230, 1348,
/* 490 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 500 */ 1230, 1230, 1230, 1278, 1230, 1230, 1230, 1230, 1230, 1230,
/* 510 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 520 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1376, 1230, 1230,
/* 530 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 540 */ 1230, 1230, 1565, 1366, 1230, 1230, 1612, 1230, 1230, 1230,
/* 550 */ 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230,
/* 560 */ 1230, 1606, 1322, 1413, 1230, 1412, 1416, 1252, 1230, 1242,
/* 570 */ 1230, 1230,
};
/********** End of lemon-generated parsing tables *****************************/
/* The next table maps tokens (terminal symbols) into fallback tokens.
** If a construct like the following:
**
** %fallback ID X Y Z.
|
| ︙ | ︙ | |||
159445 159446 159447 159448 159449 159450 159451 |
59, /* IF => ID */
0, /* NOT => nothing */
0, /* EXISTS => nothing */
59, /* TEMP => ID */
0, /* LP => nothing */
0, /* RP => nothing */
0, /* AS => nothing */
| < > | 159935 159936 159937 159938 159939 159940 159941 159942 159943 159944 159945 159946 159947 159948 159949 159950 |
59, /* IF => ID */
0, /* NOT => nothing */
0, /* EXISTS => nothing */
59, /* TEMP => ID */
0, /* LP => nothing */
0, /* RP => nothing */
0, /* AS => nothing */
0, /* COMMA => nothing */
59, /* WITHOUT => ID */
59, /* ABORT => ID */
59, /* ACTION => ID */
59, /* AFTER => ID */
59, /* ANALYZE => ID */
59, /* ASC => ID */
59, /* ATTACH => ID */
59, /* BEFORE => ID */
|
| ︙ | ︙ | |||
159717 159718 159719 159720 159721 159722 159723 | /* 18 */ "IF", /* 19 */ "NOT", /* 20 */ "EXISTS", /* 21 */ "TEMP", /* 22 */ "LP", /* 23 */ "RP", /* 24 */ "AS", | | | | 160207 160208 160209 160210 160211 160212 160213 160214 160215 160216 160217 160218 160219 160220 160221 160222 | /* 18 */ "IF", /* 19 */ "NOT", /* 20 */ "EXISTS", /* 21 */ "TEMP", /* 22 */ "LP", /* 23 */ "RP", /* 24 */ "AS", /* 25 */ "COMMA", /* 26 */ "WITHOUT", /* 27 */ "ABORT", /* 28 */ "ACTION", /* 29 */ "AFTER", /* 30 */ "ANALYZE", /* 31 */ "ASC", /* 32 */ "ATTACH", /* 33 */ "BEFORE", |
| ︙ | ︙ | |||
159894 159895 159896 159897 159898 159899 159900 | /* 195 */ "create_table_args", /* 196 */ "createkw", /* 197 */ "temp", /* 198 */ "ifnotexists", /* 199 */ "dbnm", /* 200 */ "columnlist", /* 201 */ "conslist_opt", | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 160384 160385 160386 160387 160388 160389 160390 160391 160392 160393 160394 160395 160396 160397 160398 160399 160400 160401 160402 160403 160404 160405 160406 160407 160408 160409 160410 160411 160412 160413 160414 160415 160416 160417 160418 160419 160420 160421 160422 160423 160424 160425 160426 160427 160428 160429 160430 160431 160432 160433 160434 160435 160436 160437 160438 160439 160440 160441 160442 160443 160444 160445 160446 160447 160448 160449 160450 160451 160452 160453 160454 160455 160456 160457 160458 160459 160460 160461 160462 160463 160464 160465 160466 160467 160468 160469 160470 160471 160472 160473 160474 160475 160476 160477 160478 160479 160480 160481 160482 160483 160484 160485 160486 160487 160488 160489 160490 160491 160492 160493 160494 160495 160496 160497 160498 160499 160500 160501 160502 160503 160504 160505 160506 160507 160508 160509 160510 160511 160512 160513 |
/* 195 */ "create_table_args",
/* 196 */ "createkw",
/* 197 */ "temp",
/* 198 */ "ifnotexists",
/* 199 */ "dbnm",
/* 200 */ "columnlist",
/* 201 */ "conslist_opt",
/* 202 */ "table_option_set",
/* 203 */ "select",
/* 204 */ "table_option",
/* 205 */ "columnname",
/* 206 */ "carglist",
/* 207 */ "typetoken",
/* 208 */ "typename",
/* 209 */ "signed",
/* 210 */ "plus_num",
/* 211 */ "minus_num",
/* 212 */ "scanpt",
/* 213 */ "scantok",
/* 214 */ "ccons",
/* 215 */ "term",
/* 216 */ "expr",
/* 217 */ "onconf",
/* 218 */ "sortorder",
/* 219 */ "autoinc",
/* 220 */ "eidlist_opt",
/* 221 */ "refargs",
/* 222 */ "defer_subclause",
/* 223 */ "generated",
/* 224 */ "refarg",
/* 225 */ "refact",
/* 226 */ "init_deferred_pred_opt",
/* 227 */ "conslist",
/* 228 */ "tconscomma",
/* 229 */ "tcons",
/* 230 */ "sortlist",
/* 231 */ "eidlist",
/* 232 */ "defer_subclause_opt",
/* 233 */ "orconf",
/* 234 */ "resolvetype",
/* 235 */ "raisetype",
/* 236 */ "ifexists",
/* 237 */ "fullname",
/* 238 */ "selectnowith",
/* 239 */ "oneselect",
/* 240 */ "wqlist",
/* 241 */ "multiselect_op",
/* 242 */ "distinct",
/* 243 */ "selcollist",
/* 244 */ "from",
/* 245 */ "where_opt",
/* 246 */ "groupby_opt",
/* 247 */ "having_opt",
/* 248 */ "orderby_opt",
/* 249 */ "limit_opt",
/* 250 */ "window_clause",
/* 251 */ "values",
/* 252 */ "nexprlist",
/* 253 */ "sclp",
/* 254 */ "as",
/* 255 */ "seltablist",
/* 256 */ "stl_prefix",
/* 257 */ "joinop",
/* 258 */ "indexed_opt",
/* 259 */ "on_opt",
/* 260 */ "using_opt",
/* 261 */ "exprlist",
/* 262 */ "xfullname",
/* 263 */ "idlist",
/* 264 */ "nulls",
/* 265 */ "with",
/* 266 */ "where_opt_ret",
/* 267 */ "setlist",
/* 268 */ "insert_cmd",
/* 269 */ "idlist_opt",
/* 270 */ "upsert",
/* 271 */ "returning",
/* 272 */ "filter_over",
/* 273 */ "likeop",
/* 274 */ "between_op",
/* 275 */ "in_op",
/* 276 */ "paren_exprlist",
/* 277 */ "case_operand",
/* 278 */ "case_exprlist",
/* 279 */ "case_else",
/* 280 */ "uniqueflag",
/* 281 */ "collate",
/* 282 */ "vinto",
/* 283 */ "nmnum",
/* 284 */ "trigger_decl",
/* 285 */ "trigger_cmd_list",
/* 286 */ "trigger_time",
/* 287 */ "trigger_event",
/* 288 */ "foreach_clause",
/* 289 */ "when_clause",
/* 290 */ "trigger_cmd",
/* 291 */ "trnm",
/* 292 */ "tridxby",
/* 293 */ "database_kw_opt",
/* 294 */ "key_opt",
/* 295 */ "add_column_fullname",
/* 296 */ "kwcolumn_opt",
/* 297 */ "create_vtab",
/* 298 */ "vtabarglist",
/* 299 */ "vtabarg",
/* 300 */ "vtabargtoken",
/* 301 */ "lp",
/* 302 */ "anylist",
/* 303 */ "wqitem",
/* 304 */ "wqas",
/* 305 */ "windowdefn_list",
/* 306 */ "windowdefn",
/* 307 */ "window",
/* 308 */ "frame_opt",
/* 309 */ "part_opt",
/* 310 */ "filter_clause",
/* 311 */ "over_clause",
/* 312 */ "range_or_rows",
/* 313 */ "frame_bound",
/* 314 */ "frame_bound_s",
/* 315 */ "frame_bound_e",
/* 316 */ "frame_exclude_opt",
/* 317 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
#ifndef NDEBUG
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
|
| ︙ | ︙ | |||
160035 160036 160037 160038 160039 160040 160041 | /* 12 */ "cmd ::= ROLLBACK trans_opt TO savepoint_opt nm", /* 13 */ "create_table ::= createkw temp TABLE ifnotexists nm dbnm", /* 14 */ "createkw ::= CREATE", /* 15 */ "ifnotexists ::=", /* 16 */ "ifnotexists ::= IF NOT EXISTS", /* 17 */ "temp ::= TEMP", /* 18 */ "temp ::=", | | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < | | | > > | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 160526 160527 160528 160529 160530 160531 160532 160533 160534 160535 160536 160537 160538 160539 160540 160541 160542 160543 160544 160545 160546 160547 160548 160549 160550 160551 160552 160553 160554 160555 160556 160557 160558 160559 160560 160561 160562 160563 160564 160565 160566 160567 160568 160569 160570 160571 160572 160573 160574 160575 160576 160577 160578 160579 160580 160581 160582 160583 160584 160585 160586 160587 160588 160589 160590 160591 160592 160593 160594 160595 160596 160597 160598 160599 160600 160601 160602 160603 160604 160605 160606 160607 160608 160609 160610 160611 160612 160613 160614 160615 160616 160617 160618 160619 160620 160621 160622 160623 160624 160625 160626 160627 160628 160629 160630 160631 160632 160633 160634 160635 160636 160637 160638 160639 160640 160641 160642 160643 160644 160645 160646 160647 160648 160649 160650 160651 160652 160653 160654 160655 160656 160657 160658 160659 160660 160661 160662 160663 160664 160665 160666 160667 160668 160669 160670 160671 160672 160673 160674 160675 160676 160677 160678 160679 160680 160681 160682 160683 160684 160685 160686 160687 160688 160689 160690 160691 160692 160693 160694 160695 160696 160697 160698 160699 160700 160701 160702 160703 160704 160705 160706 160707 160708 160709 160710 160711 160712 160713 160714 160715 160716 160717 160718 160719 160720 160721 160722 160723 160724 160725 160726 160727 160728 160729 160730 160731 160732 160733 160734 160735 160736 160737 160738 160739 160740 160741 160742 160743 160744 160745 160746 160747 160748 160749 160750 160751 160752 160753 160754 160755 160756 160757 160758 160759 160760 160761 160762 160763 160764 160765 160766 160767 160768 160769 160770 160771 160772 160773 160774 160775 160776 160777 160778 160779 160780 160781 160782 160783 160784 160785 160786 160787 160788 160789 160790 160791 160792 160793 160794 160795 160796 160797 160798 160799 160800 160801 160802 160803 160804 160805 160806 160807 160808 160809 160810 160811 160812 160813 160814 160815 160816 160817 160818 160819 160820 160821 160822 160823 160824 160825 160826 160827 160828 160829 160830 160831 160832 160833 160834 160835 160836 160837 160838 160839 160840 160841 160842 160843 160844 160845 160846 160847 160848 160849 160850 160851 160852 160853 160854 160855 160856 160857 160858 160859 160860 160861 160862 160863 160864 160865 160866 160867 160868 160869 160870 160871 160872 160873 160874 160875 160876 160877 160878 160879 160880 160881 160882 160883 160884 160885 160886 160887 160888 160889 160890 160891 160892 160893 160894 160895 160896 160897 160898 160899 160900 160901 160902 160903 160904 160905 160906 160907 160908 160909 160910 160911 160912 160913 160914 160915 160916 160917 160918 160919 160920 160921 | /* 12 */ "cmd ::= ROLLBACK trans_opt TO savepoint_opt nm", /* 13 */ "create_table ::= createkw temp TABLE ifnotexists nm dbnm", /* 14 */ "createkw ::= CREATE", /* 15 */ "ifnotexists ::=", /* 16 */ "ifnotexists ::= IF NOT EXISTS", /* 17 */ "temp ::= TEMP", /* 18 */ "temp ::=", /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_option_set", /* 20 */ "create_table_args ::= AS select", /* 21 */ "table_option_set ::=", /* 22 */ "table_option_set ::= table_option_set COMMA table_option", /* 23 */ "table_option ::= WITHOUT nm", /* 24 */ "table_option ::= nm", /* 25 */ "columnname ::= nm typetoken", /* 26 */ "typetoken ::=", /* 27 */ "typetoken ::= typename LP signed RP", /* 28 */ "typetoken ::= typename LP signed COMMA signed RP", /* 29 */ "typename ::= typename ID|STRING", /* 30 */ "scanpt ::=", /* 31 */ "scantok ::=", /* 32 */ "ccons ::= CONSTRAINT nm", /* 33 */ "ccons ::= DEFAULT scantok term", /* 34 */ "ccons ::= DEFAULT LP expr RP", /* 35 */ "ccons ::= DEFAULT PLUS scantok term", /* 36 */ "ccons ::= DEFAULT MINUS scantok term", /* 37 */ "ccons ::= DEFAULT scantok ID|INDEXED", /* 38 */ "ccons ::= NOT NULL onconf", /* 39 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", /* 40 */ "ccons ::= UNIQUE onconf", /* 41 */ "ccons ::= CHECK LP expr RP", /* 42 */ "ccons ::= REFERENCES nm eidlist_opt refargs", /* 43 */ "ccons ::= defer_subclause", /* 44 */ "ccons ::= COLLATE ID|STRING", /* 45 */ "generated ::= LP expr RP", /* 46 */ "generated ::= LP expr RP ID", /* 47 */ "autoinc ::=", /* 48 */ "autoinc ::= AUTOINCR", /* 49 */ "refargs ::=", /* 50 */ "refargs ::= refargs refarg", /* 51 */ "refarg ::= MATCH nm", /* 52 */ "refarg ::= ON INSERT refact", /* 53 */ "refarg ::= ON DELETE refact", /* 54 */ "refarg ::= ON UPDATE refact", /* 55 */ "refact ::= SET NULL", /* 56 */ "refact ::= SET DEFAULT", /* 57 */ "refact ::= CASCADE", /* 58 */ "refact ::= RESTRICT", /* 59 */ "refact ::= NO ACTION", /* 60 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", /* 61 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", /* 62 */ "init_deferred_pred_opt ::=", /* 63 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", /* 64 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", /* 65 */ "conslist_opt ::=", /* 66 */ "tconscomma ::= COMMA", /* 67 */ "tcons ::= CONSTRAINT nm", /* 68 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", /* 69 */ "tcons ::= UNIQUE LP sortlist RP onconf", /* 70 */ "tcons ::= CHECK LP expr RP onconf", /* 71 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", /* 72 */ "defer_subclause_opt ::=", /* 73 */ "onconf ::=", /* 74 */ "onconf ::= ON CONFLICT resolvetype", /* 75 */ "orconf ::=", /* 76 */ "orconf ::= OR resolvetype", /* 77 */ "resolvetype ::= IGNORE", /* 78 */ "resolvetype ::= REPLACE", /* 79 */ "cmd ::= DROP TABLE ifexists fullname", /* 80 */ "ifexists ::= IF EXISTS", /* 81 */ "ifexists ::=", /* 82 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", /* 83 */ "cmd ::= DROP VIEW ifexists fullname", /* 84 */ "cmd ::= select", /* 85 */ "select ::= WITH wqlist selectnowith", /* 86 */ "select ::= WITH RECURSIVE wqlist selectnowith", /* 87 */ "select ::= selectnowith", /* 88 */ "selectnowith ::= selectnowith multiselect_op oneselect", /* 89 */ "multiselect_op ::= UNION", /* 90 */ "multiselect_op ::= UNION ALL", /* 91 */ "multiselect_op ::= EXCEPT|INTERSECT", /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", /* 94 */ "values ::= VALUES LP nexprlist RP", /* 95 */ "values ::= values COMMA LP nexprlist RP", /* 96 */ "distinct ::= DISTINCT", /* 97 */ "distinct ::= ALL", /* 98 */ "distinct ::=", /* 99 */ "sclp ::=", /* 100 */ "selcollist ::= sclp scanpt expr scanpt as", /* 101 */ "selcollist ::= sclp scanpt STAR", /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR", /* 103 */ "as ::= AS nm", /* 104 */ "as ::=", /* 105 */ "from ::=", /* 106 */ "from ::= FROM seltablist", /* 107 */ "stl_prefix ::= seltablist joinop", /* 108 */ "stl_prefix ::=", /* 109 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", /* 110 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", /* 111 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", /* 112 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", /* 113 */ "dbnm ::=", /* 114 */ "dbnm ::= DOT nm", /* 115 */ "fullname ::= nm", /* 116 */ "fullname ::= nm DOT nm", /* 117 */ "xfullname ::= nm", /* 118 */ "xfullname ::= nm DOT nm", /* 119 */ "xfullname ::= nm DOT nm AS nm", /* 120 */ "xfullname ::= nm AS nm", /* 121 */ "joinop ::= COMMA|JOIN", /* 122 */ "joinop ::= JOIN_KW JOIN", /* 123 */ "joinop ::= JOIN_KW nm JOIN", /* 124 */ "joinop ::= JOIN_KW nm nm JOIN", /* 125 */ "on_opt ::= ON expr", /* 126 */ "on_opt ::=", /* 127 */ "indexed_opt ::=", /* 128 */ "indexed_opt ::= INDEXED BY nm", /* 129 */ "indexed_opt ::= NOT INDEXED", /* 130 */ "using_opt ::= USING LP idlist RP", /* 131 */ "using_opt ::=", /* 132 */ "orderby_opt ::=", /* 133 */ "orderby_opt ::= ORDER BY sortlist", /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls", /* 135 */ "sortlist ::= expr sortorder nulls", /* 136 */ "sortorder ::= ASC", /* 137 */ "sortorder ::= DESC", /* 138 */ "sortorder ::=", /* 139 */ "nulls ::= NULLS FIRST", /* 140 */ "nulls ::= NULLS LAST", /* 141 */ "nulls ::=", /* 142 */ "groupby_opt ::=", /* 143 */ "groupby_opt ::= GROUP BY nexprlist", /* 144 */ "having_opt ::=", /* 145 */ "having_opt ::= HAVING expr", /* 146 */ "limit_opt ::=", /* 147 */ "limit_opt ::= LIMIT expr", /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr", /* 149 */ "limit_opt ::= LIMIT expr COMMA expr", /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", /* 151 */ "where_opt ::=", /* 152 */ "where_opt ::= WHERE expr", /* 153 */ "where_opt_ret ::=", /* 154 */ "where_opt_ret ::= WHERE expr", /* 155 */ "where_opt_ret ::= RETURNING selcollist", /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", /* 158 */ "setlist ::= setlist COMMA nm EQ expr", /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", /* 160 */ "setlist ::= nm EQ expr", /* 161 */ "setlist ::= LP idlist RP EQ expr", /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", /* 164 */ "upsert ::=", /* 165 */ "upsert ::= RETURNING selcollist", /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning", /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", /* 170 */ "returning ::= RETURNING selcollist", /* 171 */ "insert_cmd ::= INSERT orconf", /* 172 */ "insert_cmd ::= REPLACE", /* 173 */ "idlist_opt ::=", /* 174 */ "idlist_opt ::= LP idlist RP", /* 175 */ "idlist ::= idlist COMMA nm", /* 176 */ "idlist ::= nm", /* 177 */ "expr ::= LP expr RP", /* 178 */ "expr ::= ID|INDEXED", /* 179 */ "expr ::= JOIN_KW", /* 180 */ "expr ::= nm DOT nm", /* 181 */ "expr ::= nm DOT nm DOT nm", /* 182 */ "term ::= NULL|FLOAT|BLOB", /* 183 */ "term ::= STRING", /* 184 */ "term ::= INTEGER", /* 185 */ "expr ::= VARIABLE", /* 186 */ "expr ::= expr COLLATE ID|STRING", /* 187 */ "expr ::= CAST LP expr AS typetoken RP", /* 188 */ "expr ::= ID|INDEXED LP distinct exprlist RP", /* 189 */ "expr ::= ID|INDEXED LP STAR RP", /* 190 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", /* 191 */ "expr ::= ID|INDEXED LP STAR RP filter_over", /* 192 */ "term ::= CTIME_KW", /* 193 */ "expr ::= LP nexprlist COMMA expr RP", /* 194 */ "expr ::= expr AND expr", /* 195 */ "expr ::= expr OR expr", /* 196 */ "expr ::= expr LT|GT|GE|LE expr", /* 197 */ "expr ::= expr EQ|NE expr", /* 198 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", /* 199 */ "expr ::= expr PLUS|MINUS expr", /* 200 */ "expr ::= expr STAR|SLASH|REM expr", /* 201 */ "expr ::= expr CONCAT expr", /* 202 */ "likeop ::= NOT LIKE_KW|MATCH", /* 203 */ "expr ::= expr likeop expr", /* 204 */ "expr ::= expr likeop expr ESCAPE expr", /* 205 */ "expr ::= expr ISNULL|NOTNULL", /* 206 */ "expr ::= expr NOT NULL", /* 207 */ "expr ::= expr IS expr", /* 208 */ "expr ::= expr IS NOT expr", /* 209 */ "expr ::= NOT expr", /* 210 */ "expr ::= BITNOT expr", /* 211 */ "expr ::= PLUS|MINUS expr", /* 212 */ "between_op ::= BETWEEN", /* 213 */ "between_op ::= NOT BETWEEN", /* 214 */ "expr ::= expr between_op expr AND expr", /* 215 */ "in_op ::= IN", /* 216 */ "in_op ::= NOT IN", /* 217 */ "expr ::= expr in_op LP exprlist RP", /* 218 */ "expr ::= LP select RP", /* 219 */ "expr ::= expr in_op LP select RP", /* 220 */ "expr ::= expr in_op nm dbnm paren_exprlist", /* 221 */ "expr ::= EXISTS LP select RP", /* 222 */ "expr ::= CASE case_operand case_exprlist case_else END", /* 223 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", /* 224 */ "case_exprlist ::= WHEN expr THEN expr", /* 225 */ "case_else ::= ELSE expr", /* 226 */ "case_else ::=", /* 227 */ "case_operand ::= expr", /* 228 */ "case_operand ::=", /* 229 */ "exprlist ::=", /* 230 */ "nexprlist ::= nexprlist COMMA expr", /* 231 */ "nexprlist ::= expr", /* 232 */ "paren_exprlist ::=", /* 233 */ "paren_exprlist ::= LP exprlist RP", /* 234 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", /* 235 */ "uniqueflag ::= UNIQUE", /* 236 */ "uniqueflag ::=", /* 237 */ "eidlist_opt ::=", /* 238 */ "eidlist_opt ::= LP eidlist RP", /* 239 */ "eidlist ::= eidlist COMMA nm collate sortorder", /* 240 */ "eidlist ::= nm collate sortorder", /* 241 */ "collate ::=", /* 242 */ "collate ::= COLLATE ID|STRING", /* 243 */ "cmd ::= DROP INDEX ifexists fullname", /* 244 */ "cmd ::= VACUUM vinto", /* 245 */ "cmd ::= VACUUM nm vinto", /* 246 */ "vinto ::= INTO expr", /* 247 */ "vinto ::=", /* 248 */ "cmd ::= PRAGMA nm dbnm", /* 249 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", /* 250 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", /* 251 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", /* 252 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", /* 253 */ "plus_num ::= PLUS INTEGER|FLOAT", /* 254 */ "minus_num ::= MINUS INTEGER|FLOAT", /* 255 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", /* 256 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", /* 257 */ "trigger_time ::= BEFORE|AFTER", /* 258 */ "trigger_time ::= INSTEAD OF", /* 259 */ "trigger_time ::=", /* 260 */ "trigger_event ::= DELETE|INSERT", /* 261 */ "trigger_event ::= UPDATE", /* 262 */ "trigger_event ::= UPDATE OF idlist", /* 263 */ "when_clause ::=", /* 264 */ "when_clause ::= WHEN expr", /* 265 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", /* 266 */ "trigger_cmd_list ::= trigger_cmd SEMI", /* 267 */ "trnm ::= nm DOT nm", /* 268 */ "tridxby ::= INDEXED BY nm", /* 269 */ "tridxby ::= NOT INDEXED", /* 270 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", /* 271 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", /* 272 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", /* 273 */ "trigger_cmd ::= scanpt select scanpt", /* 274 */ "expr ::= RAISE LP IGNORE RP", /* 275 */ "expr ::= RAISE LP raisetype COMMA nm RP", /* 276 */ "raisetype ::= ROLLBACK", /* 277 */ "raisetype ::= ABORT", /* 278 */ "raisetype ::= FAIL", /* 279 */ "cmd ::= DROP TRIGGER ifexists fullname", /* 280 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", /* 281 */ "cmd ::= DETACH database_kw_opt expr", /* 282 */ "key_opt ::=", /* 283 */ "key_opt ::= KEY expr", /* 284 */ "cmd ::= REINDEX", /* 285 */ "cmd ::= REINDEX nm dbnm", /* 286 */ "cmd ::= ANALYZE", /* 287 */ "cmd ::= ANALYZE nm dbnm", /* 288 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", /* 289 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", /* 290 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", /* 291 */ "add_column_fullname ::= fullname", /* 292 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", /* 293 */ "cmd ::= create_vtab", /* 294 */ "cmd ::= create_vtab LP vtabarglist RP", /* 295 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", /* 296 */ "vtabarg ::=", /* 297 */ "vtabargtoken ::= ANY", /* 298 */ "vtabargtoken ::= lp anylist RP", /* 299 */ "lp ::= LP", /* 300 */ "with ::= WITH wqlist", /* 301 */ "with ::= WITH RECURSIVE wqlist", /* 302 */ "wqas ::= AS", /* 303 */ "wqas ::= AS MATERIALIZED", /* 304 */ "wqas ::= AS NOT MATERIALIZED", /* 305 */ "wqitem ::= nm eidlist_opt wqas LP select RP", /* 306 */ "wqlist ::= wqitem", /* 307 */ "wqlist ::= wqlist COMMA wqitem", /* 308 */ "windowdefn_list ::= windowdefn", /* 309 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", /* 310 */ "windowdefn ::= nm AS LP window RP", /* 311 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", /* 312 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", /* 313 */ "window ::= ORDER BY sortlist frame_opt", /* 314 */ "window ::= nm ORDER BY sortlist frame_opt", /* 315 */ "window ::= frame_opt", /* 316 */ "window ::= nm frame_opt", /* 317 */ "frame_opt ::=", /* 318 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", /* 319 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", /* 320 */ "range_or_rows ::= RANGE|ROWS|GROUPS", /* 321 */ "frame_bound_s ::= frame_bound", /* 322 */ "frame_bound_s ::= UNBOUNDED PRECEDING", /* 323 */ "frame_bound_e ::= frame_bound", /* 324 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", /* 325 */ "frame_bound ::= expr PRECEDING|FOLLOWING", /* 326 */ "frame_bound ::= CURRENT ROW", /* 327 */ "frame_exclude_opt ::=", /* 328 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", /* 329 */ "frame_exclude ::= NO OTHERS", /* 330 */ "frame_exclude ::= CURRENT ROW", /* 331 */ "frame_exclude ::= GROUP|TIES", /* 332 */ "window_clause ::= WINDOW windowdefn_list", /* 333 */ "filter_over ::= filter_clause over_clause", /* 334 */ "filter_over ::= over_clause", /* 335 */ "filter_over ::= filter_clause", /* 336 */ "over_clause ::= OVER LP window RP", /* 337 */ "over_clause ::= OVER nm", /* 338 */ "filter_clause ::= FILTER LP WHERE expr RP", /* 339 */ "input ::= cmdlist", /* 340 */ "cmdlist ::= cmdlist ecmd", /* 341 */ "cmdlist ::= ecmd", /* 342 */ "ecmd ::= SEMI", /* 343 */ "ecmd ::= cmdx SEMI", /* 344 */ "ecmd ::= explain cmdx SEMI", /* 345 */ "trans_opt ::=", /* 346 */ "trans_opt ::= TRANSACTION", /* 347 */ "trans_opt ::= TRANSACTION nm", /* 348 */ "savepoint_opt ::= SAVEPOINT", /* 349 */ "savepoint_opt ::=", /* 350 */ "cmd ::= create_table create_table_args", /* 351 */ "table_option_set ::= table_option", /* 352 */ "columnlist ::= columnlist COMMA columnname carglist", /* 353 */ "columnlist ::= columnname carglist", /* 354 */ "nm ::= ID|INDEXED", /* 355 */ "nm ::= STRING", /* 356 */ "nm ::= JOIN_KW", /* 357 */ "typetoken ::= typename", /* 358 */ "typename ::= ID|STRING", /* 359 */ "signed ::= plus_num", /* 360 */ "signed ::= minus_num", /* 361 */ "carglist ::= carglist ccons", /* 362 */ "carglist ::=", /* 363 */ "ccons ::= NULL onconf", /* 364 */ "ccons ::= GENERATED ALWAYS AS generated", /* 365 */ "ccons ::= AS generated", /* 366 */ "conslist_opt ::= COMMA conslist", /* 367 */ "conslist ::= conslist tconscomma tcons", /* 368 */ "conslist ::= tcons", /* 369 */ "tconscomma ::=", /* 370 */ "defer_subclause_opt ::= defer_subclause", /* 371 */ "resolvetype ::= raisetype", /* 372 */ "selectnowith ::= oneselect", /* 373 */ "oneselect ::= values", /* 374 */ "sclp ::= selcollist COMMA", /* 375 */ "as ::= ID|STRING", /* 376 */ "returning ::=", /* 377 */ "expr ::= term", /* 378 */ "likeop ::= LIKE_KW|MATCH", /* 379 */ "exprlist ::= nexprlist", /* 380 */ "nmnum ::= plus_num", /* 381 */ "nmnum ::= nm", /* 382 */ "nmnum ::= ON", /* 383 */ "nmnum ::= DELETE", /* 384 */ "nmnum ::= DEFAULT", /* 385 */ "plus_num ::= INTEGER|FLOAT", /* 386 */ "foreach_clause ::=", /* 387 */ "foreach_clause ::= FOR EACH ROW", /* 388 */ "trnm ::= nm", /* 389 */ "tridxby ::=", /* 390 */ "database_kw_opt ::= DATABASE", /* 391 */ "database_kw_opt ::=", /* 392 */ "kwcolumn_opt ::=", /* 393 */ "kwcolumn_opt ::= COLUMNKW", /* 394 */ "vtabarglist ::= vtabarg", /* 395 */ "vtabarglist ::= vtabarglist COMMA vtabarg", /* 396 */ "vtabarg ::= vtabarg vtabargtoken", /* 397 */ "anylist ::=", /* 398 */ "anylist ::= anylist LP anylist RP", /* 399 */ "anylist ::= anylist ANY", /* 400 */ "with ::=", }; #endif /* NDEBUG */ #if YYSTACKDEPTH<=0 /* ** Try to increase the size of the parser stack. Return the number |
| ︙ | ︙ | |||
160540 160541 160542 160543 160544 160545 160546 |
**
** Note: during a reduce, the only symbols destroyed are those
** which appear on the RHS of the rule, but which are *not* used
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
case 203: /* select */
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | > | | | | | | | | | | | | 161034 161035 161036 161037 161038 161039 161040 161041 161042 161043 161044 161045 161046 161047 161048 161049 161050 161051 161052 161053 161054 161055 161056 161057 161058 161059 161060 161061 161062 161063 161064 161065 161066 161067 161068 161069 161070 161071 161072 161073 161074 161075 161076 161077 161078 161079 161080 161081 161082 161083 161084 161085 161086 161087 161088 161089 161090 161091 161092 161093 161094 161095 161096 161097 161098 161099 161100 161101 161102 161103 161104 161105 161106 161107 161108 161109 161110 161111 161112 161113 161114 161115 161116 161117 161118 161119 161120 161121 161122 161123 161124 161125 161126 161127 161128 161129 161130 161131 161132 161133 161134 161135 161136 161137 161138 161139 |
**
** Note: during a reduce, the only symbols destroyed are those
** which appear on the RHS of the rule, but which are *not* used
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
case 203: /* select */
case 238: /* selectnowith */
case 239: /* oneselect */
case 251: /* values */
{
sqlite3SelectDelete(pParse->db, (yypminor->yy303));
}
break;
case 215: /* term */
case 216: /* expr */
case 245: /* where_opt */
case 247: /* having_opt */
case 259: /* on_opt */
case 266: /* where_opt_ret */
case 277: /* case_operand */
case 279: /* case_else */
case 282: /* vinto */
case 289: /* when_clause */
case 294: /* key_opt */
case 310: /* filter_clause */
{
sqlite3ExprDelete(pParse->db, (yypminor->yy626));
}
break;
case 220: /* eidlist_opt */
case 230: /* sortlist */
case 231: /* eidlist */
case 243: /* selcollist */
case 246: /* groupby_opt */
case 248: /* orderby_opt */
case 252: /* nexprlist */
case 253: /* sclp */
case 261: /* exprlist */
case 267: /* setlist */
case 276: /* paren_exprlist */
case 278: /* case_exprlist */
case 309: /* part_opt */
{
sqlite3ExprListDelete(pParse->db, (yypminor->yy562));
}
break;
case 237: /* fullname */
case 244: /* from */
case 255: /* seltablist */
case 256: /* stl_prefix */
case 262: /* xfullname */
{
sqlite3SrcListDelete(pParse->db, (yypminor->yy607));
}
break;
case 240: /* wqlist */
{
sqlite3WithDelete(pParse->db, (yypminor->yy43));
}
break;
case 250: /* window_clause */
case 305: /* windowdefn_list */
{
sqlite3WindowListDelete(pParse->db, (yypminor->yy375));
}
break;
case 260: /* using_opt */
case 263: /* idlist */
case 269: /* idlist_opt */
{
sqlite3IdListDelete(pParse->db, (yypminor->yy240));
}
break;
case 272: /* filter_over */
case 306: /* windowdefn */
case 307: /* window */
case 308: /* frame_opt */
case 311: /* over_clause */
{
sqlite3WindowDelete(pParse->db, (yypminor->yy375));
}
break;
case 285: /* trigger_cmd_list */
case 290: /* trigger_cmd */
{
sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy95));
}
break;
case 287: /* trigger_event */
{
sqlite3IdListDelete(pParse->db, (yypminor->yy570).b);
}
break;
case 313: /* frame_bound */
case 314: /* frame_bound_s */
case 315: /* frame_bound_e */
{
sqlite3ExprDelete(pParse->db, (yypminor->yy81).pExpr);
}
break;
/********* End destructor definitions *****************************************/
default: break; /* If no destructor action specified: do nothing */
}
}
|
| ︙ | ︙ | |||
160941 160942 160943 160944 160945 160946 160947 | 189, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ 194, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ 196, /* (14) createkw ::= CREATE */ 198, /* (15) ifnotexists ::= */ 198, /* (16) ifnotexists ::= IF NOT EXISTS */ 197, /* (17) temp ::= TEMP */ 197, /* (18) temp ::= */ | | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 161435 161436 161437 161438 161439 161440 161441 161442 161443 161444 161445 161446 161447 161448 161449 161450 161451 161452 161453 161454 161455 161456 161457 161458 161459 161460 161461 161462 161463 161464 161465 161466 161467 161468 161469 161470 161471 161472 161473 161474 161475 161476 161477 161478 161479 161480 161481 161482 161483 161484 161485 161486 161487 161488 161489 161490 161491 161492 161493 161494 161495 161496 161497 161498 161499 161500 161501 161502 161503 161504 161505 161506 161507 161508 161509 161510 161511 161512 161513 161514 161515 161516 161517 161518 161519 161520 161521 161522 161523 161524 161525 161526 161527 161528 161529 161530 161531 161532 161533 161534 161535 161536 161537 161538 161539 161540 161541 161542 161543 161544 161545 161546 161547 161548 161549 161550 161551 161552 161553 161554 161555 161556 161557 161558 161559 161560 161561 161562 161563 161564 161565 161566 161567 161568 161569 161570 161571 161572 161573 161574 161575 161576 161577 161578 161579 161580 161581 161582 161583 161584 161585 161586 161587 161588 161589 161590 161591 161592 161593 161594 161595 161596 161597 161598 161599 161600 161601 161602 161603 161604 161605 161606 161607 161608 161609 161610 161611 161612 161613 161614 161615 161616 161617 161618 161619 161620 161621 161622 161623 161624 161625 161626 161627 161628 161629 161630 161631 161632 161633 161634 161635 161636 161637 161638 161639 161640 161641 161642 161643 161644 161645 161646 161647 161648 161649 161650 161651 161652 161653 161654 161655 161656 161657 161658 161659 161660 161661 161662 161663 161664 161665 161666 161667 161668 161669 161670 161671 161672 161673 161674 161675 161676 161677 161678 161679 161680 161681 161682 161683 161684 161685 161686 161687 161688 161689 161690 161691 161692 161693 161694 161695 161696 161697 161698 161699 161700 161701 161702 161703 161704 161705 161706 161707 161708 161709 161710 161711 161712 161713 161714 161715 161716 161717 161718 161719 161720 161721 161722 161723 161724 161725 161726 161727 161728 161729 161730 161731 161732 161733 161734 161735 161736 161737 161738 161739 161740 161741 161742 161743 161744 161745 161746 161747 161748 161749 161750 161751 161752 161753 161754 161755 161756 161757 161758 161759 161760 161761 161762 161763 161764 161765 161766 161767 161768 161769 161770 161771 161772 161773 161774 161775 161776 161777 161778 161779 161780 161781 161782 161783 161784 161785 161786 161787 161788 161789 161790 161791 161792 161793 161794 161795 161796 161797 161798 161799 161800 161801 161802 161803 161804 161805 161806 161807 161808 161809 161810 161811 161812 161813 161814 161815 161816 161817 161818 161819 161820 161821 161822 161823 161824 161825 161826 161827 161828 161829 161830 |
189, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
194, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
196, /* (14) createkw ::= CREATE */
198, /* (15) ifnotexists ::= */
198, /* (16) ifnotexists ::= IF NOT EXISTS */
197, /* (17) temp ::= TEMP */
197, /* (18) temp ::= */
195, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
195, /* (20) create_table_args ::= AS select */
202, /* (21) table_option_set ::= */
202, /* (22) table_option_set ::= table_option_set COMMA table_option */
204, /* (23) table_option ::= WITHOUT nm */
204, /* (24) table_option ::= nm */
205, /* (25) columnname ::= nm typetoken */
207, /* (26) typetoken ::= */
207, /* (27) typetoken ::= typename LP signed RP */
207, /* (28) typetoken ::= typename LP signed COMMA signed RP */
208, /* (29) typename ::= typename ID|STRING */
212, /* (30) scanpt ::= */
213, /* (31) scantok ::= */
214, /* (32) ccons ::= CONSTRAINT nm */
214, /* (33) ccons ::= DEFAULT scantok term */
214, /* (34) ccons ::= DEFAULT LP expr RP */
214, /* (35) ccons ::= DEFAULT PLUS scantok term */
214, /* (36) ccons ::= DEFAULT MINUS scantok term */
214, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
214, /* (38) ccons ::= NOT NULL onconf */
214, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
214, /* (40) ccons ::= UNIQUE onconf */
214, /* (41) ccons ::= CHECK LP expr RP */
214, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
214, /* (43) ccons ::= defer_subclause */
214, /* (44) ccons ::= COLLATE ID|STRING */
223, /* (45) generated ::= LP expr RP */
223, /* (46) generated ::= LP expr RP ID */
219, /* (47) autoinc ::= */
219, /* (48) autoinc ::= AUTOINCR */
221, /* (49) refargs ::= */
221, /* (50) refargs ::= refargs refarg */
224, /* (51) refarg ::= MATCH nm */
224, /* (52) refarg ::= ON INSERT refact */
224, /* (53) refarg ::= ON DELETE refact */
224, /* (54) refarg ::= ON UPDATE refact */
225, /* (55) refact ::= SET NULL */
225, /* (56) refact ::= SET DEFAULT */
225, /* (57) refact ::= CASCADE */
225, /* (58) refact ::= RESTRICT */
225, /* (59) refact ::= NO ACTION */
222, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
222, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
226, /* (62) init_deferred_pred_opt ::= */
226, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
226, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
201, /* (65) conslist_opt ::= */
228, /* (66) tconscomma ::= COMMA */
229, /* (67) tcons ::= CONSTRAINT nm */
229, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
229, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
229, /* (70) tcons ::= CHECK LP expr RP onconf */
229, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
232, /* (72) defer_subclause_opt ::= */
217, /* (73) onconf ::= */
217, /* (74) onconf ::= ON CONFLICT resolvetype */
233, /* (75) orconf ::= */
233, /* (76) orconf ::= OR resolvetype */
234, /* (77) resolvetype ::= IGNORE */
234, /* (78) resolvetype ::= REPLACE */
189, /* (79) cmd ::= DROP TABLE ifexists fullname */
236, /* (80) ifexists ::= IF EXISTS */
236, /* (81) ifexists ::= */
189, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
189, /* (83) cmd ::= DROP VIEW ifexists fullname */
189, /* (84) cmd ::= select */
203, /* (85) select ::= WITH wqlist selectnowith */
203, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
203, /* (87) select ::= selectnowith */
238, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
241, /* (89) multiselect_op ::= UNION */
241, /* (90) multiselect_op ::= UNION ALL */
241, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
239, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
239, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
251, /* (94) values ::= VALUES LP nexprlist RP */
251, /* (95) values ::= values COMMA LP nexprlist RP */
242, /* (96) distinct ::= DISTINCT */
242, /* (97) distinct ::= ALL */
242, /* (98) distinct ::= */
253, /* (99) sclp ::= */
243, /* (100) selcollist ::= sclp scanpt expr scanpt as */
243, /* (101) selcollist ::= sclp scanpt STAR */
243, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
254, /* (103) as ::= AS nm */
254, /* (104) as ::= */
244, /* (105) from ::= */
244, /* (106) from ::= FROM seltablist */
256, /* (107) stl_prefix ::= seltablist joinop */
256, /* (108) stl_prefix ::= */
255, /* (109) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
255, /* (110) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
255, /* (111) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
255, /* (112) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
199, /* (113) dbnm ::= */
199, /* (114) dbnm ::= DOT nm */
237, /* (115) fullname ::= nm */
237, /* (116) fullname ::= nm DOT nm */
262, /* (117) xfullname ::= nm */
262, /* (118) xfullname ::= nm DOT nm */
262, /* (119) xfullname ::= nm DOT nm AS nm */
262, /* (120) xfullname ::= nm AS nm */
257, /* (121) joinop ::= COMMA|JOIN */
257, /* (122) joinop ::= JOIN_KW JOIN */
257, /* (123) joinop ::= JOIN_KW nm JOIN */
257, /* (124) joinop ::= JOIN_KW nm nm JOIN */
259, /* (125) on_opt ::= ON expr */
259, /* (126) on_opt ::= */
258, /* (127) indexed_opt ::= */
258, /* (128) indexed_opt ::= INDEXED BY nm */
258, /* (129) indexed_opt ::= NOT INDEXED */
260, /* (130) using_opt ::= USING LP idlist RP */
260, /* (131) using_opt ::= */
248, /* (132) orderby_opt ::= */
248, /* (133) orderby_opt ::= ORDER BY sortlist */
230, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
230, /* (135) sortlist ::= expr sortorder nulls */
218, /* (136) sortorder ::= ASC */
218, /* (137) sortorder ::= DESC */
218, /* (138) sortorder ::= */
264, /* (139) nulls ::= NULLS FIRST */
264, /* (140) nulls ::= NULLS LAST */
264, /* (141) nulls ::= */
246, /* (142) groupby_opt ::= */
246, /* (143) groupby_opt ::= GROUP BY nexprlist */
247, /* (144) having_opt ::= */
247, /* (145) having_opt ::= HAVING expr */
249, /* (146) limit_opt ::= */
249, /* (147) limit_opt ::= LIMIT expr */
249, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
249, /* (149) limit_opt ::= LIMIT expr COMMA expr */
189, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
245, /* (151) where_opt ::= */
245, /* (152) where_opt ::= WHERE expr */
266, /* (153) where_opt_ret ::= */
266, /* (154) where_opt_ret ::= WHERE expr */
266, /* (155) where_opt_ret ::= RETURNING selcollist */
266, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
189, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
267, /* (158) setlist ::= setlist COMMA nm EQ expr */
267, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
267, /* (160) setlist ::= nm EQ expr */
267, /* (161) setlist ::= LP idlist RP EQ expr */
189, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
189, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
270, /* (164) upsert ::= */
270, /* (165) upsert ::= RETURNING selcollist */
270, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
270, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
270, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
270, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
271, /* (170) returning ::= RETURNING selcollist */
268, /* (171) insert_cmd ::= INSERT orconf */
268, /* (172) insert_cmd ::= REPLACE */
269, /* (173) idlist_opt ::= */
269, /* (174) idlist_opt ::= LP idlist RP */
263, /* (175) idlist ::= idlist COMMA nm */
263, /* (176) idlist ::= nm */
216, /* (177) expr ::= LP expr RP */
216, /* (178) expr ::= ID|INDEXED */
216, /* (179) expr ::= JOIN_KW */
216, /* (180) expr ::= nm DOT nm */
216, /* (181) expr ::= nm DOT nm DOT nm */
215, /* (182) term ::= NULL|FLOAT|BLOB */
215, /* (183) term ::= STRING */
215, /* (184) term ::= INTEGER */
216, /* (185) expr ::= VARIABLE */
216, /* (186) expr ::= expr COLLATE ID|STRING */
216, /* (187) expr ::= CAST LP expr AS typetoken RP */
216, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */
216, /* (189) expr ::= ID|INDEXED LP STAR RP */
216, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
216, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */
215, /* (192) term ::= CTIME_KW */
216, /* (193) expr ::= LP nexprlist COMMA expr RP */
216, /* (194) expr ::= expr AND expr */
216, /* (195) expr ::= expr OR expr */
216, /* (196) expr ::= expr LT|GT|GE|LE expr */
216, /* (197) expr ::= expr EQ|NE expr */
216, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
216, /* (199) expr ::= expr PLUS|MINUS expr */
216, /* (200) expr ::= expr STAR|SLASH|REM expr */
216, /* (201) expr ::= expr CONCAT expr */
273, /* (202) likeop ::= NOT LIKE_KW|MATCH */
216, /* (203) expr ::= expr likeop expr */
216, /* (204) expr ::= expr likeop expr ESCAPE expr */
216, /* (205) expr ::= expr ISNULL|NOTNULL */
216, /* (206) expr ::= expr NOT NULL */
216, /* (207) expr ::= expr IS expr */
216, /* (208) expr ::= expr IS NOT expr */
216, /* (209) expr ::= NOT expr */
216, /* (210) expr ::= BITNOT expr */
216, /* (211) expr ::= PLUS|MINUS expr */
274, /* (212) between_op ::= BETWEEN */
274, /* (213) between_op ::= NOT BETWEEN */
216, /* (214) expr ::= expr between_op expr AND expr */
275, /* (215) in_op ::= IN */
275, /* (216) in_op ::= NOT IN */
216, /* (217) expr ::= expr in_op LP exprlist RP */
216, /* (218) expr ::= LP select RP */
216, /* (219) expr ::= expr in_op LP select RP */
216, /* (220) expr ::= expr in_op nm dbnm paren_exprlist */
216, /* (221) expr ::= EXISTS LP select RP */
216, /* (222) expr ::= CASE case_operand case_exprlist case_else END */
278, /* (223) case_exprlist ::= case_exprlist WHEN expr THEN expr */
278, /* (224) case_exprlist ::= WHEN expr THEN expr */
279, /* (225) case_else ::= ELSE expr */
279, /* (226) case_else ::= */
277, /* (227) case_operand ::= expr */
277, /* (228) case_operand ::= */
261, /* (229) exprlist ::= */
252, /* (230) nexprlist ::= nexprlist COMMA expr */
252, /* (231) nexprlist ::= expr */
276, /* (232) paren_exprlist ::= */
276, /* (233) paren_exprlist ::= LP exprlist RP */
189, /* (234) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
280, /* (235) uniqueflag ::= UNIQUE */
280, /* (236) uniqueflag ::= */
220, /* (237) eidlist_opt ::= */
220, /* (238) eidlist_opt ::= LP eidlist RP */
231, /* (239) eidlist ::= eidlist COMMA nm collate sortorder */
231, /* (240) eidlist ::= nm collate sortorder */
281, /* (241) collate ::= */
281, /* (242) collate ::= COLLATE ID|STRING */
189, /* (243) cmd ::= DROP INDEX ifexists fullname */
189, /* (244) cmd ::= VACUUM vinto */
189, /* (245) cmd ::= VACUUM nm vinto */
282, /* (246) vinto ::= INTO expr */
282, /* (247) vinto ::= */
189, /* (248) cmd ::= PRAGMA nm dbnm */
189, /* (249) cmd ::= PRAGMA nm dbnm EQ nmnum */
189, /* (250) cmd ::= PRAGMA nm dbnm LP nmnum RP */
189, /* (251) cmd ::= PRAGMA nm dbnm EQ minus_num */
189, /* (252) cmd ::= PRAGMA nm dbnm LP minus_num RP */
210, /* (253) plus_num ::= PLUS INTEGER|FLOAT */
211, /* (254) minus_num ::= MINUS INTEGER|FLOAT */
189, /* (255) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
284, /* (256) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
286, /* (257) trigger_time ::= BEFORE|AFTER */
286, /* (258) trigger_time ::= INSTEAD OF */
286, /* (259) trigger_time ::= */
287, /* (260) trigger_event ::= DELETE|INSERT */
287, /* (261) trigger_event ::= UPDATE */
287, /* (262) trigger_event ::= UPDATE OF idlist */
289, /* (263) when_clause ::= */
289, /* (264) when_clause ::= WHEN expr */
285, /* (265) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
285, /* (266) trigger_cmd_list ::= trigger_cmd SEMI */
291, /* (267) trnm ::= nm DOT nm */
292, /* (268) tridxby ::= INDEXED BY nm */
292, /* (269) tridxby ::= NOT INDEXED */
290, /* (270) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
290, /* (271) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
290, /* (272) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
290, /* (273) trigger_cmd ::= scanpt select scanpt */
216, /* (274) expr ::= RAISE LP IGNORE RP */
216, /* (275) expr ::= RAISE LP raisetype COMMA nm RP */
235, /* (276) raisetype ::= ROLLBACK */
235, /* (277) raisetype ::= ABORT */
235, /* (278) raisetype ::= FAIL */
189, /* (279) cmd ::= DROP TRIGGER ifexists fullname */
189, /* (280) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
189, /* (281) cmd ::= DETACH database_kw_opt expr */
294, /* (282) key_opt ::= */
294, /* (283) key_opt ::= KEY expr */
189, /* (284) cmd ::= REINDEX */
189, /* (285) cmd ::= REINDEX nm dbnm */
189, /* (286) cmd ::= ANALYZE */
189, /* (287) cmd ::= ANALYZE nm dbnm */
189, /* (288) cmd ::= ALTER TABLE fullname RENAME TO nm */
189, /* (289) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
189, /* (290) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
295, /* (291) add_column_fullname ::= fullname */
189, /* (292) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
189, /* (293) cmd ::= create_vtab */
189, /* (294) cmd ::= create_vtab LP vtabarglist RP */
297, /* (295) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
299, /* (296) vtabarg ::= */
300, /* (297) vtabargtoken ::= ANY */
300, /* (298) vtabargtoken ::= lp anylist RP */
301, /* (299) lp ::= LP */
265, /* (300) with ::= WITH wqlist */
265, /* (301) with ::= WITH RECURSIVE wqlist */
304, /* (302) wqas ::= AS */
304, /* (303) wqas ::= AS MATERIALIZED */
304, /* (304) wqas ::= AS NOT MATERIALIZED */
303, /* (305) wqitem ::= nm eidlist_opt wqas LP select RP */
240, /* (306) wqlist ::= wqitem */
240, /* (307) wqlist ::= wqlist COMMA wqitem */
305, /* (308) windowdefn_list ::= windowdefn */
305, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */
306, /* (310) windowdefn ::= nm AS LP window RP */
307, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
307, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
307, /* (313) window ::= ORDER BY sortlist frame_opt */
307, /* (314) window ::= nm ORDER BY sortlist frame_opt */
307, /* (315) window ::= frame_opt */
307, /* (316) window ::= nm frame_opt */
308, /* (317) frame_opt ::= */
308, /* (318) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
308, /* (319) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
312, /* (320) range_or_rows ::= RANGE|ROWS|GROUPS */
314, /* (321) frame_bound_s ::= frame_bound */
314, /* (322) frame_bound_s ::= UNBOUNDED PRECEDING */
315, /* (323) frame_bound_e ::= frame_bound */
315, /* (324) frame_bound_e ::= UNBOUNDED FOLLOWING */
313, /* (325) frame_bound ::= expr PRECEDING|FOLLOWING */
313, /* (326) frame_bound ::= CURRENT ROW */
316, /* (327) frame_exclude_opt ::= */
316, /* (328) frame_exclude_opt ::= EXCLUDE frame_exclude */
317, /* (329) frame_exclude ::= NO OTHERS */
317, /* (330) frame_exclude ::= CURRENT ROW */
317, /* (331) frame_exclude ::= GROUP|TIES */
250, /* (332) window_clause ::= WINDOW windowdefn_list */
272, /* (333) filter_over ::= filter_clause over_clause */
272, /* (334) filter_over ::= over_clause */
272, /* (335) filter_over ::= filter_clause */
311, /* (336) over_clause ::= OVER LP window RP */
311, /* (337) over_clause ::= OVER nm */
310, /* (338) filter_clause ::= FILTER LP WHERE expr RP */
184, /* (339) input ::= cmdlist */
185, /* (340) cmdlist ::= cmdlist ecmd */
185, /* (341) cmdlist ::= ecmd */
186, /* (342) ecmd ::= SEMI */
186, /* (343) ecmd ::= cmdx SEMI */
186, /* (344) ecmd ::= explain cmdx SEMI */
191, /* (345) trans_opt ::= */
191, /* (346) trans_opt ::= TRANSACTION */
191, /* (347) trans_opt ::= TRANSACTION nm */
193, /* (348) savepoint_opt ::= SAVEPOINT */
193, /* (349) savepoint_opt ::= */
189, /* (350) cmd ::= create_table create_table_args */
202, /* (351) table_option_set ::= table_option */
200, /* (352) columnlist ::= columnlist COMMA columnname carglist */
200, /* (353) columnlist ::= columnname carglist */
192, /* (354) nm ::= ID|INDEXED */
192, /* (355) nm ::= STRING */
192, /* (356) nm ::= JOIN_KW */
207, /* (357) typetoken ::= typename */
208, /* (358) typename ::= ID|STRING */
209, /* (359) signed ::= plus_num */
209, /* (360) signed ::= minus_num */
206, /* (361) carglist ::= carglist ccons */
206, /* (362) carglist ::= */
214, /* (363) ccons ::= NULL onconf */
214, /* (364) ccons ::= GENERATED ALWAYS AS generated */
214, /* (365) ccons ::= AS generated */
201, /* (366) conslist_opt ::= COMMA conslist */
227, /* (367) conslist ::= conslist tconscomma tcons */
227, /* (368) conslist ::= tcons */
228, /* (369) tconscomma ::= */
232, /* (370) defer_subclause_opt ::= defer_subclause */
234, /* (371) resolvetype ::= raisetype */
238, /* (372) selectnowith ::= oneselect */
239, /* (373) oneselect ::= values */
253, /* (374) sclp ::= selcollist COMMA */
254, /* (375) as ::= ID|STRING */
271, /* (376) returning ::= */
216, /* (377) expr ::= term */
273, /* (378) likeop ::= LIKE_KW|MATCH */
261, /* (379) exprlist ::= nexprlist */
283, /* (380) nmnum ::= plus_num */
283, /* (381) nmnum ::= nm */
283, /* (382) nmnum ::= ON */
283, /* (383) nmnum ::= DELETE */
283, /* (384) nmnum ::= DEFAULT */
210, /* (385) plus_num ::= INTEGER|FLOAT */
288, /* (386) foreach_clause ::= */
288, /* (387) foreach_clause ::= FOR EACH ROW */
291, /* (388) trnm ::= nm */
292, /* (389) tridxby ::= */
293, /* (390) database_kw_opt ::= DATABASE */
293, /* (391) database_kw_opt ::= */
296, /* (392) kwcolumn_opt ::= */
296, /* (393) kwcolumn_opt ::= COLUMNKW */
298, /* (394) vtabarglist ::= vtabarg */
298, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */
299, /* (396) vtabarg ::= vtabarg vtabargtoken */
302, /* (397) anylist ::= */
302, /* (398) anylist ::= anylist LP anylist RP */
302, /* (399) anylist ::= anylist ANY */
265, /* (400) with ::= */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
-1, /* (0) explain ::= EXPLAIN */
-3, /* (1) explain ::= EXPLAIN QUERY PLAN */
|
| ︙ | ︙ | |||
161344 161345 161346 161347 161348 161349 161350 |
-5, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
-6, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
-1, /* (14) createkw ::= CREATE */
0, /* (15) ifnotexists ::= */
-3, /* (16) ifnotexists ::= IF NOT EXISTS */
-1, /* (17) temp ::= TEMP */
0, /* (18) temp ::= */
| | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 161841 161842 161843 161844 161845 161846 161847 161848 161849 161850 161851 161852 161853 161854 161855 161856 161857 161858 161859 161860 161861 161862 161863 161864 161865 161866 161867 161868 161869 161870 161871 161872 161873 161874 161875 161876 161877 161878 161879 161880 161881 161882 161883 161884 161885 161886 161887 161888 161889 161890 161891 161892 161893 161894 161895 161896 161897 161898 161899 161900 161901 161902 161903 161904 161905 161906 161907 161908 161909 161910 161911 161912 161913 161914 161915 161916 161917 161918 161919 161920 161921 161922 161923 161924 161925 161926 161927 161928 161929 161930 161931 161932 161933 161934 161935 161936 161937 161938 161939 161940 161941 161942 161943 161944 161945 161946 161947 161948 161949 161950 161951 161952 161953 161954 161955 161956 161957 161958 161959 161960 161961 161962 161963 161964 161965 161966 161967 161968 161969 161970 161971 161972 161973 161974 161975 161976 161977 161978 161979 161980 161981 161982 161983 161984 161985 161986 161987 161988 161989 161990 161991 161992 161993 161994 161995 161996 161997 161998 161999 162000 162001 162002 162003 162004 162005 162006 162007 162008 162009 162010 162011 162012 162013 162014 162015 162016 162017 162018 162019 162020 162021 162022 162023 162024 162025 162026 162027 162028 162029 162030 162031 162032 162033 162034 162035 162036 162037 162038 162039 162040 162041 162042 162043 162044 162045 162046 162047 162048 162049 162050 162051 162052 162053 162054 162055 162056 162057 162058 162059 162060 162061 162062 162063 162064 162065 162066 162067 162068 162069 162070 162071 162072 162073 162074 162075 162076 162077 162078 162079 162080 162081 162082 162083 162084 162085 162086 162087 162088 162089 162090 162091 162092 162093 162094 162095 162096 162097 162098 162099 162100 162101 162102 162103 162104 162105 162106 162107 162108 162109 162110 162111 162112 162113 162114 162115 162116 162117 162118 162119 162120 162121 162122 162123 162124 162125 162126 162127 162128 162129 162130 162131 162132 162133 162134 162135 162136 162137 162138 162139 162140 162141 162142 162143 162144 162145 162146 162147 162148 162149 162150 162151 162152 162153 162154 162155 162156 162157 162158 162159 162160 162161 162162 162163 162164 162165 162166 162167 162168 162169 162170 162171 162172 162173 162174 162175 162176 162177 162178 162179 162180 162181 162182 162183 162184 162185 162186 162187 162188 162189 162190 162191 162192 162193 162194 162195 162196 162197 162198 162199 162200 162201 162202 162203 162204 162205 162206 162207 162208 162209 162210 162211 162212 162213 162214 162215 162216 162217 162218 162219 162220 162221 162222 162223 162224 162225 162226 162227 162228 162229 162230 162231 162232 162233 162234 162235 162236 |
-5, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
-6, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
-1, /* (14) createkw ::= CREATE */
0, /* (15) ifnotexists ::= */
-3, /* (16) ifnotexists ::= IF NOT EXISTS */
-1, /* (17) temp ::= TEMP */
0, /* (18) temp ::= */
-5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
-2, /* (20) create_table_args ::= AS select */
0, /* (21) table_option_set ::= */
-3, /* (22) table_option_set ::= table_option_set COMMA table_option */
-2, /* (23) table_option ::= WITHOUT nm */
-1, /* (24) table_option ::= nm */
-2, /* (25) columnname ::= nm typetoken */
0, /* (26) typetoken ::= */
-4, /* (27) typetoken ::= typename LP signed RP */
-6, /* (28) typetoken ::= typename LP signed COMMA signed RP */
-2, /* (29) typename ::= typename ID|STRING */
0, /* (30) scanpt ::= */
0, /* (31) scantok ::= */
-2, /* (32) ccons ::= CONSTRAINT nm */
-3, /* (33) ccons ::= DEFAULT scantok term */
-4, /* (34) ccons ::= DEFAULT LP expr RP */
-4, /* (35) ccons ::= DEFAULT PLUS scantok term */
-4, /* (36) ccons ::= DEFAULT MINUS scantok term */
-3, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
-3, /* (38) ccons ::= NOT NULL onconf */
-5, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
-2, /* (40) ccons ::= UNIQUE onconf */
-4, /* (41) ccons ::= CHECK LP expr RP */
-4, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
-1, /* (43) ccons ::= defer_subclause */
-2, /* (44) ccons ::= COLLATE ID|STRING */
-3, /* (45) generated ::= LP expr RP */
-4, /* (46) generated ::= LP expr RP ID */
0, /* (47) autoinc ::= */
-1, /* (48) autoinc ::= AUTOINCR */
0, /* (49) refargs ::= */
-2, /* (50) refargs ::= refargs refarg */
-2, /* (51) refarg ::= MATCH nm */
-3, /* (52) refarg ::= ON INSERT refact */
-3, /* (53) refarg ::= ON DELETE refact */
-3, /* (54) refarg ::= ON UPDATE refact */
-2, /* (55) refact ::= SET NULL */
-2, /* (56) refact ::= SET DEFAULT */
-1, /* (57) refact ::= CASCADE */
-1, /* (58) refact ::= RESTRICT */
-2, /* (59) refact ::= NO ACTION */
-3, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-2, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
0, /* (62) init_deferred_pred_opt ::= */
-2, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
-2, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
0, /* (65) conslist_opt ::= */
-1, /* (66) tconscomma ::= COMMA */
-2, /* (67) tcons ::= CONSTRAINT nm */
-7, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-5, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
-5, /* (70) tcons ::= CHECK LP expr RP onconf */
-10, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
0, /* (72) defer_subclause_opt ::= */
0, /* (73) onconf ::= */
-3, /* (74) onconf ::= ON CONFLICT resolvetype */
0, /* (75) orconf ::= */
-2, /* (76) orconf ::= OR resolvetype */
-1, /* (77) resolvetype ::= IGNORE */
-1, /* (78) resolvetype ::= REPLACE */
-4, /* (79) cmd ::= DROP TABLE ifexists fullname */
-2, /* (80) ifexists ::= IF EXISTS */
0, /* (81) ifexists ::= */
-9, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
-4, /* (83) cmd ::= DROP VIEW ifexists fullname */
-1, /* (84) cmd ::= select */
-3, /* (85) select ::= WITH wqlist selectnowith */
-4, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
-1, /* (87) select ::= selectnowith */
-3, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
-1, /* (89) multiselect_op ::= UNION */
-2, /* (90) multiselect_op ::= UNION ALL */
-1, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
-9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
-10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
-4, /* (94) values ::= VALUES LP nexprlist RP */
-5, /* (95) values ::= values COMMA LP nexprlist RP */
-1, /* (96) distinct ::= DISTINCT */
-1, /* (97) distinct ::= ALL */
0, /* (98) distinct ::= */
0, /* (99) sclp ::= */
-5, /* (100) selcollist ::= sclp scanpt expr scanpt as */
-3, /* (101) selcollist ::= sclp scanpt STAR */
-5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
-2, /* (103) as ::= AS nm */
0, /* (104) as ::= */
0, /* (105) from ::= */
-2, /* (106) from ::= FROM seltablist */
-2, /* (107) stl_prefix ::= seltablist joinop */
0, /* (108) stl_prefix ::= */
-7, /* (109) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
-9, /* (110) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
-7, /* (111) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
-7, /* (112) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
0, /* (113) dbnm ::= */
-2, /* (114) dbnm ::= DOT nm */
-1, /* (115) fullname ::= nm */
-3, /* (116) fullname ::= nm DOT nm */
-1, /* (117) xfullname ::= nm */
-3, /* (118) xfullname ::= nm DOT nm */
-5, /* (119) xfullname ::= nm DOT nm AS nm */
-3, /* (120) xfullname ::= nm AS nm */
-1, /* (121) joinop ::= COMMA|JOIN */
-2, /* (122) joinop ::= JOIN_KW JOIN */
-3, /* (123) joinop ::= JOIN_KW nm JOIN */
-4, /* (124) joinop ::= JOIN_KW nm nm JOIN */
-2, /* (125) on_opt ::= ON expr */
0, /* (126) on_opt ::= */
0, /* (127) indexed_opt ::= */
-3, /* (128) indexed_opt ::= INDEXED BY nm */
-2, /* (129) indexed_opt ::= NOT INDEXED */
-4, /* (130) using_opt ::= USING LP idlist RP */
0, /* (131) using_opt ::= */
0, /* (132) orderby_opt ::= */
-3, /* (133) orderby_opt ::= ORDER BY sortlist */
-5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
-3, /* (135) sortlist ::= expr sortorder nulls */
-1, /* (136) sortorder ::= ASC */
-1, /* (137) sortorder ::= DESC */
0, /* (138) sortorder ::= */
-2, /* (139) nulls ::= NULLS FIRST */
-2, /* (140) nulls ::= NULLS LAST */
0, /* (141) nulls ::= */
0, /* (142) groupby_opt ::= */
-3, /* (143) groupby_opt ::= GROUP BY nexprlist */
0, /* (144) having_opt ::= */
-2, /* (145) having_opt ::= HAVING expr */
0, /* (146) limit_opt ::= */
-2, /* (147) limit_opt ::= LIMIT expr */
-4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
-4, /* (149) limit_opt ::= LIMIT expr COMMA expr */
-6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
0, /* (151) where_opt ::= */
-2, /* (152) where_opt ::= WHERE expr */
0, /* (153) where_opt_ret ::= */
-2, /* (154) where_opt_ret ::= WHERE expr */
-2, /* (155) where_opt_ret ::= RETURNING selcollist */
-4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
-9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
-5, /* (158) setlist ::= setlist COMMA nm EQ expr */
-7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
-3, /* (160) setlist ::= nm EQ expr */
-5, /* (161) setlist ::= LP idlist RP EQ expr */
-7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
-8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
0, /* (164) upsert ::= */
-2, /* (165) upsert ::= RETURNING selcollist */
-12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
-9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
-5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
-8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
-2, /* (170) returning ::= RETURNING selcollist */
-2, /* (171) insert_cmd ::= INSERT orconf */
-1, /* (172) insert_cmd ::= REPLACE */
0, /* (173) idlist_opt ::= */
-3, /* (174) idlist_opt ::= LP idlist RP */
-3, /* (175) idlist ::= idlist COMMA nm */
-1, /* (176) idlist ::= nm */
-3, /* (177) expr ::= LP expr RP */
-1, /* (178) expr ::= ID|INDEXED */
-1, /* (179) expr ::= JOIN_KW */
-3, /* (180) expr ::= nm DOT nm */
-5, /* (181) expr ::= nm DOT nm DOT nm */
-1, /* (182) term ::= NULL|FLOAT|BLOB */
-1, /* (183) term ::= STRING */
-1, /* (184) term ::= INTEGER */
-1, /* (185) expr ::= VARIABLE */
-3, /* (186) expr ::= expr COLLATE ID|STRING */
-6, /* (187) expr ::= CAST LP expr AS typetoken RP */
-5, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */
-4, /* (189) expr ::= ID|INDEXED LP STAR RP */
-6, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
-5, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */
-1, /* (192) term ::= CTIME_KW */
-5, /* (193) expr ::= LP nexprlist COMMA expr RP */
-3, /* (194) expr ::= expr AND expr */
-3, /* (195) expr ::= expr OR expr */
-3, /* (196) expr ::= expr LT|GT|GE|LE expr */
-3, /* (197) expr ::= expr EQ|NE expr */
-3, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
-3, /* (199) expr ::= expr PLUS|MINUS expr */
-3, /* (200) expr ::= expr STAR|SLASH|REM expr */
-3, /* (201) expr ::= expr CONCAT expr */
-2, /* (202) likeop ::= NOT LIKE_KW|MATCH */
-3, /* (203) expr ::= expr likeop expr */
-5, /* (204) expr ::= expr likeop expr ESCAPE expr */
-2, /* (205) expr ::= expr ISNULL|NOTNULL */
-3, /* (206) expr ::= expr NOT NULL */
-3, /* (207) expr ::= expr IS expr */
-4, /* (208) expr ::= expr IS NOT expr */
-2, /* (209) expr ::= NOT expr */
-2, /* (210) expr ::= BITNOT expr */
-2, /* (211) expr ::= PLUS|MINUS expr */
-1, /* (212) between_op ::= BETWEEN */
-2, /* (213) between_op ::= NOT BETWEEN */
-5, /* (214) expr ::= expr between_op expr AND expr */
-1, /* (215) in_op ::= IN */
-2, /* (216) in_op ::= NOT IN */
-5, /* (217) expr ::= expr in_op LP exprlist RP */
-3, /* (218) expr ::= LP select RP */
-5, /* (219) expr ::= expr in_op LP select RP */
-5, /* (220) expr ::= expr in_op nm dbnm paren_exprlist */
-4, /* (221) expr ::= EXISTS LP select RP */
-5, /* (222) expr ::= CASE case_operand case_exprlist case_else END */
-5, /* (223) case_exprlist ::= case_exprlist WHEN expr THEN expr */
-4, /* (224) case_exprlist ::= WHEN expr THEN expr */
-2, /* (225) case_else ::= ELSE expr */
0, /* (226) case_else ::= */
-1, /* (227) case_operand ::= expr */
0, /* (228) case_operand ::= */
0, /* (229) exprlist ::= */
-3, /* (230) nexprlist ::= nexprlist COMMA expr */
-1, /* (231) nexprlist ::= expr */
0, /* (232) paren_exprlist ::= */
-3, /* (233) paren_exprlist ::= LP exprlist RP */
-12, /* (234) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
-1, /* (235) uniqueflag ::= UNIQUE */
0, /* (236) uniqueflag ::= */
0, /* (237) eidlist_opt ::= */
-3, /* (238) eidlist_opt ::= LP eidlist RP */
-5, /* (239) eidlist ::= eidlist COMMA nm collate sortorder */
-3, /* (240) eidlist ::= nm collate sortorder */
0, /* (241) collate ::= */
-2, /* (242) collate ::= COLLATE ID|STRING */
-4, /* (243) cmd ::= DROP INDEX ifexists fullname */
-2, /* (244) cmd ::= VACUUM vinto */
-3, /* (245) cmd ::= VACUUM nm vinto */
-2, /* (246) vinto ::= INTO expr */
0, /* (247) vinto ::= */
-3, /* (248) cmd ::= PRAGMA nm dbnm */
-5, /* (249) cmd ::= PRAGMA nm dbnm EQ nmnum */
-6, /* (250) cmd ::= PRAGMA nm dbnm LP nmnum RP */
-5, /* (251) cmd ::= PRAGMA nm dbnm EQ minus_num */
-6, /* (252) cmd ::= PRAGMA nm dbnm LP minus_num RP */
-2, /* (253) plus_num ::= PLUS INTEGER|FLOAT */
-2, /* (254) minus_num ::= MINUS INTEGER|FLOAT */
-5, /* (255) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
-11, /* (256) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
-1, /* (257) trigger_time ::= BEFORE|AFTER */
-2, /* (258) trigger_time ::= INSTEAD OF */
0, /* (259) trigger_time ::= */
-1, /* (260) trigger_event ::= DELETE|INSERT */
-1, /* (261) trigger_event ::= UPDATE */
-3, /* (262) trigger_event ::= UPDATE OF idlist */
0, /* (263) when_clause ::= */
-2, /* (264) when_clause ::= WHEN expr */
-3, /* (265) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
-2, /* (266) trigger_cmd_list ::= trigger_cmd SEMI */
-3, /* (267) trnm ::= nm DOT nm */
-3, /* (268) tridxby ::= INDEXED BY nm */
-2, /* (269) tridxby ::= NOT INDEXED */
-9, /* (270) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-8, /* (271) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
-6, /* (272) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-3, /* (273) trigger_cmd ::= scanpt select scanpt */
-4, /* (274) expr ::= RAISE LP IGNORE RP */
-6, /* (275) expr ::= RAISE LP raisetype COMMA nm RP */
-1, /* (276) raisetype ::= ROLLBACK */
-1, /* (277) raisetype ::= ABORT */
-1, /* (278) raisetype ::= FAIL */
-4, /* (279) cmd ::= DROP TRIGGER ifexists fullname */
-6, /* (280) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
-3, /* (281) cmd ::= DETACH database_kw_opt expr */
0, /* (282) key_opt ::= */
-2, /* (283) key_opt ::= KEY expr */
-1, /* (284) cmd ::= REINDEX */
-3, /* (285) cmd ::= REINDEX nm dbnm */
-1, /* (286) cmd ::= ANALYZE */
-3, /* (287) cmd ::= ANALYZE nm dbnm */
-6, /* (288) cmd ::= ALTER TABLE fullname RENAME TO nm */
-7, /* (289) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
-6, /* (290) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
-1, /* (291) add_column_fullname ::= fullname */
-8, /* (292) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
-1, /* (293) cmd ::= create_vtab */
-4, /* (294) cmd ::= create_vtab LP vtabarglist RP */
-8, /* (295) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
0, /* (296) vtabarg ::= */
-1, /* (297) vtabargtoken ::= ANY */
-3, /* (298) vtabargtoken ::= lp anylist RP */
-1, /* (299) lp ::= LP */
-2, /* (300) with ::= WITH wqlist */
-3, /* (301) with ::= WITH RECURSIVE wqlist */
-1, /* (302) wqas ::= AS */
-2, /* (303) wqas ::= AS MATERIALIZED */
-3, /* (304) wqas ::= AS NOT MATERIALIZED */
-6, /* (305) wqitem ::= nm eidlist_opt wqas LP select RP */
-1, /* (306) wqlist ::= wqitem */
-3, /* (307) wqlist ::= wqlist COMMA wqitem */
-1, /* (308) windowdefn_list ::= windowdefn */
-3, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */
-5, /* (310) windowdefn ::= nm AS LP window RP */
-5, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
-6, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
-4, /* (313) window ::= ORDER BY sortlist frame_opt */
-5, /* (314) window ::= nm ORDER BY sortlist frame_opt */
-1, /* (315) window ::= frame_opt */
-2, /* (316) window ::= nm frame_opt */
0, /* (317) frame_opt ::= */
-3, /* (318) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
-6, /* (319) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
-1, /* (320) range_or_rows ::= RANGE|ROWS|GROUPS */
-1, /* (321) frame_bound_s ::= frame_bound */
-2, /* (322) frame_bound_s ::= UNBOUNDED PRECEDING */
-1, /* (323) frame_bound_e ::= frame_bound */
-2, /* (324) frame_bound_e ::= UNBOUNDED FOLLOWING */
-2, /* (325) frame_bound ::= expr PRECEDING|FOLLOWING */
-2, /* (326) frame_bound ::= CURRENT ROW */
0, /* (327) frame_exclude_opt ::= */
-2, /* (328) frame_exclude_opt ::= EXCLUDE frame_exclude */
-2, /* (329) frame_exclude ::= NO OTHERS */
-2, /* (330) frame_exclude ::= CURRENT ROW */
-1, /* (331) frame_exclude ::= GROUP|TIES */
-2, /* (332) window_clause ::= WINDOW windowdefn_list */
-2, /* (333) filter_over ::= filter_clause over_clause */
-1, /* (334) filter_over ::= over_clause */
-1, /* (335) filter_over ::= filter_clause */
-4, /* (336) over_clause ::= OVER LP window RP */
-2, /* (337) over_clause ::= OVER nm */
-5, /* (338) filter_clause ::= FILTER LP WHERE expr RP */
-1, /* (339) input ::= cmdlist */
-2, /* (340) cmdlist ::= cmdlist ecmd */
-1, /* (341) cmdlist ::= ecmd */
-1, /* (342) ecmd ::= SEMI */
-2, /* (343) ecmd ::= cmdx SEMI */
-3, /* (344) ecmd ::= explain cmdx SEMI */
0, /* (345) trans_opt ::= */
-1, /* (346) trans_opt ::= TRANSACTION */
-2, /* (347) trans_opt ::= TRANSACTION nm */
-1, /* (348) savepoint_opt ::= SAVEPOINT */
0, /* (349) savepoint_opt ::= */
-2, /* (350) cmd ::= create_table create_table_args */
-1, /* (351) table_option_set ::= table_option */
-4, /* (352) columnlist ::= columnlist COMMA columnname carglist */
-2, /* (353) columnlist ::= columnname carglist */
-1, /* (354) nm ::= ID|INDEXED */
-1, /* (355) nm ::= STRING */
-1, /* (356) nm ::= JOIN_KW */
-1, /* (357) typetoken ::= typename */
-1, /* (358) typename ::= ID|STRING */
-1, /* (359) signed ::= plus_num */
-1, /* (360) signed ::= minus_num */
-2, /* (361) carglist ::= carglist ccons */
0, /* (362) carglist ::= */
-2, /* (363) ccons ::= NULL onconf */
-4, /* (364) ccons ::= GENERATED ALWAYS AS generated */
-2, /* (365) ccons ::= AS generated */
-2, /* (366) conslist_opt ::= COMMA conslist */
-3, /* (367) conslist ::= conslist tconscomma tcons */
-1, /* (368) conslist ::= tcons */
0, /* (369) tconscomma ::= */
-1, /* (370) defer_subclause_opt ::= defer_subclause */
-1, /* (371) resolvetype ::= raisetype */
-1, /* (372) selectnowith ::= oneselect */
-1, /* (373) oneselect ::= values */
-2, /* (374) sclp ::= selcollist COMMA */
-1, /* (375) as ::= ID|STRING */
0, /* (376) returning ::= */
-1, /* (377) expr ::= term */
-1, /* (378) likeop ::= LIKE_KW|MATCH */
-1, /* (379) exprlist ::= nexprlist */
-1, /* (380) nmnum ::= plus_num */
-1, /* (381) nmnum ::= nm */
-1, /* (382) nmnum ::= ON */
-1, /* (383) nmnum ::= DELETE */
-1, /* (384) nmnum ::= DEFAULT */
-1, /* (385) plus_num ::= INTEGER|FLOAT */
0, /* (386) foreach_clause ::= */
-3, /* (387) foreach_clause ::= FOR EACH ROW */
-1, /* (388) trnm ::= nm */
0, /* (389) tridxby ::= */
-1, /* (390) database_kw_opt ::= DATABASE */
0, /* (391) database_kw_opt ::= */
0, /* (392) kwcolumn_opt ::= */
-1, /* (393) kwcolumn_opt ::= COLUMNKW */
-1, /* (394) vtabarglist ::= vtabarg */
-3, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */
-2, /* (396) vtabarg ::= vtabarg vtabargtoken */
0, /* (397) anylist ::= */
-4, /* (398) anylist ::= anylist LP anylist RP */
-2, /* (399) anylist ::= anylist ANY */
0, /* (400) with ::= */
};
static void yy_accept(yyParser*); /* Forward Declaration */
/*
** Perform a reduce action and the shift that must immediately
** follow the reduce.
|
| ︙ | ︙ | |||
161774 161775 161776 161777 161778 161779 161780 |
case 1: /* explain ::= EXPLAIN QUERY PLAN */
{ pParse->explain = 2; }
break;
case 2: /* cmdx ::= cmd */
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
| | | | | | 162274 162275 162276 162277 162278 162279 162280 162281 162282 162283 162284 162285 162286 162287 162288 162289 162290 162291 162292 162293 162294 162295 162296 162297 |
case 1: /* explain ::= EXPLAIN QUERY PLAN */
{ pParse->explain = 2; }
break;
case 2: /* cmdx ::= cmd */
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy64);}
break;
case 4: /* transtype ::= */
{yymsp[1].minor.yy64 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
case 320: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==320);
{yymsp[0].minor.yy64 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
{sqlite3EndTransaction(pParse,yymsp[-1].major);}
break;
case 10: /* cmd ::= SAVEPOINT nm */
{
|
| ︙ | ︙ | |||
161806 161807 161808 161809 161810 161811 161812 |
case 12: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &yymsp[0].minor.yy0);
}
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
| | < | | | | | | | | | | | | | > > > | > > > > | | > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 162306 162307 162308 162309 162310 162311 162312 162313 162314 162315 162316 162317 162318 162319 162320 162321 162322 162323 162324 162325 162326 162327 162328 162329 162330 162331 162332 162333 162334 162335 162336 162337 162338 162339 162340 162341 162342 162343 162344 162345 162346 162347 162348 162349 162350 162351 162352 162353 162354 162355 162356 162357 162358 162359 162360 162361 162362 162363 162364 162365 162366 162367 162368 162369 162370 162371 162372 162373 162374 162375 162376 162377 162378 162379 162380 162381 162382 162383 162384 162385 162386 162387 162388 162389 162390 162391 162392 162393 162394 162395 162396 162397 162398 162399 162400 162401 162402 162403 162404 162405 162406 162407 162408 162409 162410 162411 162412 162413 162414 162415 162416 162417 162418 162419 162420 162421 162422 162423 162424 162425 162426 162427 162428 162429 162430 162431 162432 162433 162434 162435 162436 162437 162438 162439 162440 162441 162442 162443 162444 162445 162446 162447 162448 162449 162450 162451 162452 162453 162454 162455 162456 162457 162458 162459 162460 162461 162462 162463 162464 162465 162466 162467 162468 162469 162470 162471 162472 162473 162474 162475 162476 162477 162478 162479 162480 162481 162482 162483 162484 162485 162486 162487 162488 162489 162490 162491 162492 162493 162494 162495 162496 162497 162498 162499 162500 162501 162502 162503 162504 162505 162506 162507 162508 162509 162510 162511 162512 162513 162514 162515 162516 162517 162518 162519 162520 162521 162522 162523 162524 162525 162526 162527 162528 162529 162530 162531 162532 162533 162534 162535 162536 162537 162538 162539 162540 162541 162542 162543 162544 162545 162546 162547 162548 162549 162550 162551 162552 162553 162554 162555 162556 162557 162558 162559 162560 162561 162562 162563 162564 162565 162566 162567 162568 162569 162570 162571 162572 162573 162574 162575 162576 162577 162578 162579 162580 162581 162582 162583 162584 162585 162586 162587 162588 162589 162590 162591 162592 162593 162594 162595 162596 162597 162598 162599 162600 162601 162602 162603 162604 162605 162606 162607 162608 162609 162610 162611 162612 162613 162614 162615 162616 162617 162618 162619 162620 162621 162622 162623 162624 162625 162626 162627 162628 162629 162630 162631 162632 162633 162634 162635 162636 162637 162638 162639 162640 162641 162642 162643 162644 162645 162646 162647 162648 162649 162650 162651 162652 162653 162654 162655 162656 162657 162658 162659 162660 162661 162662 162663 162664 162665 162666 162667 162668 162669 162670 162671 162672 162673 162674 162675 162676 162677 162678 162679 162680 162681 162682 162683 162684 162685 162686 162687 162688 162689 162690 162691 162692 162693 162694 162695 162696 162697 162698 162699 162700 162701 162702 162703 162704 162705 162706 162707 162708 162709 162710 162711 162712 162713 162714 162715 162716 162717 162718 162719 162720 162721 162722 162723 162724 162725 162726 162727 162728 162729 162730 162731 162732 162733 162734 162735 162736 162737 162738 162739 162740 162741 162742 162743 162744 162745 162746 162747 162748 162749 162750 162751 162752 162753 162754 162755 162756 162757 162758 162759 162760 162761 162762 162763 162764 162765 162766 162767 162768 162769 162770 162771 162772 162773 162774 162775 162776 162777 162778 162779 162780 162781 162782 162783 162784 162785 162786 162787 162788 162789 162790 162791 162792 162793 162794 162795 162796 162797 162798 162799 162800 162801 162802 162803 162804 162805 162806 162807 162808 162809 162810 162811 162812 162813 162814 162815 162816 162817 162818 162819 162820 162821 162822 162823 162824 162825 162826 162827 162828 162829 162830 162831 162832 162833 162834 162835 162836 162837 162838 162839 162840 162841 162842 162843 162844 162845 162846 162847 162848 162849 162850 162851 162852 162853 162854 162855 162856 162857 162858 162859 162860 162861 162862 162863 162864 162865 162866 162867 162868 162869 162870 162871 162872 162873 162874 162875 162876 162877 162878 162879 162880 162881 162882 162883 162884 162885 162886 162887 162888 162889 162890 162891 162892 162893 162894 162895 162896 162897 162898 162899 162900 162901 162902 162903 162904 162905 162906 162907 162908 162909 162910 162911 162912 162913 162914 162915 162916 162917 162918 162919 162920 162921 162922 162923 162924 162925 162926 162927 162928 162929 162930 162931 162932 162933 162934 162935 162936 162937 162938 162939 162940 162941 162942 162943 162944 162945 162946 162947 162948 162949 162950 162951 162952 162953 162954 162955 162956 162957 162958 162959 162960 162961 162962 162963 162964 162965 162966 162967 162968 162969 162970 162971 162972 162973 162974 162975 162976 162977 162978 162979 162980 162981 162982 162983 162984 162985 162986 162987 162988 162989 162990 162991 162992 162993 162994 162995 162996 162997 162998 162999 163000 163001 163002 163003 163004 163005 163006 163007 163008 163009 163010 163011 163012 163013 163014 163015 163016 163017 163018 163019 163020 163021 163022 163023 163024 163025 163026 163027 163028 163029 163030 163031 163032 163033 163034 163035 163036 163037 163038 163039 163040 163041 163042 163043 163044 163045 163046 163047 163048 163049 163050 163051 163052 163053 163054 163055 163056 163057 163058 163059 163060 163061 163062 163063 163064 163065 163066 163067 163068 163069 163070 163071 163072 163073 163074 163075 163076 163077 163078 163079 163080 163081 163082 163083 163084 163085 163086 163087 163088 163089 163090 163091 163092 163093 163094 163095 163096 163097 163098 163099 163100 163101 163102 163103 163104 163105 163106 163107 163108 163109 163110 163111 163112 163113 163114 163115 163116 163117 163118 163119 163120 163121 163122 163123 163124 163125 163126 163127 163128 163129 163130 163131 163132 163133 163134 163135 163136 163137 163138 163139 163140 163141 163142 163143 163144 163145 163146 163147 163148 163149 163150 163151 163152 163153 163154 163155 163156 163157 163158 163159 163160 163161 163162 163163 163164 163165 163166 163167 163168 163169 163170 163171 163172 163173 163174 163175 163176 163177 163178 163179 163180 163181 163182 163183 163184 163185 163186 163187 163188 163189 163190 163191 163192 163193 163194 163195 163196 163197 163198 163199 163200 163201 163202 163203 163204 163205 163206 163207 163208 163209 163210 163211 163212 163213 163214 163215 163216 163217 163218 163219 163220 163221 163222 163223 163224 163225 163226 163227 163228 163229 163230 163231 163232 163233 163234 163235 163236 163237 163238 163239 163240 163241 163242 163243 163244 163245 163246 163247 163248 163249 163250 163251 163252 163253 163254 163255 163256 163257 163258 163259 163260 163261 163262 163263 163264 163265 163266 163267 163268 163269 163270 163271 163272 163273 163274 163275 163276 163277 163278 163279 163280 163281 163282 163283 163284 163285 163286 163287 163288 163289 163290 163291 163292 163293 163294 163295 163296 163297 163298 163299 163300 163301 163302 163303 163304 163305 163306 163307 163308 163309 163310 163311 163312 163313 163314 163315 163316 163317 163318 163319 163320 163321 163322 163323 163324 163325 163326 163327 163328 163329 163330 163331 163332 163333 163334 163335 163336 163337 163338 163339 163340 163341 163342 163343 163344 163345 163346 163347 163348 163349 163350 163351 163352 163353 163354 163355 163356 163357 163358 163359 163360 163361 163362 163363 163364 163365 163366 163367 163368 163369 163370 163371 163372 163373 163374 163375 163376 163377 163378 163379 163380 163381 163382 163383 163384 163385 163386 163387 163388 163389 163390 163391 163392 163393 163394 163395 163396 163397 163398 163399 163400 163401 163402 163403 163404 163405 163406 163407 163408 163409 163410 163411 163412 163413 163414 163415 163416 163417 163418 163419 163420 163421 163422 163423 163424 163425 163426 163427 163428 163429 163430 163431 163432 163433 163434 163435 163436 163437 163438 163439 163440 163441 163442 163443 163444 163445 163446 163447 163448 163449 163450 163451 163452 163453 163454 163455 163456 163457 163458 163459 163460 163461 163462 163463 163464 163465 163466 163467 163468 163469 163470 163471 163472 163473 163474 163475 163476 163477 163478 163479 163480 163481 163482 163483 163484 163485 163486 163487 163488 163489 163490 163491 163492 163493 163494 163495 163496 163497 163498 163499 163500 163501 163502 163503 163504 163505 163506 163507 163508 163509 163510 163511 163512 163513 163514 163515 163516 163517 163518 163519 163520 163521 163522 163523 163524 163525 163526 163527 163528 163529 163530 163531 163532 163533 163534 163535 163536 163537 163538 163539 163540 163541 163542 163543 163544 163545 163546 163547 163548 163549 163550 163551 163552 163553 163554 163555 163556 163557 163558 163559 163560 163561 163562 163563 163564 163565 163566 163567 163568 163569 163570 163571 163572 163573 163574 163575 163576 163577 163578 163579 163580 163581 163582 163583 163584 163585 163586 163587 163588 163589 163590 163591 163592 163593 163594 163595 163596 163597 163598 163599 163600 163601 163602 163603 163604 163605 163606 163607 163608 163609 163610 163611 163612 163613 163614 163615 163616 163617 163618 163619 163620 163621 163622 163623 163624 163625 163626 163627 163628 163629 163630 163631 163632 163633 163634 163635 163636 163637 163638 163639 163640 163641 163642 163643 163644 163645 163646 163647 163648 163649 163650 163651 163652 163653 163654 163655 163656 163657 163658 163659 163660 163661 163662 163663 163664 163665 163666 163667 163668 163669 163670 163671 163672 163673 163674 163675 163676 163677 163678 163679 163680 163681 163682 163683 163684 163685 163686 163687 163688 163689 163690 163691 163692 163693 163694 163695 163696 163697 163698 163699 163700 163701 163702 163703 163704 163705 163706 163707 163708 163709 163710 163711 163712 163713 163714 163715 163716 163717 163718 163719 163720 163721 163722 163723 163724 163725 163726 163727 163728 163729 163730 163731 163732 163733 163734 163735 163736 163737 163738 163739 163740 163741 163742 163743 163744 163745 163746 163747 163748 163749 163750 163751 163752 |
case 12: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &yymsp[0].minor.yy0);
}
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy64,0,0,yymsp[-2].minor.yy64);
}
break;
case 14: /* createkw ::= CREATE */
{disableLookaside(pParse);}
break;
case 15: /* ifnotexists ::= */
case 18: /* temp ::= */ yytestcase(yyruleno==18);
case 47: /* autoinc ::= */ yytestcase(yyruleno==47);
case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62);
case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72);
case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
case 98: /* distinct ::= */ yytestcase(yyruleno==98);
case 241: /* collate ::= */ yytestcase(yyruleno==241);
{yymsp[1].minor.yy64 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
{yymsp[-2].minor.yy64 = 1;}
break;
case 17: /* temp ::= TEMP */
{yymsp[0].minor.yy64 = pParse->db->init.busy==0;}
break;
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */
{
sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy51,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy303);
sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy303);
}
break;
case 21: /* table_option_set ::= */
{yymsp[1].minor.yy51 = 0;}
break;
case 22: /* table_option_set ::= table_option_set COMMA table_option */
{yylhsminor.yy51 = yymsp[-2].minor.yy51|yymsp[0].minor.yy51;}
yymsp[-2].minor.yy51 = yylhsminor.yy51;
break;
case 23: /* table_option ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
yymsp[-1].minor.yy51 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
yymsp[-1].minor.yy51 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
break;
case 24: /* table_option ::= nm */
{
if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){
yylhsminor.yy51 = TF_Strict;
}else{
yylhsminor.yy51 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
yymsp[0].minor.yy51 = yylhsminor.yy51;
break;
case 25: /* columnname ::= nm typetoken */
{sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);}
break;
case 26: /* typetoken ::= */
case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65);
case 104: /* as ::= */ yytestcase(yyruleno==104);
{yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;}
break;
case 27: /* typetoken ::= typename LP signed RP */
{
yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z);
}
break;
case 28: /* typetoken ::= typename LP signed COMMA signed RP */
{
yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z);
}
break;
case 29: /* typename ::= typename ID|STRING */
{yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
break;
case 30: /* scanpt ::= */
{
assert( yyLookahead!=YYNOCODE );
yymsp[1].minor.yy600 = yyLookaheadToken.z;
}
break;
case 31: /* scantok ::= */
{
assert( yyLookahead!=YYNOCODE );
yymsp[1].minor.yy0 = yyLookaheadToken;
}
break;
case 32: /* ccons ::= CONSTRAINT nm */
case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67);
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
case 33: /* ccons ::= DEFAULT scantok term */
{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy626,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 34: /* ccons ::= DEFAULT LP expr RP */
{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy626,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
case 35: /* ccons ::= DEFAULT PLUS scantok term */
{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy626,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 36: /* ccons ::= DEFAULT MINUS scantok term */
{
Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy626, 0);
sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
}
break;
case 37: /* ccons ::= DEFAULT scantok ID|INDEXED */
{
Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0);
if( p ){
sqlite3ExprIdToTrueFalse(p);
testcase( p->op==TK_TRUEFALSE && sqlite3ExprTruthValue(p) );
}
sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n);
}
break;
case 38: /* ccons ::= NOT NULL onconf */
{sqlite3AddNotNull(pParse, yymsp[0].minor.yy64);}
break;
case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy64,yymsp[0].minor.yy64,yymsp[-2].minor.yy64);}
break;
case 40: /* ccons ::= UNIQUE onconf */
{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy64,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 41: /* ccons ::= CHECK LP expr RP */
{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy626,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
break;
case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */
{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy562,yymsp[0].minor.yy64);}
break;
case 43: /* ccons ::= defer_subclause */
{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy64);}
break;
case 44: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 45: /* generated ::= LP expr RP */
{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy626,0);}
break;
case 46: /* generated ::= LP expr RP ID */
{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy626,&yymsp[0].minor.yy0);}
break;
case 48: /* autoinc ::= AUTOINCR */
{yymsp[0].minor.yy64 = 1;}
break;
case 49: /* refargs ::= */
{ yymsp[1].minor.yy64 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
case 50: /* refargs ::= refargs refarg */
{ yymsp[-1].minor.yy64 = (yymsp[-1].minor.yy64 & ~yymsp[0].minor.yy83.mask) | yymsp[0].minor.yy83.value; }
break;
case 51: /* refarg ::= MATCH nm */
{ yymsp[-1].minor.yy83.value = 0; yymsp[-1].minor.yy83.mask = 0x000000; }
break;
case 52: /* refarg ::= ON INSERT refact */
{ yymsp[-2].minor.yy83.value = 0; yymsp[-2].minor.yy83.mask = 0x000000; }
break;
case 53: /* refarg ::= ON DELETE refact */
{ yymsp[-2].minor.yy83.value = yymsp[0].minor.yy64; yymsp[-2].minor.yy83.mask = 0x0000ff; }
break;
case 54: /* refarg ::= ON UPDATE refact */
{ yymsp[-2].minor.yy83.value = yymsp[0].minor.yy64<<8; yymsp[-2].minor.yy83.mask = 0x00ff00; }
break;
case 55: /* refact ::= SET NULL */
{ yymsp[-1].minor.yy64 = OE_SetNull; /* EV: R-33326-45252 */}
break;
case 56: /* refact ::= SET DEFAULT */
{ yymsp[-1].minor.yy64 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
case 57: /* refact ::= CASCADE */
{ yymsp[0].minor.yy64 = OE_Cascade; /* EV: R-33326-45252 */}
break;
case 58: /* refact ::= RESTRICT */
{ yymsp[0].minor.yy64 = OE_Restrict; /* EV: R-33326-45252 */}
break;
case 59: /* refact ::= NO ACTION */
{ yymsp[-1].minor.yy64 = OE_None; /* EV: R-33326-45252 */}
break;
case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
{yymsp[-2].minor.yy64 = 0;}
break;
case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76);
case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171);
{yymsp[-1].minor.yy64 = yymsp[0].minor.yy64;}
break;
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
case 213: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==213);
case 216: /* in_op ::= NOT IN */ yytestcase(yyruleno==216);
case 242: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==242);
{yymsp[-1].minor.yy64 = 1;}
break;
case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
{yymsp[-1].minor.yy64 = 0;}
break;
case 66: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy562,yymsp[0].minor.yy64,yymsp[-2].minor.yy64,0);}
break;
case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */
{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy562,yymsp[0].minor.yy64,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 70: /* tcons ::= CHECK LP expr RP onconf */
{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy626,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
break;
case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy562, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy562, yymsp[-1].minor.yy64);
sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy64);
}
break;
case 73: /* onconf ::= */
case 75: /* orconf ::= */ yytestcase(yyruleno==75);
{yymsp[1].minor.yy64 = OE_Default;}
break;
case 74: /* onconf ::= ON CONFLICT resolvetype */
{yymsp[-2].minor.yy64 = yymsp[0].minor.yy64;}
break;
case 77: /* resolvetype ::= IGNORE */
{yymsp[0].minor.yy64 = OE_Ignore;}
break;
case 78: /* resolvetype ::= REPLACE */
case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172);
{yymsp[0].minor.yy64 = OE_Replace;}
break;
case 79: /* cmd ::= DROP TABLE ifexists fullname */
{
sqlite3DropTable(pParse, yymsp[0].minor.yy607, 0, yymsp[-1].minor.yy64);
}
break;
case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy562, yymsp[0].minor.yy303, yymsp[-7].minor.yy64, yymsp[-5].minor.yy64);
}
break;
case 83: /* cmd ::= DROP VIEW ifexists fullname */
{
sqlite3DropTable(pParse, yymsp[0].minor.yy607, 1, yymsp[-1].minor.yy64);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
sqlite3Select(pParse, yymsp[0].minor.yy303, &dest);
sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy303);
}
break;
case 85: /* select ::= WITH wqlist selectnowith */
{yymsp[-2].minor.yy303 = attachWithToSelect(pParse,yymsp[0].minor.yy303,yymsp[-1].minor.yy43);}
break;
case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
{yymsp[-3].minor.yy303 = attachWithToSelect(pParse,yymsp[0].minor.yy303,yymsp[-1].minor.yy43);}
break;
case 87: /* select ::= selectnowith */
{
Select *p = yymsp[0].minor.yy303;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
yymsp[0].minor.yy303 = p; /*A-overwrites-X*/
}
break;
case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
Select *pRhs = yymsp[0].minor.yy303;
Select *pLhs = yymsp[-2].minor.yy303;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
x.n = 0;
parserDoubleLinkSelect(pParse, pRhs);
pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0);
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
pRhs->op = (u8)yymsp[-1].minor.yy64;
pRhs->pPrior = pLhs;
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
if( yymsp[-1].minor.yy64!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
yymsp[-2].minor.yy303 = pRhs;
}
break;
case 89: /* multiselect_op ::= UNION */
case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91);
{yymsp[0].minor.yy64 = yymsp[0].major; /*A-overwrites-OP*/}
break;
case 90: /* multiselect_op ::= UNION ALL */
{yymsp[-1].minor.yy64 = TK_ALL;}
break;
case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
yymsp[-8].minor.yy303 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy562,yymsp[-5].minor.yy607,yymsp[-4].minor.yy626,yymsp[-3].minor.yy562,yymsp[-2].minor.yy626,yymsp[-1].minor.yy562,yymsp[-7].minor.yy64,yymsp[0].minor.yy626);
}
break;
case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
yymsp[-9].minor.yy303 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy562,yymsp[-6].minor.yy607,yymsp[-5].minor.yy626,yymsp[-4].minor.yy562,yymsp[-3].minor.yy626,yymsp[-1].minor.yy562,yymsp[-8].minor.yy64,yymsp[0].minor.yy626);
if( yymsp[-9].minor.yy303 ){
yymsp[-9].minor.yy303->pWinDefn = yymsp[-2].minor.yy375;
}else{
sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy375);
}
}
break;
case 94: /* values ::= VALUES LP nexprlist RP */
{
yymsp[-3].minor.yy303 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy562,0,0,0,0,0,SF_Values,0);
}
break;
case 95: /* values ::= values COMMA LP nexprlist RP */
{
Select *pRight, *pLeft = yymsp[-4].minor.yy303;
pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy562,0,0,0,0,0,SF_Values|SF_MultiValue,0);
if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
if( pRight ){
pRight->op = TK_ALL;
pRight->pPrior = pLeft;
yymsp[-4].minor.yy303 = pRight;
}else{
yymsp[-4].minor.yy303 = pLeft;
}
}
break;
case 96: /* distinct ::= DISTINCT */
{yymsp[0].minor.yy64 = SF_Distinct;}
break;
case 97: /* distinct ::= ALL */
{yymsp[0].minor.yy64 = SF_All;}
break;
case 99: /* sclp ::= */
case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142);
case 229: /* exprlist ::= */ yytestcase(yyruleno==229);
case 232: /* paren_exprlist ::= */ yytestcase(yyruleno==232);
case 237: /* eidlist_opt ::= */ yytestcase(yyruleno==237);
{yymsp[1].minor.yy562 = 0;}
break;
case 100: /* selcollist ::= sclp scanpt expr scanpt as */
{
yymsp[-4].minor.yy562 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy562, yymsp[-2].minor.yy626);
if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy562, &yymsp[0].minor.yy0, 1);
sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy562,yymsp[-3].minor.yy600,yymsp[-1].minor.yy600);
}
break;
case 101: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
yymsp[-2].minor.yy562 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy562, p);
}
break;
case 102: /* selcollist ::= sclp scanpt nm DOT STAR */
{
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
yymsp[-4].minor.yy562 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy562, pDot);
}
break;
case 103: /* as ::= AS nm */
case 114: /* dbnm ::= DOT nm */ yytestcase(yyruleno==114);
case 253: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==253);
case 254: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==254);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 105: /* from ::= */
case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108);
{yymsp[1].minor.yy607 = 0;}
break;
case 106: /* from ::= FROM seltablist */
{
yymsp[-1].minor.yy607 = yymsp[0].minor.yy607;
sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy607);
}
break;
case 107: /* stl_prefix ::= seltablist joinop */
{
if( ALWAYS(yymsp[-1].minor.yy607 && yymsp[-1].minor.yy607->nSrc>0) ) yymsp[-1].minor.yy607->a[yymsp[-1].minor.yy607->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy64;
}
break;
case 109: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
{
yymsp[-6].minor.yy607 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy607,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy626,yymsp[0].minor.yy240);
sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy607, &yymsp[-2].minor.yy0);
}
break;
case 110: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
{
yymsp[-8].minor.yy607 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy607,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy626,yymsp[0].minor.yy240);
sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy607, yymsp[-4].minor.yy562);
}
break;
case 111: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
{
yymsp[-6].minor.yy607 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy607,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy303,yymsp[-1].minor.yy626,yymsp[0].minor.yy240);
}
break;
case 112: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
{
if( yymsp[-6].minor.yy607==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy626==0 && yymsp[0].minor.yy240==0 ){
yymsp[-6].minor.yy607 = yymsp[-4].minor.yy607;
}else if( yymsp[-4].minor.yy607->nSrc==1 ){
yymsp[-6].minor.yy607 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy607,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy626,yymsp[0].minor.yy240);
if( yymsp[-6].minor.yy607 ){
SrcItem *pNew = &yymsp[-6].minor.yy607->a[yymsp[-6].minor.yy607->nSrc-1];
SrcItem *pOld = yymsp[-4].minor.yy607->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
if( pOld->fg.isTabFunc ){
pNew->u1.pFuncArg = pOld->u1.pFuncArg;
pOld->u1.pFuncArg = 0;
pOld->fg.isTabFunc = 0;
pNew->fg.isTabFunc = 1;
}
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy607);
}else{
Select *pSubquery;
sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy607);
pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy607,0,0,0,0,SF_NestedFrom,0);
yymsp[-6].minor.yy607 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy607,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy626,yymsp[0].minor.yy240);
}
}
break;
case 113: /* dbnm ::= */
case 127: /* indexed_opt ::= */ yytestcase(yyruleno==127);
{yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
break;
case 115: /* fullname ::= nm */
{
yylhsminor.yy607 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
if( IN_RENAME_OBJECT && yylhsminor.yy607 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy607->a[0].zName, &yymsp[0].minor.yy0);
}
yymsp[0].minor.yy607 = yylhsminor.yy607;
break;
case 116: /* fullname ::= nm DOT nm */
{
yylhsminor.yy607 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
if( IN_RENAME_OBJECT && yylhsminor.yy607 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy607->a[0].zName, &yymsp[0].minor.yy0);
}
yymsp[-2].minor.yy607 = yylhsminor.yy607;
break;
case 117: /* xfullname ::= nm */
{yymsp[0].minor.yy607 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
case 118: /* xfullname ::= nm DOT nm */
{yymsp[-2].minor.yy607 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 119: /* xfullname ::= nm DOT nm AS nm */
{
yymsp[-4].minor.yy607 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
if( yymsp[-4].minor.yy607 ) yymsp[-4].minor.yy607->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 120: /* xfullname ::= nm AS nm */
{
yymsp[-2].minor.yy607 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
if( yymsp[-2].minor.yy607 ) yymsp[-2].minor.yy607->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 121: /* joinop ::= COMMA|JOIN */
{ yymsp[0].minor.yy64 = JT_INNER; }
break;
case 122: /* joinop ::= JOIN_KW JOIN */
{yymsp[-1].minor.yy64 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
case 123: /* joinop ::= JOIN_KW nm JOIN */
{yymsp[-2].minor.yy64 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
case 124: /* joinop ::= JOIN_KW nm nm JOIN */
{yymsp[-3].minor.yy64 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
case 125: /* on_opt ::= ON expr */
case 145: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==145);
case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152);
case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154);
case 225: /* case_else ::= ELSE expr */ yytestcase(yyruleno==225);
case 246: /* vinto ::= INTO expr */ yytestcase(yyruleno==246);
{yymsp[-1].minor.yy626 = yymsp[0].minor.yy626;}
break;
case 126: /* on_opt ::= */
case 144: /* having_opt ::= */ yytestcase(yyruleno==144);
case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
case 226: /* case_else ::= */ yytestcase(yyruleno==226);
case 228: /* case_operand ::= */ yytestcase(yyruleno==228);
case 247: /* vinto ::= */ yytestcase(yyruleno==247);
{yymsp[1].minor.yy626 = 0;}
break;
case 128: /* indexed_opt ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 129: /* indexed_opt ::= NOT INDEXED */
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
case 130: /* using_opt ::= USING LP idlist RP */
{yymsp[-3].minor.yy240 = yymsp[-1].minor.yy240;}
break;
case 131: /* using_opt ::= */
case 173: /* idlist_opt ::= */ yytestcase(yyruleno==173);
{yymsp[1].minor.yy240 = 0;}
break;
case 133: /* orderby_opt ::= ORDER BY sortlist */
case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143);
{yymsp[-2].minor.yy562 = yymsp[0].minor.yy562;}
break;
case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */
{
yymsp[-4].minor.yy562 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy562,yymsp[-2].minor.yy626);
sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy562,yymsp[-1].minor.yy64,yymsp[0].minor.yy64);
}
break;
case 135: /* sortlist ::= expr sortorder nulls */
{
yymsp[-2].minor.yy562 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy626); /*A-overwrites-Y*/
sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy562,yymsp[-1].minor.yy64,yymsp[0].minor.yy64);
}
break;
case 136: /* sortorder ::= ASC */
{yymsp[0].minor.yy64 = SQLITE_SO_ASC;}
break;
case 137: /* sortorder ::= DESC */
{yymsp[0].minor.yy64 = SQLITE_SO_DESC;}
break;
case 138: /* sortorder ::= */
case 141: /* nulls ::= */ yytestcase(yyruleno==141);
{yymsp[1].minor.yy64 = SQLITE_SO_UNDEFINED;}
break;
case 139: /* nulls ::= NULLS FIRST */
{yymsp[-1].minor.yy64 = SQLITE_SO_ASC;}
break;
case 140: /* nulls ::= NULLS LAST */
{yymsp[-1].minor.yy64 = SQLITE_SO_DESC;}
break;
case 147: /* limit_opt ::= LIMIT expr */
{yymsp[-1].minor.yy626 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy626,0);}
break;
case 148: /* limit_opt ::= LIMIT expr OFFSET expr */
{yymsp[-3].minor.yy626 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy626,yymsp[0].minor.yy626);}
break;
case 149: /* limit_opt ::= LIMIT expr COMMA expr */
{yymsp[-3].minor.yy626 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy626,yymsp[-2].minor.yy626);}
break;
case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
{
sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy607, &yymsp[-1].minor.yy0);
sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy607,yymsp[0].minor.yy626,0,0);
}
break;
case 155: /* where_opt_ret ::= RETURNING selcollist */
{sqlite3AddReturning(pParse,yymsp[0].minor.yy562); yymsp[-1].minor.yy626 = 0;}
break;
case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
{sqlite3AddReturning(pParse,yymsp[0].minor.yy562); yymsp[-3].minor.yy626 = yymsp[-2].minor.yy626;}
break;
case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
{
sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy607, &yymsp[-4].minor.yy0);
sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy562,"set list");
yymsp[-5].minor.yy607 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy607, yymsp[-1].minor.yy607);
sqlite3Update(pParse,yymsp[-5].minor.yy607,yymsp[-2].minor.yy562,yymsp[0].minor.yy626,yymsp[-6].minor.yy64,0,0,0);
}
break;
case 158: /* setlist ::= setlist COMMA nm EQ expr */
{
yymsp[-4].minor.yy562 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy562, yymsp[0].minor.yy626);
sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy562, &yymsp[-2].minor.yy0, 1);
}
break;
case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
yymsp[-6].minor.yy562 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy562, yymsp[-3].minor.yy240, yymsp[0].minor.yy626);
}
break;
case 160: /* setlist ::= nm EQ expr */
{
yylhsminor.yy562 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy626);
sqlite3ExprListSetName(pParse, yylhsminor.yy562, &yymsp[-2].minor.yy0, 1);
}
yymsp[-2].minor.yy562 = yylhsminor.yy562;
break;
case 161: /* setlist ::= LP idlist RP EQ expr */
{
yymsp[-4].minor.yy562 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy240, yymsp[0].minor.yy626);
}
break;
case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
sqlite3Insert(pParse, yymsp[-3].minor.yy607, yymsp[-1].minor.yy303, yymsp[-2].minor.yy240, yymsp[-5].minor.yy64, yymsp[0].minor.yy138);
}
break;
case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
{
sqlite3Insert(pParse, yymsp[-4].minor.yy607, 0, yymsp[-3].minor.yy240, yymsp[-6].minor.yy64, 0);
}
break;
case 164: /* upsert ::= */
{ yymsp[1].minor.yy138 = 0; }
break;
case 165: /* upsert ::= RETURNING selcollist */
{ yymsp[-1].minor.yy138 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy562); }
break;
case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
{ yymsp[-11].minor.yy138 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy562,yymsp[-6].minor.yy626,yymsp[-2].minor.yy562,yymsp[-1].minor.yy626,yymsp[0].minor.yy138);}
break;
case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
{ yymsp[-8].minor.yy138 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy562,yymsp[-3].minor.yy626,0,0,yymsp[0].minor.yy138); }
break;
case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */
{ yymsp[-4].minor.yy138 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
break;
case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
{ yymsp[-7].minor.yy138 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy562,yymsp[-1].minor.yy626,0);}
break;
case 170: /* returning ::= RETURNING selcollist */
{sqlite3AddReturning(pParse,yymsp[0].minor.yy562);}
break;
case 174: /* idlist_opt ::= LP idlist RP */
{yymsp[-2].minor.yy240 = yymsp[-1].minor.yy240;}
break;
case 175: /* idlist ::= idlist COMMA nm */
{yymsp[-2].minor.yy240 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy240,&yymsp[0].minor.yy0);}
break;
case 176: /* idlist ::= nm */
{yymsp[0].minor.yy240 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
case 177: /* expr ::= LP expr RP */
{yymsp[-2].minor.yy626 = yymsp[-1].minor.yy626;}
break;
case 178: /* expr ::= ID|INDEXED */
case 179: /* expr ::= JOIN_KW */ yytestcase(yyruleno==179);
{yymsp[0].minor.yy626=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 180: /* expr ::= nm DOT nm */
{
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
}
yylhsminor.yy626 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
yymsp[-2].minor.yy626 = yylhsminor.yy626;
break;
case 181: /* expr ::= nm DOT nm DOT nm */
{
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
}
yylhsminor.yy626 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
yymsp[-4].minor.yy626 = yylhsminor.yy626;
break;
case 182: /* term ::= NULL|FLOAT|BLOB */
case 183: /* term ::= STRING */ yytestcase(yyruleno==183);
{yymsp[0].minor.yy626=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 184: /* term ::= INTEGER */
{
yylhsminor.yy626 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
}
yymsp[0].minor.yy626 = yylhsminor.yy626;
break;
case 185: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
yymsp[0].minor.yy626 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy626, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
** in the virtual machine. #N is the N-th register. */
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
yymsp[0].minor.yy626 = 0;
}else{
yymsp[0].minor.yy626 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
if( yymsp[0].minor.yy626 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy626->iTable);
}
}
}
break;
case 186: /* expr ::= expr COLLATE ID|STRING */
{
yymsp[-2].minor.yy626 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy626, &yymsp[0].minor.yy0, 1);
}
break;
case 187: /* expr ::= CAST LP expr AS typetoken RP */
{
yymsp[-5].minor.yy626 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy626, yymsp[-3].minor.yy626, 0);
}
break;
case 188: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
yylhsminor.yy626 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy562, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy64);
}
yymsp[-4].minor.yy626 = yylhsminor.yy626;
break;
case 189: /* expr ::= ID|INDEXED LP STAR RP */
{
yylhsminor.yy626 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
yymsp[-3].minor.yy626 = yylhsminor.yy626;
break;
case 190: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
{
yylhsminor.yy626 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy562, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy64);
sqlite3WindowAttach(pParse, yylhsminor.yy626, yymsp[0].minor.yy375);
}
yymsp[-5].minor.yy626 = yylhsminor.yy626;
break;
case 191: /* expr ::= ID|INDEXED LP STAR RP filter_over */
{
yylhsminor.yy626 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
sqlite3WindowAttach(pParse, yylhsminor.yy626, yymsp[0].minor.yy375);
}
yymsp[-4].minor.yy626 = yylhsminor.yy626;
break;
case 192: /* term ::= CTIME_KW */
{
yylhsminor.yy626 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
yymsp[0].minor.yy626 = yylhsminor.yy626;
break;
case 193: /* expr ::= LP nexprlist COMMA expr RP */
{
ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy562, yymsp[-1].minor.yy626);
yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
if( yymsp[-4].minor.yy626 ){
yymsp[-4].minor.yy626->x.pList = pList;
if( ALWAYS(pList->nExpr) ){
yymsp[-4].minor.yy626->flags |= pList->a[0].pExpr->flags & EP_Propagate;
}
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
break;
case 194: /* expr ::= expr AND expr */
{yymsp[-2].minor.yy626=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy626,yymsp[0].minor.yy626);}
break;
case 195: /* expr ::= expr OR expr */
case 196: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==196);
case 197: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==197);
case 198: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==198);
case 199: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==199);
case 200: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==200);
case 201: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==201);
{yymsp[-2].minor.yy626=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy626,yymsp[0].minor.yy626);}
break;
case 202: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
case 203: /* expr ::= expr likeop expr */
{
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy626);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy626);
yymsp[-2].minor.yy626 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
if( bNot ) yymsp[-2].minor.yy626 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy626, 0);
if( yymsp[-2].minor.yy626 ) yymsp[-2].minor.yy626->flags |= EP_InfixFunc;
}
break;
case 204: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy626);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy626);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy626);
yymsp[-4].minor.yy626 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
if( bNot ) yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy626, 0);
if( yymsp[-4].minor.yy626 ) yymsp[-4].minor.yy626->flags |= EP_InfixFunc;
}
break;
case 205: /* expr ::= expr ISNULL|NOTNULL */
{yymsp[-1].minor.yy626 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy626,0);}
break;
case 206: /* expr ::= expr NOT NULL */
{yymsp[-2].minor.yy626 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy626,0);}
break;
case 207: /* expr ::= expr IS expr */
{
yymsp[-2].minor.yy626 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy626,yymsp[0].minor.yy626);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy626, yymsp[-2].minor.yy626, TK_ISNULL);
}
break;
case 208: /* expr ::= expr IS NOT expr */
{
yymsp[-3].minor.yy626 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy626,yymsp[0].minor.yy626);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy626, yymsp[-3].minor.yy626, TK_NOTNULL);
}
break;
case 209: /* expr ::= NOT expr */
case 210: /* expr ::= BITNOT expr */ yytestcase(yyruleno==210);
{yymsp[-1].minor.yy626 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy626, 0);/*A-overwrites-B*/}
break;
case 211: /* expr ::= PLUS|MINUS expr */
{
yymsp[-1].minor.yy626 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy626, 0);
/*A-overwrites-B*/
}
break;
case 212: /* between_op ::= BETWEEN */
case 215: /* in_op ::= IN */ yytestcase(yyruleno==215);
{yymsp[0].minor.yy64 = 0;}
break;
case 214: /* expr ::= expr between_op expr AND expr */
{
ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy626);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy626);
yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy626, 0);
if( yymsp[-4].minor.yy626 ){
yymsp[-4].minor.yy626->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
if( yymsp[-3].minor.yy64 ) yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy626, 0);
}
break;
case 217: /* expr ::= expr in_op LP exprlist RP */
{
if( yymsp[-1].minor.yy562==0 ){
/* Expressions of the form
**
** expr1 IN ()
** expr1 NOT IN ()
**
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy626);
yymsp[-4].minor.yy626 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy64 ? "1" : "0");
}else{
Expr *pRHS = yymsp[-1].minor.yy562->a[0].pExpr;
if( yymsp[-1].minor.yy562->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy626->op!=TK_VECTOR ){
yymsp[-1].minor.yy562->a[0].pExpr = 0;
sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy562);
pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy626, pRHS);
}else{
yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy626, 0);
if( yymsp[-4].minor.yy626==0 ){
sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy562);
}else if( yymsp[-4].minor.yy626->pLeft->op==TK_VECTOR ){
int nExpr = yymsp[-4].minor.yy626->pLeft->x.pList->nExpr;
Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy562);
if( pSelectRHS ){
parserDoubleLinkSelect(pParse, pSelectRHS);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy626, pSelectRHS);
}
}else{
yymsp[-4].minor.yy626->x.pList = yymsp[-1].minor.yy562;
sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy626);
}
}
if( yymsp[-3].minor.yy64 ) yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy626, 0);
}
}
break;
case 218: /* expr ::= LP select RP */
{
yymsp[-2].minor.yy626 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy626, yymsp[-1].minor.yy303);
}
break;
case 219: /* expr ::= expr in_op LP select RP */
{
yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy626, 0);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy626, yymsp[-1].minor.yy303);
if( yymsp[-3].minor.yy64 ) yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy626, 0);
}
break;
case 220: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
if( yymsp[0].minor.yy562 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy562);
yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy626, 0);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy626, pSelect);
if( yymsp[-3].minor.yy64 ) yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy626, 0);
}
break;
case 221: /* expr ::= EXISTS LP select RP */
{
Expr *p;
p = yymsp[-3].minor.yy626 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy303);
}
break;
case 222: /* expr ::= CASE case_operand case_exprlist case_else END */
{
yymsp[-4].minor.yy626 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy626, 0);
if( yymsp[-4].minor.yy626 ){
yymsp[-4].minor.yy626->x.pList = yymsp[-1].minor.yy626 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy562,yymsp[-1].minor.yy626) : yymsp[-2].minor.yy562;
sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy626);
}else{
sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy562);
sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy626);
}
}
break;
case 223: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
yymsp[-4].minor.yy562 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy562, yymsp[-2].minor.yy626);
yymsp[-4].minor.yy562 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy562, yymsp[0].minor.yy626);
}
break;
case 224: /* case_exprlist ::= WHEN expr THEN expr */
{
yymsp[-3].minor.yy562 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy626);
yymsp[-3].minor.yy562 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy562, yymsp[0].minor.yy626);
}
break;
case 227: /* case_operand ::= expr */
{yymsp[0].minor.yy626 = yymsp[0].minor.yy626; /*A-overwrites-X*/}
break;
case 230: /* nexprlist ::= nexprlist COMMA expr */
{yymsp[-2].minor.yy562 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy562,yymsp[0].minor.yy626);}
break;
case 231: /* nexprlist ::= expr */
{yymsp[0].minor.yy562 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy626); /*A-overwrites-Y*/}
break;
case 233: /* paren_exprlist ::= LP exprlist RP */
case 238: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==238);
{yymsp[-2].minor.yy562 = yymsp[-1].minor.yy562;}
break;
case 234: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy562, yymsp[-10].minor.yy64,
&yymsp[-11].minor.yy0, yymsp[0].minor.yy626, SQLITE_SO_ASC, yymsp[-8].minor.yy64, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
}
break;
case 235: /* uniqueflag ::= UNIQUE */
case 277: /* raisetype ::= ABORT */ yytestcase(yyruleno==277);
{yymsp[0].minor.yy64 = OE_Abort;}
break;
case 236: /* uniqueflag ::= */
{yymsp[1].minor.yy64 = OE_None;}
break;
case 239: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
yymsp[-4].minor.yy562 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy562, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy64, yymsp[0].minor.yy64);
}
break;
case 240: /* eidlist ::= nm collate sortorder */
{
yymsp[-2].minor.yy562 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy64, yymsp[0].minor.yy64); /*A-overwrites-Y*/
}
break;
case 243: /* cmd ::= DROP INDEX ifexists fullname */
{sqlite3DropIndex(pParse, yymsp[0].minor.yy607, yymsp[-1].minor.yy64);}
break;
case 244: /* cmd ::= VACUUM vinto */
{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy626);}
break;
case 245: /* cmd ::= VACUUM nm vinto */
{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy626);}
break;
case 248: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
case 249: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
case 250: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
case 251: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
case 252: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
case 255: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy95, &all);
}
break;
case 256: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy64, yymsp[-4].minor.yy570.a, yymsp[-4].minor.yy570.b, yymsp[-2].minor.yy607, yymsp[0].minor.yy626, yymsp[-10].minor.yy64, yymsp[-8].minor.yy64);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
case 257: /* trigger_time ::= BEFORE|AFTER */
{ yymsp[0].minor.yy64 = yymsp[0].major; /*A-overwrites-X*/ }
break;
case 258: /* trigger_time ::= INSTEAD OF */
{ yymsp[-1].minor.yy64 = TK_INSTEAD;}
break;
case 259: /* trigger_time ::= */
{ yymsp[1].minor.yy64 = TK_BEFORE; }
break;
case 260: /* trigger_event ::= DELETE|INSERT */
case 261: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==261);
{yymsp[0].minor.yy570.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy570.b = 0;}
break;
case 262: /* trigger_event ::= UPDATE OF idlist */
{yymsp[-2].minor.yy570.a = TK_UPDATE; yymsp[-2].minor.yy570.b = yymsp[0].minor.yy240;}
break;
case 263: /* when_clause ::= */
case 282: /* key_opt ::= */ yytestcase(yyruleno==282);
{ yymsp[1].minor.yy626 = 0; }
break;
case 264: /* when_clause ::= WHEN expr */
case 283: /* key_opt ::= KEY expr */ yytestcase(yyruleno==283);
{ yymsp[-1].minor.yy626 = yymsp[0].minor.yy626; }
break;
case 265: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
assert( yymsp[-2].minor.yy95!=0 );
yymsp[-2].minor.yy95->pLast->pNext = yymsp[-1].minor.yy95;
yymsp[-2].minor.yy95->pLast = yymsp[-1].minor.yy95;
}
break;
case 266: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
assert( yymsp[-1].minor.yy95!=0 );
yymsp[-1].minor.yy95->pLast = yymsp[-1].minor.yy95;
}
break;
case 267: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
"qualified table names are not allowed on INSERT, UPDATE, and DELETE "
"statements within triggers");
}
break;
case 268: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
case 269: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
case 270: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
{yylhsminor.yy95 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy607, yymsp[-3].minor.yy562, yymsp[-1].minor.yy626, yymsp[-7].minor.yy64, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy600);}
yymsp[-8].minor.yy95 = yylhsminor.yy95;
break;
case 271: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
yylhsminor.yy95 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy240,yymsp[-2].minor.yy303,yymsp[-6].minor.yy64,yymsp[-1].minor.yy138,yymsp[-7].minor.yy600,yymsp[0].minor.yy600);/*yylhsminor.yy95-overwrites-yymsp[-6].minor.yy64*/
}
yymsp[-7].minor.yy95 = yylhsminor.yy95;
break;
case 272: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
{yylhsminor.yy95 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy626, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy600);}
yymsp[-5].minor.yy95 = yylhsminor.yy95;
break;
case 273: /* trigger_cmd ::= scanpt select scanpt */
{yylhsminor.yy95 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy303, yymsp[-2].minor.yy600, yymsp[0].minor.yy600); /*yylhsminor.yy95-overwrites-yymsp[-1].minor.yy303*/}
yymsp[-2].minor.yy95 = yylhsminor.yy95;
break;
case 274: /* expr ::= RAISE LP IGNORE RP */
{
yymsp[-3].minor.yy626 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
if( yymsp[-3].minor.yy626 ){
yymsp[-3].minor.yy626->affExpr = OE_Ignore;
}
}
break;
case 275: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
yymsp[-5].minor.yy626 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
if( yymsp[-5].minor.yy626 ) {
yymsp[-5].minor.yy626->affExpr = (char)yymsp[-3].minor.yy64;
}
}
break;
case 276: /* raisetype ::= ROLLBACK */
{yymsp[0].minor.yy64 = OE_Rollback;}
break;
case 278: /* raisetype ::= FAIL */
{yymsp[0].minor.yy64 = OE_Fail;}
break;
case 279: /* cmd ::= DROP TRIGGER ifexists fullname */
{
sqlite3DropTrigger(pParse,yymsp[0].minor.yy607,yymsp[-1].minor.yy64);
}
break;
case 280: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
sqlite3Attach(pParse, yymsp[-3].minor.yy626, yymsp[-1].minor.yy626, yymsp[0].minor.yy626);
}
break;
case 281: /* cmd ::= DETACH database_kw_opt expr */
{
sqlite3Detach(pParse, yymsp[0].minor.yy626);
}
break;
case 284: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
case 285: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
case 286: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
case 287: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
case 288: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy607,&yymsp[0].minor.yy0);
}
break;
case 289: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
case 290: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
{
sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy607, &yymsp[0].minor.yy0);
}
break;
case 291: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy607);
}
break;
case 292: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy607, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
case 293: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
case 294: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
case 295: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy64);
}
break;
case 296: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
case 297: /* vtabargtoken ::= ANY */
case 298: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==298);
case 299: /* lp ::= LP */ yytestcase(yyruleno==299);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
case 300: /* with ::= WITH wqlist */
case 301: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==301);
{ sqlite3WithPush(pParse, yymsp[0].minor.yy43, 1); }
break;
case 302: /* wqas ::= AS */
{yymsp[0].minor.yy534 = M10d_Any;}
break;
case 303: /* wqas ::= AS MATERIALIZED */
{yymsp[-1].minor.yy534 = M10d_Yes;}
break;
case 304: /* wqas ::= AS NOT MATERIALIZED */
{yymsp[-2].minor.yy534 = M10d_No;}
break;
case 305: /* wqitem ::= nm eidlist_opt wqas LP select RP */
{
yymsp[-5].minor.yy255 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy562, yymsp[-1].minor.yy303, yymsp[-3].minor.yy534); /*A-overwrites-X*/
}
break;
case 306: /* wqlist ::= wqitem */
{
yymsp[0].minor.yy43 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy255); /*A-overwrites-X*/
}
break;
case 307: /* wqlist ::= wqlist COMMA wqitem */
{
yymsp[-2].minor.yy43 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy43, yymsp[0].minor.yy255);
}
break;
case 308: /* windowdefn_list ::= windowdefn */
{ yylhsminor.yy375 = yymsp[0].minor.yy375; }
yymsp[0].minor.yy375 = yylhsminor.yy375;
break;
case 309: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
assert( yymsp[0].minor.yy375!=0 );
sqlite3WindowChain(pParse, yymsp[0].minor.yy375, yymsp[-2].minor.yy375);
yymsp[0].minor.yy375->pNextWin = yymsp[-2].minor.yy375;
yylhsminor.yy375 = yymsp[0].minor.yy375;
}
yymsp[-2].minor.yy375 = yylhsminor.yy375;
break;
case 310: /* windowdefn ::= nm AS LP window RP */
{
if( ALWAYS(yymsp[-1].minor.yy375) ){
yymsp[-1].minor.yy375->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
yylhsminor.yy375 = yymsp[-1].minor.yy375;
}
yymsp[-4].minor.yy375 = yylhsminor.yy375;
break;
case 311: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
yymsp[-4].minor.yy375 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy375, yymsp[-2].minor.yy562, yymsp[-1].minor.yy562, 0);
}
break;
case 312: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
yylhsminor.yy375 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy375, yymsp[-2].minor.yy562, yymsp[-1].minor.yy562, &yymsp[-5].minor.yy0);
}
yymsp[-5].minor.yy375 = yylhsminor.yy375;
break;
case 313: /* window ::= ORDER BY sortlist frame_opt */
{
yymsp[-3].minor.yy375 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy375, 0, yymsp[-1].minor.yy562, 0);
}
break;
case 314: /* window ::= nm ORDER BY sortlist frame_opt */
{
yylhsminor.yy375 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy375, 0, yymsp[-1].minor.yy562, &yymsp[-4].minor.yy0);
}
yymsp[-4].minor.yy375 = yylhsminor.yy375;
break;
case 315: /* window ::= frame_opt */
case 334: /* filter_over ::= over_clause */ yytestcase(yyruleno==334);
{
yylhsminor.yy375 = yymsp[0].minor.yy375;
}
yymsp[0].minor.yy375 = yylhsminor.yy375;
break;
case 316: /* window ::= nm frame_opt */
{
yylhsminor.yy375 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy375, 0, 0, &yymsp[-1].minor.yy0);
}
yymsp[-1].minor.yy375 = yylhsminor.yy375;
break;
case 317: /* frame_opt ::= */
{
yymsp[1].minor.yy375 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
case 318: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
yylhsminor.yy375 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy64, yymsp[-1].minor.yy81.eType, yymsp[-1].minor.yy81.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy534);
}
yymsp[-2].minor.yy375 = yylhsminor.yy375;
break;
case 319: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
yylhsminor.yy375 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy64, yymsp[-3].minor.yy81.eType, yymsp[-3].minor.yy81.pExpr, yymsp[-1].minor.yy81.eType, yymsp[-1].minor.yy81.pExpr, yymsp[0].minor.yy534);
}
yymsp[-5].minor.yy375 = yylhsminor.yy375;
break;
case 321: /* frame_bound_s ::= frame_bound */
case 323: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==323);
{yylhsminor.yy81 = yymsp[0].minor.yy81;}
yymsp[0].minor.yy81 = yylhsminor.yy81;
break;
case 322: /* frame_bound_s ::= UNBOUNDED PRECEDING */
case 324: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==324);
case 326: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==326);
{yylhsminor.yy81.eType = yymsp[-1].major; yylhsminor.yy81.pExpr = 0;}
yymsp[-1].minor.yy81 = yylhsminor.yy81;
break;
case 325: /* frame_bound ::= expr PRECEDING|FOLLOWING */
{yylhsminor.yy81.eType = yymsp[0].major; yylhsminor.yy81.pExpr = yymsp[-1].minor.yy626;}
yymsp[-1].minor.yy81 = yylhsminor.yy81;
break;
case 327: /* frame_exclude_opt ::= */
{yymsp[1].minor.yy534 = 0;}
break;
case 328: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
{yymsp[-1].minor.yy534 = yymsp[0].minor.yy534;}
break;
case 329: /* frame_exclude ::= NO OTHERS */
case 330: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==330);
{yymsp[-1].minor.yy534 = yymsp[-1].major; /*A-overwrites-X*/}
break;
case 331: /* frame_exclude ::= GROUP|TIES */
{yymsp[0].minor.yy534 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 332: /* window_clause ::= WINDOW windowdefn_list */
{ yymsp[-1].minor.yy375 = yymsp[0].minor.yy375; }
break;
case 333: /* filter_over ::= filter_clause over_clause */
{
if( yymsp[0].minor.yy375 ){
yymsp[0].minor.yy375->pFilter = yymsp[-1].minor.yy626;
}else{
sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy626);
}
yylhsminor.yy375 = yymsp[0].minor.yy375;
}
yymsp[-1].minor.yy375 = yylhsminor.yy375;
break;
case 335: /* filter_over ::= filter_clause */
{
yylhsminor.yy375 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( yylhsminor.yy375 ){
yylhsminor.yy375->eFrmType = TK_FILTER;
yylhsminor.yy375->pFilter = yymsp[0].minor.yy626;
}else{
sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy626);
}
}
yymsp[0].minor.yy375 = yylhsminor.yy375;
break;
case 336: /* over_clause ::= OVER LP window RP */
{
yymsp[-3].minor.yy375 = yymsp[-1].minor.yy375;
assert( yymsp[-3].minor.yy375!=0 );
}
break;
case 337: /* over_clause ::= OVER nm */
{
yymsp[-1].minor.yy375 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( yymsp[-1].minor.yy375 ){
yymsp[-1].minor.yy375->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
}
}
break;
case 338: /* filter_clause ::= FILTER LP WHERE expr RP */
{ yymsp[-4].minor.yy626 = yymsp[-1].minor.yy626; }
break;
default:
/* (339) input ::= cmdlist */ yytestcase(yyruleno==339);
/* (340) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==340);
/* (341) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=341);
/* (342) ecmd ::= SEMI */ yytestcase(yyruleno==342);
/* (343) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==343);
/* (344) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=344);
/* (345) trans_opt ::= */ yytestcase(yyruleno==345);
/* (346) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==346);
/* (347) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==347);
/* (348) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==348);
/* (349) savepoint_opt ::= */ yytestcase(yyruleno==349);
/* (350) cmd ::= create_table create_table_args */ yytestcase(yyruleno==350);
/* (351) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=351);
/* (352) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==352);
/* (353) columnlist ::= columnname carglist */ yytestcase(yyruleno==353);
/* (354) nm ::= ID|INDEXED */ yytestcase(yyruleno==354);
/* (355) nm ::= STRING */ yytestcase(yyruleno==355);
/* (356) nm ::= JOIN_KW */ yytestcase(yyruleno==356);
/* (357) typetoken ::= typename */ yytestcase(yyruleno==357);
/* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358);
/* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359);
/* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360);
/* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361);
/* (362) carglist ::= */ yytestcase(yyruleno==362);
/* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363);
/* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364);
/* (365) ccons ::= AS generated */ yytestcase(yyruleno==365);
/* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366);
/* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367);
/* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368);
/* (369) tconscomma ::= */ yytestcase(yyruleno==369);
/* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370);
/* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371);
/* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372);
/* (373) oneselect ::= values */ yytestcase(yyruleno==373);
/* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374);
/* (375) as ::= ID|STRING */ yytestcase(yyruleno==375);
/* (376) returning ::= */ yytestcase(yyruleno==376);
/* (377) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=377);
/* (378) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==378);
/* (379) exprlist ::= nexprlist */ yytestcase(yyruleno==379);
/* (380) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=380);
/* (381) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=381);
/* (382) nmnum ::= ON */ yytestcase(yyruleno==382);
/* (383) nmnum ::= DELETE */ yytestcase(yyruleno==383);
/* (384) nmnum ::= DEFAULT */ yytestcase(yyruleno==384);
/* (385) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==385);
/* (386) foreach_clause ::= */ yytestcase(yyruleno==386);
/* (387) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==387);
/* (388) trnm ::= nm */ yytestcase(yyruleno==388);
/* (389) tridxby ::= */ yytestcase(yyruleno==389);
/* (390) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==390);
/* (391) database_kw_opt ::= */ yytestcase(yyruleno==391);
/* (392) kwcolumn_opt ::= */ yytestcase(yyruleno==392);
/* (393) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==393);
/* (394) vtabarglist ::= vtabarg */ yytestcase(yyruleno==394);
/* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==395);
/* (396) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==396);
/* (397) anylist ::= */ yytestcase(yyruleno==397);
/* (398) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==398);
/* (399) anylist ::= anylist ANY */ yytestcase(yyruleno==399);
/* (400) with ::= */ yytestcase(yyruleno==400);
break;
/********** End reduce actions ************************************************/
};
assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
yygoto = yyRuleInfoLhs[yyruleno];
yysize = yyRuleInfoNRhs[yyruleno];
yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
|
| ︙ | ︙ | |||
163477 163478 163479 163480 163481 163482 163483 |
fprintf(yyTraceFILE,"%sDiscard input token %s\n",
yyTracePrompt,yyTokenName[yymajor]);
}
#endif
yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
yymajor = YYNOCODE;
}else{
| | | < | < > | | 163995 163996 163997 163998 163999 164000 164001 164002 164003 164004 164005 164006 164007 164008 164009 164010 164011 164012 164013 164014 164015 |
fprintf(yyTraceFILE,"%sDiscard input token %s\n",
yyTracePrompt,yyTokenName[yymajor]);
}
#endif
yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
yymajor = YYNOCODE;
}else{
while( yypParser->yytos > yypParser->yystack ){
yyact = yy_find_reduce_action(yypParser->yytos->stateno,
YYERRORSYMBOL);
if( yyact<=YY_MAX_SHIFTREDUCE ) break;
yy_pop_parser_stack(yypParser);
}
if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
yy_parse_failed(yypParser);
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
yymajor = YYNOCODE;
}else if( yymx!=YYERRORSYMBOL ){
|
| ︙ | ︙ | |||
166551 166552 166553 166554 166555 166556 166557 |
/* Closing the handle. Fourth parameter is passed the value 2. */
sqlite3GlobalConfig.xSqllog(sqlite3GlobalConfig.pSqllogArg, db, 0, 2);
}
#endif
/* Convert the connection into a zombie and then close it.
*/
| | | 167068 167069 167070 167071 167072 167073 167074 167075 167076 167077 167078 167079 167080 167081 167082 |
/* Closing the handle. Fourth parameter is passed the value 2. */
sqlite3GlobalConfig.xSqllog(sqlite3GlobalConfig.pSqllogArg, db, 0, 2);
}
#endif
/* Convert the connection into a zombie and then close it.
*/
db->eOpenState = SQLITE_STATE_ZOMBIE;
sqlite3LeaveMutexAndCloseZombie(db);
return SQLITE_OK;
}
/*
** Return the transaction state for a single databse, or the maximum
** transaction state over all attached databases if zSchema is null.
|
| ︙ | ︙ | |||
166615 166616 166617 166618 166619 166620 166621 | HashElem *i; /* Hash table iterator */ int j; /* If there are outstanding sqlite3_stmt or sqlite3_backup objects ** or if the connection has not yet been closed by sqlite3_close_v2(), ** then just leave the mutex and return. */ | | | 167132 167133 167134 167135 167136 167137 167138 167139 167140 167141 167142 167143 167144 167145 167146 |
HashElem *i; /* Hash table iterator */
int j;
/* If there are outstanding sqlite3_stmt or sqlite3_backup objects
** or if the connection has not yet been closed by sqlite3_close_v2(),
** then just leave the mutex and return.
*/
if( db->eOpenState!=SQLITE_STATE_ZOMBIE || connectionIsBusy(db) ){
sqlite3_mutex_leave(db->mutex);
return;
}
/* If we reach this point, it means that the database connection has
** closed all sqlite3_stmt and sqlite3_backup objects and has been
** passed to sqlite3_close (meaning that it is a zombie). Therefore,
|
| ︙ | ︙ | |||
166701 166702 166703 166704 166705 166706 166707 | sqlite3ValueFree(db->pErr); sqlite3CloseExtensions(db); #if SQLITE_USER_AUTHENTICATION sqlite3_free(db->auth.zAuthUser); sqlite3_free(db->auth.zAuthPW); #endif | | | | 167218 167219 167220 167221 167222 167223 167224 167225 167226 167227 167228 167229 167230 167231 167232 167233 167234 167235 167236 167237 167238 167239 167240 167241 167242 |
sqlite3ValueFree(db->pErr);
sqlite3CloseExtensions(db);
#if SQLITE_USER_AUTHENTICATION
sqlite3_free(db->auth.zAuthUser);
sqlite3_free(db->auth.zAuthPW);
#endif
db->eOpenState = SQLITE_STATE_ERROR;
/* The temp-database schema is allocated differently from the other schema
** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()).
** So it needs to be freed here. Todo: Why not roll the temp schema into
** the same sqliteMalloc() as the one that allocates the database
** structure?
*/
sqlite3DbFree(db, db->aDb[1].pSchema);
sqlite3_mutex_leave(db->mutex);
db->eOpenState = SQLITE_STATE_CLOSED;
sqlite3_mutex_free(db->mutex);
assert( sqlite3LookasideUsed(db,0)==0 );
if( db->lookaside.bMalloced ){
sqlite3_free(db->lookaside.pStart);
}
sqlite3_free(db);
}
|
| ︙ | ︙ | |||
167099 167100 167101 167102 167103 167104 167105 |
}
/*
** Cause any pending operation to stop at its earliest opportunity.
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
| | | 167616 167617 167618 167619 167620 167621 167622 167623 167624 167625 167626 167627 167628 167629 167630 |
}
/*
** Cause any pending operation to stop at its earliest opportunity.
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) ){
(void)SQLITE_MISUSE_BKPT;
return;
}
#endif
AtomicStore(&db->u1.isInterrupted, 1);
}
|
| ︙ | ︙ | |||
167128 167129 167130 167131 167132 167133 167134 |
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInverse)(sqlite3_context*,int,sqlite3_value **),
FuncDestructor *pDestructor
){
FuncDef *p;
| < | | 167645 167646 167647 167648 167649 167650 167651 167652 167653 167654 167655 167656 167657 167658 167659 167660 167661 167662 167663 167664 167665 167666 167667 167668 |
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInverse)(sqlite3_context*,int,sqlite3_value **),
FuncDestructor *pDestructor
){
FuncDef *p;
int extraFlags;
assert( sqlite3_mutex_held(db->mutex) );
assert( xValue==0 || xSFunc==0 );
if( zFunctionName==0 /* Must have a valid name */
|| (xSFunc!=0 && xFinal!=0) /* Not both xSFunc and xFinal */
|| ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */
|| ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */
|| (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG)
|| (255<sqlite3Strlen30(zFunctionName))
){
return SQLITE_MISUSE_BKPT;
}
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
|
| ︙ | ︙ | |||
168405 168406 168407 168408 168409 168410 168411 |
/*
** This routine does the core work of extracting URI parameters from a
** database filename for the sqlite3_uri_parameter() interface.
*/
static const char *uriParameter(const char *zFilename, const char *zParam){
zFilename += sqlite3Strlen30(zFilename) + 1;
| | | 168921 168922 168923 168924 168925 168926 168927 168928 168929 168930 168931 168932 168933 168934 168935 |
/*
** This routine does the core work of extracting URI parameters from a
** database filename for the sqlite3_uri_parameter() interface.
*/
static const char *uriParameter(const char *zFilename, const char *zParam){
zFilename += sqlite3Strlen30(zFilename) + 1;
while( ALWAYS(zFilename!=0) && zFilename[0] ){
int x = strcmp(zFilename, zParam);
zFilename += sqlite3Strlen30(zFilename) + 1;
if( x==0 ) return zFilename;
zFilename += sqlite3Strlen30(zFilename) + 1;
}
return 0;
}
|
| ︙ | ︙ | |||
168502 168503 168504 168505 168506 168507 168508 |
if( isThreadsafe==0 ){
sqlite3MutexWarnOnContention(db->mutex);
}
}
sqlite3_mutex_enter(db->mutex);
db->errMask = 0xff;
db->nDb = 2;
| | > > > > > > > > | 169018 169019 169020 169021 169022 169023 169024 169025 169026 169027 169028 169029 169030 169031 169032 169033 169034 169035 169036 169037 169038 169039 169040 169041 169042 169043 169044 169045 169046 169047 169048 169049 169050 169051 169052 |
if( isThreadsafe==0 ){
sqlite3MutexWarnOnContention(db->mutex);
}
}
sqlite3_mutex_enter(db->mutex);
db->errMask = 0xff;
db->nDb = 2;
db->eOpenState = SQLITE_STATE_BUSY;
db->aDb = db->aDbStatic;
db->lookaside.bDisable = 1;
db->lookaside.sz = 0;
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS;
db->autoCommit = 1;
db->nextAutovac = -1;
db->szMmap = sqlite3GlobalConfig.szMmap;
db->nextPagesize = 0;
db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */
#ifdef SQLITE_ENABLE_SORTER_MMAP
/* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map
** the temporary files used to do external sorts (see code in vdbesort.c)
** is disabled. It can still be used either by defining
** SQLITE_ENABLE_SORTER_MMAP at compile time or by using the
** SQLITE_TESTCTRL_SORTER_MMAP test-control at runtime. */
db->nMaxSorterMmap = 0x7FFFFFFF;
#endif
db->flags |= SQLITE_ShortColNames
| SQLITE_EnableTrigger
| SQLITE_EnableView
| SQLITE_CacheSpill
#if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0
| SQLITE_TrustedSchema
#endif
|
| ︙ | ︙ | |||
168662 168663 168664 168665 168666 168667 168668 | ** database it is OFF. This matches the pager layer defaults. */ db->aDb[0].zDbSName = "main"; db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; db->aDb[1].zDbSName = "temp"; db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; | | | 169186 169187 169188 169189 169190 169191 169192 169193 169194 169195 169196 169197 169198 169199 169200 |
** database it is OFF. This matches the pager layer defaults.
*/
db->aDb[0].zDbSName = "main";
db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
db->aDb[1].zDbSName = "temp";
db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
db->eOpenState = SQLITE_STATE_OPEN;
if( db->mallocFailed ){
goto opendb_out;
}
/* Register all built-in functions, but do not attempt to read the
** database schema yet. This is delayed until the first time the database
** is accessed.
|
| ︙ | ︙ | |||
168729 168730 168731 168732 168733 168734 168735 |
}
rc = sqlite3_errcode(db);
assert( db!=0 || rc==SQLITE_NOMEM );
if( rc==SQLITE_NOMEM ){
sqlite3_close(db);
db = 0;
}else if( rc!=SQLITE_OK ){
| | | 169253 169254 169255 169256 169257 169258 169259 169260 169261 169262 169263 169264 169265 169266 169267 |
}
rc = sqlite3_errcode(db);
assert( db!=0 || rc==SQLITE_NOMEM );
if( rc==SQLITE_NOMEM ){
sqlite3_close(db);
db = 0;
}else if( rc!=SQLITE_OK ){
db->eOpenState = SQLITE_STATE_SICK;
}
*ppDb = db;
#ifdef SQLITE_ENABLE_SQLLOG
if( sqlite3GlobalConfig.xSqllog ){
/* Opening a db handle. Fourth parameter is passed 0. */
void *pArg = sqlite3GlobalConfig.pSqllogArg;
sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0);
|
| ︙ | ︙ | |||
169745 169746 169747 169748 169749 169750 169751 |
/*
** Return a pointer to the name of Nth query parameter of the filename.
*/
SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N){
if( zFilename==0 || N<0 ) return 0;
zFilename = databaseName(zFilename);
zFilename += sqlite3Strlen30(zFilename) + 1;
| | | 170269 170270 170271 170272 170273 170274 170275 170276 170277 170278 170279 170280 170281 170282 170283 |
/*
** Return a pointer to the name of Nth query parameter of the filename.
*/
SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N){
if( zFilename==0 || N<0 ) return 0;
zFilename = databaseName(zFilename);
zFilename += sqlite3Strlen30(zFilename) + 1;
while( ALWAYS(zFilename) && zFilename[0] && (N--)>0 ){
zFilename += sqlite3Strlen30(zFilename) + 1;
zFilename += sqlite3Strlen30(zFilename) + 1;
}
return zFilename[0] ? zFilename : 0;
}
/*
|
| ︙ | ︙ | |||
169788 169789 169790 169791 169792 169793 169794 169795 169796 169797 169798 169799 |
** It is an error to pass this routine a filename string that was not
** passed into the VFS from the SQLite core. Doing so is similar to
** passing free() a pointer that was not obtained from malloc() - it is
** an error that we cannot easily detect but that will likely cause memory
** corruption.
*/
SQLITE_API const char *sqlite3_filename_database(const char *zFilename){
return databaseName(zFilename);
}
SQLITE_API const char *sqlite3_filename_journal(const char *zFilename){
zFilename = databaseName(zFilename);
zFilename += sqlite3Strlen30(zFilename) + 1;
| > > | | | 170312 170313 170314 170315 170316 170317 170318 170319 170320 170321 170322 170323 170324 170325 170326 170327 170328 170329 170330 170331 170332 170333 170334 170335 170336 170337 170338 170339 170340 170341 170342 170343 170344 |
** It is an error to pass this routine a filename string that was not
** passed into the VFS from the SQLite core. Doing so is similar to
** passing free() a pointer that was not obtained from malloc() - it is
** an error that we cannot easily detect but that will likely cause memory
** corruption.
*/
SQLITE_API const char *sqlite3_filename_database(const char *zFilename){
if( zFilename==0 ) return 0;
return databaseName(zFilename);
}
SQLITE_API const char *sqlite3_filename_journal(const char *zFilename){
if( zFilename==0 ) return 0;
zFilename = databaseName(zFilename);
zFilename += sqlite3Strlen30(zFilename) + 1;
while( ALWAYS(zFilename) && zFilename[0] ){
zFilename += sqlite3Strlen30(zFilename) + 1;
zFilename += sqlite3Strlen30(zFilename) + 1;
}
return zFilename + 1;
}
SQLITE_API const char *sqlite3_filename_wal(const char *zFilename){
#ifdef SQLITE_OMIT_WAL
return 0;
#else
zFilename = sqlite3_filename_journal(zFilename);
if( zFilename ) zFilename += sqlite3Strlen30(zFilename) + 1;
return zFilename;
#endif
}
/*
** Return the Btree pointer identified by zDbName. Return NULL if not found.
*/
|
| ︙ | ︙ | |||
171097 171098 171099 171100 171101 171102 171103 | ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* ** Macros indicating that conditional expressions are always true or ** false. */ | | > > > | | | | | < < | | | 171623 171624 171625 171626 171627 171628 171629 171630 171631 171632 171633 171634 171635 171636 171637 171638 171639 171640 171641 171642 171643 171644 171645 171646 171647 171648 | ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* ** Macros indicating that conditional expressions are always true or ** false. */ #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 #endif #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif /* ** Internal types used by SQLite. */ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */ typedef short int i16; /* 2-byte (or larger) signed integer */ |
| ︙ | ︙ | |||
171623 171624 171625 171626 171627 171628 171629 |
#endif
static int fts3EvalNext(Fts3Cursor *pCsr);
static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **);
| < < < < < < < | 172150 172151 172152 172153 172154 172155 172156 172157 172158 172159 172160 172161 172162 172163 |
#endif
static int fts3EvalNext(Fts3Cursor *pCsr);
static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **);
/*
** This variable is set to false when running tests for which the on disk
** structures should not be corrupt. Otherwise, true. If it is false, extra
** assert() conditions in the fts3 code are activated - conditions that are
** only true if it is guaranteed that the fts3 database is not corrupt.
*/
#ifdef SQLITE_DEBUG
|
| ︙ | ︙ | |||
185322 185323 185324 185325 185326 185327 185328 |
}
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
if( nPrefix>p->term.n || nSuffix>p->nNode-p->iOff || nSuffix==0 ){
return FTS_CORRUPT_VTAB;
}
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
| | | 185842 185843 185844 185845 185846 185847 185848 185849 185850 185851 185852 185853 185854 185855 185856 |
}
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
if( nPrefix>p->term.n || nSuffix>p->nNode-p->iOff || nSuffix==0 ){
return FTS_CORRUPT_VTAB;
}
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
if( rc==SQLITE_OK && ALWAYS(p->term.a!=0) ){
memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix);
p->term.n = nPrefix+nSuffix;
p->iOff += nSuffix;
if( p->iChild==0 ){
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
if( (p->nNode-p->iOff)<p->nDoclist ){
return FTS_CORRUPT_VTAB;
|
| ︙ | ︙ | |||
185716 185717 185718 185719 185720 185721 185722 |
static int fts3TermCmp(
const char *zLhs, int nLhs, /* LHS of comparison */
const char *zRhs, int nRhs /* RHS of comparison */
){
int nCmp = MIN(nLhs, nRhs);
int res;
| > | > > > | 186236 186237 186238 186239 186240 186241 186242 186243 186244 186245 186246 186247 186248 186249 186250 186251 186252 186253 186254 |
static int fts3TermCmp(
const char *zLhs, int nLhs, /* LHS of comparison */
const char *zRhs, int nRhs /* RHS of comparison */
){
int nCmp = MIN(nLhs, nRhs);
int res;
if( nCmp && ALWAYS(zLhs) && ALWAYS(zRhs) ){
res = memcmp(zLhs, zRhs, nCmp);
}else{
res = 0;
}
if( res==0 ) res = nLhs - nRhs;
return res;
}
/*
|
| ︙ | ︙ | |||
186360 186361 186362 186363 186364 186365 186366 |
sqlite3_bind_int(pSelect, 1, FTS_STAT_INCRMERGEHINT);
if( SQLITE_ROW==sqlite3_step(pSelect) ){
const char *aHint = sqlite3_column_blob(pSelect, 0);
int nHint = sqlite3_column_bytes(pSelect, 0);
if( aHint ){
blobGrowBuffer(pHint, nHint, &rc);
if( rc==SQLITE_OK ){
| | | 186884 186885 186886 186887 186888 186889 186890 186891 186892 186893 186894 186895 186896 186897 186898 |
sqlite3_bind_int(pSelect, 1, FTS_STAT_INCRMERGEHINT);
if( SQLITE_ROW==sqlite3_step(pSelect) ){
const char *aHint = sqlite3_column_blob(pSelect, 0);
int nHint = sqlite3_column_bytes(pSelect, 0);
if( aHint ){
blobGrowBuffer(pHint, nHint, &rc);
if( rc==SQLITE_OK ){
if( ALWAYS(pHint->a!=0) ) memcpy(pHint->a, aHint, nHint);
pHint->n = nHint;
}
}
}
rc2 = sqlite3_reset(pSelect);
if( rc==SQLITE_OK ) rc = rc2;
}
|
| ︙ | ︙ | |||
188451 188452 188453 188454 188455 188456 188457 |
/*
** Advance the iterator passed as an argument to the next position. Return
** 1 if the iterator is at EOF or if it now points to the start of the
** position list for the next column.
*/
static int fts3LcsIteratorAdvance(LcsIterator *pIter){
| | > > | 188975 188976 188977 188978 188979 188980 188981 188982 188983 188984 188985 188986 188987 188988 188989 188990 188991 188992 188993 188994 |
/*
** Advance the iterator passed as an argument to the next position. Return
** 1 if the iterator is at EOF or if it now points to the start of the
** position list for the next column.
*/
static int fts3LcsIteratorAdvance(LcsIterator *pIter){
char *pRead;
sqlite3_int64 iRead;
int rc = 0;
if( NEVER(pIter==0) ) return 1;
pRead = pIter->pRead;
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
if( iRead==0 || iRead==1 ){
pRead = 0;
rc = 1;
}else{
pIter->iPos += (int)(iRead-2);
}
|
| ︙ | ︙ | |||
189976 189977 189978 189979 189980 189981 189982 189983 189984 189985 189986 189987 189988 189989 | #ifndef SQLITE_AMALGAMATION /* Unsigned integer types. These are already defined in the sqliteInt.h, ** but the definitions need to be repeated for separate compilation. */ typedef sqlite3_uint64 u64; typedef unsigned int u32; typedef unsigned short int u16; typedef unsigned char u8; #endif /* Objects */ typedef struct JsonString JsonString; typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; | > > > > > > > > > > > > > | 190502 190503 190504 190505 190506 190507 190508 190509 190510 190511 190512 190513 190514 190515 190516 190517 190518 190519 190520 190521 190522 190523 190524 190525 190526 190527 190528 | #ifndef SQLITE_AMALGAMATION /* Unsigned integer types. These are already defined in the sqliteInt.h, ** but the definitions need to be repeated for separate compilation. */ typedef sqlite3_uint64 u64; typedef unsigned int u32; typedef unsigned short int u16; typedef unsigned char u8; # if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 # endif # if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) # elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) # else # define ALWAYS(X) (X) # define NEVER(X) (X) # endif #endif /* Objects */ typedef struct JsonString JsonString; typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; |
| ︙ | ︙ | |||
190318 190319 190320 190321 190322 190323 190324 190325 |
** the number of JsonNode objects that are encoded.
*/
static void jsonRenderNode(
JsonNode *pNode, /* The node to render */
JsonString *pOut, /* Write JSON here */
sqlite3_value **aReplace /* Replacement values */
){
if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){
| > | | 190857 190858 190859 190860 190861 190862 190863 190864 190865 190866 190867 190868 190869 190870 190871 190872 190873 |
** the number of JsonNode objects that are encoded.
*/
static void jsonRenderNode(
JsonNode *pNode, /* The node to render */
JsonString *pOut, /* Write JSON here */
sqlite3_value **aReplace /* Replacement values */
){
assert( pNode!=0 );
if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){
if( (pNode->jnFlags & JNODE_REPLACE)!=0 && ALWAYS(aReplace!=0) ){
jsonAppendValue(pOut, aReplace[pNode->u.iReplace]);
return;
}
pNode = pNode->u.pPatch;
}
switch( pNode->eType ){
default: {
|
| ︙ | ︙ | |||
190485 190486 190487 190488 190489 190490 190491 |
}
i = i*10 + v;
}
if( pNode->u.zJContent[0]=='-' ){ i = -i; }
sqlite3_result_int64(pCtx, i);
int_done:
break;
| | | 191025 191026 191027 191028 191029 191030 191031 191032 191033 191034 191035 191036 191037 191038 191039 |
}
i = i*10 + v;
}
if( pNode->u.zJContent[0]=='-' ){ i = -i; }
sqlite3_result_int64(pCtx, i);
int_done:
break;
int_as_real: ; /* no break */ deliberate_fall_through
}
case JSON_REAL: {
double r;
#ifdef SQLITE_AMALGAMATION
const char *z = pNode->u.zJContent;
sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
#else
|
| ︙ | ︙ | |||
190639 190640 190641 190642 190643 190644 190645 |
static int jsonParseAddNode(
JsonParse *pParse, /* Append the node to this object */
u32 eType, /* Node type */
u32 n, /* Content size or sub-node count */
const char *zContent /* Content */
){
JsonNode *p;
| | | 191179 191180 191181 191182 191183 191184 191185 191186 191187 191188 191189 191190 191191 191192 191193 |
static int jsonParseAddNode(
JsonParse *pParse, /* Append the node to this object */
u32 eType, /* Node type */
u32 n, /* Content size or sub-node count */
const char *zContent /* Content */
){
JsonNode *p;
if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){
return jsonParseAddNodeExpand(pParse, eType, n, zContent);
}
p = &pParse->aNode[pParse->nNode];
p->eType = (u8)eType;
p->jnFlags = 0;
p->n = n;
p->u.zJContent = zContent;
|
| ︙ | ︙ | |||
192579 192580 192581 192582 192583 192584 192585 | /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #else /* #include "sqlite3.h" */ #endif SQLITE_PRIVATE int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */ | > > > > | > > > > > > > > > > > > > | 193119 193120 193121 193122 193123 193124 193125 193126 193127 193128 193129 193130 193131 193132 193133 193134 193135 193136 193137 193138 193139 193140 193141 193142 193143 193144 193145 193146 193147 193148 193149 193150 193151 193152 193153 193154 193155 193156 193157 193158 193159 193160 193161 193162 193163 | /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #else /* #include "sqlite3.h" */ #endif SQLITE_PRIVATE int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */ /* ** If building separately, we will need some setup that is normally ** found in sqliteInt.h */ #if !defined(SQLITE_AMALGAMATION) #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif #if defined(NDEBUG) && defined(SQLITE_DEBUG) # undef NDEBUG #endif #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 #endif #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif #endif /* !defined(SQLITE_AMALGAMATION) */ /* #include <string.h> */ /* #include <stdio.h> */ /* #include <assert.h> */ /* #include <stdlib.h> */ /* The following macro is used to suppress compiler warnings. |
| ︙ | ︙ | |||
192650 192651 192652 192653 192654 192655 192656 192657 192658 192659 192660 192661 192662 192663 192664 | int iNodeSize; /* Size in bytes of each node in the node table */ u8 nDim; /* Number of dimensions */ u8 nDim2; /* Twice the number of dimensions */ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ u8 nAuxNotNull; /* Number of initial not-null aux columns */ #ifdef SQLITE_DEBUG u8 bCorrupt; /* Shadow table corruption detected */ #endif int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ u32 nBusy; /* Current number of users of this structure */ | > > | 193207 193208 193209 193210 193211 193212 193213 193214 193215 193216 193217 193218 193219 193220 193221 193222 193223 | int iNodeSize; /* Size in bytes of each node in the node table */ u8 nDim; /* Number of dimensions */ u8 nDim2; /* Twice the number of dimensions */ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ #ifdef SQLITE_ENABLE_GEOPOLY u8 nAuxNotNull; /* Number of initial not-null aux columns */ #endif #ifdef SQLITE_DEBUG u8 bCorrupt; /* Shadow table corruption detected */ #endif int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ u32 nBusy; /* Current number of users of this structure */ |
| ︙ | ︙ | |||
192932 192933 192934 192935 192936 192937 192938 | #endif #endif /* The testcase() macro should already be defined in the amalgamation. If ** it is not, make it a no-op. */ #ifndef SQLITE_AMALGAMATION | > > > > | > | 193491 193492 193493 193494 193495 193496 193497 193498 193499 193500 193501 193502 193503 193504 193505 193506 193507 193508 193509 193510 |
#endif
#endif
/* The testcase() macro should already be defined in the amalgamation. If
** it is not, make it a no-op.
*/
#ifndef SQLITE_AMALGAMATION
# ifdef SQLITE_COVERAGE_TEST
unsigned int sqlite3RtreeTestcase = 0;
# define testcase(X) if( X ){ sqlite3RtreeTestcase += __LINE__; }
# else
# define testcase(X)
# endif
#endif
/*
** Make sure that the compiler intrinsics we desire are enabled when
** compiling with an appropriate version of MSVC unless prevented by
** the SQLITE_DISABLE_INTRINSIC define.
*/
|
| ︙ | ︙ | |||
193181 193182 193183 193184 193185 193186 193187 |
if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
sqlite3_blob *pBlob = pRtree->pNodeBlob;
pRtree->pNodeBlob = 0;
sqlite3_blob_close(pBlob);
}
}
| < < < < < < < < < < < < < < < < < < < | | 193745 193746 193747 193748 193749 193750 193751 193752 193753 193754 193755 193756 193757 193758 193759 193760 193761 193762 193763 193764 193765 193766 193767 193768 193769 193770 193771 193772 193773 193774 193775 |
if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
sqlite3_blob *pBlob = pRtree->pNodeBlob;
pRtree->pNodeBlob = 0;
sqlite3_blob_close(pBlob);
}
}
/*
** 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 = SQLITE_OK;
RtreeNode *pNode = 0;
/* 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))!=0 ){
if( pParent && pParent!=pNode->pParent ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
pNode->nRef++;
*ppNode = pNode;
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
193274 193275 193276 193277 193278 193279 193280 | /* 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. */ | | | 193819 193820 193821 193822 193823 193824 193825 193826 193827 193828 193829 193830 193831 193832 193833 |
/* 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( rc==SQLITE_OK && pNode && iNode==1 ){
pRtree->iDepth = readInt16(pNode->zData);
if( pRtree->iDepth>RTREE_MAX_DEPTH ){
rc = SQLITE_CORRUPT_VTAB;
RTREE_IS_CORRUPT(pRtree);
}
}
|
| ︙ | ︙ | |||
193880 193881 193882 193883 193884 193885 193886 |
/*
** 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;
| | | | | > | 194425 194426 194427 194428 194429 194430 194431 194432 194433 194434 194435 194436 194437 194438 194439 194440 194441 194442 194443 194444 |
/*
** 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( ALWAYS(pParent) ){
return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex);
}else{
*piIndex = -1;
return SQLITE_OK;
}
}
/*
** Compare two search points. Return negative, zero, or positive if the first
** is less than, equal to, or greater than the second.
**
** The rScore is the primary key. Smaller rScore values come first.
|
| ︙ | ︙ | |||
194007 194008 194009 194010 194011 194012 194013 |
|| (pFirst->rScore==rScore && pFirst->iLevel>iLevel)
){
if( pCur->bPoint ){
int ii;
pNew = rtreeEnqueue(pCur, rScore, iLevel);
if( pNew==0 ) return 0;
ii = (int)(pNew - pCur->aPoint) + 1;
| > | | 194553 194554 194555 194556 194557 194558 194559 194560 194561 194562 194563 194564 194565 194566 194567 194568 |
|| (pFirst->rScore==rScore && pFirst->iLevel>iLevel)
){
if( pCur->bPoint ){
int ii;
pNew = rtreeEnqueue(pCur, rScore, iLevel);
if( pNew==0 ) return 0;
ii = (int)(pNew - pCur->aPoint) + 1;
assert( ii==1 );
if( ALWAYS(ii<RTREE_CACHE_SZ) ){
assert( pCur->aNode[ii]==0 );
pCur->aNode[ii] = pCur->aNode[0];
}else{
nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]);
}
pCur->aNode[0] = 0;
*pNew = pCur->sPoint;
|
| ︙ | ︙ | |||
194068 194069 194070 194071 194072 194073 194074 |
if( p->aNode[i] ){
nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]);
p->aNode[i] = 0;
}
if( p->bPoint ){
p->anQueue[p->sPoint.iLevel]--;
p->bPoint = 0;
| | | 194615 194616 194617 194618 194619 194620 194621 194622 194623 194624 194625 194626 194627 194628 194629 |
if( p->aNode[i] ){
nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]);
p->aNode[i] = 0;
}
if( p->bPoint ){
p->anQueue[p->sPoint.iLevel]--;
p->bPoint = 0;
}else if( ALWAYS(p->nPoint) ){
p->anQueue[p->aPoint[0].iLevel]--;
n = --p->nPoint;
p->aPoint[0] = p->aPoint[n];
if( n<RTREE_CACHE_SZ-1 ){
p->aNode[1] = p->aNode[n+1];
p->aNode[n+1] = 0;
}
|
| ︙ | ︙ | |||
194209 194210 194211 194212 194213 194214 194215 |
** Rtree virtual table module xRowid method.
*/
static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
| | | | 194756 194757 194758 194759 194760 194761 194762 194763 194764 194765 194766 194767 194768 194769 194770 194771 194772 194773 194774 194775 194776 194777 194778 194779 194780 194781 194782 194783 194784 194785 194786 194787 194788 |
** Rtree virtual table module xRowid method.
*/
static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
if( rc==SQLITE_OK && ALWAYS(p) ){
*pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
}
return rc;
}
/*
** Rtree virtual table module xColumn method.
*/
static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
Rtree *pRtree = (Rtree *)cur->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)cur;
RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
RtreeCoord c;
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
if( rc ) return rc;
if( NEVER(p==0) ) return SQLITE_OK;
if( i==0 ){
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
}else if( i<=pRtree->nDim2 ){
nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
sqlite3_result_double(ctx, c.f);
|
| ︙ | ︙ | |||
194426 194427 194428 194429 194430 194431 194432 194433 |
}
}
}
}
}
if( rc==SQLITE_OK ){
RtreeSearchPoint *pNew;
pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
| > > | > | 194973 194974 194975 194976 194977 194978 194979 194980 194981 194982 194983 194984 194985 194986 194987 194988 194989 194990 194991 |
}
}
}
}
}
if( rc==SQLITE_OK ){
RtreeSearchPoint *pNew;
assert( pCsr->bPoint==0 ); /* Due to the resetCursor() call above */
pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
if( NEVER(pNew==0) ){ /* Because pCsr->bPoint was FALSE */
return SQLITE_NOMEM;
}
pNew->id = 1;
pNew->iCell = 0;
pNew->eWithin = PARTLY_WITHIN;
assert( pCsr->bPoint==1 );
pCsr->aNode[0] = pRoot;
pRoot = 0;
RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:");
|
| ︙ | ︙ | |||
194504 194505 194506 194507 194508 194509 194510 |
}
assert( pIdxInfo->idxStr==0 );
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
if( bMatch==0 && p->usable
| | | 195054 195055 195056 195057 195058 195059 195060 195061 195062 195063 195064 195065 195066 195067 195068 |
}
assert( pIdxInfo->idxStr==0 );
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
if( bMatch==0 && p->usable
&& p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ
){
/* We have an equality constraint on the rowid. Use strategy 1. */
int jj;
for(jj=0; jj<ii; jj++){
pIdxInfo->aConstraintUsage[jj].argvIndex = 0;
pIdxInfo->aConstraintUsage[jj].omit = 0;
}
|
| ︙ | ︙ | |||
194710 194711 194712 194713 194714 194715 194716 |
sqlite3_int64 iBest = 0;
RtreeDValue fMinGrowth = RTREE_ZERO;
RtreeDValue fMinArea = RTREE_ZERO;
int nCell = NCELL(pNode);
RtreeCell cell;
| | | 195260 195261 195262 195263 195264 195265 195266 195267 195268 195269 195270 195271 195272 195273 195274 |
sqlite3_int64 iBest = 0;
RtreeDValue fMinGrowth = RTREE_ZERO;
RtreeDValue fMinArea = RTREE_ZERO;
int nCell = NCELL(pNode);
RtreeCell cell;
RtreeNode *pChild = 0;
RtreeCell *aCell = 0;
/* 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.
*/
|
| ︙ | ︙ | |||
194757 194758 194759 194760 194761 194762 194763 194764 194765 194766 194767 194768 |
static int AdjustTree(
Rtree *pRtree, /* Rtree table */
RtreeNode *pNode, /* Adjust ancestry of this node. */
RtreeCell *pCell /* This cell was just inserted */
){
RtreeNode *p = pNode;
int cnt = 0;
while( p->pParent ){
RtreeNode *pParent = p->pParent;
RtreeCell cell;
int iCell;
| > > > > > > | > | 195307 195308 195309 195310 195311 195312 195313 195314 195315 195316 195317 195318 195319 195320 195321 195322 195323 195324 195325 195326 195327 195328 195329 195330 195331 195332 195333 |
static int AdjustTree(
Rtree *pRtree, /* Rtree table */
RtreeNode *pNode, /* Adjust ancestry of this node. */
RtreeCell *pCell /* This cell was just inserted */
){
RtreeNode *p = pNode;
int cnt = 0;
int rc;
while( p->pParent ){
RtreeNode *pParent = p->pParent;
RtreeCell cell;
int iCell;
cnt++;
if( NEVER(cnt>100) ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
rc = nodeParentIndex(pRtree, p, &iCell);
if( NEVER(rc!=SQLITE_OK) ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
nodeGetCell(pRtree, pParent, iCell, &cell);
if( !cellContains(pRtree, &cell, pCell) ){
cellUnion(pRtree, &cell, pCell);
|
| ︙ | ︙ | |||
195051 195052 195053 195054 195055 195056 195057 195058 195059 195060 195061 195062 195063 195064 195065 195066 195067 195068 195069 195070 |
RtreeNode *pNode,
int iHeight
){
int (*xSetMapping)(Rtree *, sqlite3_int64, sqlite3_int64);
xSetMapping = ((iHeight==0)?rowidWrite:parentWrite);
if( iHeight>0 ){
RtreeNode *pChild = nodeHashLookup(pRtree, iRowid);
if( pChild ){
nodeRelease(pRtree, pChild->pParent);
nodeReference(pNode);
pChild->pParent = pNode;
}
}
return xSetMapping(pRtree, iRowid, pNode->iNode);
}
static int SplitNode(
Rtree *pRtree,
RtreeNode *pNode,
RtreeCell *pCell,
| > > > > > | 195608 195609 195610 195611 195612 195613 195614 195615 195616 195617 195618 195619 195620 195621 195622 195623 195624 195625 195626 195627 195628 195629 195630 195631 195632 |
RtreeNode *pNode,
int iHeight
){
int (*xSetMapping)(Rtree *, sqlite3_int64, sqlite3_int64);
xSetMapping = ((iHeight==0)?rowidWrite:parentWrite);
if( iHeight>0 ){
RtreeNode *pChild = nodeHashLookup(pRtree, iRowid);
RtreeNode *p;
for(p=pNode; p; p=p->pParent){
if( p==pChild ) return SQLITE_CORRUPT_VTAB;
}
if( pChild ){
nodeRelease(pRtree, pChild->pParent);
nodeReference(pNode);
pChild->pParent = pNode;
}
}
if( NEVER(pNode==0) ) return SQLITE_ERROR;
return xSetMapping(pRtree, iRowid, pNode->iNode);
}
static int SplitNode(
Rtree *pRtree,
RtreeNode *pNode,
RtreeCell *pCell,
|
| ︙ | ︙ | |||
195146 195147 195148 195149 195150 195151 195152 |
if( rc!=SQLITE_OK ){
goto splitnode_out;
}
}else{
RtreeNode *pParent = pLeft->pParent;
int iCell;
rc = nodeParentIndex(pRtree, pLeft, &iCell);
| | > | | 195708 195709 195710 195711 195712 195713 195714 195715 195716 195717 195718 195719 195720 195721 195722 195723 195724 195725 195726 195727 |
if( rc!=SQLITE_OK ){
goto splitnode_out;
}
}else{
RtreeNode *pParent = pLeft->pParent;
int iCell;
rc = nodeParentIndex(pRtree, pLeft, &iCell);
if( ALWAYS(rc==SQLITE_OK) ){
nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell);
rc = AdjustTree(pRtree, pParent, &leftbbox);
assert( rc==SQLITE_OK );
}
if( NEVER(rc!=SQLITE_OK) ){
goto splitnode_out;
}
}
if( (rc = rtreeInsertCell(pRtree, pRight->pParent, &rightbbox, iHeight+1)) ){
goto splitnode_out;
}
|
| ︙ | ︙ | |||
195225 195226 195227 195228 195229 195230 195231 |
/* 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);
| | | 195788 195789 195790 195791 195792 195793 195794 195795 195796 195797 195798 195799 195800 195801 195802 |
/* 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==0 ){
rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent);
}
}
rc = sqlite3_reset(pRtree->pReadParent);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK && !pChild->pParent ){
RTREE_IS_CORRUPT(pRtree);
|
| ︙ | ︙ | |||
195256 195257 195258 195259 195260 195261 195262 195263 195264 195265 195266 195267 195268 195269 |
/* 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;
| > | 195819 195820 195821 195822 195823 195824 195825 195826 195827 195828 195829 195830 195831 195832 195833 |
/* 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);
testcase( rc!=SQLITE_OK );
}
rc2 = nodeRelease(pRtree, pParent);
if( rc==SQLITE_OK ){
rc = rc2;
}
if( rc!=SQLITE_OK ){
return rc;
|
| ︙ | ︙ | |||
195478 195479 195480 195481 195482 195483 195484 |
rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
pRtree->iReinsertHeight = iHeight;
rc = Reinsert(pRtree, pNode, pCell, iHeight);
}
}else{
rc = AdjustTree(pRtree, pNode, pCell);
| | | 196042 196043 196044 196045 196046 196047 196048 196049 196050 196051 196052 196053 196054 196055 196056 |
rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
pRtree->iReinsertHeight = iHeight;
rc = Reinsert(pRtree, pNode, pCell, iHeight);
}
}else{
rc = AdjustTree(pRtree, pNode, pCell);
if( ALWAYS(rc==SQLITE_OK) ){
if( iHeight==0 ){
rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode);
}else{
rc = parentWrite(pRtree, pCell->iRowid, pNode->iNode);
}
}
}
|
| ︙ | ︙ | |||
195584 195585 195586 195587 195588 195589 195590 |
** 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 = 0;
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
| | | 196148 196149 196150 196151 196152 196153 196154 196155 196156 196157 196158 196159 196160 196161 196162 |
** 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 = 0;
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); /* tag-20210916a */
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--;
|
| ︙ | ︙ | |||
195919 195920 195921 195922 195923 195924 195925 |
** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST.
*/
static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
const char *zFmt = "SELECT stat FROM %Q.sqlite_stat1 WHERE tbl = '%q_rowid'";
char *zSql;
sqlite3_stmt *p;
int rc;
| | < < < < < < < < < < | | 196483 196484 196485 196486 196487 196488 196489 196490 196491 196492 196493 196494 196495 196496 196497 196498 196499 196500 196501 196502 196503 196504 196505 196506 196507 196508 196509 196510 196511 196512 196513 196514 196515 196516 196517 |
** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST.
*/
static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
const char *zFmt = "SELECT stat FROM %Q.sqlite_stat1 WHERE tbl = '%q_rowid'";
char *zSql;
sqlite3_stmt *p;
int rc;
i64 nRow = RTREE_MIN_ROWEST;
rc = sqlite3_table_column_metadata(
db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0
);
if( rc!=SQLITE_OK ){
pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
return rc==SQLITE_ERROR ? SQLITE_OK : rc;
}
zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(db, zSql, -1, &p, 0);
if( rc==SQLITE_OK ){
if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0);
rc = sqlite3_finalize(p);
}
sqlite3_free(zSql);
}
pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST);
return rc;
}
/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
|
| ︙ | ︙ | |||
196099 196100 196101 196102 196103 196104 196105 196106 196107 |
}else{
sqlite3_str *p = sqlite3_str_new(db);
int ii;
char *zSql;
sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix);
for(ii=0; ii<pRtree->nAux; ii++){
if( ii ) sqlite3_str_append(p, ",", 1);
if( ii<pRtree->nAuxNotNull ){
sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii);
| > | > > | 196653 196654 196655 196656 196657 196658 196659 196660 196661 196662 196663 196664 196665 196666 196667 196668 196669 196670 196671 196672 |
}else{
sqlite3_str *p = sqlite3_str_new(db);
int ii;
char *zSql;
sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix);
for(ii=0; ii<pRtree->nAux; ii++){
if( ii ) sqlite3_str_append(p, ",", 1);
#ifdef SQLITE_ENABLE_GEOPOLY
if( ii<pRtree->nAuxNotNull ){
sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii);
}else
#endif
{
sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2);
}
}
sqlite3_str_appendf(p, " WHERE rowid=?1");
zSql = sqlite3_str_finish(p);
if( zSql==0 ){
rc = SQLITE_NOMEM;
|
| ︙ | ︙ | |||
196366 196367 196368 196369 196370 196371 196372 196373 196374 196375 196376 196377 196378 196379 |
memset(&node, 0, sizeof(RtreeNode));
memset(&tree, 0, sizeof(Rtree));
tree.nDim = (u8)sqlite3_value_int(apArg[0]);
if( tree.nDim<1 || tree.nDim>5 ) return;
tree.nDim2 = tree.nDim*2;
tree.nBytesPerCell = 8 + 8 * tree.nDim;
node.zData = (u8 *)sqlite3_value_blob(apArg[1]);
nData = sqlite3_value_bytes(apArg[1]);
if( nData<4 ) return;
if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
pOut = sqlite3_str_new(0);
for(ii=0; ii<NCELL(&node); ii++){
RtreeCell cell;
| > | 196923 196924 196925 196926 196927 196928 196929 196930 196931 196932 196933 196934 196935 196936 196937 |
memset(&node, 0, sizeof(RtreeNode));
memset(&tree, 0, sizeof(Rtree));
tree.nDim = (u8)sqlite3_value_int(apArg[0]);
if( tree.nDim<1 || tree.nDim>5 ) return;
tree.nDim2 = tree.nDim*2;
tree.nBytesPerCell = 8 + 8 * tree.nDim;
node.zData = (u8 *)sqlite3_value_blob(apArg[1]);
if( node.zData==0 ) return;
nData = sqlite3_value_bytes(apArg[1]);
if( nData<4 ) return;
if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
pOut = sqlite3_str_new(0);
for(ii=0; ii<NCELL(&node); ii++){
RtreeCell cell;
|
| ︙ | ︙ | |||
196780 196781 196782 196783 196784 196785 196786 |
/* Find the number of auxiliary columns */
if( check.rc==SQLITE_OK ){
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
if( pStmt ){
nAux = sqlite3_column_count(pStmt) - 2;
sqlite3_finalize(pStmt);
| | > | > | 197338 197339 197340 197341 197342 197343 197344 197345 197346 197347 197348 197349 197350 197351 197352 197353 197354 197355 |
/* Find the number of auxiliary columns */
if( check.rc==SQLITE_OK ){
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
if( pStmt ){
nAux = sqlite3_column_count(pStmt) - 2;
sqlite3_finalize(pStmt);
}else
if( check.rc!=SQLITE_NOMEM ){
check.rc = SQLITE_OK;
}
}
/* Find number of dimensions in the rtree table. */
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab);
if( pStmt ){
int rc;
check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2;
|
| ︙ | ︙ | |||
197195 197196 197197 197198 197199 197200 197201 197202 197203 197204 197205 197206 197207 |
static GeoPoly *geopolyFuncParam(
sqlite3_context *pCtx, /* Context for error messages */
sqlite3_value *pVal, /* The value to decode */
int *pRc /* Write error here */
){
GeoPoly *p = 0;
int nByte;
if( sqlite3_value_type(pVal)==SQLITE_BLOB
&& (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord))
){
const unsigned char *a = sqlite3_value_blob(pVal);
int nVertex;
if( a==0 ){
| > | | 197755 197756 197757 197758 197759 197760 197761 197762 197763 197764 197765 197766 197767 197768 197769 197770 197771 197772 197773 197774 197775 197776 |
static GeoPoly *geopolyFuncParam(
sqlite3_context *pCtx, /* Context for error messages */
sqlite3_value *pVal, /* The value to decode */
int *pRc /* Write error here */
){
GeoPoly *p = 0;
int nByte;
testcase( pCtx==0 );
if( sqlite3_value_type(pVal)==SQLITE_BLOB
&& (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord))
){
const unsigned char *a = sqlite3_value_blob(pVal);
int nVertex;
if( a==0 ){
if( pCtx ) sqlite3_result_error_nomem(pCtx);
return 0;
}
nVertex = (a[1]<<16) + (a[2]<<8) + a[3];
if( (a[0]==0 || a[0]==1)
&& (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte
){
p = sqlite3_malloc64( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) );
|
| ︙ | ︙ | |||
198028 198029 198030 198031 198032 198033 198034 |
pSeg->y = pSeg->y0;
pSeg->pNext = pActive;
pActive = pSeg;
needSort = 1;
}else{
/* Remove a segment */
if( pActive==pThisEvent->pSeg ){
| | | | 198589 198590 198591 198592 198593 198594 198595 198596 198597 198598 198599 198600 198601 198602 198603 198604 198605 198606 198607 |
pSeg->y = pSeg->y0;
pSeg->pNext = pActive;
pActive = pSeg;
needSort = 1;
}else{
/* Remove a segment */
if( pActive==pThisEvent->pSeg ){
pActive = ALWAYS(pActive) ? pActive->pNext : 0;
}else{
for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
if( pSeg->pNext==pThisEvent->pSeg ){
pSeg->pNext = ALWAYS(pSeg->pNext) ? pSeg->pNext->pNext : 0;
break;
}
}
}
}
pThisEvent = pThisEvent->pNext;
}
|
| ︙ | ︙ | |||
198276 198277 198278 198279 198280 198281 198282 198283 198284 198285 198286 198287 198288 198289 |
** with the configured constraints.
*/
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
if( rc==SQLITE_OK && idxNum<=3 ){
RtreeCoord bbox[4];
RtreeConstraint *p;
assert( argc==1 );
geopolyBBox(0, argv[0], bbox, &rc);
if( rc ){
goto geopoly_filter_end;
}
pCsr->aConstraint = p = sqlite3_malloc(sizeof(RtreeConstraint)*4);
pCsr->nConstraint = 4;
if( p==0 ){
| > | 198837 198838 198839 198840 198841 198842 198843 198844 198845 198846 198847 198848 198849 198850 198851 |
** with the configured constraints.
*/
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
if( rc==SQLITE_OK && idxNum<=3 ){
RtreeCoord bbox[4];
RtreeConstraint *p;
assert( argc==1 );
assert( argv[0]!=0 );
geopolyBBox(0, argv[0], bbox, &rc);
if( rc ){
goto geopoly_filter_end;
}
pCsr->aConstraint = p = sqlite3_malloc(sizeof(RtreeConstraint)*4);
pCsr->nConstraint = 4;
if( p==0 ){
|
| ︙ | ︙ | |||
198503 198504 198505 198506 198507 198508 198509 198510 198511 198512 198513 198514 198515 198516 |
cell.iRowid = newRowid;
if( nData>1 /* not a DELETE */
&& (!oldRowidValid /* INSERT */
|| !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */
|| oldRowid!=newRowid) /* Rowid change */
){
geopolyBBox(0, aData[2], cell.aCoord, &rc);
if( rc ){
if( rc==SQLITE_ERROR ){
pVtab->zErrMsg =
sqlite3_mprintf("_shape does not contain a valid polygon");
}
goto geopoly_update_end;
| > | 199065 199066 199067 199068 199069 199070 199071 199072 199073 199074 199075 199076 199077 199078 199079 |
cell.iRowid = newRowid;
if( nData>1 /* not a DELETE */
&& (!oldRowidValid /* INSERT */
|| !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */
|| oldRowid!=newRowid) /* Rowid change */
){
assert( aData[2]!=0 );
geopolyBBox(0, aData[2], cell.aCoord, &rc);
if( rc ){
if( rc==SQLITE_ERROR ){
pVtab->zErrMsg =
sqlite3_mprintf("_shape does not contain a valid polygon");
}
goto geopoly_update_end;
|
| ︙ | ︙ | |||
198856 198857 198858 198859 198860 198861 198862 |
void *pContext, /* Extra data passed into the callback */
void (*xDestructor)(void*) /* Destructor for the extra data */
){
RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */
/* Allocate and populate the context object. */
pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback));
| | > > > | 199419 199420 199421 199422 199423 199424 199425 199426 199427 199428 199429 199430 199431 199432 199433 199434 199435 199436 |
void *pContext, /* Extra data passed into the callback */
void (*xDestructor)(void*) /* Destructor for the extra data */
){
RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */
/* Allocate and populate the context object. */
pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback));
if( !pGeomCtx ){
if( xDestructor ) xDestructor(pContext);
return SQLITE_NOMEM;
}
pGeomCtx->xGeom = 0;
pGeomCtx->xQueryFunc = xQueryFunc;
pGeomCtx->xDestructor = xDestructor;
pGeomCtx->pContext = pContext;
return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
(void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
|
| ︙ | ︙ | |||
205641 205642 205643 205644 205645 205646 205647 205648 205649 205650 205651 205652 205653 205654 |
** official SQLite documentation.
*/
/* #include "sqliteInt.h" ** Requires access to internal data structures ** */
#if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
/*
** Page paths:
**
** The value of the 'path' column describes the path taken from the
** root-node of the b-tree structure to each page. The value of the
** root-node path is '/'.
**
| > > > > > > > > > | 206207 206208 206209 206210 206211 206212 206213 206214 206215 206216 206217 206218 206219 206220 206221 206222 206223 206224 206225 206226 206227 206228 206229 |
** official SQLite documentation.
*/
/* #include "sqliteInt.h" ** Requires access to internal data structures ** */
#if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
/*
** The pager and btree modules arrange objects in memory so that there are
** always approximately 200 bytes of addressable memory following each page
** buffer. This way small buffer overreads caused by corrupt database pages
** do not cause undefined behaviour. This module pads each page buffer
** by the following number of bytes for the same purpose.
*/
#define DBSTAT_PAGE_PADDING_BYTES 256
/*
** Page paths:
**
** The value of the 'path' column describes the path taken from the
** root-node of the b-tree structure to each page. The value of the
** root-node path is '/'.
**
|
| ︙ | ︙ | |||
205708 205709 205710 205711 205712 205713 205714 |
int nLastOvfl; /* Bytes of payload on final overflow page */
int iOvfl; /* Iterates through aOvfl[] */
};
/* Size information for a single btree page */
struct StatPage {
u32 iPgno; /* Page number */
| | < | 206283 206284 206285 206286 206287 206288 206289 206290 206291 206292 206293 206294 206295 206296 206297 206298 |
int nLastOvfl; /* Bytes of payload on final overflow page */
int iOvfl; /* Iterates through aOvfl[] */
};
/* Size information for a single btree page */
struct StatPage {
u32 iPgno; /* Page number */
u8 *aPg; /* Page buffer from sqlite3_malloc() */
int iCell; /* Current cell */
char *zPath; /* Path to this page */
/* Variables populated by statDecodePage(): */
u8 flags; /* Copy of flags byte */
int nCell; /* Number of cells on page */
int nUnused; /* Number of unused bytes on page */
StatCell *aCell; /* Array of parsed cells */
|
| ︙ | ︙ | |||
205922 205923 205924 205925 205926 205927 205928 205929 |
sqlite3_free(p->aCell);
}
p->nCell = 0;
p->aCell = 0;
}
static void statClearPage(StatPage *p){
statClearCells(p);
| > < > > | > > > > > | 206496 206497 206498 206499 206500 206501 206502 206503 206504 206505 206506 206507 206508 206509 206510 206511 206512 206513 206514 206515 206516 206517 206518 206519 206520 206521 206522 206523 206524 206525 206526 206527 206528 |
sqlite3_free(p->aCell);
}
p->nCell = 0;
p->aCell = 0;
}
static void statClearPage(StatPage *p){
u8 *aPg = p->aPg;
statClearCells(p);
sqlite3_free(p->zPath);
memset(p, 0, sizeof(StatPage));
p->aPg = aPg;
}
static void statResetCsr(StatCursor *pCsr){
int i;
/* In some circumstances, specifically if an OOM has occurred, the call
** to sqlite3_reset() may cause the pager to be reset (emptied). It is
** important that statClearPage() is called to free any page refs before
** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */
for(i=0; i<ArraySize(pCsr->aPage); i++){
statClearPage(&pCsr->aPage[i]);
sqlite3_free(pCsr->aPage[i].aPg);
pCsr->aPage[i].aPg = 0;
}
sqlite3_reset(pCsr->pStmt);
pCsr->iPage = 0;
sqlite3_free(pCsr->zPath);
pCsr->zPath = 0;
pCsr->isEof = 0;
}
/* Resize the space-used counters inside of the cursor */
|
| ︙ | ︙ | |||
205998 205999 206000 206001 206002 206003 206004 |
static int statDecodePage(Btree *pBt, StatPage *p){
int nUnused;
int iOff;
int nHdr;
int isLeaf;
int szPage;
| | | 206579 206580 206581 206582 206583 206584 206585 206586 206587 206588 206589 206590 206591 206592 206593 |
static int statDecodePage(Btree *pBt, StatPage *p){
int nUnused;
int iOff;
int nHdr;
int isLeaf;
int szPage;
u8 *aData = p->aPg;
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
p->flags = aHdr[0];
if( p->flags==0x0A || p->flags==0x0D ){
isLeaf = 1;
nHdr = 8;
}else if( p->flags==0x05 || p->flags==0x02 ){
|
| ︙ | ︙ | |||
206069 206070 206071 206072 206073 206074 206075 |
if( nLocal<0 ) goto statPageIsCorrupt;
pCell->nLocal = nLocal;
assert( nPayload>=(u32)nLocal );
assert( nLocal<=(nUsable-35) );
if( nPayload>(u32)nLocal ){
int j;
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
| | | 206650 206651 206652 206653 206654 206655 206656 206657 206658 206659 206660 206661 206662 206663 206664 |
if( nLocal<0 ) goto statPageIsCorrupt;
pCell->nLocal = nLocal;
assert( nPayload>=(u32)nLocal );
assert( nLocal<=(nUsable-35) );
if( nPayload>(u32)nLocal ){
int j;
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){
goto statPageIsCorrupt;
}
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
pCell->nOvfl = nOvfl;
pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
if( pCell->aOvfl==0 ) return SQLITE_NOMEM_BKPT;
pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]);
|
| ︙ | ︙ | |||
206127 206128 206129 206130 206131 206132 206133 206134 206135 206136 206137 206138 206139 206140 206141 206142 206143 206144 206145 206146 206147 206148 206149 206150 206151 206152 |
pCsr->szPage += x[1];
}else{
/* Not ZIPVFS: The default page size and offset */
pCsr->szPage += sqlite3BtreeGetPageSize(pBt);
pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
}
}
/*
** Move a DBSTAT cursor to the next entry. Normally, the next
** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0),
** the next entry is the next btree.
*/
static int statNext(sqlite3_vtab_cursor *pCursor){
int rc;
int nPayload;
char *z;
StatCursor *pCsr = (StatCursor *)pCursor;
StatTable *pTab = (StatTable *)pCursor->pVtab;
Btree *pBt = pTab->db->aDb[pCsr->iDb].pBt;
Pager *pPager = sqlite3BtreePager(pBt);
sqlite3_free(pCsr->zPath);
pCsr->zPath = 0;
statNextRestart:
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 206708 206709 206710 206711 206712 206713 206714 206715 206716 206717 206718 206719 206720 206721 206722 206723 206724 206725 206726 206727 206728 206729 206730 206731 206732 206733 206734 206735 206736 206737 206738 206739 206740 206741 206742 206743 206744 206745 206746 206747 206748 206749 206750 206751 206752 206753 206754 206755 206756 206757 206758 206759 206760 206761 206762 206763 206764 206765 206766 206767 206768 206769 206770 206771 206772 206773 206774 206775 206776 206777 206778 206779 206780 206781 206782 206783 206784 206785 |
pCsr->szPage += x[1];
}else{
/* Not ZIPVFS: The default page size and offset */
pCsr->szPage += sqlite3BtreeGetPageSize(pBt);
pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
}
}
/*
** Load a copy of the page data for page iPg into the buffer belonging
** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK
** if successful, or an SQLite error code otherwise.
*/
static int statGetPage(
Btree *pBt, /* Load page from this b-tree */
u32 iPg, /* Page number to load */
StatPage *pPg /* Load page into this object */
){
int pgsz = sqlite3BtreeGetPageSize(pBt);
DbPage *pDbPage = 0;
int rc;
if( pPg->aPg==0 ){
pPg->aPg = (u8*)sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES);
if( pPg->aPg==0 ){
return SQLITE_NOMEM_BKPT;
}
memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES);
}
rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPg, &pDbPage, 0);
if( rc==SQLITE_OK ){
const u8 *a = sqlite3PagerGetData(pDbPage);
memcpy(pPg->aPg, a, pgsz);
sqlite3PagerUnref(pDbPage);
}
return rc;
}
/*
** Move a DBSTAT cursor to the next entry. Normally, the next
** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0),
** the next entry is the next btree.
*/
static int statNext(sqlite3_vtab_cursor *pCursor){
int rc;
int nPayload;
char *z;
StatCursor *pCsr = (StatCursor *)pCursor;
StatTable *pTab = (StatTable *)pCursor->pVtab;
Btree *pBt = pTab->db->aDb[pCsr->iDb].pBt;
Pager *pPager = sqlite3BtreePager(pBt);
sqlite3_free(pCsr->zPath);
pCsr->zPath = 0;
statNextRestart:
if( pCsr->iPage<0 ){
/* Start measuring space on the next btree */
statResetCounts(pCsr);
rc = sqlite3_step(pCsr->pStmt);
if( rc==SQLITE_ROW ){
int nPage;
u32 iRoot = (u32)sqlite3_column_int64(pCsr->pStmt, 1);
sqlite3PagerPagecount(pPager, &nPage);
if( nPage==0 ){
pCsr->isEof = 1;
return sqlite3_reset(pCsr->pStmt);
}
rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]);
pCsr->aPage[0].iPgno = iRoot;
pCsr->aPage[0].iCell = 0;
if( !pCsr->isAgg ){
pCsr->aPage[0].zPath = z = sqlite3_mprintf("/");
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
}
pCsr->iPage = 0;
|
| ︙ | ︙ | |||
206209 206210 206211 206212 206213 206214 206215 |
}
if( p->iRightChildPg ) break;
p->iCell++;
}
if( !p->iRightChildPg || p->iCell>p->nCell ){
statClearPage(p);
| < | | | | 206822 206823 206824 206825 206826 206827 206828 206829 206830 206831 206832 206833 206834 206835 206836 206837 206838 206839 206840 206841 206842 206843 206844 206845 206846 206847 206848 206849 206850 206851 206852 206853 206854 206855 206856 |
}
if( p->iRightChildPg ) break;
p->iCell++;
}
if( !p->iRightChildPg || p->iCell>p->nCell ){
statClearPage(p);
pCsr->iPage--;
if( pCsr->isAgg && pCsr->iPage<0 ){
/* label-statNext-done: When computing aggregate space usage over
** an entire btree, this is the exit point from this function */
return SQLITE_OK;
}
goto statNextRestart; /* Tail recursion */
}
pCsr->iPage++;
if( pCsr->iPage>=ArraySize(pCsr->aPage) ){
statResetCsr(pCsr);
return SQLITE_CORRUPT_BKPT;
}
assert( p==&pCsr->aPage[pCsr->iPage-1] );
if( p->iCell==p->nCell ){
p[1].iPgno = p->iRightChildPg;
}else{
p[1].iPgno = p->aCell[p->iCell].iChildPg;
}
rc = statGetPage(pBt, p[1].iPgno, &p[1]);
pCsr->nPage++;
p[1].iCell = 0;
if( !pCsr->isAgg ){
p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
}
p->iCell++;
|
| ︙ | ︙ | |||
206360 206361 206362 206363 206364 206365 206366 206367 206368 206369 206370 206371 206372 206373 |
return SQLITE_NOMEM_BKPT;
}else{
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
sqlite3_free(zSql);
}
if( rc==SQLITE_OK ){
rc = statNext(pCursor);
}
return rc;
}
static int statColumn(
sqlite3_vtab_cursor *pCursor,
| > | 206972 206973 206974 206975 206976 206977 206978 206979 206980 206981 206982 206983 206984 206985 206986 |
return SQLITE_NOMEM_BKPT;
}else{
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
sqlite3_free(zSql);
}
if( rc==SQLITE_OK ){
pCsr->iPage = -1;
rc = statNext(pCursor);
}
return rc;
}
static int statColumn(
sqlite3_vtab_cursor *pCursor,
|
| ︙ | ︙ | |||
207311 207312 207313 207314 207315 207316 207317 |
}
n = sqlite3_value_bytes(pValue);
if( z==0 && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
nVarint = sessionVarintLen(n);
if( aBuf ){
sessionVarintPut(&aBuf[1], n);
| | | 207924 207925 207926 207927 207928 207929 207930 207931 207932 207933 207934 207935 207936 207937 207938 |
}
n = sqlite3_value_bytes(pValue);
if( z==0 && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
nVarint = sessionVarintLen(n);
if( aBuf ){
sessionVarintPut(&aBuf[1], n);
if( n>0 ) memcpy(&aBuf[nVarint + 1], z, n);
}
nByte = 1 + nVarint + n;
break;
}
}
}else{
|
| ︙ | ︙ | |||
207916 207917 207918 207919 207920 207921 207922 207923 207924 207925 207926 207927 |
"SELECT 0, 'tbl', '', 0, '', 1 UNION ALL "
"SELECT 1, 'idx', '', 0, '', 2 UNION ALL "
"SELECT 2, 'stat', '', 0, '', 0"
);
}else if( rc==SQLITE_ERROR ){
zPragma = sqlite3_mprintf("");
}else{
return rc;
}
}else{
zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
}
| > > > > | > > > > > > | > > > > > > | 208529 208530 208531 208532 208533 208534 208535 208536 208537 208538 208539 208540 208541 208542 208543 208544 208545 208546 208547 208548 208549 208550 208551 208552 208553 208554 208555 208556 208557 208558 208559 208560 208561 208562 208563 208564 208565 208566 208567 208568 |
"SELECT 0, 'tbl', '', 0, '', 1 UNION ALL "
"SELECT 1, 'idx', '', 0, '', 2 UNION ALL "
"SELECT 2, 'stat', '', 0, '', 0"
);
}else if( rc==SQLITE_ERROR ){
zPragma = sqlite3_mprintf("");
}else{
*pazCol = 0;
*pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0;
return rc;
}
}else{
zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
}
if( !zPragma ){
*pazCol = 0;
*pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0;
return SQLITE_NOMEM;
}
rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
sqlite3_free(zPragma);
if( rc!=SQLITE_OK ){
*pazCol = 0;
*pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0;
return rc;
}
nByte = nThis + 1;
while( SQLITE_ROW==sqlite3_step(pStmt) ){
nByte += sqlite3_column_bytes(pStmt, 1);
nDbCol++;
}
rc = sqlite3_reset(pStmt);
|
| ︙ | ︙ | |||
208343 208344 208345 208346 208347 208348 208349 |
/* If there is a table-filter configured, invoke it. If it returns 0,
** do not automatically add the new table. */
if( pSession->xTableFilter==0
|| pSession->xTableFilter(pSession->pFilterCtx, zName)
){
rc = sqlite3session_attach(pSession, zName);
if( rc==SQLITE_OK ){
| | > > > > | 208972 208973 208974 208975 208976 208977 208978 208979 208980 208981 208982 208983 208984 208985 208986 208987 208988 208989 208990 |
/* If there is a table-filter configured, invoke it. If it returns 0,
** do not automatically add the new table. */
if( pSession->xTableFilter==0
|| pSession->xTableFilter(pSession->pFilterCtx, zName)
){
rc = sqlite3session_attach(pSession, zName);
if( rc==SQLITE_OK ){
pRet = pSession->pTable;
while( ALWAYS(pRet) && pRet->pNext ){
pRet = pRet->pNext;
}
assert( pRet!=0 );
assert( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) );
}
}
}
assert( rc==SQLITE_OK || pRet==0 );
*ppTab = pRet;
|
| ︙ | ︙ | |||
209116 209117 209118 209119 209120 209121 209122 209123 209124 209125 209126 209127 209128 209129 |
int rc = SQLITE_OK;
SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */
int bNoop = 1; /* Set to zero if any values are modified */
int nRewind = pBuf->nBuf; /* Set to zero if any values are modified */
int i; /* Used to iterate through columns */
u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */
sessionAppendByte(pBuf, SQLITE_UPDATE, &rc);
sessionAppendByte(pBuf, p->bIndirect, &rc);
for(i=0; i<sqlite3_column_count(pStmt); i++){
int bChanged = 0;
int nAdvance;
int eType = *pCsr;
switch( eType ){
| > | 209749 209750 209751 209752 209753 209754 209755 209756 209757 209758 209759 209760 209761 209762 209763 |
int rc = SQLITE_OK;
SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */
int bNoop = 1; /* Set to zero if any values are modified */
int nRewind = pBuf->nBuf; /* Set to zero if any values are modified */
int i; /* Used to iterate through columns */
u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */
assert( abPK!=0 );
sessionAppendByte(pBuf, SQLITE_UPDATE, &rc);
sessionAppendByte(pBuf, p->bIndirect, &rc);
for(i=0; i<sqlite3_column_count(pStmt); i++){
int bChanged = 0;
int nAdvance;
int eType = *pCsr;
switch( eType ){
|
| ︙ | ︙ | |||
209420 209421 209422 209423 209424 209425 209426 |
void **ppChangeset /* OUT: Buffer containing changeset */
){
sqlite3 *db = pSession->db; /* Source database handle */
SessionTable *pTab; /* Used to iterate through attached tables */
SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
int rc; /* Return code */
| | > > | | | 210054 210055 210056 210057 210058 210059 210060 210061 210062 210063 210064 210065 210066 210067 210068 210069 210070 210071 210072 210073 210074 210075 210076 210077 210078 210079 210080 210081 210082 210083 210084 210085 210086 210087 210088 210089 210090 |
void **ppChangeset /* OUT: Buffer containing changeset */
){
sqlite3 *db = pSession->db; /* Source database handle */
SessionTable *pTab; /* Used to iterate through attached tables */
SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
int rc; /* Return code */
assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );
/* Zero the output variables in case an error occurs. If this session
** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
** this call will be a no-op. */
if( xOutput==0 ){
assert( pnChangeset!=0 && ppChangeset!=0 );
*pnChangeset = 0;
*ppChangeset = 0;
}
if( pSession->rc ) return pSession->rc;
rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_mutex_enter(sqlite3_db_mutex(db));
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
if( pTab->nEntry ){
const char *zName = pTab->zName;
int nCol = 0; /* Number of columns in table */
u8 *abPK = 0; /* Primary key array */
const char **azCol = 0; /* Table columns */
int i; /* Used to iterate through hash buckets */
sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
int nRewind = buf.nBuf; /* Initial size of write buffer */
int nNoop; /* Size of buffer after writing tbl header */
/* Check the table schema is still Ok. */
|
| ︙ | ︙ | |||
209478 209479 209480 209481 209482 209483 209484 209485 209486 209487 209488 209489 209490 209491 |
int iCol;
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
for(iCol=0; iCol<nCol; iCol++){
sessionAppendCol(&buf, pSel, iCol, &rc);
}
}else{
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
}
}else if( p->op!=SQLITE_INSERT ){
rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
| > | 210114 210115 210116 210117 210118 210119 210120 210121 210122 210123 210124 210125 210126 210127 210128 |
int iCol;
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
for(iCol=0; iCol<nCol; iCol++){
sessionAppendCol(&buf, pSel, iCol, &rc);
}
}else{
assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
}
}else if( p->op!=SQLITE_INSERT ){
rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
|
| ︙ | ︙ | |||
209538 209539 209540 209541 209542 209543 209544 |
** using sqlite3_free().
*/
SQLITE_API int sqlite3session_changeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
){
| > > > | > > > | 210175 210176 210177 210178 210179 210180 210181 210182 210183 210184 210185 210186 210187 210188 210189 210190 210191 210192 210193 210194 210195 210196 210197 210198 210199 210200 210201 210202 210203 210204 210205 210206 210207 210208 210209 210210 210211 210212 210213 210214 210215 210216 210217 210218 210219 210220 210221 210222 210223 210224 210225 210226 210227 210228 210229 210230 210231 210232 210233 210234 210235 |
** using sqlite3_free().
*/
SQLITE_API int sqlite3session_changeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
){
int rc;
if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE;
rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset);
assert( rc || pnChangeset==0
|| pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize
);
return rc;
}
/*
** Streaming version of sqlite3session_changeset().
*/
SQLITE_API int sqlite3session_changeset_strm(
sqlite3_session *pSession,
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
}
/*
** Streaming version of sqlite3session_patchset().
*/
SQLITE_API int sqlite3session_patchset_strm(
sqlite3_session *pSession,
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
}
/*
** Obtain a patchset object containing all changes recorded by the
** session object passed as the first argument.
**
** It is the responsibility of the caller to eventually free the buffer
** using sqlite3_free().
*/
SQLITE_API int sqlite3session_patchset(
sqlite3_session *pSession, /* Session object */
int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
void **ppPatchset /* OUT: Buffer containing changeset */
){
if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset);
}
/*
** Enable or disable the session object passed as the first argument.
*/
SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable){
|
| ︙ | ︙ | |||
210542 210543 210544 210545 210546 210547 210548 |
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
sOut.nBuf = 0;
if( rc!=SQLITE_OK ) goto finished_invert;
}
}
assert( rc==SQLITE_OK );
| | | | 211185 211186 211187 211188 211189 211190 211191 211192 211193 211194 211195 211196 211197 211198 211199 211200 211201 211202 211203 |
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
sOut.nBuf = 0;
if( rc!=SQLITE_OK ) goto finished_invert;
}
}
assert( rc==SQLITE_OK );
if( pnInverted && ALWAYS(ppInverted) ){
*pnInverted = sOut.nBuf;
*ppInverted = sOut.aBuf;
sOut.aBuf = 0;
}else if( sOut.nBuf>0 && ALWAYS(xOutput!=0) ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
}
finished_invert:
sqlite3_free(sOut.aBuf);
sqlite3_free(apVal);
sqlite3_free(sPK.aBuf);
|
| ︙ | ︙ | |||
211002 211003 211004 211005 211006 211007 211008 |
** argument iterator points to a suitable entry. Make sure that xValue
** is one of these to guarantee that it is safe to ignore the return
** in the code below. */
assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
for(i=0; rc==SQLITE_OK && i<nCol; i++){
if( !abPK || abPK[i] ){
| | | 211645 211646 211647 211648 211649 211650 211651 211652 211653 211654 211655 211656 211657 211658 211659 |
** argument iterator points to a suitable entry. Make sure that xValue
** is one of these to guarantee that it is safe to ignore the return
** in the code below. */
assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
for(i=0; rc==SQLITE_OK && i<nCol; i++){
if( !abPK || abPK[i] ){
sqlite3_value *pVal = 0;
(void)xValue(pIter, i, &pVal);
if( pVal==0 ){
/* The value in the changeset was "undefined". This indicates a
** corrupt changeset blob. */
rc = SQLITE_CORRUPT_BKPT;
}else{
rc = sessionBindValue(pStmt, i+1, pVal);
|
| ︙ | ︙ | |||
212145 212146 212147 212148 212149 212150 212151 |
}
}
}
if( rc==SQLITE_OK ){
if( xOutput ){
if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf);
| | | | 212788 212789 212790 212791 212792 212793 212794 212795 212796 212797 212798 212799 212800 212801 212802 212803 212804 |
}
}
}
if( rc==SQLITE_OK ){
if( xOutput ){
if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf);
}else if( ppOut ){
*ppOut = buf.aBuf;
if( pnOut ) *pnOut = buf.nBuf;
buf.aBuf = 0;
}
}
sqlite3_free(buf.aBuf);
return rc;
}
|
| ︙ | ︙ | |||
212547 212548 212549 212550 212551 212552 212553 |
}
if( rc==SQLITE_OK ){
if( xOutput ){
if( sOut.nBuf>0 ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
}
| | | 213190 213191 213192 213193 213194 213195 213196 213197 213198 213199 213200 213201 213202 213203 213204 |
}
if( rc==SQLITE_OK ){
if( xOutput ){
if( sOut.nBuf>0 ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
}
}else if( ppOut ){
*ppOut = (void*)sOut.aBuf;
*pnOut = sOut.nBuf;
sOut.aBuf = 0;
}
}
sqlite3_free(sOut.aBuf);
return rc;
|
| ︙ | ︙ | |||
213290 213291 213292 213293 213294 213295 213296 | typedef sqlite3_uint64 u64; #ifndef ArraySize # define ArraySize(x) ((int)(sizeof(x) / sizeof(x[0]))) #endif #define testcase(x) | > > > > > | | > > > > > > > | 213933 213934 213935 213936 213937 213938 213939 213940 213941 213942 213943 213944 213945 213946 213947 213948 213949 213950 213951 213952 213953 213954 213955 213956 213957 213958 213959 213960 | typedef sqlite3_uint64 u64; #ifndef ArraySize # define ArraySize(x) ((int)(sizeof(x) / sizeof(x[0]))) #endif #define testcase(x) #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 #endif #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define MAX(x,y) (((x) > (y)) ? (x) : (y)) /* ** Constants for the largest and smallest possible 64-bit signed integers. */ |
| ︙ | ︙ | |||
215508 215509 215510 215511 215512 215513 215514 |
fprintf(fts5yyTraceFILE,"%sDiscard input token %s\n",
fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]);
}
#endif
fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion);
fts5yymajor = fts5YYNOCODE;
}else{
| | | < | < > | | 216163 216164 216165 216166 216167 216168 216169 216170 216171 216172 216173 216174 216175 216176 216177 216178 216179 216180 216181 216182 216183 |
fprintf(fts5yyTraceFILE,"%sDiscard input token %s\n",
fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]);
}
#endif
fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion);
fts5yymajor = fts5YYNOCODE;
}else{
while( fts5yypParser->fts5yytos > fts5yypParser->fts5yystack ){
fts5yyact = fts5yy_find_reduce_action(fts5yypParser->fts5yytos->stateno,
fts5YYERRORSYMBOL);
if( fts5yyact<=fts5YY_MAX_SHIFTREDUCE ) break;
fts5yy_pop_parser_stack(fts5yypParser);
}
if( fts5yypParser->fts5yytos <= fts5yypParser->fts5yystack || fts5yymajor==0 ){
fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
fts5yy_parse_failed(fts5yypParser);
#ifndef fts5YYNOERRORRECOVERY
fts5yypParser->fts5yyerrcnt = -1;
#endif
fts5yymajor = fts5YYNOCODE;
}else if( fts5yymx!=fts5YYERRORSYMBOL ){
|
| ︙ | ︙ | |||
217285 217286 217287 217288 217289 217290 217291 217292 217293 217294 217295 217296 217297 217298 217299 217300 217301 217302 217303 217304 217305 217306 217307 |
int bOption = 0;
int bMustBeCol = 0;
z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol);
z = fts5ConfigSkipWhitespace(z);
if( z && *z=='=' ){
bOption = 1;
z++;
if( bMustBeCol ) z = 0;
}
z = fts5ConfigSkipWhitespace(z);
if( z && z[0] ){
int bDummy;
z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy);
if( z && z[0] ) z = 0;
}
if( rc==SQLITE_OK ){
if( z==0 ){
*pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
rc = SQLITE_ERROR;
}else{
if( bOption ){
| > | > > > > | 217939 217940 217941 217942 217943 217944 217945 217946 217947 217948 217949 217950 217951 217952 217953 217954 217955 217956 217957 217958 217959 217960 217961 217962 217963 217964 217965 217966 217967 217968 217969 217970 217971 217972 217973 217974 |
int bOption = 0;
int bMustBeCol = 0;
z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol);
z = fts5ConfigSkipWhitespace(z);
if( z && *z=='=' ){
bOption = 1;
assert( zOne!=0 );
z++;
if( bMustBeCol ) z = 0;
}
z = fts5ConfigSkipWhitespace(z);
if( z && z[0] ){
int bDummy;
z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy);
if( z && z[0] ) z = 0;
}
if( rc==SQLITE_OK ){
if( z==0 ){
*pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
rc = SQLITE_ERROR;
}else{
if( bOption ){
rc = fts5ConfigParseSpecial(pGlobal, pRet,
ALWAYS(zOne)?zOne:"",
zTwo?zTwo:"",
pzErr
);
}else{
rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
zOne = 0;
}
}
}
|
| ︙ | ︙ | |||
218118 218119 218120 218121 218122 218123 218124 218125 218126 218127 218128 218129 218130 218131 |
** that it points to.
*/
static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
i64 iRet = 0;
int bRetValid = 0;
Fts5ExprTerm *p;
assert( pTerm->pSynonym );
assert( bDesc==0 || bDesc==1 );
for(p=pTerm; p; p=p->pSynonym){
if( 0==sqlite3Fts5IterEof(p->pIter) ){
i64 iRowid = p->pIter->iRowid;
if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){
iRet = iRowid;
| > | 218777 218778 218779 218780 218781 218782 218783 218784 218785 218786 218787 218788 218789 218790 218791 |
** that it points to.
*/
static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
i64 iRet = 0;
int bRetValid = 0;
Fts5ExprTerm *p;
assert( pTerm );
assert( pTerm->pSynonym );
assert( bDesc==0 || bDesc==1 );
for(p=pTerm; p; p=p->pSynonym){
if( 0==sqlite3Fts5IterEof(p->pIter) ){
i64 iRowid = p->pIter->iRowid;
if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){
iRet = iRowid;
|
| ︙ | ︙ | |||
219558 219559 219560 219561 219562 219563 219564 |
}
}else{
/* This happens when parsing a token or quoted phrase that contains
** no token characters at all. (e.g ... MATCH '""'). */
sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
| | | 220218 220219 220220 220221 220222 220223 220224 220225 220226 220227 220228 220229 220230 220231 220232 |
}
}else{
/* This happens when parsing a token or quoted phrase that contains
** no token characters at all. (e.g ... MATCH '""'). */
sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
/* All the allocations succeeded. Put the expression object together. */
pNew->pIndex = pExpr->pIndex;
pNew->pConfig = pExpr->pConfig;
pNew->nPhrase = 1;
pNew->apExprPhrase[0] = sCtx.pPhrase;
pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase;
pNew->pRoot->pNear->nPhrase = 1;
|
| ︙ | ︙ | |||
222000 222001 222002 222003 222004 222005 222006 222007 222008 222009 222010 222011 222012 222013 |
p->rc = rc;
p->nRead++;
}
assert( (pRet==0)==(p->rc!=SQLITE_OK) );
return pRet;
}
/*
** Release a reference to data record returned by an earlier call to
** fts5DataRead().
*/
static void fts5DataRelease(Fts5Data *pData){
sqlite3_free(pData);
| > | 222660 222661 222662 222663 222664 222665 222666 222667 222668 222669 222670 222671 222672 222673 222674 |
p->rc = rc;
p->nRead++;
}
assert( (pRet==0)==(p->rc!=SQLITE_OK) );
return pRet;
}
/*
** Release a reference to data record returned by an earlier call to
** fts5DataRead().
*/
static void fts5DataRelease(Fts5Data *pData){
sqlite3_free(pData);
|
| ︙ | ︙ | |||
222140 222141 222142 222143 222144 222145 222146 222147 222148 222149 222150 222151 222152 222153 |
}
static int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){
if( p->pStruct!=(Fts5Structure*)pStruct ){
return SQLITE_ABORT;
}
return SQLITE_OK;
}
/*
** Deserialize and return the structure record currently stored in serialized
** form within buffer pData/nData.
**
** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
** are over-allocated by one slot. This allows the structure contents
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 222801 222802 222803 222804 222805 222806 222807 222808 222809 222810 222811 222812 222813 222814 222815 222816 222817 222818 222819 222820 222821 222822 222823 222824 222825 222826 222827 222828 222829 222830 222831 222832 222833 222834 222835 222836 222837 222838 222839 222840 222841 222842 222843 222844 222845 222846 222847 222848 222849 222850 |
}
static int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){
if( p->pStruct!=(Fts5Structure*)pStruct ){
return SQLITE_ABORT;
}
return SQLITE_OK;
}
/*
** Ensure that structure object (*pp) is writable.
**
** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If
** an error occurs, (*pRc) is set to an SQLite error code before returning.
*/
static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
Fts5Structure *p = *pp;
if( *pRc==SQLITE_OK && p->nRef>1 ){
int nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel);
Fts5Structure *pNew;
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
if( pNew ){
int i;
memcpy(pNew, p, nByte);
for(i=0; i<p->nLevel; i++) pNew->aLevel[i].aSeg = 0;
for(i=0; i<p->nLevel; i++){
Fts5StructureLevel *pLvl = &pNew->aLevel[i];
nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg;
pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(pRc, nByte);
if( pLvl->aSeg==0 ){
for(i=0; i<p->nLevel; i++){
sqlite3_free(pNew->aLevel[i].aSeg);
}
sqlite3_free(pNew);
return;
}
memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte);
}
p->nRef--;
pNew->nRef = 1;
}
*pp = pNew;
}
}
/*
** Deserialize and return the structure record currently stored in serialized
** form within buffer pData/nData.
**
** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
** are over-allocated by one slot. This allows the structure contents
|
| ︙ | ︙ | |||
222242 222243 222244 222245 222246 222247 222248 | } *ppOut = pRet; return rc; } /* | > | > | 222939 222940 222941 222942 222943 222944 222945 222946 222947 222948 222949 222950 222951 222952 222953 222954 222955 222956 222957 |
}
*ppOut = pRet;
return rc;
}
/*
** Add a level to the Fts5Structure.aLevel[] array of structure object
** (*ppStruct).
*/
static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
fts5StructureMakeWritable(pRc, ppStruct);
if( *pRc==SQLITE_OK ){
Fts5Structure *pStruct = *ppStruct;
int nLevel = pStruct->nLevel;
sqlite3_int64 nByte = (
sizeof(Fts5Structure) + /* Main structure */
sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
);
|
| ︙ | ︙ | |||
223038 223039 223040 223041 223042 223043 223044 223045 223046 223047 223048 223049 223050 223051 |
pIter->pSeg = pSeg;
pIter->iLeafPgno = pSeg->pgnoFirst-1;
fts5SegIterNextPage(p, pIter);
}
if( p->rc==SQLITE_OK ){
pIter->iLeafOffset = 4;
assert_nc( pIter->pLeaf->nn>4 );
assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
pIter->iPgidxOff = pIter->pLeaf->szLeaf+1;
fts5SegIterLoadTerm(p, pIter, 0);
fts5SegIterLoadNPos(p, pIter);
}
}
| > | 223737 223738 223739 223740 223741 223742 223743 223744 223745 223746 223747 223748 223749 223750 223751 |
pIter->pSeg = pSeg;
pIter->iLeafPgno = pSeg->pgnoFirst-1;
fts5SegIterNextPage(p, pIter);
}
if( p->rc==SQLITE_OK ){
pIter->iLeafOffset = 4;
assert( pIter->pLeaf!=0 );
assert_nc( pIter->pLeaf->nn>4 );
assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
pIter->iPgidxOff = pIter->pLeaf->szLeaf+1;
fts5SegIterLoadTerm(p, pIter, 0);
fts5SegIterLoadNPos(p, pIter);
}
}
|
| ︙ | ︙ | |||
223421 223422 223423 223424 223425 223426 223427 |
Fts5DlidxIter *pDlidx = pIter->pDlidx;
Fts5Data *pLast = 0;
int pgnoLast = 0;
if( pDlidx ){
int iSegid = pIter->pSeg->iSegid;
pgnoLast = fts5DlidxIterPgno(pDlidx);
| | | 224121 224122 224123 224124 224125 224126 224127 224128 224129 224130 224131 224132 224133 224134 224135 |
Fts5DlidxIter *pDlidx = pIter->pDlidx;
Fts5Data *pLast = 0;
int pgnoLast = 0;
if( pDlidx ){
int iSegid = pIter->pSeg->iSegid;
pgnoLast = fts5DlidxIterPgno(pDlidx);
pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast));
}else{
Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */
/* Currently, Fts5SegIter.iLeafOffset points to the first byte of
** position-list content for the current rowid. Back it up so that it
** points to the start of the position-list size field. */
int iPoslist;
|
| ︙ | ︙ | |||
223448 223449 223450 223451 223452 223453 223454 |
int pgno;
Fts5StructureSegment *pSeg = pIter->pSeg;
/* The last rowid in the doclist may not be on the current page. Search
** forward to find the page containing the last rowid. */
for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){
i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
| | | 224148 224149 224150 224151 224152 224153 224154 224155 224156 224157 224158 224159 224160 224161 224162 |
int pgno;
Fts5StructureSegment *pSeg = pIter->pSeg;
/* The last rowid in the doclist may not be on the current page. Search
** forward to find the page containing the last rowid. */
for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){
i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
Fts5Data *pNew = fts5LeafRead(p, iAbs);
if( pNew ){
int iRowid, bTermless;
iRowid = fts5LeafFirstRowidOff(pNew);
bTermless = fts5LeafIsTermless(pNew);
if( iRowid ){
SWAPVAL(Fts5Data*, pNew, pLast);
pgnoLast = pgno;
|
| ︙ | ︙ | |||
223479 223480 223481 223482 223483 223484 223485 223486 223487 223488 223489 223490 223491 223492 223493 |
*/
if( pLast ){
int iOff;
fts5DataRelease(pIter->pLeaf);
pIter->pLeaf = pLast;
pIter->iLeafPgno = pgnoLast;
iOff = fts5LeafFirstRowidOff(pLast);
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
pIter->iLeafOffset = iOff;
if( fts5LeafIsTermless(pLast) ){
pIter->iEndofDoclist = pLast->nn+1;
}else{
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
}
| > > > > < | 224179 224180 224181 224182 224183 224184 224185 224186 224187 224188 224189 224190 224191 224192 224193 224194 224195 224196 224197 224198 224199 224200 224201 224202 224203 224204 |
*/
if( pLast ){
int iOff;
fts5DataRelease(pIter->pLeaf);
pIter->pLeaf = pLast;
pIter->iLeafPgno = pgnoLast;
iOff = fts5LeafFirstRowidOff(pLast);
if( iOff>pLast->szLeaf ){
p->rc = FTS5_CORRUPT;
return;
}
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
pIter->iLeafOffset = iOff;
if( fts5LeafIsTermless(pLast) ){
pIter->iEndofDoclist = pLast->nn+1;
}else{
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
}
}
fts5SegIterReverseInitPage(p, pIter);
}
/*
** Iterator pIter currently points to the first rowid of a doclist.
|
| ︙ | ︙ | |||
223995 223996 223997 223998 223999 224000 224001 |
}else{
fts5DataRelease(pIter->pNextLeaf);
pIter->pNextLeaf = 0;
pIter->iLeafPgno = iLeafPgno-1;
fts5SegIterNextPage(p, pIter);
assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno );
| | | 224698 224699 224700 224701 224702 224703 224704 224705 224706 224707 224708 224709 224710 224711 224712 |
}else{
fts5DataRelease(pIter->pNextLeaf);
pIter->pNextLeaf = 0;
pIter->iLeafPgno = iLeafPgno-1;
fts5SegIterNextPage(p, pIter);
assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno );
if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){
int iOff;
u8 *a = pIter->pLeaf->p;
int n = pIter->pLeaf->szLeaf;
iOff = fts5LeafFirstRowidOff(pIter->pLeaf);
if( iOff<4 || iOff>=n ){
p->rc = FTS5_CORRUPT;
|
| ︙ | ︙ | |||
224427 224428 224429 224430 224431 224432 224433 224434 224435 224436 224437 224438 224439 224440 224441 |
*/
static void fts5SegiterPoslist(
Fts5Index *p,
Fts5SegIter *pSeg,
Fts5Colset *pColset,
Fts5Buffer *pBuf
){
if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){
memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING);
if( pColset==0 ){
fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback);
}else{
if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){
PoslistCallbackCtx sCtx;
sCtx.pBuf = pBuf;
| > > > > | 225130 225131 225132 225133 225134 225135 225136 225137 225138 225139 225140 225141 225142 225143 225144 225145 225146 225147 225148 |
*/
static void fts5SegiterPoslist(
Fts5Index *p,
Fts5SegIter *pSeg,
Fts5Colset *pColset,
Fts5Buffer *pBuf
){
assert( pBuf!=0 );
assert( pSeg!=0 );
if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){
assert( pBuf->p!=0 );
assert( pBuf->nSpace >= pBuf->n+pSeg->nPos+FTS5_DATA_ZERO_PADDING );
memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING);
if( pColset==0 ){
fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback);
}else{
if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){
PoslistCallbackCtx sCtx;
sCtx.pBuf = pBuf;
|
| ︙ | ︙ | |||
224651 224652 224653 224654 224655 224656 224657 224658 224659 224660 224661 224662 224663 224664 |
fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist);
pIter->base.pData = pIter->poslist.p;
pIter->base.nData = pIter->poslist.n;
}
}
static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
if( *pRc==SQLITE_OK ){
Fts5Config *pConfig = pIter->pIndex->pConfig;
if( pConfig->eDetail==FTS5_DETAIL_NONE ){
pIter->xSetOutputs = fts5IterSetOutputs_None;
}
else if( pIter->pColset==0 ){
| > | 225358 225359 225360 225361 225362 225363 225364 225365 225366 225367 225368 225369 225370 225371 225372 |
fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist);
pIter->base.pData = pIter->poslist.p;
pIter->base.nData = pIter->poslist.n;
}
}
static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
assert( pIter!=0 || (*pRc)!=SQLITE_OK );
if( *pRc==SQLITE_OK ){
Fts5Config *pConfig = pIter->pIndex->pConfig;
if( pConfig->eDetail==FTS5_DETAIL_NONE ){
pIter->xSetOutputs = fts5IterSetOutputs_None;
}
else if( pIter->pColset==0 ){
|
| ︙ | ︙ | |||
224722 224723 224724 224725 224726 224727 224728 |
nSeg = pStruct->nSegment;
nSeg += (p->pHash ? 1 : 0);
}else{
nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
}
}
*ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
| | > > > | 225430 225431 225432 225433 225434 225435 225436 225437 225438 225439 225440 225441 225442 225443 225444 225445 225446 225447 |
nSeg = pStruct->nSegment;
nSeg += (p->pHash ? 1 : 0);
}else{
nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
}
}
*ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
if( pNew==0 ){
assert( p->rc!=SQLITE_OK );
goto fts5MultiIterNew_post_check;
}
pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY));
pNew->pColset = pColset;
if( (flags & FTS5INDEX_QUERY_NOOUTPUT)==0 ){
fts5IterSetOutputCb(&p->rc, pNew);
}
|
| ︙ | ︙ | |||
224786 224787 224788 224789 224790 224791 224792 224793 224794 224795 224796 224797 224798 224799 |
pNew->xSetOutputs(pNew, pSeg);
}
}else{
fts5MultiIterFree(pNew);
*ppOut = 0;
}
}
/*
** Create an Fts5Iter that iterates through the doclist provided
** as the second argument.
*/
static void fts5MultiIterNew2(
| > > > > | 225497 225498 225499 225500 225501 225502 225503 225504 225505 225506 225507 225508 225509 225510 225511 225512 225513 225514 |
pNew->xSetOutputs(pNew, pSeg);
}
}else{
fts5MultiIterFree(pNew);
*ppOut = 0;
}
fts5MultiIterNew_post_check:
assert( (*ppOut)!=0 || p->rc!=SQLITE_OK );
return;
}
/*
** Create an Fts5Iter that iterates through the doclist provided
** as the second argument.
*/
static void fts5MultiIterNew2(
|
| ︙ | ︙ | |||
224833 224834 224835 224836 224837 224838 224839 |
}
/*
** Return true if the iterator is at EOF or if an error has occurred.
** False otherwise.
*/
static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){
| > | | 225548 225549 225550 225551 225552 225553 225554 225555 225556 225557 225558 225559 225560 225561 225562 225563 |
}
/*
** Return true if the iterator is at EOF or if an error has occurred.
** False otherwise.
*/
static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){
assert( pIter!=0 || p->rc!=SQLITE_OK );
assert( p->rc!=SQLITE_OK
|| (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof
);
return (p->rc || pIter->base.bEof);
}
/*
** Return the rowid of the entry that the iterator currently points
|
| ︙ | ︙ | |||
225637 225638 225639 225640 225641 225642 225643 225644 225645 225646 225647 225648 225649 225650 |
}
}
/* Flush the last leaf page to disk. Set the output segment b-tree height
** and last leaf page number at the same time. */
fts5WriteFinish(p, &writer, &pSeg->pgnoLast);
if( fts5MultiIterEof(p, pIter) ){
int i;
/* Remove the redundant segments from the %_data table */
for(i=0; i<nInput; i++){
fts5DataRemoveSegment(p, pLvl->aSeg[i].iSegid);
}
| > | 226353 226354 226355 226356 226357 226358 226359 226360 226361 226362 226363 226364 226365 226366 226367 |
}
}
/* Flush the last leaf page to disk. Set the output segment b-tree height
** and last leaf page number at the same time. */
fts5WriteFinish(p, &writer, &pSeg->pgnoLast);
assert( pIter!=0 || p->rc!=SQLITE_OK );
if( fts5MultiIterEof(p, pIter) ){
int i;
/* Remove the redundant segments from the %_data table */
for(i=0; i<nInput; i++){
fts5DataRemoveSegment(p, pLvl->aSeg[i].iSegid);
}
|
| ︙ | ︙ | |||
225737 225738 225739 225740 225741 225742 225743 |
** already occurred, this function is a no-op.
*/
static void fts5IndexAutomerge(
Fts5Index *p, /* FTS5 backend object */
Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */
int nLeaf /* Number of output leaves just written */
){
| | | 226454 226455 226456 226457 226458 226459 226460 226461 226462 226463 226464 226465 226466 226467 226468 |
** already occurred, this function is a no-op.
*/
static void fts5IndexAutomerge(
Fts5Index *p, /* FTS5 backend object */
Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */
int nLeaf /* Number of output leaves just written */
){
if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 && ALWAYS((*ppStruct)!=0) ){
Fts5Structure *pStruct = *ppStruct;
u64 nWrite; /* Initial value of write-counter */
int nWork; /* Number of work-quanta to perform */
int nRem; /* Number of leaf pages left to write */
/* Update the write-counter. While doing so, set nWork. */
nWrite = pStruct->nWriteCounter;
|
| ︙ | ︙ | |||
226847 226848 226849 226850 226851 226852 226853 |
);
fts5StructureRelease(pStruct);
}
}else{
/* Scan multiple terms in the main index */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
| > | > > | | | | > | 227564 227565 227566 227567 227568 227569 227570 227571 227572 227573 227574 227575 227576 227577 227578 227579 227580 227581 227582 227583 227584 227585 227586 |
);
fts5StructureRelease(pStruct);
}
}else{
/* Scan multiple terms in the main index */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
if( pRet==0 ){
assert( p->rc!=SQLITE_OK );
}else{
assert( pRet->pColset==0 );
fts5IterSetOutputCb(&p->rc, pRet);
if( p->rc==SQLITE_OK ){
Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
}
}
}
if( p->rc ){
sqlite3Fts5IterClose((Fts5IndexIter*)pRet);
pRet = 0;
sqlite3Fts5IndexCloseReader(p);
|
| ︙ | ︙ | |||
227099 227100 227101 227102 227103 227104 227105 |
u64 *pCksum /* IN/OUT: Checksum value */
){
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
Fts5IndexIter *pIter = 0;
int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
| | | 227820 227821 227822 227823 227824 227825 227826 227827 227828 227829 227830 227831 227832 227833 227834 |
u64 *pCksum /* IN/OUT: Checksum value */
){
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
Fts5IndexIter *pIter = 0;
int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){
i64 rowid = pIter->iRowid;
if( eDetail==FTS5_DETAIL_NONE ){
cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
}else{
Fts5PoslistReader sReader;
for(sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &sReader);
|
| ︙ | ︙ | |||
227464 227465 227466 227467 227468 227469 227470 227471 227472 227473 227474 227475 227476 227477 227478 227479 227480 |
*/
static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
int eDetail = p->pConfig->eDetail;
u64 cksum2 = 0; /* Checksum based on contents of indexes */
Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
Fts5Iter *pIter; /* Used to iterate through entire index */
Fts5Structure *pStruct; /* Index structure */
#ifdef SQLITE_DEBUG
/* Used by extra internal tests only run if NDEBUG is not defined */
u64 cksum3 = 0; /* Checksum based on contents of indexes */
Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
#endif
const int flags = FTS5INDEX_QUERY_NOOUTPUT;
/* Load the FTS index structure */
pStruct = fts5StructureRead(p);
| > > > > | > < < | | | | < | 228185 228186 228187 228188 228189 228190 228191 228192 228193 228194 228195 228196 228197 228198 228199 228200 228201 228202 228203 228204 228205 228206 228207 228208 228209 228210 228211 228212 228213 228214 228215 228216 228217 228218 228219 |
*/
static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
int eDetail = p->pConfig->eDetail;
u64 cksum2 = 0; /* Checksum based on contents of indexes */
Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
Fts5Iter *pIter; /* Used to iterate through entire index */
Fts5Structure *pStruct; /* Index structure */
int iLvl, iSeg;
#ifdef SQLITE_DEBUG
/* Used by extra internal tests only run if NDEBUG is not defined */
u64 cksum3 = 0; /* Checksum based on contents of indexes */
Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
#endif
const int flags = FTS5INDEX_QUERY_NOOUTPUT;
/* Load the FTS index structure */
pStruct = fts5StructureRead(p);
if( pStruct==0 ){
assert( p->rc!=SQLITE_OK );
return fts5IndexReturn(p);
}
/* Check that the internal nodes of each segment match the leaves */
for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
fts5IndexIntegrityCheckSegment(p, pSeg);
}
}
/* The cksum argument passed to this function is a checksum calculated
** based on all expected entries in the FTS index (including prefix index
** entries). This block checks that a checksum calculated based on the
** actual contents of FTS index is identical.
|
| ︙ | ︙ | |||
229429 229430 229431 229432 229433 229434 229435 |
/* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
** by rowid (ePlan==FTS5_PLAN_ROWID). */
pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
rc = sqlite3Fts5StorageStmt(
pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg
);
if( rc==SQLITE_OK ){
| > | | 230152 230153 230154 230155 230156 230157 230158 230159 230160 230161 230162 230163 230164 230165 230166 230167 |
/* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
** by rowid (ePlan==FTS5_PLAN_ROWID). */
pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
rc = sqlite3Fts5StorageStmt(
pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg
);
if( rc==SQLITE_OK ){
if( pRowidEq!=0 ){
assert( pCsr->ePlan==FTS5_PLAN_ROWID );
sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq);
}else{
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
}
rc = fts5NextMethod(pCursor);
}
|
| ︙ | ︙ | |||
230847 230848 230849 230850 230851 230852 230853 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
| | | 231571 231572 231573 231574 231575 231576 231577 231578 231579 231580 231581 231582 231583 231584 231585 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2021-10-05 18:33:38 a7835bead85b1b18a8affd9835240b0baf9c7af887196bbdcc3f5d58055042fc", -1, SQLITE_TRANSIENT);
}
/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){
|
| ︙ | ︙ | |||
231398 231399 231400 231401 231402 231403 231404 231405 231406 231407 |
ctx.pStorage = p;
ctx.iCol = -1;
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
if( pConfig->abUnindexed[iCol-1]==0 ){
const char *zText;
int nText;
if( pSeek ){
zText = (const char*)sqlite3_column_text(pSeek, iCol);
nText = sqlite3_column_bytes(pSeek, iCol);
| > > | > > | 232122 232123 232124 232125 232126 232127 232128 232129 232130 232131 232132 232133 232134 232135 232136 232137 232138 232139 232140 232141 232142 232143 232144 232145 |
ctx.pStorage = p;
ctx.iCol = -1;
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
if( pConfig->abUnindexed[iCol-1]==0 ){
const char *zText;
int nText;
assert( pSeek==0 || apVal==0 );
assert( pSeek!=0 || apVal!=0 );
if( pSeek ){
zText = (const char*)sqlite3_column_text(pSeek, iCol);
nText = sqlite3_column_bytes(pSeek, iCol);
}else if( ALWAYS(apVal) ){
zText = (const char*)sqlite3_value_text(apVal[iCol-1]);
nText = sqlite3_value_bytes(apVal[iCol-1]);
}else{
continue;
}
ctx.szCol = 0;
rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
zText, nText, (void*)&ctx, fts5StorageInsertCallback
);
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
if( p->aTotalSize[iCol-1]<0 ){
|
| ︙ | ︙ | |||
232039 232040 232041 232042 232043 232044 232045 |
static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
int nCol = p->pConfig->nCol; /* Number of user columns in table */
sqlite3_stmt *pLookup = 0; /* Statement to query %_docsize */
int rc; /* Return Code */
assert( p->pConfig->bColumnsize );
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
| | > > > | 232767 232768 232769 232770 232771 232772 232773 232774 232775 232776 232777 232778 232779 232780 232781 232782 232783 232784 232785 232786 232787 232788 232789 232790 232791 232792 232793 232794 232795 232796 232797 |
static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
int nCol = p->pConfig->nCol; /* Number of user columns in table */
sqlite3_stmt *pLookup = 0; /* Statement to query %_docsize */
int rc; /* Return Code */
assert( p->pConfig->bColumnsize );
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
if( pLookup ){
int bCorrupt = 1;
assert( rc==SQLITE_OK );
sqlite3_bind_int64(pLookup, 1, iRowid);
if( SQLITE_ROW==sqlite3_step(pLookup) ){
const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
int nBlob = sqlite3_column_bytes(pLookup, 0);
if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){
bCorrupt = 0;
}
}
rc = sqlite3_reset(pLookup);
if( bCorrupt && rc==SQLITE_OK ){
rc = FTS5_CORRUPT;
}
}else{
assert( rc!=SQLITE_OK );
}
return rc;
}
static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){
int rc = fts5StorageLoadTotals(p, 0);
|
| ︙ | ︙ |
Changes to src/sqlite3.h.
| ︙ | ︙ | |||
144 145 146 147 148 149 150 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.37.0" #define SQLITE_VERSION_NUMBER 3037000 | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.37.0" #define SQLITE_VERSION_NUMBER 3037000 #define SQLITE_SOURCE_ID "2021-10-06 10:36:56 566e6974892ebd3d3de8d77b24655257a5efe14434c553e1a25fc680b201b336" /* ** 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 |
| ︙ | ︙ | |||
557 558 559 560 561 562 563 564 565 566 567 568 569 570 | #define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) #define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) #define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) #define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) | > | 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 | #define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) #define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) #define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) #define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) #define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) |
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
400 401 402 403 404 405 406 | } /* ** Show the diffs associate with a single stash. */ static void stash_diff( int stashid, /* The stash entry to diff */ | < < < < > > > < | | < < | < | | < < | < | | | < < | < < < > | | > | 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 |
}
/*
** Show the diffs associate with a single stash.
*/
static void stash_diff(
int stashid, /* The stash entry to diff */
int fBaseline, /* Diff against original baseline check-in if true */
DiffConfig *pCfg /* Diff formatting options */
){
Stmt q;
Blob empty;
int bWebpage = (pCfg->diffFlags & (DIFF_WEBPAGE|DIFF_JSON|DIFF_TCL))!=0;
blob_zero(&empty);
diff_begin(pCfg);
db_prepare(&q,
"SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta"
" FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash",
stashid
);
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
int isRemoved = db_column_int(&q, 1);
int isLink = db_column_int(&q, 3);
const char *zOrig = db_column_text(&q, 4);
const char *zNew = db_column_text(&q, 5);
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
Blob a, b;
if( rid==0 ){
db_ephemeral_blob(&q, 6, &a);
if( !bWebpage ) fossil_print("ADDED %s\n", zNew);
diff_print_index(zNew, pCfg, 0);
diff_file_mem(&empty, &a, zNew, pCfg);
}else if( isRemoved ){
if( !bWebpage) fossil_print("DELETE %s\n", zOrig);
diff_print_index(zNew, pCfg, 0);
if( fBaseline ){
content_get(rid, &a);
diff_file_mem(&a, &empty, zOrig, pCfg);
}
}else{
Blob delta;
int isOrigLink = file_islink(zOPath);
db_ephemeral_blob(&q, 6, &delta);
if( !bWebpage ) fossil_print("CHANGED %s\n", zNew);
if( !isOrigLink != !isLink ){
diff_print_index(zNew, pCfg, 0);
diff_print_filenames(zOrig, zNew, pCfg, 0);
printf(DIFF_CANNOT_COMPUTE_SYMLINK);
}else{
content_get(rid, &a);
blob_delta_apply(&a, &delta, &b);
if( fBaseline ){
diff_file_mem(&a, &b, zNew, pCfg);
}else{
pCfg->diffFlags ^= DIFF_INVERT;
diff_file(&b, zOPath, zNew, pCfg, 0);
pCfg->diffFlags ^= DIFF_INVERT;
}
blob_reset(&a);
blob_reset(&b);
}
blob_reset(&delta);
}
}
db_finalize(&q);
diff_end(pCfg, 0);
}
/*
** Drop the indicated stash
*/
static void stash_drop(int stashid){
db_multi_exec(
|
| ︙ | ︙ | |||
735 736 737 738 739 740 741 |
if( memcmp(zCmd, "diff", nCmd)==0
|| memcmp(zCmd, "gdiff", nCmd)==0
|| memcmp(zCmd, "show", nCmd)==0
|| memcmp(zCmd, "gshow", nCmd)==0
|| memcmp(zCmd, "cat", nCmd)==0
|| memcmp(zCmd, "gcat", nCmd)==0
){
| < < < | < < < | < < < < < < | < | 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 |
if( memcmp(zCmd, "diff", nCmd)==0
|| memcmp(zCmd, "gdiff", nCmd)==0
|| memcmp(zCmd, "show", nCmd)==0
|| memcmp(zCmd, "gshow", nCmd)==0
|| memcmp(zCmd, "cat", nCmd)==0
|| memcmp(zCmd, "gcat", nCmd)==0
){
int fBaseline = 0;
DiffConfig DCfg;
if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
fBaseline = 1;
}
if( find_option("tk",0,0)!=0 ){
db_close(0);
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
return;
}
diff_options(&DCfg, zCmd[0]=='g', 0);
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
stash_diff(stashid, fBaseline, &DCfg);
}else
if( memcmp(zCmd, "help", nCmd)==0 ){
g.argv[1] = "help";
g.argv[2] = "stash";
g.argc = 3;
help_cmd();
}else
{
usage("SUBCOMMAND ARGS...");
}
db_end_transaction(0);
}
|
Changes to src/stat.c.
| ︙ | ︙ | |||
488 489 490 491 492 493 494 |
style_set_current_feature("stat");
style_header("URLs and Checkouts");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Stat", "stat");
style_submenu_element("Schema", "repo_schema");
iNow = db_int64(0, "SELECT strftime('%%s','now')");
| | | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 |
style_set_current_feature("stat");
style_header("URLs and Checkouts");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Stat", "stat");
style_submenu_element("Schema", "repo_schema");
iNow = db_int64(0, "SELECT strftime('%%s','now')");
@ <div class="section">URLs used to access this repository</div>
@ <table border="0" width='100%%'>
db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch'), mtime"
" FROM config WHERE name GLOB 'baseurl:*' ORDER BY 3 DESC");
cnt = 0;
nOmitted = 0;
while( db_step(&q)==SQLITE_ROW ){
if( !showAll && db_column_int64(&q,2)<(iNow - 3600*24*30) && cnt>8 ){
|
| ︙ | ︙ | |||
530 531 532 533 534 535 536 |
}
}
db_finalize(&q);
if( cnt ){
@ </table>
}
cnt = 0;
| > | | < | | | < < < < < < < < < < | | | > > | > > | 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 |
}
}
db_finalize(&q);
if( cnt ){
@ </table>
}
cnt = 0;
db_prepare(&q,
"SELECT substr(name,10), datetime(mtime,'unixepoch')"
" FROM config WHERE name GLOB 'syncwith:*'"
"UNION ALL "
"SELECT substr(name,10), datetime(mtime,'unixepoch')"
" FROM config WHERE name GLOB 'syncfrom:*'"
"UNION ALL "
"SELECT substr(name,9), datetime(mtime,'unixepoch')"
" FROM config WHERE name GLOB 'gitpush:*'"
"ORDER BY 2 DESC"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zURL = db_column_text(&q,0);
UrlData x;
if( cnt==0 ){
@ <div class="section">Sync with these URLs</div>
@ <table border='0' width='100%%'>
}
memset(&x, 0, sizeof(x));
url_parse_local(zURL, URL_OMIT_USER, &x);
@ <tr><td width='100%%'><a href='%h(x.canonical)'>%h(x.canonical)</a>
@ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
cnt++;
url_unparse(&x);
}
db_finalize(&q);
if( cnt ){
@ </table>
}
style_finish_page();
}
|
| ︙ | ︙ |
Added src/style.chat.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 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 |
/* Chat-related */
body.chat span.at-name { /* for @USERNAME references */
text-decoration: underline;
font-weight: bold;
}
/* A wrapper for a single single chat message (one row of the UI) */
body.chat .message-widget {
margin-bottom: 0.75em;
border: none;
display: flex;
flex-direction: column;
border: none;
align-items: flex-start;
}
body.chat button,
body.chat input[type=button] {
line-height: inherit/*undo skin-specific funkiness*/;
}
body.chat .message-widget:last-of-type {
/* Latest message: reduce bottom gap */
margin-bottom: 0.1em;
}
body.chat.my-messages-right .message-widget.mine {
/* Right-aligns a user's own chat messages, similar to how
most/some mobile messaging apps do it. */
align-items: flex-end;
}
body.chat.my-messages-right .message-widget.notification {
/* Center-aligns a system-level notification message. */
align-items: center;
}
/* The content area of a message. */
body.chat .message-widget-content {
display: inline-block;
border-radius: 0.25em;
border: 1px solid rgba(0,0,0,0.2);
box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
padding: 0.25em 0.5em;
margin-top: 0;
min-width: 9em /*avoid unsightly "underlap" with the neighboring
.message-widget-tab element*/;
white-space: normal;
}
body.chat.monospace-messages .message-widget-content,
/*body.chat.monospace-messages textarea,*/
/*body.chat.monospace-messages input[type=text],*/
body.chat.monospace-messages #chat-input-field{
font-family: monospace;
}
body.chat .message-widget-content > * {
margin: 0;
padding: 0;
}
body.chat .message-widget-content > pre {
white-space: pre-wrap;
}
body.chat .message-widget-content > .markdown > *:first-child {
margin-top: 0;
}
body.chat .message-widget-content > .markdown > *:last-child {
margin-bottom: 0;
}
/* User name and timestamp (a LEGEND-like element) */
body.chat .message-widget .message-widget-tab {
border-radius: 0.25em 0.25em 0 0;
margin: 0 0.25em 0em 0.15em;
padding: 0 0.5em 0.15em 0.5em;
cursor: pointer;
white-space: nowrap;
}
body.chat .fossil-tooltip.help-buttonlet-content {
font-size: 80%;
}
body.chat .message-widget .message-widget-tab .xfrom {
/* Element which holds the "this message is from user X" part
of the message banner. */
font-style: italic;
font-weight: bold;
}
/* The popup element for displaying message timestamps
and deletion controls. */
body.chat .chat-message-popup {
font-family: monospace;
font-size: 0.9em;
text-align: left;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 0.25em;
margin-top: 0.25em;
border: 1px outset;
border-radius: 0.5em;
}
/* Full message timestamps. */
body.chat .chat-message-popup > span { white-space: nowrap; }
/* Container for the message deletion buttons. */
body.chat .chat-message-popup > .toolbar {
padding: 0;
margin: 0;
border: 2px inset rgba(0,0,0,0.3);
border-radius: 0.25em;
display: flex;
flex-direction: row;
justify-content: stretch;
flex-wrap: wrap;
align-items: center;
}
body.chat .chat-message-popup > .toolbar > * {
margin: 0.35em;
}
body.chat .chat-message-popup > .toolbar > button {
flex: 1 1 auto;
}
/* The widget for loading more/older chat messages. */
body.chat #load-msg-toolbar {
border-radius: 0.25em;
padding: 0.1em 0.2em;
margin-bottom: 1em;
}
/* .all-done is set when chat has loaded all of the available
historical messages */
body.chat #load-msg-toolbar.all-done {
opacity: 0.5;
}
body.chat #load-msg-toolbar > div {
display: flex;
flex-direction: row;
justify-content: stretch;
flex-wrap: wrap;
}
body.chat #load-msg-toolbar > div > button {
flex: 1 1 auto;
}
/* "Chat-only mode" hides the site header/footer, showing only
the chat app. */
body.chat.chat-only-mode{}
body.chat #chat-button-settings {}
/** Popup widget for the /chat settings. */
body.chat .chat-settings-popup {
font-size: 0.8em;
text-align: left;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 0.25em;
z-index: 200;
}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
overflow: auto;
padding: 0 0.25em;
}
body.chat #chat-messages-wrapper.loading > * {
/* An attempt at reducing flicker when loading lots of messages. */
visibility: hidden;
}
body.chat div.content {
margin: 0;
padding: 0;
display: flex;
flex-direction: column-reverse;
/* ^^^^ In order to get good automatic scrolling of new messages on
the BOTTOM in bottom-up chat mode, such that they scroll up
instead of down, we have to use column-reverse layout, which
changes #chat-messages-wrapper's "gravity" for purposes of
scrolling! If we instead use flex-direction:column then each new
message pushes #chat-input-area down further off the screen!
*/
align-items: stretch;
}
/* Wrapper for /chat user input controls */
body.chat #chat-input-area {
display: flex;
flex-direction: column;
padding: 0;
margin: 0;
flex: 0 1 auto;
}
body.chat:not(.chat-only-mode) #chat-input-area{
/* Safari user reports that 2em is necessary to keep the file selection
widget from overlapping the page footer, whereas a margin of 0 is fine
for FF/Chrome (and 2em is a *huge* waste of space for those). */
margin-bottom: 0;
}
#chat-input-field {
display: inline-block/*supposed workaround for Chrome weirdness*/;
padding: 0.2em;
flex: 10 1 auto;
background-color: rgba(156,156,156,0.3);
overflow: auto;
resize: vertical;
white-space: pre-wrap;
/* ^^^ Firefox, when pasting plain text into a contenteditable field,
loses all newlines unless we explicitly set this. Chrome does not. */
cursor: text;
/* ^^^ In some browsers the cursor may not change for a contenteditable
element until it has focus, causing potential confusion. */
}
#chat-input-field:empty::before {
content: attr(data-placeholder);
opacity: 0.6;
}
#chat-input-field:not(:focus){
border-width: 1px;
border-style: solid;
border-radius: 0.25em;
}
#chat-input-field:focus{
/* This transparent border helps avoid the text shifting around
when the contenteditable attribute causes a border (which we
apparently cannot style) to be added. */
border-width: 1px;
border-style: solid;
border-color: transparent;
border-radius: 0.25em;
}
/* Widget holding the chat message input field, send button, and
settings button. */
body.chat #chat-input-line {
display: flex;
flex-direction: row;
align-items: stretch;
flex-wrap: nowrap;
}
/*body.chat #chat-input-line:not(.compact) {
flex-wrap: nowrap;
}*/
body.chat #chat-input-line.compact {
/* "The problem" with wrapping, together with a contenteditable input
field, is that the latter grows as the user types, so causes
wrapping to happen while they type, then to unwrap as soon as the
input field is cleared (when the message is sent). When we stay
wrapped in compact mode, the wrapped buttons simply take up too
much space. */
/*flex-wrap: wrap;
justify-content: flex-end;*/
flex-direction: column;
/**
We "really do" need column orientation here because it's the
only way to eliminate the possibility that (A) the buttons
get truncated in very narrow windows and (B) that they keep
stable positions.
*/
}
body.chat #chat-input-line.compact #chat-input-field {
}
body.chat #chat-buttons-wrapper {
flex: 0 1 auto;
display: flex;
flex-direction: column;
align-items: center;
min-width: 4em;
min-height: 1.5em;
align-self: flex-end
/*keep buttons stable at bottom/right even when input field
resizes */;
}
body.chat #chat-input-line.compact #chat-buttons-wrapper {
flex-direction: row;
flex: 1 1 auto;
align-self: stretch;
justify-content: flex-end;
/*flex-wrap: wrap;*/
/* Wrapping would be ideal except that the edit widget
grows in width as the user types, moving the buttons
around */
}
body.chat #chat-buttons-wrapper > .cbutton {
padding: 0;
display: inline-block;
border-width: 1px;
border-style: solid;
border-radius: 0.25em;
min-width: 4ex;
max-width: 4ex;
min-height: 4ex;
max-height: 4ex;
margin: 0.125em;
display: inline-flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 130%;
}
body.chat #chat-buttons-wrapper > .cbutton:hover {
background-color: rgba(200,200,200,0.3);
}
body.chat #chat-input-line.compact #chat-buttons-wrapper > .cbutton {
margin: 2px 0.125em 0 0.125em;
min-width: 6ex;
max-width: 6ex;
min-height: 2.3ex;
max-height: 2.3ex;
font-size: 120%;
}
body.chat #chat-input-line.compact #chat-buttons-wrapper #chat-button-submit {
min-width: 12ex;
}
body.chat #chat-input-line:not(.compact) #chat-input-field {
/*border-left-style: double;
border-left-width: 3px;
border-right-style: double;
border-right-width: 3px;*/
min-height: 4rem;
/*max-height: 50rem;*/
/*
Problems related to max-height:
- If we do NOT set a max-height then pasting/typing a large amount
of text can cause this element to grow without bounds, larger than
the window, and there's no way to navigate it sensibly. In this
case, manually resizing the element (desktop only - mobile doesn't
offer that) will force it to stay at the selected size even if more
content is added to it later.
- If we DO set a max-height then its growth is bounded but it also
cannot manually expanded by the user.
The lesser of the two evils seems to be to rely on the browser
feature that a manual resize of the element will pin its sits.
*/
}
body.chat #chat-input-line > #chat-button-settings{
margin: 0 0 0 0.25em;
max-width: 2em;
}
body.chat #chat-input-line > input[type=text],
body.chat #chat-input-line > textarea {
flex: 20 1 auto;
max-width: revert;
min-width: 20em;
}
body.chat #chat-input-line.compact > input[type=text] {
margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/;
}
/* Widget holding the file selection control and preview */
body.chat #chat-input-file-area {
display: flex;
flex-direction: row;
margin: 0;
}
body.chat #chat-input-file-area > .file-selection-wrapper {
align-self: flex-start;
margin-right: 0.5em;
flex: 0 1 auto;
padding: 0.25em 0.5em;
white-space: nowrap;
}
body.chat #chat-input-file {
border:1px solid rgba(0,0,0,0);/*avoid UI shift during drop-targeting*/
border-radius: 0.25em;
padding: 0.25em;
}
body.chat #chat-input-file > input {
flex: 1 0 auto;
}
/* Indicator when a drag/drop is in progress */
body.chat #chat-input-file.dragover {
border: 1px dashed green;
}
/* Widget holding the details of a selected/dropped file/image. */
body.chat #chat-drop-details {
padding: 0 1em;
white-space: pre;
font-family: monospace;
margin: auto;
flex: 0;
}
body.chat #chat-drop-details img {
max-width: 45%;
max-height: 45%;
}
body.chat .chat-view {
flex: 20 1 auto
/*ensure that these grow more than the non-.chat-view elements.
Note that setting flex shrink to 0 breaks/disables scrolling!*/;
margin-bottom: 0.2em;
}
body.chat #chat-config,
body.chat #chat-preview {
/* /chat configuration widget */
display: flex;
flex-direction: column;
overflow: auto;
padding: 0;
margin: 0;
align-items: stretch;
min-height: 6em;
}
body.chat #chat-config #chat-config-options {
/* /chat config options go here */
flex: 1 1 auto;
display: flex;
flex-direction: column;
overflow: auto;
}
body.chat #chat-config #chat-config-options .menu-entry {
display: flex;
align-items: baseline;
flex-direction: row;
flex-wrap: nowrap;
padding: 1em;
}
body.chat #chat-config #chat-config-options .menu-entry label[for] {
cursor: pointer;
}
body.chat #chat-config #chat-config-options .menu-entry > *:first-child {
min-width: 1.5rem;
}
body.chat #chat-config #chat-config-options .menu-entry span.hint {
/* Config menu hint text */
font-size: 80%;
white-space: pre-wrap;
display: inline-block;
}
body.chat #chat-config #chat-config-options .menu-entry:first-child {
}
body.chat #chat-config #chat-config-options .menu-entry div.label-wrapper {
display: flex;
flex-direction: column;
align-self: baseline;
margin-left: 1em;
}
body.chat #chat-config #chat-config-options .menu-entry select {
}
body.chat #chat-preview #chat-preview-content {
overflow: auto;
flex: 1 1 auto;
padding: 0.5em;
border: 1px dotted;
}
body.chat #chat-preview #chat-preview-content > * {
margin: 0;
padding: 0;
}
body.chat #chat-preview #chat-preview-buttons {
flex: 0 1 auto;
display: flex;
flex-direction: column;
}
body.chat #chat-config > button,
body.chat #chat-preview #chat-preview-buttons > button {
padding: 0.5em;
flex: 0 1 auto;
margin: 0.25em 0;
}
body.chat #chat-user-list-wrapper {
/* Safari can't do fieldsets right, so we emulate one. */
border-radius: 0.5em;
margin: 1em 0 0.2em 0;
padding: 0 0.5em;
border-style: inset;
border-width: 0 1px 1px 1px/*else collides with the LEGEND*/;
}
body.chat #chat-user-list-wrapper.collapsed {
padding: 0;
}
body.chat #chat-user-list-wrapper > .legend {
font-weight: initial;
padding: 0 0.5em 0 0.5em;
position: relative;
top: -1.75ex/* place it like a fieldset legend */;
cursor: pointer;
}
body.chat #chat-user-list-wrapper > .legend > * {
vertical-align: middle;
}
body.chat #chat-user-list-wrapper > .legend > *:nth-child(2){
/* Title label */
opacity: 0.6;
font-size: 0.8em;
}
body.chat #chat-user-list-wrapper.collapsed > .legend > *:nth-child(2)::after {
content: " (tap to toggle)";
}
body.chat #chat-user-list-wrapper .help-buttonlet {
margin: 0;
}
body.chat #chat-user-list-wrapper.collapsed #chat-user-list {
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
body.chat #chat-user-list {
margin-top: -1.25ex;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
}
body.chat #chat-user-list .chat-user {
margin: 0.2em;
padding: 0.1em 0.5em 0.2em 0.5em;
border-radius: 0.5em;
cursor: pointer;
text-align: center;
white-space: pre;
}
body.chat #chat-user-list .timestamp {
font-size: 85%;
font-family: monospace;
}
body.chat #chat-user-list:not(.timestamps) .timestamp {
display: none;
}
body.chat #chat-user-list .chat-user.selected {
font-weight: bold;
text-decoration: underline;
}
body.chat.fossil-dark-style #chat-button-attach > svg {
/* The black paperclip is barely visible in dark-mode
skins when they have dark buttons */
filter: invert(0.8);
}
body.chat .anim-rotate-360 {
animation: rotate-360 750ms linear;
}
@keyframes rotate-360 {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
body.chat .anim-flip-h {
animation: flip-h 750ms linear;
}
@keyframes flip-h{
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
}
body.chat .anim-flip-v {
animation: flip-v 750ms linear;
}
@keyframes flip-v{
from { transform: rotateX(0deg); }
to { transform: rotateX(360deg); }
}
body.chat .anim-fade-in {
animation: fade-in 750ms linear;
}
body.chat .anim-fade-in-fast {
animation: fade-in 350ms linear;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
body.chat .anim-fade-out-fast {
animation: fade-out 250ms linear;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
|
Changes to src/style.fileedit.css.
| ︙ | ︙ | |||
68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
padding: 0;
}
body.fileedit #fileedit-tabs {
margin: 0.5em 0 0 0;
}
body.fileedit #fileedit-tab-preview-wrapper {
overflow: auto;
}
body.fileedit #fileedit-tab-fileselect > h1 {
margin: 0;
}
body.fileedit .fileedit-options.commit-message > div {
display: flex;
flex-direction: column;
| > > > | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
padding: 0;
}
body.fileedit #fileedit-tabs {
margin: 0.5em 0 0 0;
}
body.fileedit #fileedit-tab-preview-wrapper {
overflow: auto;
}
body.fileedit #fileedit-tab-preview-wrapper > pre {
margin: 0;
}
body.fileedit #fileedit-tab-fileselect > h1 {
margin: 0;
}
body.fileedit .fileedit-options.commit-message > div {
display: flex;
flex-direction: column;
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
body.fileedit .tab-container > .tabs > .tab-panel {
display: flex;
flex-direction: column;
}
body.fileedit #fileedit-tab-diff-wrapper {
margin: 0;
padding: 0;
| > > > | | | > | 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 |
body.fileedit .tab-container > .tabs > .tab-panel {
display: flex;
flex-direction: column;
}
body.fileedit #fileedit-tab-diff-wrapper {
margin: 0;
padding: 0;
/*overflow: hidden;*/
/* ^^^ we "really" want hidden and let a sub-sub-child element
handle that, but that isn't working, for unknown reasons. */
overflow-x: auto;
/*display: flex;
flex-direction: column;
align-items: stretch;*/
}
body.fileedit #fileedit-tab-diff-wrapper > div {
margin: 0.5em 0 0.5em 0;
overflow-wrap: break-word;
}
body.fileedit table.sbsdiffcols {
/*width: initial;*/
}
body.fileedit #fileedit-tab-diff-wrapper > pre.udiff {
margin-top: 0;
}
|
| ︙ | ︙ |
Changes to src/style.wikiedit.css.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
padding: 0;
}
body.wikiedit #wikiedit-tabs {
margin: 0.5em 0 0 0;
}
body.wikiedit #wikiedit-tab-preview-wrapper {
overflow: auto;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options {
margin-top: 0;
border: none;
border-radius: 0;
border-bottom-width: 1px;
border-bottom-style: dotted;
| > > > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
padding: 0;
}
body.wikiedit #wikiedit-tabs {
margin: 0.5em 0 0 0;
}
body.wikiedit #wikiedit-tab-preview-wrapper {
overflow: auto;
}
body.wikiedit #wikiedit-tab-diff-wrapper {
/*overflow: hidden;*/
/* ^^^ we "really" want hidden and let a sub-sub-child element
handle that, but that isn't working, for unknown reasons. */
overflow-x: auto;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options {
margin-top: 0;
border: none;
border-radius: 0;
border-bottom-width: 1px;
border-bottom-style: dotted;
|
| ︙ | ︙ |
Changes to src/th_lang.c.
| ︙ | ︙ | |||
255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
zList = Th_TakeResult(interp, &nList);
}
for(i=2; i<argc; i++){
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
}
Th_SetResult(interp, zList, nList);
Th_Free(interp, zList);
return TH_OK;
}
| > | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
zList = Th_TakeResult(interp, &nList);
}
for(i=2; i<argc; i++){
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
}
Th_SetVar(interp, argv[1], argl[1], zList, nList);
Th_SetResult(interp, zList, nList);
Th_Free(interp, zList);
return TH_OK;
}
|
| ︙ | ︙ |
Changes to src/unversioned.c.
| ︙ | ︙ | |||
655 656 657 658 659 660 661 |
void uvlist_json_page(void){
Stmt q;
char *zSep = "[";
Blob json;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
| | | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
void uvlist_json_page(void){
Stmt q;
char *zSep = "[";
Blob json;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_set_content_type("application/json");
etag_check(ETAG_DATA,0);
if( !db_table_exists("repository","unversioned") ){
blob_init(&json, "[]", -1);
cgi_set_content(&json);
return;
}
blob_init(&json, 0, 0);
|
| ︙ | ︙ |
Changes to src/url.c.
| ︙ | ︙ | |||
194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
if( c==':' ){
pUrlData->port = 0;
i++;
while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
pUrlData->port = pUrlData->port*10 + c - '0';
i++;
}
pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port);
}else{
pUrlData->port = pUrlData->dfltPort;
pUrlData->hostname = pUrlData->name;
}
dehttpize(pUrlData->name);
pUrlData->path = mprintf("%s", &zUrl[i]);
| > | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
if( c==':' ){
pUrlData->port = 0;
i++;
while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
pUrlData->port = pUrlData->port*10 + c - '0';
i++;
}
if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number");
pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port);
}else{
pUrlData->port = pUrlData->dfltPort;
pUrlData->hostname = pUrlData->name;
}
dehttpize(pUrlData->name);
pUrlData->path = mprintf("%s", &zUrl[i]);
|
| ︙ | ︙ | |||
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
if( pUrlData->path[i] ){
pUrlData->path[i] = 0;
i++;
}
if( fossil_strcmp(zName,"fossil")==0 ){
pUrlData->fossil = zValue;
dehttpize(pUrlData->fossil);
zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil);
cQuerySep = '&';
}
}
dehttpize(pUrlData->path);
if( pUrlData->dfltPort==pUrlData->port ){
pUrlData->canonical = mprintf(
| > | | | 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 |
if( pUrlData->path[i] ){
pUrlData->path[i] = 0;
i++;
}
if( fossil_strcmp(zName,"fossil")==0 ){
pUrlData->fossil = zValue;
dehttpize(pUrlData->fossil);
fossil_free(zExe);
zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil);
cQuerySep = '&';
}
}
dehttpize(pUrlData->path);
if( pUrlData->dfltPort==pUrlData->port ){
pUrlData->canonical = mprintf(
"%s://%s%T%T%z",
pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe
);
}else{
pUrlData->canonical = mprintf(
"%s://%s%T:%d%T%z",
pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port,
pUrlData->path, zExe
);
}
if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++;
free(zLogin);
}else if( strncmp(zUrl, "file:", 5)==0 ){
|
| ︙ | ︙ | |||
293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
pUrlData->flags = urlFlags |= URL_REMEMBER_PW;
}else{
pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW;
}
}
}
}
/*
** Parse the given URL, which describes a sync server. Populate variables
** in the global "g.url" structure as shown below. If zUrl is NULL, then
** parse the URL given in the last-sync-url setting, taking the password
** form last-sync-pw.
**
| > > > > > > > > > > > > > > > | 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 |
pUrlData->flags = urlFlags |= URL_REMEMBER_PW;
}else{
pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW;
}
}
}
}
/*
** Reclaim malloced memory from a UrlData object
*/
void url_unparse(UrlData *p){
if( p==0 ){
p = &g.url;
}
fossil_free(p->canonical);
fossil_free(p->name);
fossil_free(p->path);
fossil_free(p->user);
fossil_free(p->passwd);
memset(p, 0, sizeof(*p));
}
/*
** Parse the given URL, which describes a sync server. Populate variables
** in the global "g.url" structure as shown below. If zUrl is NULL, then
** parse the URL given in the last-sync-url setting, taking the password
** form last-sync-pw.
**
|
| ︙ | ︙ | |||
367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
fossil_print("g.url.fossil = %s\n", g.url.fossil);
fossil_print("g.url.flags = 0x%02x\n", g.url.flags);
if( g.url.isFile || g.url.isSsh ) break;
if( i==0 ){
fossil_print("********\n");
url_enable_proxy("Using proxy: ");
}
}
}
/*
** Proxy specified on the command-line using the --proxy option.
** If there is no --proxy option on the command-line then this
** variable holds a NULL pointer.
| > | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
fossil_print("g.url.fossil = %s\n", g.url.fossil);
fossil_print("g.url.flags = 0x%02x\n", g.url.flags);
if( g.url.isFile || g.url.isSsh ) break;
if( i==0 ){
fossil_print("********\n");
url_enable_proxy("Using proxy: ");
}
url_unparse(0);
}
}
/*
** Proxy specified on the command-line using the --proxy option.
** If there is no --proxy option on the command-line then this
** variable holds a NULL pointer.
|
| ︙ | ︙ | |||
657 658 659 660 661 662 663 |
if( sqlite3_strnicmp(zTail, "www.", 4)==0 && strchr(zTail+4,'.')!=0 ){
/* Remove the "www." prefix if there are more "." characters later.
** But don't remove the "www." prefix if what follows is the suffix.
** forum:/forumpost/74e111a2ee */
zTail += 4;
}
if( zTail[0]==0 ) return 0;
| | > | 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 |
if( sqlite3_strnicmp(zTail, "www.", 4)==0 && strchr(zTail+4,'.')!=0 ){
/* Remove the "www." prefix if there are more "." characters later.
** But don't remove the "www." prefix if what follows is the suffix.
** forum:/forumpost/74e111a2ee */
zTail += 4;
}
if( zTail[0]==0 ) return 0;
for(i=0; zTail[i] && zTail[i]!='.' && zTail[i]!='?' &&
zTail[i]!=':' && zTail[i]!='/'; i++){}
if( i==0 ) return 0;
return mprintf("%.*s", i, zTail);
}
/*
** COMMAND: test-url-basename
** Usage: %fossil test-url-basenames URL ...
|
| ︙ | ︙ |
Changes to src/utf8.c.
| ︙ | ︙ | |||
86 87 88 89 90 91 92 | assert( 0 ); /* Never used in unix */ return fossil_strdup(zUtf8); /* TODO: implement for unix */ #endif } /* ** Deallocate any memory that was previously allocated by | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
assert( 0 ); /* Never used in unix */
return fossil_strdup(zUtf8); /* TODO: implement for unix */
#endif
}
/*
** Deallocate any memory that was previously allocated by
** fossil_unicode_to_utf8() or fossil_utf8_to_unicode().
*/
void fossil_unicode_free(void *pOld){
fossil_free(pOld);
}
#if defined(__APPLE__) && !defined(WITHOUT_ICONV)
# include <iconv.h>
|
| ︙ | ︙ |
Changes to src/util.c.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 39 40 41 42 | ** For the fossil_timer_xxx() family of functions... */ #ifdef _WIN32 # include <windows.h> #else # include <sys/time.h> # include <sys/resource.h> # include <unistd.h> # include <fcntl.h> # include <errno.h> #endif /* ** Exit. Take care to close the database first. | > > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | ** For the fossil_timer_xxx() family of functions... */ #ifdef _WIN32 # include <windows.h> #else # include <sys/time.h> # include <sys/resource.h> # include <sys/types.h> # include <sys/stat.h> # include <unistd.h> # include <fcntl.h> # include <errno.h> #endif /* ** Exit. Take care to close the database first. |
| ︙ | ︙ | |||
644 645 646 647 648 649 650 |
** Construct a temporary filename.
**
** The returned string is obtained from sqlite3_malloc() and must be
** freed by the caller.
*/
char *fossil_temp_filename(void){
char *zTFile = 0;
| | > > > > > > > > > > > > > > > > > > | > > > > < > > > > > > > > > > > > > > > > | > | > | 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 |
** Construct a temporary filename.
**
** The returned string is obtained from sqlite3_malloc() and must be
** freed by the caller.
*/
char *fossil_temp_filename(void){
char *zTFile = 0;
const char *zDir;
char cDirSep;
char zSep[2];
size_t nDir;
u64 r[2];
#ifdef _WIN32
char *zTempDirA = NULL;
WCHAR zTempDirW[MAX_PATH+1];
const DWORD dwTempSizeW = sizeof(zTempDirW)/sizeof(zTempDirW[0]);
DWORD dwTempLenW;
#else
int i;
static const char *azTmp[] = {"/var/tmp","/usr/tmp","/tmp"};
#endif
if( g.db ){
sqlite3_file_control(g.db, 0, SQLITE_FCNTL_TEMPFILENAME, (void*)&zTFile);
if( zTFile ) return zTFile;
}
sqlite3_randomness(sizeof(r), &r);
#if _WIN32
cDirSep = '\\';
dwTempLenW = GetTempPathW(dwTempSizeW, zTempDirW);
if( dwTempLenW>0 && dwTempLenW<dwTempSizeW
&& ( zTempDirA = fossil_path_to_utf8(zTempDirW) )){
zDir = zTempDirA;
}else{
zDir = fossil_getenv("LOCALAPPDATA");
if( zDir==0 ) zDir = ".";
}
#else
for(i=0; i<sizeof(azTmp)/sizeof(azTmp[0]); i++){
struct stat buf;
zDir = azTmp[i];
if( stat(zDir,&buf)==0 && S_ISDIR(buf.st_mode) && access(zDir,03)==0 ){
break;
}
}
if( i>=sizeof(azTmp)/sizeof(azTmp[0]) ) zDir = ".";
cDirSep = '/';
#endif
nDir = strlen(zDir);
zSep[1] = 0;
zSep[0] = (nDir && zDir[nDir-1]==cDirSep) ? 0 : cDirSep;
zTFile = sqlite3_mprintf("%s%sfossil%016llx%016llx", zDir,zSep,r[0],r[1]);
#ifdef _WIN32
if( zTempDirA ) fossil_path_free(zTempDirA);
#endif
return zTFile;
}
/*
** Turn memory limits for stack and heap on and off. The argument
** is true to turn memory limits on and false to turn them off.
**
|
| ︙ | ︙ | |||
831 832 833 834 835 836 837 |
/*
** Return the name of a command that will launch a web-browser.
*/
const char *fossil_web_browser(void){
const char *zBrowser = 0;
#if defined(_WIN32)
| | | 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 |
/*
** Return the name of a command that will launch a web-browser.
*/
const char *fossil_web_browser(void){
const char *zBrowser = 0;
#if defined(_WIN32)
zBrowser = db_get("web-browser", "start \"\"");
#elif defined(__DARWIN__) || defined(__APPLE__) || defined(__HAIKU__)
zBrowser = db_get("web-browser", "open");
#else
zBrowser = db_get("web-browser", 0);
if( zBrowser==0 ){
static const char *const azBrowserProg[] =
{ "xdg-open", "gnome-open", "firefox", "google-chrome" };
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
while( zPathInfo[0]=='/' ) zPathInfo++;
if( fossil_strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0;
}
if( zIndexPage ){
cgi_redirectf("%R/%s", zIndexPage);
}
if( !g.perm.RdWiki ){
| | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
while( zPathInfo[0]=='/' ) zPathInfo++;
if( fossil_strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0;
}
if( zIndexPage ){
cgi_redirectf("%R/%s", zIndexPage);
}
if( !g.perm.RdWiki ){
cgi_redirectf("%R/login?g=home");
}
if( zPageName ){
login_check_credentials();
g.zExtra = zPageName;
cgi_set_parameter_nocopy("name", g.zExtra, 1);
g.isHome = 1;
wiki_page();
|
| ︙ | ︙ | |||
744 745 746 747 748 749 750 |
blob_init(&wiki, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
if( !isPopup ){
| > > > | > | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 |
blob_init(&wiki, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
if( !isPopup ){
char * zLabel = mprintf("<hr /><h2><a href='%R/attachlist?name=%T'>"
"Attachments</a>:</h2><ul>",
zPageName);
attachment_list(zPageName, zLabel);
fossil_free(zLabel);
document_emit_js(/*for optional pikchr support*/);
style_finish_page();
}
}
/*
** Write a wiki artifact into the repository
|
| ︙ | ︙ | |||
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 |
*/
static void wiki_ajax_route_diff(void){
const char * zPageName = P("page");
Blob contentNew = empty_blob, contentOrig = empty_blob;
Manifest * pParent = 0;
const char * zContent = P("content");
u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR;
if( zPageName==0 || zPageName[0]==0 ){
ajax_route_error(400,"Missing page name.");
return;
}else if(!wiki_ajax_can_write(zPageName, 0)){
return;
}
switch(atoi(PD("sbs","0"))){
case 0: diffFlags |= DIFF_LINENO; break;
default: diffFlags |= DIFF_SIDEBYSIDE;
}
switch(atoi(PD("ws","2"))){
case 1: diffFlags |= DIFF_IGNORE_EOLWS; break;
case 2: diffFlags |= DIFF_IGNORE_ALLWS; break;
default: break;
}
wiki_fetch_by_name( zPageName, 0, 0, &pParent );
if( pParent && pParent->zWiki && *pParent->zWiki ){
blob_init(&contentOrig, pParent->zWiki, -1);
}else{
blob_init(&contentOrig, "", 0);
}
blob_init(&contentNew, zContent ? zContent : "", -1);
cgi_set_content_type("text/html");
| > > > > | > | 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 |
*/
static void wiki_ajax_route_diff(void){
const char * zPageName = P("page");
Blob contentNew = empty_blob, contentOrig = empty_blob;
Manifest * pParent = 0;
const char * zContent = P("content");
u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR;
char * zParentUuid = 0;
if( zPageName==0 || zPageName[0]==0 ){
ajax_route_error(400,"Missing page name.");
return;
}else if(!wiki_ajax_can_write(zPageName, 0)){
return;
}
switch(atoi(PD("sbs","0"))){
case 0: diffFlags |= DIFF_LINENO; break;
default: diffFlags |= DIFF_SIDEBYSIDE;
}
switch(atoi(PD("ws","2"))){
case 1: diffFlags |= DIFF_IGNORE_EOLWS; break;
case 2: diffFlags |= DIFF_IGNORE_ALLWS; break;
default: break;
}
wiki_fetch_by_name( zPageName, 0, 0, &pParent );
if( pParent ){
zParentUuid = rid_to_uuid(pParent->rid);
}
if( pParent && pParent->zWiki && *pParent->zWiki ){
blob_init(&contentOrig, pParent->zWiki, -1);
}else{
blob_init(&contentOrig, "", 0);
}
blob_init(&contentNew, zContent ? zContent : "", -1);
cgi_set_content_type("text/html");
ajax_render_diff(&contentOrig, zParentUuid, &contentNew, diffFlags);
blob_reset(&contentNew);
blob_reset(&contentOrig);
fossil_free(zParentUuid);
manifest_destroy(pParent);
}
/*
** Ajax route handler for /wikiajax/preview.
**
** URL params:
|
| ︙ | ︙ | |||
1317 1318 1319 1320 1321 1322 1323 |
const int includeContent = ajax_p_bool("includeContent");
cgi_set_content_type("application/json");
wiki_render_page_list_json(verbose, includeContent);
}
/*
| | | 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 |
const int includeContent = ajax_p_bool("includeContent");
cgi_set_content_type("application/json");
wiki_render_page_list_json(verbose, includeContent);
}
/*
** WEBPAGE: wikiajax hidden
**
** An internal dispatcher for wiki AJAX operations. Not for direct
** client use. All routes defined by this interface are app-internal,
** subject to change
*/
void wiki_ajax_page(void){
const char * zName = P("name");
|
| ︙ | ︙ | |||
1582 1583 1584 1585 1586 1587 1588 |
CX("<h2>Wiki Name Rules</h2>");
well_formed_wiki_name_rules();
CX("</div>"/*#wikiedit-tab-save*/);
}
builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
"storage", "popupwidget", "copybutton",
"pikchr", NULL);
| | | 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 |
CX("<h2>Wiki Name Rules</h2>");
well_formed_wiki_name_rules();
CX("</div>"/*#wikiedit-tab-save*/);
}
builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
"storage", "popupwidget", "copybutton",
"pikchr", NULL);
builtin_fossil_js_bundle_or("diff", NULL);
builtin_request_js("fossil.page.wikiedit.js");
builtin_fulfill_js_requests();
/* Dynamically populate the editor... */
style_script_begin(__FILE__,__LINE__);
{
/* Render the current page list to save us an XHR request
during page initialization. This must be OUTSIDE of
|
| ︙ | ︙ | |||
1943 1944 1945 1946 1947 1948 1949 |
*/
void wdiff_page(void){
const char *zId;
const char *zPid;
Manifest *pW1, *pW2 = 0;
int rid1, rid2, nextRid;
Blob w1, w2, d;
| | | 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 |
*/
void wdiff_page(void){
const char *zId;
const char *zPid;
Manifest *pW1, *pW2 = 0;
int rid1, rid2, nextRid;
Blob w1, w2, d;
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
zId = P("id");
if( zId==0 ){
rid1 = atoi(PD("rid","0"));
}else{
|
| ︙ | ︙ | |||
1986 1987 1988 1989 1990 1991 1992 |
nextRid = wiki_next(wiki_tagid(pW1->zWikiTitle),pW1->rDate);
if( nextRid ){
style_submenu_element("Next", "%R/wdiff?rid=%d", nextRid);
}
style_set_current_feature("wiki");
style_header("Changes To %s", pW1->zWikiTitle);
blob_zero(&d);
| | > | | 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 |
nextRid = wiki_next(wiki_tagid(pW1->zWikiTitle),pW1->rDate);
if( nextRid ){
style_submenu_element("Next", "%R/wdiff?rid=%d", nextRid);
}
style_set_current_feature("wiki");
style_header("Changes To %s", pW1->zWikiTitle);
blob_zero(&d);
construct_diff_flags(1, &DCfg);
DCfg.diffFlags |= DIFF_HTML | DIFF_LINENO;
text_diff(&w2, &w1, &d, &DCfg);
@ <pre class="udiff">
@ %s(blob_str(&d))
@ <pre>
manifest_destroy(pW1);
manifest_destroy(pW2);
style_finish_page();
}
|
| ︙ | ︙ | |||
2205 2206 2207 2208 2209 2210 2211 | db_begin_transaction(); wiki_put(&wiki, 0, wiki_need_moderation(localUser)); db_end_transaction(0); return 1; } /* | | | | 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 |
db_begin_transaction();
wiki_put(&wiki, 0, wiki_need_moderation(localUser));
db_end_transaction(0);
return 1;
}
/*
** Determine the rid for a tech note given either its id, its timestamp,
** or its tag. Returns 0 if there is no such item and -1 if the details
** are ambiguous and could refer to multiple items.
*/
int wiki_technote_to_rid(const char *zETime) {
int rid=0; /* Artifact ID of the tech note */
int nETime = strlen(zETime);
Stmt q;
if( nETime>=4 && nETime<=HNAME_MAX && validate16(zETime, nETime) ){
|
| ︙ | ︙ | |||
2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 |
" WHERE datetime(mtime)=datetime('%q')"
" AND type='e'"
" AND tagid IS NOT NULL"
" ORDER BY objid DESC LIMIT 1",
zETime);
}
}
return rid;
}
/*
** COMMAND: wiki*
**
** Usage: %fossil wiki (export|create|commit|list) WikiName
**
** Run various subcommands to work with wiki entries or tech notes.
**
** > fossil wiki export ?OPTIONS? PAGENAME ?FILE?
| > > > > > > > > > > > > > > > > > > > > > | | | > | 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 |
" WHERE datetime(mtime)=datetime('%q')"
" AND type='e'"
" AND tagid IS NOT NULL"
" ORDER BY objid DESC LIMIT 1",
zETime);
}
}
if( !rid ) {
/*
** At present, technote tags are prefixed with 'sym-', which shouldn't
** be the case, so we check for both with and without the prefix until
** such time as tags have the errant prefix dropped.
*/
rid = db_int(0, "SELECT e.objid"
" FROM event e, tag t, tagxref tx"
" WHERE e.type='e'"
" AND e.tagid IS NOT NULL"
" AND e.objid IN"
" (SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag"
" WHERE tagname GLOB '%q'))"
" OR e.objid IN"
" (SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag"
" WHERE tagname GLOB 'sym-%q'))"
" ORDER BY e.mtime DESC LIMIT 1",
zETime, zETime);
}
return rid;
}
/*
** COMMAND: wiki*
**
** Usage: %fossil wiki (export|create|commit|list) WikiName
**
** Run various subcommands to work with wiki entries or tech notes.
**
** > fossil wiki export ?OPTIONS? PAGENAME ?FILE?
** > fossil wiki export ?OPTIONS? -t|--technote DATETIME|TECHNOTE-ID|TAG ?FILE?
**
** Sends the latest version of either a wiki page or of a tech
** note to the given file or standard output. A filename of "-"
** writes the output to standard output. The directory parts of
** the output filename are created if needed.
** If PAGENAME is provided, the named wiki page will be output.
**
** Options:
** -t|--technote DATETIME|TECHNOTE-ID|TAG
** Specifies that a technote, rather than a wiki page,
** will be exported. If DATETIME is used, the most
** recently modified tech note with that DATETIME will
** output. If TAG is used, the most recently modified
** tech note with that TAG will be output.
** -h|--html The body (only) is rendered in HTML form, without
** any page header/foot or HTML/BODY tag wrappers.
** -H|--HTML Works like -h|-html but wraps the output in
** <html><body>...</body></html>.
** -p|--pre If -h|-H is used and the page or technote has
** the text/plain mimetype, its HTML-escaped output
** will be wrapped in <pre>...</pre>.
|
| ︙ | ︙ | |||
2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 | ** > fossil wiki list ?OPTIONS? ** > fossil wiki ls ?OPTIONS? ** ** Lists all wiki entries, one per line, ordered ** case-insensitively by name. ** ** Options: ** -t|--technote Technotes will be listed instead of ** pages. The technotes will be in order ** of timestamp with the most recent ** first. ** -s|--show-technote-ids The id of the tech note will be listed ** along side the timestamp. The tech note ** id will be the first word on each line. | > > | 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 | ** > fossil wiki list ?OPTIONS? ** > fossil wiki ls ?OPTIONS? ** ** Lists all wiki entries, one per line, ordered ** case-insensitively by name. ** ** Options: ** --all Include "deleted" pages in output. ** By default deleted pages are elided. ** -t|--technote Technotes will be listed instead of ** pages. The technotes will be in order ** of timestamp with the most recent ** first. ** -s|--show-technote-ids The id of the tech note will be listed ** along side the timestamp. The tech note ** id will be the first word on each line. |
| ︙ | ︙ | |||
2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 |
** The "Sandbox" wiki pseudo-page is a special case. Its name is
** checked case-insensitively and either "create" or "commit" may be
** used to update its contents.
*/
void wiki_cmd(void){
int n;
int isSandbox = 0; /* true if dealing with sandbox pseudo-page */
db_find_and_open_repository(0, 0);
if( g.argc<3 ){
goto wiki_cmd_usage;
}
n = strlen(g.argv[2]);
if( n==0 ){
| > | 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 |
** The "Sandbox" wiki pseudo-page is a special case. Its name is
** checked case-insensitively and either "create" or "commit" may be
** used to update its contents.
*/
void wiki_cmd(void){
int n;
int isSandbox = 0; /* true if dealing with sandbox pseudo-page */
const int showAll = find_option("all", 0, 0)!=0;
db_find_and_open_repository(0, 0);
if( g.argc<3 ){
goto wiki_cmd_usage;
}
n = strlen(g.argv[2]);
if( n==0 ){
|
| ︙ | ︙ | |||
2551 2552 2553 2554 2555 2556 2557 |
}else if(( strncmp(g.argv[2],"list",n)==0 )
|| ( strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
const int fTechnote = find_option("technote","t",0)!=0;
const int showIds = find_option("show-technote-ids","s",0)!=0;
verify_all_options();
if (fTechnote==0){
| | < < < | > > > > | 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 |
}else if(( strncmp(g.argv[2],"list",n)==0 )
|| ( strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
const int fTechnote = find_option("technote","t",0)!=0;
const int showIds = find_option("show-technote-ids","s",0)!=0;
verify_all_options();
if (fTechnote==0){
db_prepare(&q, listAllWikiPages/*works-like:""*/);
}else{
db_prepare(&q,
"SELECT datetime(e.mtime), substr(t.tagname,7), e.objid"
" FROM event e, tag t"
" WHERE e.type='e'"
" AND e.tagid IS NOT NULL"
" AND t.tagid=e.tagid"
" ORDER BY e.mtime DESC /*sort*/"
);
}
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const int wrid = db_column_int(&q, 2);
if(!showAll && !wrid){
continue;
}
if( showIds ){
const char *zUuid = db_column_text(&q, 1);
fossil_print("%s ",zUuid);
}
fossil_print( "%s\n",zName);
}
db_finalize(&q);
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
2358 2359 2360 2361 2362 2363 2364 |
int i;
for(i=2; i<g.argc; i++){
blob_read_from_file(&in, g.argv[i], ExtFILE);
blob_zero(&out);
htmlTidy(blob_str(&in), &out);
blob_reset(&in);
| | | 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 |
int i;
for(i=2; i<g.argc; i++){
blob_read_from_file(&in, g.argv[i], ExtFILE);
blob_zero(&out);
htmlTidy(blob_str(&in), &out);
blob_reset(&in);
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
blob_reset(&out);
}
}
/*
** Remove all HTML markup from the input text. The output written into
** pOut is pure text.
|
| ︙ | ︙ | |||
2485 2486 2487 2488 2489 2490 2491 |
int i;
for(i=2; i<g.argc; i++){
blob_read_from_file(&in, g.argv[i], ExtFILE);
blob_zero(&out);
html_to_plaintext(blob_str(&in), &out);
blob_reset(&in);
| | | 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 |
int i;
for(i=2; i<g.argc; i++){
blob_read_from_file(&in, g.argv[i], ExtFILE);
blob_zero(&out);
html_to_plaintext(blob_str(&in), &out);
blob_reset(&in);
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
blob_reset(&out);
}
}
/****************************************************************************
** safe-html:
**
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 |
double rArrivalTime; /* Time at which a message arrived */
const char *zSCode = db_get("server-code", "x");
const char *zPCode = db_get("project-code", 0);
int nErr = 0; /* Number of errors */
int nRoundtrip= 0; /* Number of HTTP requests */
int nArtifactSent = 0; /* Total artifacts sent */
int nArtifactRcvd = 0; /* Total artifacts received */
const char *zOpType = 0;/* Push, Pull, Sync, Clone */
double rSkew = 0.0; /* Maximum time skew */
int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
int uvDoPush = 0; /* Generate uvfile messages to send to server */
int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
| > | 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 |
double rArrivalTime; /* Time at which a message arrived */
const char *zSCode = db_get("server-code", "x");
const char *zPCode = db_get("project-code", 0);
int nErr = 0; /* Number of errors */
int nRoundtrip= 0; /* Number of HTTP requests */
int nArtifactSent = 0; /* Total artifacts sent */
int nArtifactRcvd = 0; /* Total artifacts received */
int nPriorArtifact = 0; /* Artifacts received on prior round-trips */
const char *zOpType = 0;/* Push, Pull, Sync, Clone */
double rSkew = 0.0; /* Maximum time skew */
int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
int uvDoPush = 0; /* Generate uvfile messages to send to server */
int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
|
| ︙ | ︙ | |||
2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 |
if( syncFlags & SYNC_PUSH ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
go = 0;
nUvGimmeSent = 0;
nUvFileRcvd = 0;
/* Process the reply that came back from the server */
while( blob_line(&recv, &xfer.line) ){
if( blob_buffer(&xfer.line)[0]=='#' ){
const char *zLine = blob_buffer(&xfer.line);
if( memcmp(zLine, "# timestamp ", 12)==0 ){
char zTime[20];
| > | 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 |
if( syncFlags & SYNC_PUSH ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
go = 0;
nUvGimmeSent = 0;
nUvFileRcvd = 0;
nPriorArtifact = nArtifactRcvd;
/* Process the reply that came back from the server */
while( blob_line(&recv, &xfer.line) ){
if( blob_buffer(&xfer.line)[0]=='#' ){
const char *zLine = blob_buffer(&xfer.line);
if( memcmp(zLine, "# timestamp ", 12)==0 ){
char zTime[20];
|
| ︙ | ︙ | |||
2626 2627 2628 2629 2630 2631 2632 |
fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
nRoundtrip, nArtifactSent, nArtifactRcvd);
}
nUncRcvd += blob_size(&recv);
blob_reset(&recv);
nCycle++;
| | | < > > > > > > > > > > > > | > > > > > > < < < < < < < < < < < < < < < < < < < < < < < | 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 |
fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
nRoundtrip, nArtifactSent, nArtifactRcvd);
}
nUncRcvd += blob_size(&recv);
blob_reset(&recv);
nCycle++;
/* Set go to 1 if we need to continue the sync/push/pull/clone for
** another round. Set go to 0 if it is time to quit. */
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( (syncFlags & SYNC_CLONE)!=0 && nFileRecv>0 ){
go = 1;
}else if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){
/* Go another round if files are queued to send */
go = 1;
}else if( xfer.nPrivIGot>0 && nCycle==1 ){
go = 1;
}else if( (syncFlags & SYNC_CLONE)!=0 ){
if( nCycle==1 ){
go = 1; /* go at least two rounds on a clone */
}else if( cloneSeqno>0 && nArtifactRcvd>nPriorArtifact ){
/* Continue the clone until we see the clone_seqno 0" card or
** until we stop receiving artifacts */
go = 1;
}
}else if( nUvGimmeSent>0 && (nUvFileRcvd>0 || nCycle<3) ){
/* Continue looping as long as new uvfile cards are being received
** and uvgimme cards are being sent. */
go = 1;
}
nCardRcvd = 0;
xfer.nFileRcvd = 0;
xfer.nDeltaRcvd = 0;
xfer.nDanglingFile = 0;
db_multi_exec("DROP TABLE onremote; DROP TABLE unk;");
if( go ){
manifest_crosslink_end(MC_PERMIT_HOOKS);
}else{
manifest_crosslink_end(MC_PERMIT_HOOKS);
content_enable_dephantomize(1);
}
|
| ︙ | ︙ |
Changes to test/diff-test-1.wiki.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
* <a href="../../../fdiff?v1=d1c60722e0b9d775&v2=58d1a8991bacb113"
target="testwindow">Column alignment with multibyte characters.</a>
The edit of a line with multibyte characters is the first chunk.
* <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6"
target="testwindow">Large diff of sqlite3.c</a>. This diff was very
slow prior to the performance enhancement change [9e15437e97].
* <a href="../../../info/bda00cbada#chunk49" target="testwindow">
| | > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
* <a href="../../../fdiff?v1=d1c60722e0b9d775&v2=58d1a8991bacb113"
target="testwindow">Column alignment with multibyte characters.</a>
The edit of a line with multibyte characters is the first chunk.
* <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6"
target="testwindow">Large diff of sqlite3.c</a>. This diff was very
slow prior to the performance enhancement change [9e15437e97].
* <a href="../../../info/bda00cbada#chunk49" target="testwindow">
A difficult indentation change.</a> UPDATE: Notice also the improved
multi-segment update marks on lines 122648 and 122763 on the new side.
* <a href="../../../fdiff?v1=bc8100c9ee01b8c2&v2=1d2acc1a2a65c2bf#chunk42"
target="testwindow">Inverse of the previous.</a>
* <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13"
target="testwindow">Another tricky indentation.</a> Notice especially
lines 59398 and 59407 on the left.
* <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13"
target="testwindow">Inverse of the previous.</a>
* <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk24"
target="testwindow">A complex change</a> that is difficult to align, and
|
| ︙ | ︙ |
Changes to test/tester.tcl.
| ︙ | ︙ | |||
303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
default-perms \
diff-binary \
diff-command \
dont-push \
dotfiles \
editor \
email-admin \
email-self \
email-send-command \
email-send-db \
email-send-dir \
email-send-method \
email-send-relayhost \
email-subname \
| > | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
default-perms \
diff-binary \
diff-command \
dont-push \
dotfiles \
editor \
email-admin \
email-renew-interval \
email-self \
email-send-command \
email-send-db \
email-send-dir \
email-send-method \
email-send-relayhost \
email-subname \
|
| ︙ | ︙ | |||
329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
ignore-glob \
keep-glob \
localauth \
lock-timeout \
main-branch \
mainmenu \
manifest \
max-loadavg \
max-upload \
mimetypes \
mtime-changes \
pgp-command \
preferred-diff-type \
proxy \
| > | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
ignore-glob \
keep-glob \
localauth \
lock-timeout \
main-branch \
mainmenu \
manifest \
max-cache-entry \
max-loadavg \
max-upload \
mimetypes \
mtime-changes \
pgp-command \
preferred-diff-type \
proxy \
|
| ︙ | ︙ |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
621 622 623 624 625 626 627 | $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/accordion.js \ $(SRCDIR)/alerts/bflat2.wav \ $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ | < > | | | > < > | 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 | $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/accordion.js \ $(SRCDIR)/alerts/bflat2.wav \ $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ $(SRCDIR)/fossil.diff.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ $(SRCDIR)/sounds/4.wav \ $(SRCDIR)/sounds/5.wav \ $(SRCDIR)/sounds/6.wav \ $(SRCDIR)/sounds/7.wav \ $(SRCDIR)/sounds/8.wav \ $(SRCDIR)/sounds/9.wav \ $(SRCDIR)/sounds/a.wav \ $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ |
| ︙ | ︙ |
Changes to win/Makefile.mingw.mistachkin.
| ︙ | ︙ | |||
511 512 513 514 515 516 517 518 519 520 521 522 523 524 | $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/path.c \ $(SRCDIR)/piechart.c \ $(SRCDIR)/pikchr.c \ $(SRCDIR)/pikchrshow.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ | > | 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ $(SRCDIR)/piechart.c \ $(SRCDIR)/pikchr.c \ $(SRCDIR)/pikchrshow.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ |
| ︙ | ︙ | |||
624 625 626 627 628 629 630 631 632 633 634 635 | $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ $(SRCDIR)/chat.js \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ | > | | | < | 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 | $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ $(SRCDIR)/chat.js \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ $(SRCDIR)/fossil.diff.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ |
| ︙ | ︙ | |||
768 769 770 771 772 773 774 775 776 777 778 779 780 781 | $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/piechart_.c \ $(OBJDIR)/pikchr_.c \ $(OBJDIR)/pikchrshow_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ | > | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/piechart_.c \ $(OBJDIR)/pikchr_.c \ $(OBJDIR)/pikchrshow_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ |
| ︙ | ︙ | |||
917 918 919 920 921 922 923 924 925 926 927 928 929 930 | $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/path.o \ $(OBJDIR)/piechart.o \ $(OBJDIR)/pikchr.o \ $(OBJDIR)/pikchrshow.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ | > | 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 | $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/patch.o \ $(OBJDIR)/path.o \ $(OBJDIR)/piechart.o \ $(OBJDIR)/pikchr.o \ $(OBJDIR)/pikchrshow.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ |
| ︙ | ︙ | |||
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 | $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \ $(OBJDIR)/pikchr_.c:$(OBJDIR)/pikchr.h \ $(OBJDIR)/pikchrshow_.c:$(OBJDIR)/pikchrshow.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ | > | 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 | $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \ $(OBJDIR)/pikchr_.c:$(OBJDIR)/pikchr.h \ $(OBJDIR)/pikchrshow_.c:$(OBJDIR)/pikchrshow.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| ︙ | ︙ | |||
2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 | $(OBJDIR)/name_.c: $(SRCDIR)/name.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/name.c >$@ $(OBJDIR)/name.o: $(OBJDIR)/name_.c $(OBJDIR)/name.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c $(OBJDIR)/name.h: $(OBJDIR)/headers $(OBJDIR)/path_.c: $(SRCDIR)/path.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/path.c >$@ $(OBJDIR)/path.o: $(OBJDIR)/path_.c $(OBJDIR)/path.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c | > > > > > > > > | 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 | $(OBJDIR)/name_.c: $(SRCDIR)/name.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/name.c >$@ $(OBJDIR)/name.o: $(OBJDIR)/name_.c $(OBJDIR)/name.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c $(OBJDIR)/name.h: $(OBJDIR)/headers $(OBJDIR)/patch_.c: $(SRCDIR)/patch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/patch.c >$@ $(OBJDIR)/patch.o: $(OBJDIR)/patch_.c $(OBJDIR)/patch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/patch.o -c $(OBJDIR)/patch_.c $(OBJDIR)/patch.h: $(OBJDIR)/headers $(OBJDIR)/path_.c: $(SRCDIR)/path.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/path.c >$@ $(OBJDIR)/path.o: $(OBJDIR)/path_.c $(OBJDIR)/path.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
563 564 565 566 567 568 569 |
"$(SRCDIR)\..\skins\xekri\footer.txt" \
"$(SRCDIR)\..\skins\xekri\header.txt" \
"$(SRCDIR)\accordion.js" \
"$(SRCDIR)\alerts\bflat2.wav" \
"$(SRCDIR)\alerts\bflat3.wav" \
"$(SRCDIR)\alerts\bloop.wav" \
"$(SRCDIR)\alerts\plunk.wav" \
| < > | | | > < > | 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 |
"$(SRCDIR)\..\skins\xekri\footer.txt" \
"$(SRCDIR)\..\skins\xekri\header.txt" \
"$(SRCDIR)\accordion.js" \
"$(SRCDIR)\alerts\bflat2.wav" \
"$(SRCDIR)\alerts\bflat3.wav" \
"$(SRCDIR)\alerts\bloop.wav" \
"$(SRCDIR)\alerts\plunk.wav" \
"$(SRCDIR)\ci_edit.js" \
"$(SRCDIR)\copybtn.js" \
"$(SRCDIR)\default.css" \
"$(SRCDIR)\diff.js" \
"$(SRCDIR)\diff.tcl" \
"$(SRCDIR)\forum.js" \
"$(SRCDIR)\fossil.bootstrap.js" \
"$(SRCDIR)\fossil.confirmer.js" \
"$(SRCDIR)\fossil.copybutton.js" \
"$(SRCDIR)\fossil.diff.js" \
"$(SRCDIR)\fossil.dom.js" \
"$(SRCDIR)\fossil.fetch.js" \
"$(SRCDIR)\fossil.numbered-lines.js" \
"$(SRCDIR)\fossil.page.brlist.js" \
"$(SRCDIR)\fossil.page.chat.js" \
"$(SRCDIR)\fossil.page.fileedit.js" \
"$(SRCDIR)\fossil.page.forumpost.js" \
"$(SRCDIR)\fossil.page.pikchrshow.js" \
"$(SRCDIR)\fossil.page.whistory.js" \
"$(SRCDIR)\fossil.page.wikiedit.js" \
"$(SRCDIR)\fossil.pikchr.js" \
"$(SRCDIR)\fossil.popupwidget.js" \
"$(SRCDIR)\fossil.storage.js" \
"$(SRCDIR)\fossil.tabs.js" \
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
"$(SRCDIR)\graph.js" \
"$(SRCDIR)\hbmenu.js" \
"$(SRCDIR)\href.js" \
"$(SRCDIR)\login.js" \
"$(SRCDIR)\markdown.md" \
"$(SRCDIR)\menu.js" \
"$(SRCDIR)\scroll.js" \
"$(SRCDIR)\skin.js" \
"$(SRCDIR)\sorttable.js" \
"$(SRCDIR)\sounds\0.wav" \
"$(SRCDIR)\sounds\1.wav" \
"$(SRCDIR)\sounds\2.wav" \
"$(SRCDIR)\sounds\3.wav" \
"$(SRCDIR)\sounds\4.wav" \
"$(SRCDIR)\sounds\5.wav" \
"$(SRCDIR)\sounds\6.wav" \
"$(SRCDIR)\sounds\7.wav" \
"$(SRCDIR)\sounds\8.wav" \
"$(SRCDIR)\sounds\9.wav" \
"$(SRCDIR)\sounds\a.wav" \
"$(SRCDIR)\sounds\b.wav" \
"$(SRCDIR)\sounds\c.wav" \
"$(SRCDIR)\sounds\d.wav" \
"$(SRCDIR)\sounds\e.wav" \
"$(SRCDIR)\sounds\f.wav" \
"$(SRCDIR)\style.admin_log.css" \
"$(SRCDIR)\style.chat.css" \
"$(SRCDIR)\style.fileedit.css" \
"$(SRCDIR)\style.wikiedit.css" \
"$(SRCDIR)\tree.js" \
"$(SRCDIR)\useredit.js" \
"$(SRCDIR)\wiki.wiki"
OBJ = "$(OX)\add$O" \
|
| ︙ | ︙ | |||
1171 1172 1173 1174 1175 1176 1177 | echo "$(SRCDIR)\../skins/xekri/footer.txt" >> $@ echo "$(SRCDIR)\../skins/xekri/header.txt" >> $@ echo "$(SRCDIR)\accordion.js" >> $@ echo "$(SRCDIR)\alerts/bflat2.wav" >> $@ echo "$(SRCDIR)\alerts/bflat3.wav" >> $@ echo "$(SRCDIR)\alerts/bloop.wav" >> $@ echo "$(SRCDIR)\alerts/plunk.wav" >> $@ | < > | | | > < > | 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 | echo "$(SRCDIR)\../skins/xekri/footer.txt" >> $@ echo "$(SRCDIR)\../skins/xekri/header.txt" >> $@ echo "$(SRCDIR)\accordion.js" >> $@ echo "$(SRCDIR)\alerts/bflat2.wav" >> $@ echo "$(SRCDIR)\alerts/bflat3.wav" >> $@ echo "$(SRCDIR)\alerts/bloop.wav" >> $@ echo "$(SRCDIR)\alerts/plunk.wav" >> $@ echo "$(SRCDIR)\ci_edit.js" >> $@ echo "$(SRCDIR)\copybtn.js" >> $@ echo "$(SRCDIR)\default.css" >> $@ echo "$(SRCDIR)\diff.js" >> $@ echo "$(SRCDIR)\diff.tcl" >> $@ echo "$(SRCDIR)\forum.js" >> $@ echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ echo "$(SRCDIR)\fossil.confirmer.js" >> $@ echo "$(SRCDIR)\fossil.copybutton.js" >> $@ echo "$(SRCDIR)\fossil.diff.js" >> $@ echo "$(SRCDIR)\fossil.dom.js" >> $@ echo "$(SRCDIR)\fossil.fetch.js" >> $@ echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ echo "$(SRCDIR)\fossil.page.brlist.js" >> $@ echo "$(SRCDIR)\fossil.page.chat.js" >> $@ echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@ echo "$(SRCDIR)\fossil.page.whistory.js" >> $@ echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ echo "$(SRCDIR)\fossil.pikchr.js" >> $@ echo "$(SRCDIR)\fossil.popupwidget.js" >> $@ echo "$(SRCDIR)\fossil.storage.js" >> $@ echo "$(SRCDIR)\fossil.tabs.js" >> $@ echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ echo "$(SRCDIR)\graph.js" >> $@ echo "$(SRCDIR)\hbmenu.js" >> $@ echo "$(SRCDIR)\href.js" >> $@ echo "$(SRCDIR)\login.js" >> $@ echo "$(SRCDIR)\markdown.md" >> $@ echo "$(SRCDIR)\menu.js" >> $@ echo "$(SRCDIR)\scroll.js" >> $@ echo "$(SRCDIR)\skin.js" >> $@ echo "$(SRCDIR)\sorttable.js" >> $@ echo "$(SRCDIR)\sounds/0.wav" >> $@ echo "$(SRCDIR)\sounds/1.wav" >> $@ echo "$(SRCDIR)\sounds/2.wav" >> $@ echo "$(SRCDIR)\sounds/3.wav" >> $@ echo "$(SRCDIR)\sounds/4.wav" >> $@ echo "$(SRCDIR)\sounds/5.wav" >> $@ echo "$(SRCDIR)\sounds/6.wav" >> $@ echo "$(SRCDIR)\sounds/7.wav" >> $@ echo "$(SRCDIR)\sounds/8.wav" >> $@ echo "$(SRCDIR)\sounds/9.wav" >> $@ echo "$(SRCDIR)\sounds/a.wav" >> $@ echo "$(SRCDIR)\sounds/b.wav" >> $@ echo "$(SRCDIR)\sounds/c.wav" >> $@ echo "$(SRCDIR)\sounds/d.wav" >> $@ echo "$(SRCDIR)\sounds/e.wav" >> $@ echo "$(SRCDIR)\sounds/f.wav" >> $@ echo "$(SRCDIR)\style.admin_log.css" >> $@ echo "$(SRCDIR)\style.chat.css" >> $@ echo "$(SRCDIR)\style.fileedit.css" >> $@ echo "$(SRCDIR)\style.wikiedit.css" >> $@ echo "$(SRCDIR)\tree.js" >> $@ echo "$(SRCDIR)\useredit.js" >> $@ echo "$(SRCDIR)\wiki.wiki" >> $@ "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h" |
| ︙ | ︙ |
Changes to www/aboutcgi.wiki.
| ︙ | ︙ | |||
182 183 184 185 186 187 188 |
at "/home/www/resps/subdir.fossil" but there is no such repository.
So then it looks at "/home/www/repos/subdir/three.fossil" and finds
a repository. The PATH_INFO is shortened by removing
"subdir/three/" leaving it at just "timeline".
<li> Fossil looks at the rest of PATH_INFO to see that the webpage
requested is "timeline".
</ol>
| | | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
at "/home/www/resps/subdir.fossil" but there is no such repository.
So then it looks at "/home/www/repos/subdir/three.fossil" and finds
a repository. The PATH_INFO is shortened by removing
"subdir/three/" leaving it at just "timeline".
<li> Fossil looks at the rest of PATH_INFO to see that the webpage
requested is "timeline".
</ol>
<a id="cgivar"></a>
The web server sets many environment variables in step 2 in addition
to just PATH_INFO. The following diagram shows a few of these variables
and their relationship to the request URL:
<pre>
REQUEST_URI
______________|___________________
/ \
|
| ︙ | ︙ |
Changes to www/adding_code.wiki.
| ︙ | ︙ | |||
100 101 102 103 104 105 106 | the makefiles, you should be able to recompile Fossil and have it include your new source file, even before you source file contains any code. It is recommended that you try this. Be sure to [/help/add|fossil add] your new source file to the self-hosting Fossil repository and then [/help/commit|commit] your changes! | < | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
the makefiles, you should be able to recompile Fossil and have it include
your new source file, even before you source file contains any code.
It is recommended that you try this.
Be sure to [/help/add|fossil add] your new source file to the self-hosting
Fossil repository and then [/help/commit|commit] your changes!
<h2 id="newcmd">4.0 Creating A New Command</h2>
By "commands" we mean the keywords that follow "fossil" when invoking
Fossil from the command-line. So, for example, in
<b>fossil diff xyzzy.c</b>
The "command" is "diff". Commands may optionally be followed by
|
| ︙ | ︙ | |||
166 167 168 169 170 171 172 | Fossil for parsing command-line options and for opening and accessing and manipulating the repository and the working check-out. Study implementations of existing commands to get an idea of how things are done. You can easily find the implementations of existing commands by searching for "COMMAND: <i>name</i>" in the files of the "src/" directory. | < | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | Fossil for parsing command-line options and for opening and accessing and manipulating the repository and the working check-out. Study implementations of existing commands to get an idea of how things are done. You can easily find the implementations of existing commands by searching for "COMMAND: <i>name</i>" in the files of the "src/" directory. <h2 id="newpage">5.0 Creating A New Web Page</h2> As with commands, new webpages can be added simply by inserting a function that generates the webpage together with a special header comment. A template follows: <blockquote><verbatim> /* |
| ︙ | ︙ |
Changes to www/alerts.md.
| ︙ | ︙ | |||
321 322 323 324 325 326 327 | 4. Send emails to an SMTP relay. 5. Send emails directly to the recipients via SMTP. This wide range of options allows Fossil to talk to pretty much any SMTP setup. The first four options let Fossil delegate email handling to an existing | | | | 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 | 4. Send emails to an SMTP relay. 5. Send emails directly to the recipients via SMTP. This wide range of options allows Fossil to talk to pretty much any SMTP setup. The first four options let Fossil delegate email handling to an existing [MTA] so that Fossil does not need to implement the [roughly two dozen][mprotos] separate [RFCs][rfcs] required in order to properly support SMTP email in this complex world we've built. As well, this design choice means you do not need to do duplicate configuration, such as to point Fossil at your server's TLS certificate in order to support users behind mail servers that require STARTTLS encryption. [mprotos]: http://sqlite.1065341.n5.nabble.com/Many-ML-emails-going-to-GMail-s-SPAM-tp98685p98722.html [rfcs]: https://en.wikipedia.org/wiki/Request_for_Comments <a id="pipe"></a> ### Method 1: Pipe to a Command This is our ["quick setup" option](#quick) above, but there are some details we ignored which we'll cover now. Fossil pipes the email message in [RFC 822 format][rfc822] to the standard input of the command you gave as the "Email Send Method", defaulting to `sendmail -ti`. This constitutes a protocol between Fossil and the SMTP [message transfer agent (MTA)][MTA]. Any other MTA which speaks the same protocol can be used in place of the most common options: Sendmail, Exim, and Postfix. The `-t` option tells the command to expect the list of email recipients in a `To` header in the RFC 822 message presented on its standard input. Without this option, the `sendmail` command expects to receive the recipient list on the command line, but that's not possible with the |
| ︙ | ︙ | |||
376 377 378 379 380 381 382 | It is probably also possible to configure [`procmail`][pmdoc] to work with this protocol. If you know how to do it, a patch to this document or a how-to on [the Fossil forum][ff] would be appreciated. [ff]: https://fossil-scm.org/forum/ [msmtp]: https://marlam.de/msmtp/ | | | 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | It is probably also possible to configure [`procmail`][pmdoc] to work with this protocol. If you know how to do it, a patch to this document or a how-to on [the Fossil forum][ff] would be appreciated. [ff]: https://fossil-scm.org/forum/ [msmtp]: https://marlam.de/msmtp/ [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent [pmdoc]: http://pm-doc.sourceforge.net/doc/ [rfc822]: https://www.w3.org/Protocols/rfc822/ <a id="db"></a> ### Method 2: Store in a Database |
| ︙ | ︙ | |||
441 442 443 444 445 446 447 | This method may work over a file sharing mechanism that doesn't do file locking properly, as long as the reading process is somehow restricted from reading a message file as it's being written. It might be useful in testing and debugging to temporarily switch to this method, since you can easily read the generated email messages | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | This method may work over a file sharing mechanism that doesn't do file locking properly, as long as the reading process is somehow restricted from reading a message file as it's being written. It might be useful in testing and debugging to temporarily switch to this method, since you can easily read the generated email messages without needing to involve an [MTA]. <a id="relay"></a> ### Method 4: SMTP Relay In this configuration, the Fossil server contacts an open SMTP relay and sends the messages to it. This method is only appropriate when: |
| ︙ | ︙ |
Changes to www/blockchain.md.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 | ## The Dictionary Argument The [Wikipedia definition of "blockchain"][bcwp] begins: > "A blockchain…is a growing list of records, called blocks, which are linked using | | | | | | | | < | | 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 | ## The Dictionary Argument The [Wikipedia definition of "blockchain"][bcwp] begins: > "A blockchain…is a growing list of records, called blocks, which are linked using cryptography… Each block contains a cryptographic hash of the previous block, a timestamp, and transaction data (generally represented as a Merkle tree)." Point-for-point, Fossil follows this partial definition. The blocks are Fossil’s ["manifest" artifacts](./fileformat.wiki#manifest). Each manifest has a cryptographically-strong [SHA-1] or [SHA-3] hash linking it to one or more “parent” blocks. The manifest also contains a timestamp and the transactional data needed to express a commit to the repository. To traverse the Fossil repository from the tips of its [DAG] to the root by following the parent hashes in each manifest is to traverse a Merkle tree. Every change in Fossil starts by adding one or more manifests to the repository, extending this Merkle tree. [bcwp]: https://en.wikipedia.org/wiki/Blockchain [DAG]: https://en.wikipedia.org/wiki/Directed_acyclic_graph [SHA-1]: https://en.wikipedia.org/wiki/SHA-1 [SHA-3]: https://en.wikipedia.org/wiki/SHA-3 |
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
claims to be a $100 bill.
* **Type 2** is creation of new fraudulent currency that will pass
in commerce. To extend our analogy, it is the creation of new
US $10 bills. There are two sub-types to this fraud. In terms of
our analogy, they are:
| | > | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
claims to be a $100 bill.
* **Type 2** is creation of new fraudulent currency that will pass
in commerce. To extend our analogy, it is the creation of new
US $10 bills. There are two sub-types to this fraud. In terms of
our analogy, they are:
* **Type 2a**: copying an existing legitimate $10 bill<p>
* **Type 2b**: printing a new $10 bill that is unlike an existing
legitimate one, yet which will still pass in commerce
* **Type 3** is double-spending existing legitimate cryptocurrency.
There is no analogy in paper money due to its physical form; it is a
problem unique to digital currency due to its infinitely-copyable
nature.
|
| ︙ | ︙ | |||
90 91 92 93 94 95 96 |
3 frauds by making the *prior* owner of a block sign it over to
the new owner. To avoid an O(n²) auditing problem as a result,
cryptocurrencies add a separate chain of hashes to make checking
for double-spending quick and easy.
Fossil has [a disabled-by-default feature][cs] to call out to an
external copy of [PGP] or [GPG] to sign commit manifests before
| | | | | | | > > | | 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 |
3 frauds by making the *prior* owner of a block sign it over to
the new owner. To avoid an O(n²) auditing problem as a result,
cryptocurrencies add a separate chain of hashes to make checking
for double-spending quick and easy.
Fossil has [a disabled-by-default feature][cs] to call out to an
external copy of [PGP] or [GPG] to sign commit manifests before
inserting them into the repository. You can couple that with
a server-side [after-receive hook][arh] to reject unsigned commits.
Although there are several distinctions you can draw between the way
Fossil’s commit signing scheme works and the way block signing works
in cryptocurrencies, only one is of material interest for our
purposes here: Fossil commit signatures apply only to a single
commit. Fossil does not sign one commit over to the next “owner” of
that commit in the way that a blockchain-based cryptocurrency must
when transferring currency from one user to another, beacuse there
is no useful analog to the double-spending problem in Fossil. The
closest you can come to this is double-insert of commits into the
blockchain, which we’ll address shortly.
What Fossil commit signatures actually do is provide in-tree forgery
prevention, both Type 1 and Type 2. You cannot modify existing
commits (Type 1 forgery) because you do not have the original
committer’s private signing key, and you cannot forge new commits
attesting to come from some other trusted committer (Type 2) because
you don’t have any of their private signing keys, either.
Cryptocurrencies use the work problem to prevent Type 2
forgeries, but the application of that to Fossil is a matter we get
to [later](#work).
Although you have complete control over the contents of your local
Fossil repository clone, you cannot perform Type 1 forgery on its
contents short of executing a [preimage attack][prei] on the hash
algorithm. ([SHA3-256][SHA-3] by default in the current version of
Fossil.) Even if you could, Fossil’s sync protocol will prevent the
modification from being pushed into another repository: the remote
Fossil instance says, “I’ve already got that one, thanks,” and
ignores the push. Thus, short of breaking into the remote server
and modifying the repository in place, you couldn’t make use of
a preimage attack even if you had that power. Further, that would be an attack on the
server itself, not on Fossil’s data structures, so while it is
useful to think through this problem, it is not helpful in answering
our questions here.
The Fossil sync protocol’s duplication detection also prevents the closest analog to Type 3
frauds in Fossil: copying a commit manifest in your local repo clone
won’t result in a double-commit on sync.
In the absence of digital signatures, Fossil’s [RBAC system][caps]
restricts Type 2 forgery to trusted committers. Thus once again
we’re reduced to an infosec problem, not a data structure design
question.
(Inversely, enabling commit clearsigning is a good idea
if you have committers on your repo whom you don’t trust not to
commit Type 2 frauds. But let us be clear: your choice of setting
does not answer the question of whether Fossil is a blockchain.)
If Fossil signatures prevent Type 1 and Type 2 frauds, you
may wonder why they are not enabled by default. It is because
they are defense-in-depth measures, not the minimum sufficient
|
| ︙ | ︙ | |||
243 244 245 246 247 248 249 | file, effectively destroying money in a similar way. These features do not permit forgery of either type described above: you can’t use them to change the value of existing commits (Type 1) or add new commits to the repository (Type 2). What if we removed those features from Fossil, creating an append-only Fossil variant? Is it a DLT then? Arguably still not, because [today’s Fossil | | | | | > > | | > | | 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 | file, effectively destroying money in a similar way. These features do not permit forgery of either type described above: you can’t use them to change the value of existing commits (Type 1) or add new commits to the repository (Type 2). What if we removed those features from Fossil, creating an append-only Fossil variant? Is it a DLT then? Arguably still not, because [today’s Fossil is an AP-mode system][ctap], which means there can be no guaranteed consensus on the content of the ledger at any given time. An AP-mode accounts receivable system would allow different bottom-line totals at different sites, because you’ve cast away “C” to get AP-mode operation. (See the prior link or [Wikipedia’s article on the CAP theorem][cap] if you aren’t following this terminology.) By the same token, you cannot guarantee that the command “`fossil info tip`” gives the same result everywhere. You would need to recast Fossil as a CA or CP-mode system to solve that. (Everyone not partitioned away from the majority of the network at any rate, in the CP case.) What are the prospects for CA-mode or CP-mode Fossil? [We don’t want CA-mode Fossil][ctca], but [CP-mode could be useful][ctcp]. Until the latter exists, this author believes Fossil is not a distributed ledger in a technologically defensible sense. |
| ︙ | ︙ | |||
364 365 366 367 368 369 370 | * You could enable [the `self-register` setting][sreg] and choose not to enable [commit clear-signing][cs] so that anonymous users could push commits into your repository under any name they want. On the server side, you can also [scrub] the logging that remembers where each commit came from. | | | | | 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | * You could enable [the `self-register` setting][sreg] and choose not to enable [commit clear-signing][cs] so that anonymous users could push commits into your repository under any name they want. On the server side, you can also [scrub] the logging that remembers where each commit came from. Commit source info isn’t transmitted from the remote server on clone or pull: the size of the `rcvfrom` table after initial clone is 1, containing only the remote server’s IP address. On each pull containing new artifacts, your local `fossil` instance adds another entry to this table, likely with the same IP address unless the server has moved or you’re using [multiple remotes][mrep]. This table is far more interesting on the server side, containing the IP addresses of all contentful pushes; thus [the `scrub` command][scrub]. Because Fossil doesn’t |
| ︙ | ︙ | |||
450 451 452 453 454 455 456 | [sreg]: /help?cmd=self-register # Conclusion This author believes it is technologically indefensible to call Fossil a “blockchain” in any sense likely to be understood by a majority of those | | < < | < | < | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | [sreg]: /help?cmd=self-register # Conclusion This author believes it is technologically indefensible to call Fossil a “blockchain” in any sense likely to be understood by a majority of those you’re communicating with. Using a term in a nonstandard way just because you can defend it means you’ve failed any goal that requires clear communication. The people you’re communicating your ideas to must have the same concept of the terms you use. What term should you use instead? Fossil stores a DAG of hash-chained commits, so an indisputably correct term is a [Merkle tree][mt], named after [its inventor][drrm]. You could also use the more generic term “hash tree.” Fossil is a technological peer to many common sorts of blockchain technology. There is a lot of overlap in concepts and implementation details, but when speaking of what most people understand as “blockchain,” Fossil is not that. [drrm]: https://en.wikipedia.org/wiki/Ralph_Merkle [mt]: https://en.wikipedia.org/wiki/Merkle_tree |
Changes to www/caps/admin-v-setup.md.
1 2 3 4 5 6 7 8 9 10 11 | # Differences Between Setup and Admin Users This document explains the distinction between [Setup users][caps] and [Admin users][capa]. For other information about use types, see: * [Administering User Capabilities](./) * [How Moderation Works](../forum.wiki#moderation) * [Users vs Subscribers](../alerts.md#uvs) * [Defense Against Spiders](../antibot.wiki) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # Differences Between Setup and Admin Users This document explains the distinction between [Setup users][caps] and [Admin users][capa]. For other information about use types, see: * [Administering User Capabilities](./) * [How Moderation Works](../forum.wiki#moderation) * [Users vs Subscribers](../alerts.md#uvs) * [Defense Against Spiders](../antibot.wiki) ## <a id="philosophy"></a>Philosophical Core The Setup user "owns" the Fossil repository and may delegate a subset of that power to one or more Admin users. The Setup user can grant Admin capability and take it away, but Admin users cannot grant themselves Setup capability, either directly via the Admin → Users UI page or via any indirect means. (If you discover |
| ︙ | ︙ | |||
55 56 57 58 59 60 61 | We’ll explore these distinctions in the rest of this document. [bc]: ../blockchain.md [ucap]: ./index.md#ucap [uv]: ../unvers.wiki | | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | We’ll explore these distinctions in the rest of this document. [bc]: ../blockchain.md [ucap]: ./index.md#ucap [uv]: ../unvers.wiki ## <a id="binary"></a>No Granularity Fossil doesn’t make any distinction between these two user types beyond this binary choice: Setup or Admin. A few features of Fossil are broken down so that only part of the feature is accessible to Admin, with the rest left only to Setup users, but for the most part each feature affected by this distinction is |
| ︙ | ︙ | |||
89 90 91 92 93 94 95 | the Admin user is allowed to do, and which must be left to Setup? Do we assign a separate capability letter to each step in `/setup_skin`? Do we assign one more each to the five sections of a skin? (Header, Footer, CSS, JavaScript, and Details.) It quickly becomes unmanageable. | | | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | the Admin user is allowed to do, and which must be left to Setup? Do we assign a separate capability letter to each step in `/setup_skin`? Do we assign one more each to the five sections of a skin? (Header, Footer, CSS, JavaScript, and Details.) It quickly becomes unmanageable. ## <a id="capgroups"></a>Capability Groups We can break up the set of powers the Admin user capability grants into several groups, then defend each group as a coherent whole. ### <a id="security"></a>Security While establishing the Fossil repository's security policy is a task for the Setup user, *maintaining* that policy is something that Fossil allows a Setup user to delegate to trustworthy users via the Admin user capability: * **Manage users**: The only thing an Admin-only user cannot do on the |
| ︙ | ︙ | |||
132 133 134 135 136 137 138 | Some security-conscious people might be bothered by the fact that Admin-only users have these abilities. Think of a large IT organization: if the CIO hires a [tiger team][tt] to test the company's internal IT defenses, the line grunts fix the reported problems, not the CIO. | | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | Some security-conscious people might be bothered by the fact that Admin-only users have these abilities. Think of a large IT organization: if the CIO hires a [tiger team][tt] to test the company's internal IT defenses, the line grunts fix the reported problems, not the CIO. ### <a id="administrivia"></a>Administrivia It is perfectly fine for a Fossil repository to only have Setup users, no Admin users. The smaller the repository, the more likely the repository has no Admin-only users. If the Setup user neither needs nor wants to grant Admin power to others, there is no requirement in Fossil to do so. [Setup capability is a pure superset of Admin capability.][sia] |
| ︙ | ︙ | |||
202 203 204 205 206 207 208 | * **Configure search** [ale]: ../alerts.md [shun]: ../shunning.wiki | | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | * **Configure search** [ale]: ../alerts.md [shun]: ../shunning.wiki ### <a id="cosmetics"></a>Cosmetics While the Setup user is responsible for setting up the initial "look" of a Fossil repository, the Setup user entrusts Admin users with *maintaining* that look. An Admin-only user therefore has the following special abilities: * Modify the repository skin |
| ︙ | ︙ | |||
225 226 227 228 229 230 231 | These capabilities allow an Admin-only user to affect the branding and possibly even the back-end finances of a project. This is why we began this document with a philosophical discussion: if you cannot entrust a user with these powers, you should not grant that user Admin capability. | | | | 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 | These capabilities allow an Admin-only user to affect the branding and possibly even the back-end finances of a project. This is why we began this document with a philosophical discussion: if you cannot entrust a user with these powers, you should not grant that user Admin capability. ## <a id="clones"></a>Clones and Backups Fossil is a *distributed* version control system, which has direct effects on the “Setup user” concept in the face of clones. When you clone a repository, your local user becomes a Setup user on the local clone even if you are not one on the remote repository. This may be surprising to you, but it should also be sensible once you realize that your operating system will generally give you full control over the local repository file. What use trying to apply remote restrictions on the local file, then? The distinctions above therefore are intransitive: they apply only within a single repository instance. Fossil behaves differently when you do a clone as a user with Setup capability on the remote repository, which primarily has effects on the fidelity of clone-as-backup, which we cover [elsewhere](../backup.md). We strongly encourage you to read that document if you expect to use a clone as a complete replacement for the remote repository. ## <a id="apsu"></a>The All-Powerful Setup User Setup users get [every user capability](./ref.html) of Fossil except for [two exceptionally dangerous capabilities](#dcap), which they can later grant to themselves or to others. In addition, Setup users can use every feature of the Fossil UI. If Fossil can do a thing, a Setup user on that repo can make Fossil do it. |
| ︙ | ︙ | |||
382 383 384 385 386 387 388 | through that UI instance, regardless of the capability set defined in the repo’s user table. This is true even if you cloned a remote repo where you do not have Setup caps. This is why `ui` always binds to `localhost` without needing the `--localhost` flag: in this mode, anyone who can connect to that repo’s web UI has full power over that repo. | | | | | 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 | through that UI instance, regardless of the capability set defined in the repo’s user table. This is true even if you cloned a remote repo where you do not have Setup caps. This is why `ui` always binds to `localhost` without needing the `--localhost` flag: in this mode, anyone who can connect to that repo’s web UI has full power over that repo. ## <a id="dcap"></a>Dangerous Capabilities Initially Denied to Everyone There are two capabilities that Fossil doesn’t grant by default to Setup or Admin users automatically. They are exceptionally dangerous, so Fossil makes these users grant themselves (or others) these capabilities deliberately, hopefully after careful consideration. ### <a id="y"></a>Write Unversioned Fossil currently doesn’t distinguish the sub-operations of [`fossil uv`](/help?cmd=uv); they’re all covered by [**WrUnver**][capy] (“y”) capability. Since some of these operations are unconditionally destructive due to the nature of unversioned content, and since this goes against Fossil’s philosophy of immutable history, nobody gets cap “y” on a Fossil repo by default, not even the Setup or Admin users. A Setup or Admin user must grant cap “y” to someone — not necessarily themselves! — before modifications to remote unversioned content are possible. Operations on unversioned content made without this capability affect your local clone only. In this way, your local unversioned file table can have different content from that in its parent repo. This state of affairs will continue until your user either gets cap “y” and syncs that content with its parent or you say `fossil uv revert` to make your local unversioned content table match that of its parent repo. ### <a id="x"></a>Private Branch Push For private branches to remain private, they must never be accidentally pushed to a public repository. It can be [difficult to impossible][shun] to recover from such a mistake, so nobody gets [**Private**][capx] (“x”) capability on a Fossil repo by default, not even Admin or Setup users. There are two common uses for private branches. |
| ︙ | ︙ |
Changes to www/caps/impl.md.
1 2 | # Implementation Details of User Capabilities | | | | 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 | # Implementation Details of User Capabilities ## <a id="choices"></a>Capability Letter Choices We [assigned][ref] user capability characters using only lowercase ASCII letters at first, so those are the most important within Fossil: they control the functions most core to Fossil’s operation. Once we used up most of the lowercase letters, we started using uppercase, and then during the development of the [forum feature][for] we assigned most of the decimal numerals. All of the lowercase ASCII letters are now assigned. Eventually, we might have to start using ASCII punctuation and symbols. We expect to run out of reasons to define new caps before we’re forced to switch to Unicode, though the possibilities for [mnemonic][mn] assignments with emoji are intriguing. <span style="vertical-align: bottom">😉</span> The existing caps are usually mnemonic, especially among the earliest and therefore most central assignments, made when we still had lots of letters to choose from. There is still hope for good future mnemonic assignments among the uppercase letters, which are mostly still unused. ## <a id="bitfield"></a>Why Not Bitfields? Some may question the use of ASCII character strings for [capability sets][ucap] instead of bitfields, which are more efficient, both in terms of storage and processing time. Fossil handles these character strings in one of two ways. For most HTTP hits, Fossil [expands][sexp] the string into a [`struct` full of |
| ︙ | ︙ | |||
41 42 43 44 45 46 47 | network connection, followed by the required I/O to satisfy the request. Either method is plenty fast in that context. In exchange for this immeasurable cost per hit, we get human-readable capability sets. | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
network connection, followed by the required I/O to satisfy the request.
Either method is plenty fast in that context.
In exchange for this immeasurable cost per hit, we get human-readable
capability sets.
## <a id="filter"></a>Why Doesn’t Fossil Filter “Bad” Artifacts on Sync?
Fossil is more trusting about the content it receives from a remote
clone during sync than you might expect. Common manifestations of this
design choice are:
1. A user may be able to impersonate other users. This can be
[accidental](./index.md#defuser) as well as purposeful.
|
| ︙ | ︙ |
Changes to www/caps/index.md.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 | [an]: https://en.wikipedia.org/wiki/Alphanumeric [avs]: ./admin-v-setup.md [lg]: ./login-groups.md [rbac]: https://en.wikipedia.org/wiki/Role-based_access_control | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | [an]: https://en.wikipedia.org/wiki/Alphanumeric [avs]: ./admin-v-setup.md [lg]: ./login-groups.md [rbac]: https://en.wikipedia.org/wiki/Role-based_access_control ## <a id="ucat"></a>User Categories Before we explain individual user capabilities and their proper administration, we want to talk about an oft-overlooked and misunderstood feature of Fossil: user categories. Fossil defines four user categories. Two of these apply based on the user’s login status: **nobody** and **anonymous**. The other two act |
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | treat the same category as if it were called “member.” There is currently no way to define custom user categories. [svr]: ../server/ | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | treat the same category as if it were called “member.” There is currently no way to define custom user categories. [svr]: ../server/ ## <a id="ucap"></a>Individual User Capabilities When one or more users need to be different from the basic capabilities defined in user categories, you can assign caps to individual users. You may want to have the [cap reference][ref] open when doing such work. It is useful at this time to expand on the logical expression [above](#cat), which covered only the four fixed user categories. |
| ︙ | ︙ | |||
116 117 118 119 120 121 122 | subscribers, which is why we show it in square brackets. (See [Users vs Subscribers](../alerts.md#uvs).) [apsu]: ./admin-v-setup.md#apsu [avsp]: ./admin-v-setup.md#philosophy | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | subscribers, which is why we show it in square brackets. (See [Users vs Subscribers](../alerts.md#uvs).) [apsu]: ./admin-v-setup.md#apsu [avsp]: ./admin-v-setup.md#philosophy ## <a id="new"></a>New Repository Defaults Fossil creates one user account in new repos, which is named after your OS user name [by default](#defuser). Fossil gives the initial repository user the [all-powerful Setup capability][apsu]. |
| ︙ | ︙ | |||
155 156 157 158 159 160 161 | Those in the “developer” category get the “nobody” and “anonymous” cap sets plus **[e][e][i][i]**: view sensitive user material and check in changes. [bot]: ../antibot.wiki | | | | 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 | Those in the “developer” category get the “nobody” and “anonymous” cap sets plus **[e][e][i][i]**: view sensitive user material and check in changes. [bot]: ../antibot.wiki ## <a id="pvt"></a>Consequences of Taking a Repository Private When you click Admin → Security-Audit → “Take it private,” one of the things it does is set the user capabilities for the “nobody” and “anonymous” user categories to blank, so that users who haven’t logged in can’t even see your project’s home page, and the option to log in as “anonymous” isn’t even offered. Until you log in with a user name, all you see is the repository’s skin and those few UI elements that work without any user capability checks at all, such as the “Login” link. Beware: Fossil does not reassign the capabilities these users had to other users or to the “reader” or “developer” user category! All users except those with Setup capability will lose all capabilities they inherited from “nobody” and “anonymous” categories. Setup is the [lone exception][apsu]. If you will have non-Setup users in your private repo, you should parcel out some subset of the capability set the “nobody” and “anonymous” categories had to other categories or to individual users first. ## <a id="read-v-clone"></a>Reading vs. Cloning Fossil has two capabilities that are often confused: [**Read**](./ref.html#o) and [**Clone**](./ref.html#g). The **Read** capability has nothing to do with reading data from a local repository, because [caps affect Fossil’s web interfaces only](#webonly). Once you’ve cloned a remote repository to your local |
| ︙ | ︙ | |||
204 205 206 207 208 209 210 | on private or semi-private repos to prevent them from pulling individual elements of the repo over the web one at a time, as someone may do when denied the bulk **Clone** capability. [edoc]: ../embeddeddoc.wiki | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | on private or semi-private repos to prevent them from pulling individual elements of the repo over the web one at a time, as someone may do when denied the bulk **Clone** capability. [edoc]: ../embeddeddoc.wiki ## <a id="defuser"></a>Default User Name By default, Fossil assumes your OS user account name is the same as the one you use in any Fossil repository. It is the [default for a new repository](#new), though you can override this with [the `--admin-user` option][auo]. Fossil has other ways of overriding this in other contexts such as the `name@` syntax in clone URLs. |
| ︙ | ︙ | |||
232 233 234 235 236 237 238 | [auo]: /help?name=new [fos]: ./impl.md#filter [shun]: ../shunning.wiki | | | | | | > > > > > > | | > | > | > | | > | < < > | > | | | 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 |
[auo]: /help?name=new
[fos]: ./impl.md#filter
[shun]: ../shunning.wiki
## <a id="utclone"></a>Cloning the User Table
When cloning over HTTP, the initial user table in the local clone is set
to its “[new state:](#new)” only one user with Setup capability, named
after either your OS user account, per the default above, or after the
user given in the clone URL.
There is one exception: if you clone as a named Setup user, you get a
complete copy of the user information. This restriction keeps the user
table private except for the only user allowed to make absolutely
complete clones of a remote repo, such as for failover or backup
purposes. Every other user’s clone is missing this and a few other
items, either for information security or PII privacy reasons.
When cloning with file system paths, `file://` URLs, or over SSH, you
get a complete clone, including the parent repo’s complete user table.
All of the above applies to [login groups][lg] as well.
## <a id="webonly"></a>Caps Affect Web Interfaces Only
Fossil’s user capability system only affects accesses over `http[s]://`
URLs. This includes clone, sync/push/pull, the [UI pages][wp], and [the
JSON API][japi]. For everything else, the user caps aren’t consulted at
all.
The only checks made when working directly with a local repository are
the operating system’s file system permissions. This should strike you
as sensible, since if you have read access to the repository file, you
can do anything you want to that repo DB including giving your user’s
record the [**Setup**][s] capability, after which Fossil’s user
capability system is effectively bypassed. (Or, create another Setup
user, with the same end effect.) If you’re objecting that you need
*write* access to the DB file to achieve this, realize that you can copy
a read-only file to another location, giving yourself write access to
it.
This is why the `fossil ui` command
gives you Setup permissions within Fossil UI: it can’t usefully prevent
you from doing anything through the UI since only the local file system
permissions actually matter, and you can’t start `fossil ui` without
having at least read access to that file.
What may be more surprising to you is that this is also true when
working on a *clone* done over a local file path, except that there are
then two sets of file system permission checks: once to modify the
working check-out’s repo clone DB file, then again on [sync][sync] with
the parent DB file. The Fossil capability checks are effectively
defeated because your user has [**Setup**][s] capability on both sides
of the sync. Be aware that those file checks do still matter, however:
Fossil requires write access to a repo DB while cloning from it, so you
can’t clone from a read-only repo DB file over a local file path.
Even more surprising to you may be the fact that user caps do not affect
cloning and syncing over SSH! (Not unless you go [out of your way][sshfc]
patch around it, at any rate.) When you make a change to such a
repository, the stock Fossil behavior is that the change first goes to the
local repo clone where file system
permissions are all that matter, but then upon sync, the situation is
effectively the same as when the parent repo is on the local file
system. The reason behind this is that if you can log into the remote
system over SSH and that user has the necessary file system permissions
on that remote repo DB file to allow clone and sync operations, then
we’re back in the same situation as with local files: there’s no point
trying to enforce the Fossil user capabilities when you can just modify
the remote DB directly, so the operation proceeds unimpeded by any user
capability settings on the remote repo.
Where this gets confusing is that *all* Fossil syncs are done over the
HTTP protocol, including those done over `file://` and `ssh://` URLs,
not just those done over `http[s]://` URLs:
* For `ssh://` URLs, Fossil pipes the HTTP conversation through a
local SSH client to a remote instance of Fossil running the
[`test-http`](/help?name=test-http) command to receive the tunneled
HTTP connection. [This interface is intentionally permissionless][sxycap].
* For `file://` URLs — as opposed to plain local file paths —
the “sending” Fossil instance writes its side of
the HTTP conversation out to a temporary file in the same directory
as the local repo clone and then calls itself on the “receiving”
repository to read that same HTTP transcript file back in to apply
those changes to that repository. Presumably Fossil does this
instead of using a pipe to ease portability to Windows.
Despite use of HTTP for these URL types, the fact remains that
checks for capabilities like [**Read**][o] and [**Write**][i] within the
HTTP conversation between two Fossil instances only have a useful effect
when done over an `http[s]://` URL.
[sshfc]: ../server/any/http-over-ssh.md
[sxycap]: /file?ci=ec5efceb8aac6cb4&name=src/main.c&ln=2748-2752
## <a id="pubpg"></a>Public Pages
In Admin → Access, there is an option for giving a list of [globs][glob]
to name URLs which get treated as if the visitor had [the default cap
set](#defcap). For example, you could take the [**Read**][o] capability
away from the “nobody” user category, who has it by default, to prevent
users without logins from pulling down your repository contents one
artifact at a time, yet give those users the ability to read the project
documentation by setting the glob to match your [embedded
documentation][edoc]’s URL root.
## <a id="defcap"></a>Default User Capability Set
In Admin → Access, you can define a default user capability set, which
is used as:
1. the default caps for users newly created by an Admin or Setup user
2. the default caps for self-registered users, an option in that same UI
3. the effective caps for URIs considered [public pages](#pubpg)
|
| ︙ | ︙ |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | < | > > > > > > > | | > > | > > | > > > | > > > > | > > | < > > > > > > | > < | | 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 |
<title>Change Log</title>
<h2 id='v2_17'>Changes for version 2.17 (2021-10-09)</h2>
* Major improvements to the "diff" subsystem, including: <ul>
<li> Added new [/help?cmd=diff|formatting options]: --by, -b, --webpage, --json, --tcl.
<li> Partial-line matching for unified diffs
<li> Better partial-line matching for side-by-side diffs
<li> Buttons on web-based diffs to show more context
<li> Performance improvements
</ul>
* The --branchcolor option on [/help?cmd=commit|fossil commit] and
[/help?cmd=amend|fossil amend] can now take the value "auto" to
force Fossil to use its built-in automatic color choosing algorithm.
* Fossil now [./concepts.wiki#workflow|autosyncs] prior to running
[/help?cmd=open|fossil open].
* Add the [/help?cmd=ticket-default-report|ticket-default-report setting],
which if set to the title of a ticket report causes that ticket report
to be displayed below the search box in the /ticket page.
* The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page
causes all graph coloring to be omitted.
* Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]"
feature so that it works better on a wider variety of platforms.
* In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for
the current page and list URLs suitable for pasting them into the page.
* Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
and similar.
* Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
the --verbose option.
* Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
</tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command].
* The "-p" option to [/help?cmd=branch|fossil branch list] shows only
private branches.
* The [/md_rules|Markdown formatter] now interprets the content of
block HTML markup (such as <table>) in most cases. Only content
of <pre> and <script> is passed through verbatim.
* The [/help?cmd=wiki|wiki list command] no longer lists "deleted"
pages by default. Use the new <tt>--all</tt> option to include deleted
pages in the output.
* The [/help?cmd=all|fossil all git status] command only shows reports for
the subset of repositories that have a configured Git export.
* The [/help?cmd=/chat|/chat] configuration was reimplemented and
provides new options, including the ability for a repository
administrator to
[./chat.md#notifications|extend the selection of notification sounds]
using unversioned files.
* Chat now uses fossil's full complement of markdown features,
instead of the prior small subset of markup it previously supported.
This retroactively applies to all chat messages, as they are
markdown-processed when they are sent instead of when they
are saved.
* Added a chat message preview mode so messages can be previewed
before being sent. Similarly, added a per-message ability to view
the raw un-parsed message text.
* The hotkey to activate preview mode in [/help?cmd=/wikiedit|/wikiedit],
[/help?cmd=/fileedit|/fileedit], and [/help?cmd=/pikchrshow|/pikchrshow]
was changed from ctrl-enter to shift-enter in order to align with
[/help?cmd=/chat|/chat]'s new preview feature and related future
changes.
<h2 id='v2_16'>Changes for Version 2.16 (2021-07-02)</h2>
* <b>Security:</b> Fix the client-side TLS so that it verifies that the
server hostname matches its certificate.
* The default "ssh" command on Windows is changed to "ssh" instead of the
legacy "plink", as ssh is now generally available on Windows systems.
Installations that still need to use the legacy "plink" can make that
happen by running: '<tt>fossil set ssh-command "plink -ssh" --global</tt>'.
* Added the [./patchcmd.md|fossil patch] command.
|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
abandoned email accounts forever.
* SQL that defines [/tktsetup_tab|database objects for tickets] now
[/timeline?c=c717f1ef9a1a4c91|can DROP] a VIEW or an INDEX provided
that its name starts with '<code>ticket</code>' or '<code>fx_</code>'.
* Update the built-in SQLite to version 3.36.0.
* Numerous other minor enhancements.
| < | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
abandoned email accounts forever.
* SQL that defines [/tktsetup_tab|database objects for tickets] now
[/timeline?c=c717f1ef9a1a4c91|can DROP] a VIEW or an INDEX provided
that its name starts with '<code>ticket</code>' or '<code>fx_</code>'.
* Update the built-in SQLite to version 3.36.0.
* Numerous other minor enhancements.
<h2 id='v2_15'>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)
and 2.15.2 on (2021-06-15)</h2>
* <b>Patch 2.15.2:</b> Fix the client-side TLS so that it verifies that the
server hostname matches its certificate. <b>Upgrading to
the patch is recommended.</b>
* <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
the patch is recommended.</b>
* The [./defcsp.md|default CSP] has been relaxed slightly to allow
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
is recent enough.
* Webpage that shows [/help?cmd=/whistory|history of a wiki page]
gained client-side UI to help with comparison between two arbitrary
versions of a wiki (by the means of anchoring a "baseline" version)
and the ability to squeeze several sequential edits made by the same
user into a single "recycled" row (the latest edit in that sequence).
| < | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
is recent enough.
* Webpage that shows [/help?cmd=/whistory|history of a wiki page]
gained client-side UI to help with comparison between two arbitrary
versions of a wiki (by the means of anchoring a "baseline" version)
and the ability to squeeze several sequential edits made by the same
user into a single "recycled" row (the latest edit in that sequence).
<h2 id='v2_14'>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07)
and 2.14.2 on (2021-06-15)</h2>
* <b>Patch 2.14.2:</b> Fix the client-side TLS so that it verifies that the
server hostname matches its certificate. <b>Upgrading to
the patch is recommended.</b><
* <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server.
<b>Upgrading to the patch is recommended.</b>
* <b>Schema Update Notice #1:</b>
|
| ︙ | ︙ | |||
227 228 229 230 231 232 233 |
for custom ticket configurations.
* The built-in SQLite is updated to version 3.35.0 alpha containing
performance optimizations, especially performance associated with
startup, and minor improvements to the CLI.
* Performance optimizations to Fossil itself.
* Countless improvements and enhancements to the documentation
| < | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
for custom ticket configurations.
* The built-in SQLite is updated to version 3.35.0 alpha containing
performance optimizations, especially performance associated with
startup, and minor improvements to the CLI.
* Performance optimizations to Fossil itself.
* Countless improvements and enhancements to the documentation
<h2 id='v2_13'>Changes for Version 2.13 (2020-11-01)</h2>
* Added support for [./interwiki.md|interwiki links].
* Enable <del> and <ins> markup in wiki.
* Improvements to the Forum threading display.
* Added support for embedding [./pikchr.md|pikchr]
markup in markdown and fossil-wiki content.
* The new "[/help?cmd=pikchr|pikchr]" command can render
|
| ︙ | ︙ | |||
258 259 260 261 262 263 264 |
* The built-in SQLite is updated to an alpha of version 3.34.0, and
the minimum SQLite version is increased to 3.34.0 because the
/finfo change in the previous bullet depends on enhancements to
recursive common table expressions that are only available in
SQLite 3.34.0 and later.
* Countless other minor refinements and documentation improvements.
| < | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
* The built-in SQLite is updated to an alpha of version 3.34.0, and
the minimum SQLite version is increased to 3.34.0 because the
/finfo change in the previous bullet depends on enhancements to
recursive common table expressions that are only available in
SQLite 3.34.0 and later.
* Countless other minor refinements and documentation improvements.
<h2 id='v2_12'>Changes for Version 2.12.1 (2020-08-20)</h2>
* (2.12.1): Fix client-side vulnerabilities discovered by Max Justicz.
* Security fix in the "[/help?cmd=git|fossil git export]" command.
The same fix is also backported to version 2.10.1 and 2.11.1.
New "safety-net" features were added to prevent similar problems
in the future.
* Enhancements to the graph display for cases when there are
|
| ︙ | ︙ | |||
331 332 333 334 335 336 337 |
* Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to
the "[/help?cmd=sql|fossil sql]" command, providing access to the
dispatch table including all help text, and the builtin data files,
respectively.
* [./delta_format.wiki|Delta compression] is now applied to forum edits.
* The [/help?cmd=/wikiedit|wiki editor] has been modernized and is
now Ajax-based. The WYSIWYG editing option for Fossil-format wiki
| | < | | 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 |
* Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to
the "[/help?cmd=sql|fossil sql]" command, providing access to the
dispatch table including all help text, and the builtin data files,
respectively.
* [./delta_format.wiki|Delta compression] is now applied to forum edits.
* The [/help?cmd=/wikiedit|wiki editor] has been modernized and is
now Ajax-based. The WYSIWYG editing option for Fossil-format wiki
pages was removed. (Please let us know, via the site's Forum menu,
if that removal unduly impacts you.) This also changes the semantics
of the wiki "Sandbox": that pseudo-page may be freely edited but
no longer saved via the UI (the [/help?cmd=wiki|wiki CLI command]
can, though).
* The [/help?cmd=allow-symlinks|allow-symlinks setting] no longer
syncs. It must be activated individually on any clones which require
symlinks.
* Countless documentation enhancements.
<h2 id='v2_11'>Changes for Version 2.11 (2020-05-25)</h2>
* (2.11.2): Backport security fixes from 2.12.1
* (2.11.1): Backport security fix for the "fossil git export" command.
* Support [/md_rules|Markdown] in the default ticket configuration.
* Timestamp strings in [./checkin_names.wiki|object names]
can now omit punctation. So, for example, "202004181942" and
"2020-04-18 19:42" mean the same thing.
|
| ︙ | ︙ | |||
432 433 434 435 436 437 438 |
page so that it does not add "anonymous" capabilities to the
"nobody" user.
* Update internal Unicode character tables, used in regular expression
handling, from version 12.1 to 13.
* Many documentation enhancements.
* Many minor enhancements to existing features.
| < | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
page so that it does not add "anonymous" capabilities to the
"nobody" user.
* Update internal Unicode character tables, used in regular expression
handling, from version 12.1 to 13.
* Many documentation enhancements.
* Many minor enhancements to existing features.
<h2 id='v2_10'>Changes for Version 2.10 (2019-10-04)</h2>
* (2.10.2): backport security fixes from 2.12.1
* (2.10.1): backport security fix for the "fossil git export" command.
* Added support for [./serverext.wiki|CGI-based Server Extensions].
* Added the [/help?cmd=repolist-skin|repolist-skin] setting used to
add style to repository list pages.
* Enhance the hierarchical display of Forum threads to do less
|
| ︙ | ︙ | |||
469 470 471 472 473 474 475 |
* The check-in lock interval is reduced from 24 hours to 60 seconds,
though the interval is now configurable using a setting.
An additional check for conflicts is added after interactive
check-in comment entry, to compensate for the reduced lock interval.
* Performance optimizations.
* Many documentation improvements.
| < | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 |
* The check-in lock interval is reduced from 24 hours to 60 seconds,
though the interval is now configurable using a setting.
An additional check for conflicts is added after interactive
check-in comment entry, to compensate for the reduced lock interval.
* Performance optimizations.
* Many documentation improvements.
<h2 id='v2_9'>Changes for Version 2.9 (2019-07-13)</h2>
* Added the [/help?cmd=git|fossil git export] command and instructions
for [./mirrortogithub.md|creating a GitHub mirror of a Fossil project].
* Improved handling of relative hyperlinks on the
[/help?cmd=/artifact|/artifact] pages for wiki. For example,
hyperlinks and the lizard <img> now work correctly
for both [/artifact/2ff24ab0887cf522] and
|
| ︙ | ︙ | |||
540 541 542 543 544 545 546 |
caused by concurrent commits when operating in auto-sync mode.
* Fix a bug ([https://fossil-scm.org/forum/forumpost/c51b9a1169|details])
that can cause repository databases to be overwritten with debugging
output, thus corrupting the repository. This is only a factor when
CGI debugging is enabled, and even then is a rare occurrence, but it is
obviously an important fix.
| < | | 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 |
caused by concurrent commits when operating in auto-sync mode.
* Fix a bug ([https://fossil-scm.org/forum/forumpost/c51b9a1169|details])
that can cause repository databases to be overwritten with debugging
output, thus corrupting the repository. This is only a factor when
CGI debugging is enabled, and even then is a rare occurrence, but it is
obviously an important fix.
<h2 id='v2_8'>Changes for Version 2.8 (2019-02-20)</h2>
* Show cherry-pick merges as dotted lines on the timeline graph.
→ The "fossil rebuild" command must be run to create and
populate the new "cherrypick" table in the repository in order
for this feature to operate.
* Add the ability to associate branches, check-ins, and tags with
specially-named Wiki pages. This gives the ability to better
|
| ︙ | ︙ | |||
607 608 609 610 611 612 613 |
to the checkout database to avoid any problems.
* Add the backoffice-disable setting to completely disable the
backoffice feature.
* Update the built-in SQLite to version 3.27.1.
* Various other small enhancements to webpages and documentation.
| < | | 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
to the checkout database to avoid any problems.
* Add the backoffice-disable setting to completely disable the
backoffice feature.
* Update the built-in SQLite to version 3.27.1.
* Various other small enhancements to webpages and documentation.
<h2 id='v2_7'>Changes for Version 2.7 (2018-09-22)</h2>
* Add the [./alerts.md|email alerts] feature for commits, ticket
changes, wiki changes, forum posts, and announcements. This is
still a work in progress. It is functional, but it is not as easy to
setup and use as it ought to be.
* Add the [./forum.wiki|discussion forum] feature.
* Add new user capabilities letters needed to support alerts and forum.
|
| ︙ | ︙ | |||
650 651 652 653 654 655 656 |
* The `mv-rm-files` setting is now compiled into Fossil in the
default Fossil configuration; no longer must you say
<tt>./configure --with-legacy-mv-rm</tt> to make it available. The
setting remains disabled by default, however, so you must still say
<tt>fossil set mv-rm-files 1</tt> to enable it on each repository
where you want hard <tt>mv/rm</tt> behavior.
| < | | 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
* The `mv-rm-files` setting is now compiled into Fossil in the
default Fossil configuration; no longer must you say
<tt>./configure --with-legacy-mv-rm</tt> to make it available. The
setting remains disabled by default, however, so you must still say
<tt>fossil set mv-rm-files 1</tt> to enable it on each repository
where you want hard <tt>mv/rm</tt> behavior.
<h2 id='v2_6'>Changes for Version 2.6 (2018-05-04)</h2>
* Fix a bug that was causing crashes while trying to clone the TCL
repository. This fix is the main reason for the current release.
* Added the new "Classic" timeline viewing mode. "Classic" is the
same as "Verbose" in the previous release. The "Verbose" mode is
now like "Compact" except the extra check-in details are shown by
default.
|
| ︙ | ︙ | |||
679 680 681 682 683 684 685 |
time column.
* In the tarball cache replacement algorithm, give extra weight to
tarballs that have been accessed more than once.
* Additional defenses against web-based attacks. There have not been
any known vulnerabilities. We are just being paranoid.
* Update the built-in SQLite to an alpha version of 3.24.0.
| < | | 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 |
time column.
* In the tarball cache replacement algorithm, give extra weight to
tarballs that have been accessed more than once.
* Additional defenses against web-based attacks. There have not been
any known vulnerabilities. We are just being paranoid.
* Update the built-in SQLite to an alpha version of 3.24.0.
<h2 id='v2_5'>Changes for Version 2.5 (2018-02-07)</h2>
* Numerous enhancements to the look and feel of the web interface.
Especially: Added separate "Modern", "Compact", "Verbose", and
"Columnar" view options on timelines.
* Common display settings (such as the "view" option and the number
of rows in a timeline) are held in a cookie and thus persist
across multiple pages.
|
| ︙ | ︙ | |||
715 716 717 718 719 720 721 |
* Begin factoring out in-line javascript into separately loaded
script files. This is a step along the
road toward supporting a strict Content Security Policy. More work
is to be done.
* Initial infrastructure is in place to make use of the pledge()
system call in OpenBSD. More work is to be done.
| < | | 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
* Begin factoring out in-line javascript into separately loaded
script files. This is a step along the
road toward supporting a strict Content Security Policy. More work
is to be done.
* Initial infrastructure is in place to make use of the pledge()
system call in OpenBSD. More work is to be done.
<h2 id='v2_4'>Changes for Version 2.4 (2017-11-03)</h2>
* New feature: URL Aliases. URL Aliases allow an administrator
to define their own URLs on the web interface that are rewritten to
built-in URLs with specific parameters. Create and configure URL Aliases
using the /Setup/URL_Aliases menu option in the web interface.
* Add tech-note search capability.
* Add the -r|--revision and -o|--origin options to the
|
| ︙ | ︙ | |||
755 756 757 758 759 760 761 |
[/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to
honor the versioned manifest setting when outside of an open checkout
directory.
* The admin-log and access-log settings are now on by default for
new repositories.
* Update the built-in SQLite to version 3.21.0.
| < | < | < | < | < | | 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 |
[/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to
honor the versioned manifest setting when outside of an open checkout
directory.
* The admin-log and access-log settings are now on by default for
new repositories.
* Update the built-in SQLite to version 3.21.0.
<h2 id='v2_3'>Changes for Version 2.3 (2017-07-21)</h2>
* Update the built-in SQLite to version 3.20.0 (beta).
* Update internal Unicode character tables, used in regular expression
handling, from version 9.0 to 10.0.
* Show the last-sync-URL on the [/help?cmd=/urllist|/urllist] page.
* Added the "Event Summary" activity report.
[/reports?type=ci&view=lastchng|example]
* Added the "Security Audit" page, available to administrators only
* Added the Last Login time to the user list page, for administrators only
* Added the --numstat option to the [/help?cmd=diff|fossil diff] command
* Limit the size of the heap and stack on unix systems, as a proactive
defense against the
[https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt|Stack Clash]
attack.
* Fix "database locked" warnings caused by "PRAGMA optimize".
* Fix a potential XSS vulnerability on the
[/help?cmd=/help|/help] webpage.
* Documentation updates
<h2 id='v2_2'>Changes for Version 2.2 (2017-04-11)</h2>
* GIT comment tags are now handled by Fossil during import/export.
* Show the content of README files on directory listings.
([/file/skins|example])
* Support for Basic Authentication if enabled (default off).
* Show the hash algorithms used on the
[/help?cmd=/rcvfromlist|/rcvfromlist] page.
* The [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] pages
now use the the r= query parameter
to select which check-in to deliver. The uuid= query parameter
is still accepted for backwards compatibility.
* Update the built-in SQLite to version 3.18.0.
* Run "[https://www.sqlite.org/pragma.html#pragma_optimize|PRAGMA optimize]"
on the database connection as it is closing.
<h2 id='v2_1'>Changes for Version 2.1 (2017-03-10)</h2>
* Add support for [./hashpolicy.wiki|hash policies] that control which
of the Hardened-SHA1 or SHA3-256 algorithms is used to name new
artifacts.
* Add the "gshow" and "gcat" subcommands to [/help?cmd=stash|fossil stash].
* Add the [/help?cmd=/juvlist|/juvlist] web page and use it to construct
the [/uv/download.html|Download Page] of the Fossil self-hosting website
using Ajax.
<h2 id='v2_0'>Changes for Version 2.0 (2017-03-03)</h2>
* Use the
[https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
implementation by Marc Stevens and Dan Shumow.
* Add the ability to read and understand
[./fileformat.wiki#names|artifact names] that are based on SHA3-256
rather than SHA1, but do not actually generate any such names.
* Added the [/help?cmd=sha3sum|sha3sum] command.
* Update the built-in SQLite to version 3.17.0.
<h2 id='v1_37'>Changes for Version 1.37 (2017-01-16)</h2>
* Add checkbox widgets to various web pages. See [/technote/8d18bf27e9|
this technote] for more information. To get the checkboxes to look as
intended, you must update the CSS in your repository and all clones.
* Add the [/help/all|fossil all ui] command
* Add the [/help?cmd=/file|/file] webpage
* Enhance the [/help?cmd=/brlist|/brlist] webpage to make use of branch colors.
|
| ︙ | ︙ | |||
854 855 856 857 858 859 860 |
* Fixes for incremental git import/export.
* Minor security enhancements to
[./encryptedrepos.wiki|encrypted repositories].
* Update the built-in SQLite to version 3.16.2.
* Update the built-in Zlib to version 1.2.11.
| < | | 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 |
* Fixes for incremental git import/export.
* Minor security enhancements to
[./encryptedrepos.wiki|encrypted repositories].
* Update the built-in SQLite to version 3.16.2.
* Update the built-in Zlib to version 1.2.11.
<h2 id='v1_36'>Changes for Version 1.36 (2016-10-24)</h2>
* Add support for [./unvers.wiki|unversioned content],
the [/help?cmd=unversioned|fossil unversioned] command and the
[/help?cmd=/uv|/uv] and [/uvlist] web pages.
* The [/uv/download.html|download page] is moved into
[./unvers.wiki|unversioned content] so that the self-hosting Fossil
websites no longer uses any external content.
|
| ︙ | ︙ | |||
885 886 887 888 889 890 891 |
queries are filled in using HTTP query parameter values.
* Added support for [./childprojects.wiki|child projects] that are
able to pull from their parent but not push.
* Added the -nocomplain option to the TH1 "query" command.
* Added support for the chng=GLOBLIST query parameter on the
[/help?cmd=/timeline|/timeline] webpage.
| < | | 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 |
queries are filled in using HTTP query parameter values.
* Added support for [./childprojects.wiki|child projects] that are
able to pull from their parent but not push.
* Added the -nocomplain option to the TH1 "query" command.
* Added support for the chng=GLOBLIST query parameter on the
[/help?cmd=/timeline|/timeline] webpage.
<h2 id='v1_35'>Changes for Version 1.35 (2016-06-14)</h2>
* Enable symlinks by default on all non-Windows platforms.
* Enhance the [/md_rules|Markdown formatting] so that hyperlinks that begin
with "/" are relative to the root of the Fossil repository.
* Rework the [/help?cmd=/setup_ulist|/setup_list page] (the User List page)
to display all users in a click-to-sort table.
* Fix backslash-octal escape on filenames while importing from git
|
| ︙ | ︙ | |||
929 930 931 932 933 934 935 |
* Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
* If the FOSSIL_PWREADER environment variable is set, then use the program it
names in place of getpass() to read passwords and passphrases
* Option --baseurl now works on Windows.
* Numerous documentation improvements.
* Update the built-in SQLite to version 3.13.0.
| < | | 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 |
* Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
* If the FOSSIL_PWREADER environment variable is set, then use the program it
names in place of getpass() to read passwords and passphrases
* Option --baseurl now works on Windows.
* Numerous documentation improvements.
* Update the built-in SQLite to version 3.13.0.
<h2 id='v1_34'>Changes for Version 1.34 (2015-11-02)</h2>
* Make the [/help?cmd=clean|fossil clean] command undoable for files less
than 10MiB.
* Update internal Unicode character tables, used in regular expression
handling, from version 7.0 to 8.0.
* Add the new [/help?cmd=amend|amend] command which is used to modify
tags of a "check-in".
|
| ︙ | ︙ | |||
965 966 967 968 969 970 971 |
* Fix --hard option to [/help?cmd=mv|fossil mv] and [/help?cmd=rm|fossil rm]
to enable them to work properly with certain relative paths.
* Change the mimetype for ".n" and ".man" files to text/plain.
* Display improvements in the [/help?cmd=bisect|fossil bisect chart] command.
* Updated the built-in SQLite to version 3.9.1 and activated JSON1 and FTS5
support (both currently unused within Fossil).
| < | | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
* Fix --hard option to [/help?cmd=mv|fossil mv] and [/help?cmd=rm|fossil rm]
to enable them to work properly with certain relative paths.
* Change the mimetype for ".n" and ".man" files to text/plain.
* Display improvements in the [/help?cmd=bisect|fossil bisect chart] command.
* Updated the built-in SQLite to version 3.9.1 and activated JSON1 and FTS5
support (both currently unused within Fossil).
<h2 id='v1_33'>Changes for Version 1.33 (2015-05-23)</h2>
* Improved fork detection on [/help?cmd=update|fossil update],
[/help?cmd=status|fossil status] and related commands.
* Change the default skin to what used to be called "San Francisco Modern".
* Add the [/repo-tabsize] web page
* Add [/help?cmd=import|fossil import --svn], for importing a subversion
repository into fossil which was exported using "svnadmin dump".
* Add the "--compress-only" option to [/help?cmd=rebuild|fossil rebuild].
|
| ︙ | ︙ | |||
1015 1016 1017 1018 1019 1020 1021 |
* Permit filtering weekday and file [/help?cmd=/reports|reports] by user.
Also ensure the user parameter is preserved when changing types. Add a
field for direct entry of the user name to each applicable report.
* Create parent directories of [/help?cmd=settings|empty-dirs] if they don't
already exist.
* Inhibit timeline links to wiki pages that have been deleted.
| < | < | | 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 |
* Permit filtering weekday and file [/help?cmd=/reports|reports] by user.
Also ensure the user parameter is preserved when changing types. Add a
field for direct entry of the user name to each applicable report.
* Create parent directories of [/help?cmd=settings|empty-dirs] if they don't
already exist.
* Inhibit timeline links to wiki pages that have been deleted.
<h2 id='v1_33'>Changes for Version 1.32 (2015-03-14)</h2>
* When creating a new repository using [/help?cmd=init|fossil init], ensure
that the new repository is fully compatible with historical versions of
Fossil by having a valid manifest as RID 1.
* Anti-aliased rendering of arrowheads on timeline graphs.
* Added vi/less-style key bindings to the --tk diff GUI.
* Documentation updates to fix spellings and changes all "checkins" to
"check-ins".
* Add the --repolist option to server commands such as
[/help?cmd=server|fossil server] or [/help?cmd=http|fossil http].
* Added the "Xekri" skin.
* Enhance the "ln=" query parameter on artifact displays to accept multiple
ranges, separate by spaces (or "+" when URL-encoded).
* Added [/help?cmd=forget|fossil forget] as an alias for
[/help?cmd=rm|fossil rm].
<h2 id='v1_31'>Changes For Version 1.31 (2015-02-23)</h2>
* Change the auxiliary schema by adding columns MLINK.ISAUX and MLINK.PMID
columns to the schema, to support better drawing of file change graphs.
A [/help?cmd=rebuild|fossil rebuild] is recommended but is not required.
so that the new graph drawing logic can work effectively.
* Added [/search|search] over Check-in comments, Documents, Tickets and
Wiki. Disabled by default. The search can be either a full-scan or it
can use an index that is kept up-to-date automatically. The new
|
| ︙ | ︙ | |||
1084 1085 1086 1087 1088 1089 1090 |
* Added the [/mimetype_list] page.
* Added the [/hash-collisions] page.
* Allow the user of Common Table Expressions in the SQL that defaults
ticket reports.
* Break out the components (css, footer, and header) for the
various built-in skins into separate files in the source tree.
| < | | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 |
* Added the [/mimetype_list] page.
* Added the [/hash-collisions] page.
* Allow the user of Common Table Expressions in the SQL that defaults
ticket reports.
* Break out the components (css, footer, and header) for the
various built-in skins into separate files in the source tree.
<h2 id='v1_30'>Changes For Version 1.30 (2015-01-19)</h2>
* Added the [/help?cmd=bundle|fossil bundle] command.
* Added the [/help?cmd=purge|fossil purge] command.
* Added the [/help?cmd=publish|fossil publish] command.
* Added the [/help?cmd=unpublished|fossil unpublished] command.
* Enhance the [/tree] webpage to show the age of each file with the option
to sort by age.
* Enhance the [/brlist] webpage to show additional information about each branch
|
| ︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 |
diff option in a separate file for easier editing.
* (Internal:) Implement a system of compile-time checks to help ensure
the correctness of printf-style formatting strings.
* Fix CVE-2014-3566, also known as the POODLE SSL 3.0 vulnerability.
* Numerous documentation fixes and improvements.
* Other obscure and minor bug fixes - see the timeline for details.
| < | | 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 |
diff option in a separate file for easier editing.
* (Internal:) Implement a system of compile-time checks to help ensure
the correctness of printf-style formatting strings.
* Fix CVE-2014-3566, also known as the POODLE SSL 3.0 vulnerability.
* Numerous documentation fixes and improvements.
* Other obscure and minor bug fixes - see the timeline for details.
<h2 id='v1_29'>Changes For Version 1.29 (2014-06-12)</h2>
* Add the ability to display content, diffs and annotations for UTF16
text files in the web interface.
* Add the "SaveAs..." and "Invert" buttons
to the graphical diff display that results
from using the --tk option with the [/help/diff | fossil diff] command.
* The [/reports] page now requires Read ("o") permissions. The "byweek"
report now properly propagates the selected year through the event type
|
| ︙ | ︙ |
Changes to www/chat.md.
1 2 3 4 5 6 7 8 | # Fossil Chat ## Introduction As of version 2.14, Fossil supports a developer chatroom feature. The chatroom provides an ephemeral discussion venue for insiders. Design goals include: | | > | | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Fossil Chat
## Introduction
As of version 2.14,
Fossil supports a developer chatroom feature. The chatroom provides an
ephemeral discussion venue for insiders. Design goals include:
* **Simple but functional** →
Fossil chat is designed to provide a convenient real-time
communication mechanism for geographically dispersed developers.
Fossil chat is *not* intended as a replacement or competitor for
IRC, Slack, Discord, Telegram, Google Hangouts, etc.
* **Low administration** →
You can activate the chatroom in seconds without having to
mess with configuration files or install new software.
In an existing [server setup](./server/),
simply enable the [C capability](/setup_ucap_list) for users
whom you want to give access to the chatroom.
|
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | There are also some settings under /Admin/Chat that control the behavior of chat, though the default settings are reasonable so in most cases those settings can be ignored. The settings control things like the amount of time that chat messages are retained before being purged from the repository database. | | | < | < < | < < < < < < | < < | 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 | There are also some settings under /Admin/Chat that control the behavior of chat, though the default settings are reasonable so in most cases those settings can be ignored. The settings control things like the amount of time that chat messages are retained before being purged from the repository database. ## <a id="usage"></a>Usage For users with appropriate permissions, simply browse to the [/chat](/help?cmd=/chat) to start up a chat session. The default skin includes a "Chat" entry on the menu bar on wide screens for people with chat privilege. There is also a "Chat" option on the [Sitemap page](/sitemap), which means that chat will appear as an option under the hamburger menu for many [skins](./customskin.md). As of version 2.17, chat messages are subject to [fossil's full range of markdown processing](/md_rules). Because chat messages are stored as-is when they arrive from a client, this change applies retroactively to messages stored by previous fossil versions. Files may be sent via chat using the file selection element at the bottom of the page. If the desktop environment system supports it, files may be dragged and dropped onto that element. Files are not automatically sent - selection of a file can be cancelled using the Cancel button which appears only when a file is selected. When the Send button is pressed, any pending text is submitted along with the |
| ︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 | at the top of the message and clicking the button which appears. Such deletions are local-only, and the messages will reappear if the page is reloaded. The user who posted a given message, or any Admin users, may additionally choose to globally delete a message from the chat record, which deletes it not only from their own browser but also propagates the removal to all connected clients the next time they poll for new messages. ## Implementation Details *You do not need to understand how Fossil chat works in order to use it. But many developers prefer to know how their tools work. This section is provided for the benefit of those curious developers.* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
at the top of the message and clicking the button which appears. Such
deletions are local-only, and the messages will reappear if the page
is reloaded. The user who posted a given message, or any Admin users,
may additionally choose to globally delete a message from the chat
record, which deletes it not only from their own browser but also
propagates the removal to all connected clients the next time they
poll for new messages.
### <a id='notifications'></a>Customizing New-message Notification Sounds
By default, the list of new-message notification sounds is limited to
a few built in to the fossil binary. In addition, any
[unversioned files](./unvers.wiki) named `alert-sounds/*.{mp3,wav,ogg}`
will be included in that list. To switch sounds, tap the "settings"
button.
### <a id='connection'></a> Who's Online?
Because the chat app has to be able to work over transient CGI-based
connections, as opposed to a stable socket connection to the server,
real-time tracking of "who's online" is not feasible. As of version
2.17, chat offers an optional feature, toggleable in the settings,
which can list users who have posted messages in the client's current
list of loaded messages. This is not the same thing as tracking who's
online, but it gives an overview of which users have been active most
recently, noting that "lurkers" (people who post no messages) will not
show up in that list, nor does the chat infrastructure have a way to
track and present those. That list can be used to filter messages on a
specific user by tapping on that user's name, tapping a second time to
remove the filter.
Sidebar: message deletion is a type of message and deletions count
towards updates in the recent activity list (counted for the person
who performed the deletion, not the author of the deleted
comment). That can potentially lead to odd corner cases where a user
shows up in the list but has no messages which are currently visible
because they were deleted, or an admin user who has not posted
anything but deleted a message. That is a known minor cosmetic-only
bug with a resolution of "will not fix."
## Implementation Details
*You do not need to understand how Fossil chat works in order to use it.
But many developers prefer to know how their tools work.
This section is provided for the benefit of those curious developers.*
|
| ︙ | ︙ |
Changes to www/concepts.wiki.
| ︙ | ︙ | |||
94 95 96 97 98 99 100 | is a duplicate of a remote repository. Communication between repositories is via HTTP. Remote repositories are identified by URL. You can also point a web browser at a repository and get human-readable status, history, and tracking information about the project. | | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | is a duplicate of a remote repository. Communication between repositories is via HTTP. Remote repositories are identified by URL. You can also point a web browser at a repository and get human-readable status, history, and tracking information about the project. <h3 id="artifacts">2.1 Identification Of Artifacts</h3> A particular version of a particular file is called an "artifact". Each artifact has a universally unique name which is the <a href="http://en.wikipedia.org/wiki/SHA1">SHA1</a> or <a href="http://en.wikipedia.org/wiki/SHA3">SHA3-256</a> hash of the content of that file expressed as either 40 or 64 characters of lower-case hexadecimal. (See the [./hashpolicy.wiki|hash policy |
| ︙ | ︙ | |||
180 181 182 183 184 185 186 | manifest also contains a check-in comment, the date and time when the check-in was established, who created the check-in, and links to other check-ins from which the current check-in is derived. There is also a couple of checksums used to verify the integrity of the check-in. And the whole manifest might be PGP clearsigned.</p> | < | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
manifest also contains a check-in comment, the date and time
when the check-in was established, who created the check-in,
and links to other check-ins from which the current check-in
is derived. There is also a couple of checksums used to verify
the integrity of the check-in. And the whole manifest might
be PGP clearsigned.</p>
<h3 id="keyconc">2.3 Key concepts</h3>
<ul>
<li>A <b>check-in</b> is a set of files arranged
in a hierarchy.</li>
<li>A <b>repository</b> keeps a record of historical check-ins.</li>
<li>Repositories share their changes using <b>push</b>, <b>pull</b>,
<b>sync</b>, and <b>clone</b>.</li>
|
| ︙ | ︙ | |||
245 246 247 248 249 250 251 | fossil help </b></blockquote> In the next section, when we say things like "use the <b>help</b> command" we mean to use the command name "help" as the first token after the name of the Fossil executable, as shown above. | < | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
fossil help
</b></blockquote>
In the next section, when we say things like "use the <b>help</b>
command" we mean to use the command name "help" as the first
token after the name of the Fossil executable, as shown above.
<h2 id="workflow">4.0 Workflow</h2>
<verbatim type="pikchr float-right">
down
R1: cylinder "Remote" "Repository" fill 0xadd8e6 rad 70%
move 150%
R2: cylinder same "Local" "Repository" fill 0x90ee90
move 120%
|
| ︙ | ︙ |
Added www/css/diff.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
Notes On Diff Formatting
========================
There are two main kinds of diff display for the web interface:
unified and side-by-side. Both displays are implemented using
a <table>. The unified diff is a 4-column table, and the
side-by-side diff is a 5-column table. In a page like /info that
might show multiple file diffs, each file diff is in a separate
<table>. For side-by-side diffs, a small amount of Javascript
code is used to resize the text columns so that they fill the screen
width and to keep horizontal scrollbars in sync.
For the unified diff, the basic structure
is like this:
> ~~~~
<table class='diff udiff'>
<tr>
<td class='diffln difflnl'><pre>
Line numbers for the left-hand file
</pre></td>
<td class='diffln difflnr'><pre>
Line numbers for the right-hand file
</pre></td>
<td class='diffsep'><pre>
Change marks. "+" or "=" or nothing
</pre></td>
<td class='difftxt difftxtu'><pre>
The text
</pre></td>
</tr>
</table>
~~~~
The structure for a side-by-side diff follows the
same basic pattern, though with 5 columns instead of
4, and slightly different class names:
> ~~~~
<table class='diff splitdiff'>
<tr>
<td class='diffln difflnl'><pre>
Line numbers for the left-hand file
</pre></td>
<td class='difftxt difftxtl'><pre>
The text for the left side
</pre></td>
<td class='diffsep'><pre>
Change marks. "+" or "=" or nothing
</pre></td>
<td class='diffln difflnr'><pre>
Line numbers for the right-hand file
</pre></td>
<td class='difftxt difftxtr'><pre>
The text on the right-hand side
</pre></td>
</tr>
</table>
~~~~
The outer <table> always has class "diff". The "diffu" class
is added for unified diffs and the "splitdiff" class is added for
side-by-side diffs.
All line-number columns have the "diffln" class. They also always
have one of "difflnl" or "difflnr" depending on whether they hold
line numbers for the left or right files, respectively.
Text is always kept in a separate column so that it can be scraped
and copied by the user. All text columns have the "difftxt" class.
One additional class "difftxtu", "difftxtl", or "difftxtr" is added
depending on if the text is for a unified diff, the left column of
a side-by-side diff, or the right column of a side-by-side diff.
The content of all columns is a single <pre> that contains the
appropriate diff-text for that column. Scrolling is done on the
<pre> element.
Within text columns, highlighting is done with <del> and
<ins> markup. All text on a line that contains an isert or
delete is surrounded by <ins>...</ins> or
<del>..</del>. Within that line, specific characters
of text that specifically inserted deleted have an additional
layer of <ins> or <del> markup. Thus CSS like the
following is appropriate:
> ~~~~
td.difftxt ins {
background-color: #dafbe1; /* Light green for the whole line */
text-decoration: none;
}
td.difftxt ins > ins {
background-color: #a0e4b2; /* Dark green for specific characters that change */
text-decoration: none;
}
~~~~
In a side-by-side diff, if an interior <ins> or <del> that mark
specific characters that change correspond to a delete/insert on the other
side, they they have the "edit" class tag. (ex: <ins class='edit'>
or <del class='edit'>). Some skins choose to paint these "modified"
regions blue:
> ~~~~
td.difftxt ins > ins.edit {
background-color: #c0c0ff; /* Blue for "modified" text region */
text-decoration: none;
}
~~~~
Line number text also has <ins> and <del> tags for lines which
are pure insert or pure delete. But the tags do not nest for line numbers.
|
Added www/css/index.md.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | Cascading Style Sheet Notes =========================== This is a collection of technical notes that document the design of the Document Object Model (DOM) and corresponding Cascading Style Sheet (CSS) attributes used for customing the look-and-feel of Fossil. These notes are of interest to people who want to customize the Fossil skin or enhance the internal display logic. This is a collection of documents that we hope will grow over time. * [Diff styling](./diff.md) |
Changes to www/custom_ticket.wiki.
| ︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 | </tr> <th1>enable_output 1</th1> </pre> This bit of code will get rid of the "email" field entry for logged-in users. Since we know the user's information, we don't have to ask for it. NOTE: it might be good to automatically scoop up the user's email and put it here. </p> </blockquote> <h2>Modify the 'view ticket' page</h2><blockquote> <p> Look for the text "Contact:" (about halfway through). Then insert these lines after the closing tr tag and before the "enable_output" line: <pre> | > > > > > > > | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | </tr> <th1>enable_output 1</th1> </pre> This bit of code will get rid of the "email" field entry for logged-in users. Since we know the user's information, we don't have to ask for it. NOTE: it might be good to automatically scoop up the user's email and put it here. </p> <p> You might also want to enable people to actually assign the ticket to a specific person during creation. For this to work, you need to add the code for "assigned_to" as shown below under the heading "Modify the 'edit ticket' page". This will give you an additional combobox where you can choose a person during ticket creation. </p> </blockquote> <h2>Modify the 'view ticket' page</h2><blockquote> <p> Look for the text "Contact:" (about halfway through). Then insert these lines after the closing tr tag and before the "enable_output" line: <pre> |
| ︙ | ︙ | |||
93 94 95 96 97 98 99 | <p> Before the "Severity:" line, add this: <pre> <tr><td align="right">Assigned to:</td><td> <th1>combobox assigned_to $assigned_choices 1</th1> </td></tr> </pre> | | > > > > > > > | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | <p> Before the "Severity:" line, add this: <pre> <tr><td align="right">Assigned to:</td><td> <th1>combobox assigned_to $assigned_choices 1</th1> </td></tr> </pre> That will give you a drop-down list of assignees. The first argument to the TH1 command 'combobox' is the database field which the combobox is associated to. The next argument is the list of choices you want to show in the combobox (and that you specified in the second step above. The last argument should be 1 for a true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for details).</p> <p>Now, similar to the previous section, look for "Contact:" and add this: <pre> <tr><td align="right">Reported by:</td><td> <input type="text" name="opened_by" size="40" value="$<opened_by>"> </td></tr> </pre> |
| ︙ | ︙ |
Changes to www/customgraph.md.
1 2 3 4 5 6 7 8 9 10 11 | # Customizing the Timeline Graph Beginning with version 1.33, Fossil gives users and skin authors significantly more control over the look and feel of the timeline graph. ## <a id="basic-style"></a>Basic Style Options Fossil includes several options for changing the graph's style without having to delve into CSS. These can be found in the details.txt file of your skin or under Admin/Skins/Details in the web UI. | | | | | | 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 |
# Customizing the Timeline Graph
Beginning with version 1.33, Fossil gives users and skin authors significantly
more control over the look and feel of the timeline graph.
## <a id="basic-style"></a>Basic Style Options
Fossil includes several options for changing the graph's style without having
to delve into CSS. These can be found in the details.txt file of your skin or
under Admin/Skins/Details in the web UI.
* ### `timeline-arrowheads`
Set this to `0` to hide arrowheads on primary child lines.
* ### `timeline-circle-nodes`
Set this to `1` to make check-in nodes circular instead of square.
* ### `timeline-color-graph-lines`
Set this to `1` to colorize primary child lines.
* ### `white-foreground`
Set this to `1` if your skin uses white (or any light color) text.
This tells Fossil to generate darker background colors for branches.
## <a id="adv-style"></a>Advanced Styling
|
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | latter, less obvious type. ## <a id="pos-elems"></a>Positioning Elements These elements aren't intended to be seen. They're only used to help position the graph and its visible elements. | | | | | | | | | 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 |
latter, less obvious type.
## <a id="pos-elems"></a>Positioning Elements
These elements aren't intended to be seen. They're only used to help position
the graph and its visible elements.
* ### <a id="tl-canvas"></a>`.tl-canvas`
Set the left and right margins on this class to give the desired amount
of space between the graph and its adjacent columns in the timeline.
#### Additional Classes
* `.sel`: See [`.tl-node`](#tl-node) for more information.
* ### <a id="tl-rail"></a>`.tl-rail`
Think of rails as invisible vertical lines on which check-in nodes are
placed. The more simultaneous branches in a graph, the more rails required
to draw it. Setting the `width` property on this class determines the
maximum spacing between rails. This spacing is automatically reduced as
the number of rails increases. If you change the `width` of `.tl-node`
elements, you'll probably need to change this value, too.
* ### <a id="tl-mergeoffset"></a>`.tl-mergeoffset`
A merge line often runs vertically right beside a primary child line. This
class's `width` property specifies the maximum spacing between the two.
Setting this value to `0` will eliminate the vertical merge lines.
Instead, the merge arrow will extend directly off the primary child line.
As with rail spacing, this is also adjusted automatically as needed.
* ### <a id="tl-nodemark"></a>`.tl-nodemark`
In the timeline table, the second cell in each check-in row contains an
invisible div with this class. These divs are used to determine the
vertical position of the nodes. By setting the `margin-top` property,
you can adjust this position.
## <a id="vis-elems"></a>Visible Elements
These are the elements you can actually see on the timeline graph: the nodes,
arrows, and lines. Each of these elements may also have additional classes
attached to them, depending on their context.
* ### <a id="tl-node"></a>`.tl-node`
A node exists for each check-in in the timeline.
#### Additional Classes
* `.leaf`: Specifies that the check-in is a leaf (i.e. that it has no
children in the same branch).
* `.merge`: Specifies that the check-in contains a merge.
* `.sel`: When the user clicks a node to designate it as the beginning
of a diff, this class is added to both the node itself and the
[`.tl-canvas`](#tl-canvas) element. The class is removed from both
elements when the node is clicked again.
* ### <a id="tl-arrow"></a>`.tl-arrow`
Arrows point from parent nodes to their children. Technically, this
class is just for the arrowhead. The rest of the arrow is composed
of [`.tl-line`](#tl-line) elements.
There are six additional classes that are used to distinguish the different
types of arrows. However, only these combinations are valid:
* `.u`: Up arrow that points to a child from its primary parent.
* `.u.sm`: Smaller up arrow, used when there is limited space between
parent and child nodes.
* `.merge.l` or `.merge.r`: Merge arrow pointing either to the left or
right.
* `.warp`: A timewarped arrow (always points to the right), used when a
misconfigured clock makes a check-in appear to have occurred before its
parent ([example](https://www.sqlite.org/src/timeline?c=2010-09-29&nd)).
* ### <a id="tl-line"></a>`.tl-line`
Along with arrows, lines connect parent and child nodes. Line thickness is
determined by the `width` property, regardless of whether the line is
horizontal or vertical. You can also use borders to create special line
styles. Here's a CSS snippet for making dotted merge lines:
.tl-line.merge {
|
| ︙ | ︙ |
Changes to www/customskin.md.
1 2 3 4 5 6 7 | # Skinning the Fossil Web Interface The Fossil web interface comes with a pre-configured look and feel. The default look and feel works fine in many situations. However, you may want to change the look and feel (the "skin") of Fossil to better suite your own individual tastes. This document provides background information to aid you in that task. | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Skinning the Fossil Web Interface The Fossil web interface comes with a pre-configured look and feel. The default look and feel works fine in many situations. However, you may want to change the look and feel (the "skin") of Fossil to better suite your own individual tastes. This document provides background information to aid you in that task. ## <a id="builtin"></a>Built-in Skins Fossil comes with [multiple built-in skins](/skins). If the default skin does not suite your tastes, perhaps one of the other built-in skins will work better. If nothing else, the built-in skins can serve as examples or templates that you can use to develop your own custom skin. The sources to these built-ins can |
| ︙ | ︙ | |||
23 24 25 26 27 28 29 | * footer.txt * header.txt * js.txt Try out the built-in skins by using the --skin option on the [fossil ui](/help?cmd=ui) or [fossil server](/help?cmd=server) commands. | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | * footer.txt * header.txt * js.txt Try out the built-in skins by using the --skin option on the [fossil ui](/help?cmd=ui) or [fossil server](/help?cmd=server) commands. ## <a id="sharing"></a>Sharing Skins The skin of a repository is not part of the versioned state and does not "push" or "pull" like checked-in files. The skin is local to the repository. However, skins can be shared between repositories using the [fossil config](/help?cmd=configuration) command. The "fossil config push skin" command will send the local skin to a remote repository and the "fossil config pull skin" command will import a skin |
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
Finally, Fossil always adds its own footer (unless overridden)
to close out the generated HTML:
</body>
</html>
| | | | 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 |
Finally, Fossil always adds its own footer (unless overridden)
to close out the generated HTML:
</body>
</html>
## <a id="mainmenu"></a>Changing the Main Menu Contents
As of Fossil 2.15, the actual text content of the skin’s main menu is no
longer part of the skin proper if you’re using one of the stock skins.
If you look at the Header section of the skin, you’ll find a
`<div class="mainmenu">` element whose contents are set by a short
[TH1](./th1.md) script from the contents of the **Main Menu** section of
the Setup → Configuration screen.
This feature allows the main menu contents to stay the same across
different skins, so you no longer have to reapply menu customizations
when trying different skins.
See the [`capexpr`](./th1.md#capexpr) section of the TH1 docs for help
on interpreting the default contents of this block.
## <a id="override"></a>Overriding the HTML Header and Footer
Notice that the `<html>`, `<head>`, and opening `<body>`
elements at the beginning of the document,
and the closing `</body>` and `</html>` elements at the end are automatically
generated by Fossil. This is recommended.
However, for maximum design flexibility, Fossil allows those elements to be
|
| ︙ | ︙ | |||
314 315 316 317 318 319 320 | put your web browser into developer mode and disable the cache. If you fail to do this, then you might make some change to your skin under development and press "Reload" only to find that the display did not change. After you have finished work your skin, the caches should synchronize with your new design and you can reactivate your web browser's cache and take it out of developer mode. | | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | put your web browser into developer mode and disable the cache. If you fail to do this, then you might make some change to your skin under development and press "Reload" only to find that the display did not change. After you have finished work your skin, the caches should synchronize with your new design and you can reactivate your web browser's cache and take it out of developer mode. ## <a id="headfoot"></a>Header and Footer Processing The `header.txt` and `footer.txt` control files of a skin are the HTML text of the Content Header and Content Footer, except that before being inserted into the output stream, the text is run through a [TH1 interpreter](./th1.md) that might adjust the text as follows: * All text within <th1>...</th1> is omitted from the |
| ︙ | ︙ | |||
346 347 348 349 350 351 352 | As you can see, two TH1 variable substitutions were done. The same TH1 interpreter is used for both the header and the footer and for all scripts contained within them both. Hence, any global TH1 variables that are set by the header are available to the footer. | | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | As you can see, two TH1 variable substitutions were done. The same TH1 interpreter is used for both the header and the footer and for all scripts contained within them both. Hence, any global TH1 variables that are set by the header are available to the footer. ## <a id="menu"></a>Customizing the ≡ Hamburger Menu The menu bar of the default skin has an entry to open a drop-down menu with additional navigation links, represented by the ≡ button (hence the name "hamburger menu"). The Javascript logic to open and close the hamburger menu when the button is clicked is usually handled by a script named "hbmenu.js" that is one of the [built-in resource files](/test-builtin-files) that are part of Fossil. |
| ︙ | ︙ | |||
410 411 412 413 414 415 416 | The custom `data-anim-ms` attribute can be added to the panel element to direct the Javascript logic to override the default menu animation duration of 400 ms. A faster animation duration of 80-200 ms may be preferred for smaller menus. The animation is disabled by setting the attribute to `"0"`. | | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
The custom `data-anim-ms` attribute can be added to the panel element to direct
the Javascript logic to override the default menu animation duration of 400 ms.
A faster animation duration of 80-200 ms may be preferred for smaller menus. The
animation is disabled by setting the attribute to `"0"`.
## <a id="vars"></a>TH1 Variables
Before expanding the TH1 within the header and footer, Fossil first
initializes a number of TH1 variables to values that depend on
repository settings and the specific page being generated.
* **project_name** - The project_name variable is filled with the
name of the project as configured under the Admin/Configuration
|
| ︙ | ︙ | |||
487 488 489 490 491 492 493 | All of the above are variables in the sense that either the header or the footer is free to change or erase them. But they should probably be treated as constants. New predefined values are likely to be added in future releases of Fossil. | | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 |
All of the above are variables in the sense that either the header or the
footer is free to change or erase them. But they should probably be treated
as constants. New predefined values are likely to be added in future
releases of Fossil.
## <a id="procedure"></a>Suggested Skin Customization Procedure
Developers are free, of course, to develop new skins using any method they
want, but the following is a technique that has worked well in the past and
can serve as a starting point for future work:
1. Select a built-in skin that is closest to the desired look. Make
copies of the css, footer, and header into files name "css.txt",
|
| ︙ | ︙ |
Changes to www/defcsp.md.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
<pre>
default-src *;
</pre>
The following sections detail the maining of the default CSP setting.
| | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<pre>
default-src *;
</pre>
The following sections detail the maining of the default CSP setting.
### <a id="base"></a> default-src 'self' data:
This policy means mixed-origin content isn’t allowed, so you can’t refer
to resources on other web domains. Browsers will ignore a link like the
one in the following Markdown under our default CSP:

|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 | There are many other cases, [covered below](#serving). [b64]: https://en.wikipedia.org/wiki/Base64 [svr]: ./server/ | | | | | 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 |
There are many other cases, [covered below](#serving).
[b64]: https://en.wikipedia.org/wiki/Base64
[svr]: ./server/
### <a id="img"></a> img-src * data:
As of Fossil 2.15, we don’t restrict the source of inline images at all.
You can pull them in from remote systems as well as pull them from
within the Fossil repository itself, or use `data:` URIs.
If you are certain all images come from only within the repository, you
can close off certain risks — tracking pixels, broken image format
decoders, system dialog box spoofing, etc. — by changing this to
“`img-src 'self'`” possibly followed by “`data:`” if you will also use
`data:` URIs.
### <a id="style"></a> style-src 'self' 'unsafe-inline'
This policy allows CSS information to come from separate files hosted
under the Fossil repo server’s Internet domain. It also allows inline CSS
`<style>` tags within the document text.
The `'unsafe-inline'` declaration allows CSS within individual HTML
elements:
<p style="margin-left: 4em">Indented text.</p>
As the "`unsafe-`" prefix on the name implies, the `'unsafe-inline'`
feature is suboptimal for security. However, there are
a few places in the Fossil-generated HTML that benefit from this
flexibility and the work-arounds are verbose and difficult to maintain.
Furthermore, the harm that can be done with style injections is far
less than the harm possible with injected javascript. And so the
`'unsafe-inline'` compromise is accepted for now, though it might
go away in some future release of Fossil.
### <a id="script"></a> script-src 'self' 'nonce-%s'
This policy disables in-line JavaScript and only allows `<script>`
elements if the `<script>` includes a `nonce` attribute that matches the
one declared by the CSP. That nonce is a large random number, unique for
each HTTP page generated by Fossil, so an attacker cannot guess the
value, so the browser will ignore an attacker’s injected JavaScript.
|
| ︙ | ︙ | |||
152 153 154 155 156 157 158 |
can only be installed by the Fossil server’s system administrator,
this path is also considered safe.
[ext]: ./serverext.wiki
[su]: ./caps/admin-v-setup.md#apsu
| | | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
can only be installed by the Fossil server’s system administrator,
this path is also considered safe.
[ext]: ./serverext.wiki
[su]: ./caps/admin-v-setup.md#apsu
#### <a id="xss"></a>Cross-Site Scripting via Ordinary User Capabilities
We’re so restrictive about how we treat JavaScript because it can lead
to difficult-to-avoid scripting attacks. If we used the same CSP for
`<script>` tags [as for `<style>` tags](#style), anyone with check-in
rights on your repository could add a JavaScript file to your repository
and then refer to it from other content added to the site. Since
JavaScript code can access any data from any URI served under its same
|
| ︙ | ︙ | |||
212 213 214 215 216 217 218 | through check-ins. [ed]: ./embeddeddoc.wiki [edtf]: ./embeddeddoc.wiki#th1 [hfed]: ./embeddeddoc.wiki#html | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
through check-ins.
[ed]: ./embeddeddoc.wiki
[edtf]: ./embeddeddoc.wiki#th1
[hfed]: ./embeddeddoc.wiki#html
## <a id="serving"></a>Serving Files Within the Limits
There are several ways to serve files within the above restrictions,
avoiding the need to [override the default CSP](#override). In
decreasing order of simplicity and preference:
1. Within [embedded documentation][ed] (only!) you can refer to files
stored in the repo using document-relative file URLs:
|
| ︙ | ︙ | |||
303 304 305 306 307 308 309 | [tkt]: ./tickets.wiki [tn]: ./event.wiki [uu]: /help?cmd=/uv [uv]: ./unvers.wiki [wiki]: ./wikitheory.wiki | | | | 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 | [tkt]: ./tickets.wiki [tn]: ./event.wiki [uu]: /help?cmd=/uv [uv]: ./unvers.wiki [wiki]: ./wikitheory.wiki ## <a id="override"></a>Overriding the Default CSP If you wish to relax the default CSP’s restrictions or to tighten them further, there are multiple ways to accomplish that. The following methods are listed in top-down order to give the simplest and most straightforward method first. Further methods dig down deeper into the stack, which is helpful to understand even if you end up using a higher-level method. ### <a id="cspsetting"></a>The `default-csp` Setting If the [`default-csp` setting](/help?cmd=default-csp) is defined and is not an empty string, its value is injected into the page using [TH1](./th1.md) via one or more of the methods below, depending on the skin you’re using and local configuration. Changing this setting is the easiest way to set a nonstandard CSP on |
| ︙ | ︙ | |||
353 354 355 356 357 358 359 |
2. For more complicated CSPs, the quoting rules for your shell and the
CSP syntax may interact, making it difficult or impossible to set
your desired CSP via the command line. Setting it via the web UI
doesn’t have this problem.
| | | | | 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 |
2. For more complicated CSPs, the quoting rules for your shell and the
CSP syntax may interact, making it difficult or impossible to set
your desired CSP via the command line. Setting it via the web UI
doesn’t have this problem.
### <a id="th1"></a>TH1 Setup Hook
Fossil sets [the TH1 variable `$default_csp`][thvar] from the
`default-csp` setting and uses *that* to inject the value into generated
HTML pages in its stock configuration.
This means that another way you can override this value is to use
the [`th1-setup` hook script](./th1-hooks.md), which runs before TH1
processing happens during skin processing:
$ fossil set th1-setup "set default_csp {default-src 'self'}"
After [the above](#admin-ui), this is the cleanest method.
[thvar]: ./customskin.md#vars
### <a id="csrc"></a>Fossil C Source Code
When you do neither of the above things, Fossil uses
[a hard-coded default](/info?ln=527-530&name=65a555d0d4fb846b).
We tell you about this not to suggest that you hack the Fossil C source
code to change the CSP but simply to document the next step before we
move down-stack.
### <a id="header"></a>Skin Header
[In the normal case](./customskin.md#override), Fossil injects the CSP
retrieved by one of the above methods into the header of all HTML
documents it generates:
```HTML
<head>...
|
| ︙ | ︙ | |||
443 444 445 446 447 448 449 | `$default_csp` variable like the Bootstrap skin does so you can use one of the methods above with your custom skin, so the CSP can vary independently of the skin. [dcinj]: /info?ln=7&name=bef080a6929a3e6f | | | 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 | `$default_csp` variable like the Bootstrap skin does so you can use one of the methods above with your custom skin, so the CSP can vary independently of the skin. [dcinj]: /info?ln=7&name=bef080a6929a3e6f ### <a id="fep"></a>Front-End Proxy If your Fossil repo is behind some sort of HTTP [front-end proxy][svr], the [preferred method][pmcsp] for setting the CSP is via a custom HTTP header, which most HTTP reverse proxy programs allow. Beware that if you have a CSP set via both the HTTP and HTML headers that the two CSPs [merge](https://stackoverflow.com/a/51153816/142454), |
| ︙ | ︙ |
Changes to www/delta_encoder_algorithm.wiki.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 | companion specification titled "<a href="delta_format.wiki">Fossil Delta Format</a>". </p> <p>The algorithm is inspired by <a href="http://samba.anu.edu.au/rsync/">rsync</a>.</p> | | | | | 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 | companion specification titled "<a href="delta_format.wiki">Fossil Delta Format</a>". </p> <p>The algorithm is inspired by <a href="http://samba.anu.edu.au/rsync/">rsync</a>.</p> <h2 id="argresparam">1.0 Arguments, Results, and Parameters</h2> <p>The encoder takes two byte-sequences as input, the "original", and the "target", and returns a single byte-sequence containing the "delta" which transforms the original into the target upon its application.</p> <p>Note that the data of a "byte-sequence" includes its length, i.e. the number of bytes contained in the sequence.</p> <p>The algorithm has one parameter named "NHASH", the size of the "sliding window" for the "rolling hash", in bytes. These two terms are explained in the next section. The value of this parameter has to be a power of two for the algorithm to work. For Fossil the value of this parameter is set to "16".</p> <h2 id="operation">2.0 Operation</h2> <p>The algorithm is split into three phases which generate the <a href="delta_format.wiki#header">header</a>, <a href="delta_format.wiki#slist">segment list</a>, and <a href="delta_format.wiki#trailer">trailer</a> of the delta, per its general <a href="delta_format.wiki#structure">structure</a>.</p> <p>The two phases generating header and trailer are not covered here as their implementation trivially follows directly from the specification of the <a href="delta_format.wiki">delta format</a>.</p> <p>This leaves the segment-list. Its generation is done in two phases, a pre-processing step operating on the "original" byte-sequence, followed by the processing of the "target" byte-sequence using the information gathered by the first step.</p> <h3 id="preprocessing">2.1 Preprocessing the original</h3> <p>A major part of the processing of the "target" is to find a range in the "original" which contains the same content as found at the current location in the "target".</p> <p>A naive approach to this would be to search the whole "original" for such content. This however is very inefficient as it would search |
| ︙ | ︙ | |||
81 82 83 84 85 86 87 | computed. </li> <li>A hash table is filled, mapping from the hashes of the chunks to the list of chunk locations having this hash. </li> </ol> | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | computed. </li> <li>A hash table is filled, mapping from the hashes of the chunks to the list of chunk locations having this hash. </li> </ol> <h3 id="processing">2.1 Processing the target</h3> <p>This, the main phase of the encoder, processes the target in a loop from beginning to end. The state of the encoder is captured by two locations, the "base" and the "slide". "base" points to the first byte of the target for which no delta output has been generated yet, and "slide" is the location of the window used to look in the "origin" for commonalities. This window is NHASH bytes long.</p> |
| ︙ | ︙ | |||
192 193 194 195 196 197 198 | </p> <p>If the processing loop left bytes unencoded, i.e. "base" not exactly at the end of the "target", as is possible for both end conditions, then one last insert instruction is emitted to put these bytes into the delta.<p> | | | | | | 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 | </p> <p>If the processing loop left bytes unencoded, i.e. "base" not exactly at the end of the "target", as is possible for both end conditions, then one last insert instruction is emitted to put these bytes into the delta.<p> <h2 id="exceptions">3.0 Exceptions</h2> <p>If the "original" is at most NHASH bytes long no compression of changes is possible, and the segment-list of the delta consists of a single literal which contains the entire "target".</p> <p>This is actually equivalent to the second end condition of the processing loop described in the previous section, just checked before actually entering the loop.</p> <h2 id="rollhash">4.0 The rolling hash</h2> <p>The rolling hash described below and used to compute content signatures was chosen not only for good hashing properties, but also to enable the easy (incremental) recalculation of its value for a sliding window, i.e. where the oldest byte is removed from the window and a new byte is shifted in.<p> <h3 id="rhdef">4.1 Definition</h3> <p>Assuming an array Z of NHASH bytes (indexing starting at 0) the hash V is computed via</p> <p align=center><table><tr><td> <p><img src="encode1.gif" align="center"></p> <p><img src="encode2.gif" align="center"></p> <p><img src="encode3.gif" align="center"></p> </td></tr></table></p> where A and B are unsigned 16-bit integers (hence the <u>mod</u>), and V is a 32-bit unsigned integer with B as MSB, A as LSB. <h3 id="rhincr">4.2 Incremental recalculation</h3> <p>Assuming an array Z of NHASH bytes (indexing starting at 0) with hash V (and components A and B), the dropped byte <img src="encode4.gif" align="center">, and the new byte <img src="encode5.gif" align="center"> , the new hash can be computed incrementally via: </p> |
| ︙ | ︙ |
Changes to www/delta_format.wiki.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
Return the size of the output that would result from applying delta D.
* <b>delta_parse(</b><i>D</i>)</b> → This is a table-valued function
that returns one row for the header, for the trailer, and for each segment
in delta D.
| | | | | | 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 |
Return the size of the output that would result from applying delta D.
* <b>delta_parse(</b><i>D</i>)</b> → This is a table-valued function
that returns one row for the header, for the trailer, and for each segment
in delta D.
<h1 id="structure">2.0 Structure</h1>
<verbatim type="pikchr">
leftmargin = 0.1
box height 50% "Header"
box same "Segments"
box same "Trailer"
</verbatim>
<p>A delta consists of three parts, a "header", a "trailer", and a
"segment-list" between them.</p>
<p>Both header and trailer provide information about the target
helping the decoder, and the segment-list describes how the target can
be constructed from the original.</p>
<h2 id="header">2.1 Header</h2>
<verbatim type="pikchr">
leftmargin = 0.1
box height 50% "Size"
box same "\"\\n\""
</verbatim>
<p>The header consists of a single number followed by a newline
character (ASCII 0x0a). The number is the length of the target in
bytes.</p>
<p>This means that, given a delta, the decoder can compute the size of
the target (and allocate any necessary memory based on that) by simply
reading the first line of the delta and decoding the number found
there. In other words, before it has to decode everything else.</p>
<h2 id="trailer">2.2 Trailer</h2>
<verbatim type="pikchr">
leftmargin = 0.1
box height 50% "Checksum"
box same "\";\""
</verbatim>
<p>The trailer consists of a single number followed by a semicolon (ASCII
0x3b). This number is a checksum of the target and can be used by a
decoder to verify that the delta applied correctly, reconstructing the
target from the original.</p>
<p>The checksum is computed by treating the target as a series of
32-bit integer numbers (MSB first), and summing these up, modulo
2^32-1. A target whose length is not a multiple of 4 is padded with
0-bytes (ASCII 0x00) at the end.</p>
<p>By putting this information at the end of the delta a decoder has
it available immediately after the target has been reconstructed
fully.</p>
<h2 id="slist">2.3 Segment-List</h2>
<verbatim type="pikchr">
leftmargin = 0.1
PART1: [
B1: box height 50% width 15% ""
B2: box same ""
B3: box same ""
"***"
|
| ︙ | ︙ | |||
146 147 148 149 150 151 152 | compression takes place, by encoding the large common parts of original and target in small copy instructions.</p> <p>The target is constructed from beginning to end, with the data generated by each instruction appended after the data of all previous instructions, with no gaps.</p> | | | | | 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 |
compression takes place, by encoding the large common parts of
original and target in small copy instructions.</p>
<p>The target is constructed from beginning to end, with the data
generated by each instruction appended after the data of all previous
instructions, with no gaps.</p>
<h3 id="insertlit">2.3.1 Insert Literal</h3>
<p>A literal is specified by two elements, the size of the literal in
bytes, and the bytes of the literal itself.</p>
<verbatim type="pikchr">
leftmargin = 0.1
box "Length" height 50%
box "\":\"" same
box "Bytes" same
</verbatim>
<p>The length is written first, followed by a colon character (ASCII
0x3a), followed by the bytes of the literal.</p>
<h3 id="copyrange">2.3.2 Copy Range</h3>
<p>A range to copy is specified by two numbers, the offset of the
first byte in the original to copy, and the size of the range, in
bytes. The size zero is special, its usage indicates that the range
extends to the end of the original.</p>
<verbatim type="pikchr">
leftmargin = 0.1
box "Length" height 50%
box "\"@\"" same
box "Offset" same
box "\",\"" same
</verbatim>
<p>The length is written first, followed by an "at" character (ASCII
0x40), then the offset, followed by a comma (ASCII 0x2c).</p>
<h1 id="intcoding">3.0 Encoding of integers</h1>
<p>
The format currently handles only 32 bit integer numbers. They are
written base-64 encoded, MSB first, and without leading
"0"-characters, except if they are significant (i.e. 0 => "0").
</p>
|
| ︙ | ︙ | |||
205 206 207 208 209 210 211 | the first character, followed by the next 6 bits, and so on until all non-zero bits of the integer are encoded. The minimum number of encoding characters is used. Note that for integers less than 10, the base-64 coding is a ASCII decimal rendering of the number itself. </p> | | | | | 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 | the first character, followed by the next 6 bits, and so on until all non-zero bits of the integer are encoded. The minimum number of encoding characters is used. Note that for integers less than 10, the base-64 coding is a ASCII decimal rendering of the number itself. </p> <h1 id="examples">4.0 Examples</h1> <h2 id="examplesint">4.1 Integer encoding</h2> <table border=1> <tr> <th>Value</th> <th>Encoding</th> </tr> <tr> <td>0</td> <td>0</td> </tr> <tr> <td>6246</td> <td>1Xb</td> </tr> <tr> <td>-1101438770</td> <td>2zMM3E</td> </tr> </table> <h2 id="examplesdelta">4.2 Delta encoding</h2> <p>An example of a delta using the specified encoding is:</p> <table border=1><tr><td><pre> 1Xb 4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre> </td></tr></table> |
| ︙ | ︙ | |||
304 305 306 307 308 309 310 | * Ticketing interface (expand this bullet) </pre></td></tr></table> | | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | * Ticketing interface (expand this bullet) </pre></td></tr></table> <h1 id="notes">Notes</h1> <ul> <li>Pure text files generate a pure text delta. </li> <li>Binary files generate a delta that may contain some binary data. </li> <li>The delta encoding does not attempt to compress the content. It was considered to be much more sensible to do compression using a separate general-purpose compression library, like <a href="http://www.zlib.net">zlib</a>. </li> </ul> |
Changes to www/embeddeddoc.wiki.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 | The <i><version></i> is the [./checkin_names.wiki|name of a check-in] that contains the embedded document. This might be a hash prefix for the check-in, or it might be the name of a branch or tag, or it might be a timestamp. See the prior link for more possibilities and examples. | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | The <i><version></i> is the [./checkin_names.wiki|name of a check-in] that contains the embedded document. This might be a hash prefix for the check-in, or it might be the name of a branch or tag, or it might be a timestamp. See the prior link for more possibilities and examples. The <i id="ckout"><version></i> can also be the special identifier "<b>ckout</b>". The "<b>ckout</b>" keywords means to pull the documentation file from the local source tree on disk, not from the any check-in. The "<b>ckout</b>" keyword only works when you start your server using the "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" commands. The "/doc/ckout" URL is intended to show a preview of |
| ︙ | ︙ | |||
96 97 98 99 100 101 102 | [/md_rules | Markdown markup language]. Documentation files ending in ".txt" are plain text. Wiki, markdown, and plain text documentation files are rendered with the standard fossil header and footer added. Most other mimetypes are delivered directly to the requesting web browser without interpretation, additions, or changes. | | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
[/md_rules | Markdown markup language].
Documentation files ending in ".txt" are plain text.
Wiki, markdown, and plain text documentation files
are rendered with the standard fossil header and footer added.
Most other mimetypes are delivered directly to the requesting
web browser without interpretation, additions, or changes.
<h2 id="html">1.1 HTML Rendering With Fossil Headers And Footers</h2>
Files with the mimetype "text/html" (the .html or .htm suffix) are
usually rendered directly to the browser without interpretation.
However, if the file begins with a <div> element like this:
<b><div class='fossil-doc' data-title='<i>Title Text</i>'></b>
Then the standard Fossil header and footer are added to the document
prior to being displayed. The "class='fossil-doc'" attribute is
|
| ︙ | ︙ |
Changes to www/env-opts.md.
| ︙ | ︙ | |||
415 416 417 418 419 420 421 | If the default VFS underneath SQLite is not suitable, an alternative can be selected with either the `--vfs VFSNAME` option or the `FOSSIL_VFS` environment variable. The `--vfs` option takes precedence. | | | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | If the default VFS underneath SQLite is not suitable, an alternative can be selected with either the `--vfs VFSNAME` option or the `FOSSIL_VFS` environment variable. The `--vfs` option takes precedence. ### <a id="temp"></a>Temporary File Location Fossil places some temporary files in the checkout directory. Most notably, supporting files related to merge conflicts are placed in the same folder as the merge result. Other temporary files need a different home. The rules for choosing one are complicated. |
| ︙ | ︙ |
Changes to www/event.wiki.
| ︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
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 technotes.
No project is required to use technotes. But technotes can help many projects
stay better organized and provide a better historical record of the
development progress.
<h2>Viewing Technotes</h2>
| > > > > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
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 technotes.
* <b>Signed Checksums</b>. Technotes containing cryptographically signed
checksums can be linked to repository artifacts, thereby creating a
traceable, auditable chain so that users can readily verify the integrity
and authenticity of project deliverables. And the command line interface
to technotes enables embedding such processes in scripts.
No project is required to use technotes. But technotes can help many projects
stay better organized and provide a better historical record of the
development progress.
<h2>Viewing Technotes</h2>
|
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using <b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline. Different technotes can have the same timestamp. The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote that appears in the timeline. | | > > > > > > > > > > > > > > > | 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 | the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using <b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline. Different technotes can have the same timestamp. The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote that appears in the timeline. To view all technotes, use the <b>wiki ls</b> command: <blockquote> <b> fossil wiki ls --technote --show-technote-ids<br> <tt>z739263a134bf0da1d28e939f4c4367f51ef4c51 2020-12-19 13:20:19</tt><br> <tt>e15a918a8bed71c2ac091d74dc397b8d3340d5e1 2018-09-22 17:40:10</tt><br> </b> </blockquote> A technote ID is the UUID of the technote. To view an individual technote, use the <b>wiki export</b> command: <blockquote> <b> fossil wiki export --technote version-2.16<br> Release Notes 2021-07-02 This note describes changes in the Fossil snapshot for ... </b> </blockquote> The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>. See the [/help?cmd=wiki | wiki help] for specifics. Users must have check-in privileges (permission "i") in order to create or edit technotes. In addition, users must have create-wiki privilege (permission "f") to create new technotes and edit-wiki privilege (permission "k") in order to edit existing technotes. Technote content may be formatted as [/wiki_rules | Fossil wiki], [/md_rules | Markdown], or a plain text. |
Changes to www/faq.tcl.
| ︙ | ︙ | |||
165 166 167 168 169 170 171 |
for {set i 1} {$i<$cnt} {incr i} {
puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>"
}
puts {</ol>}
puts {<hr>}
for {set i 1} {$i<$cnt} {incr i} {
| < | | 165 166 167 168 169 170 171 172 173 174 175 176 177 |
for {set i 1} {$i<$cnt} {incr i} {
puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>"
}
puts {</ol>}
puts {<hr>}
for {set i 1} {$i<$cnt} {incr i} {
puts "<p id=\"q$i\"><b>($i) [lindex $faq($i) 0]</b></p>\n"
set body [lindex $faq($i) 1]
regsub -all "\n *" [string trim $body] "\n" body
puts "<blockquote>$body</blockquote></li>\n"
}
puts {</ol>}
|
Changes to www/faq.wiki.
1 2 3 | <title>Fossil FAQ</title> <h1 align="center">Frequently Asked Questions</h1> | | < < < | | < < < < | < | | 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 | <title>Fossil FAQ</title> <h1 align="center">Frequently Asked Questions</h1> <p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. <ol> <li><a href="#q1">What GUIs are available for fossil?</a></li> <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> <li><a href="#q3">How do I create a new branch?</a></li> <li><a href="#q4">How do I tag a check-in?</a></li> <li><a href="#q5">How do I create a private branch that won't get pushed back to the main repository.</a></li> <li><a href="#q6">How can I delete inappropriate content from my fossil repository?</a></li> <li><a href="#q7">How do I make a clone of the fossil self-hosting repository?</a></li> <li><a href="#q8">How do I import or export content from and to other version control systems?</a></li> </ol> <hr> <p id="q1"><b>(1) What GUIs are available for fossil?</b></p> <blockquote>The fossil executable comes with a [./webui.wiki | web-based GUI] built in. Just run: <blockquote> <b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i> </blockquote> And your default web browser should pop up and automatically point to the fossil interface. (Hint: You can omit the <i>REPOSITORY-FILENAME</i> if you are within an open check-out.)</blockquote></li> <p id="q2"><b>(2) What is the difference between a "branch" and a "fork"?</b></p> <blockquote>This is a big question - too big to answer in a FAQ. Please read the <a href="branching.wiki">Branching, Forking, Merging, and Tagging</a> document.</blockquote></li> <p id="q3"><b>(3) How do I create a new branch?</b></p> <blockquote>There are lots of ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add the option "--branch <i>BRANCH-NAME</i>" to make the new check-in be the first check-in for a new branch. |
| ︙ | ︙ | |||
67 68 69 70 71 72 73 | the initial check-in of your branch on the timeline and click on its link so that you are on the <b>ci</b> page. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. On the "Edit Check-in" page, check the box beside "Branching:" and fill in the name of your new branch to the right and press the "Apply Changes" button.</blockquote></li> | < | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | the initial check-in of your branch on the timeline and click on its link so that you are on the <b>ci</b> page. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. On the "Edit Check-in" page, check the box beside "Branching:" and fill in the name of your new branch to the right and press the "Apply Changes" button.</blockquote></li> <p id="q4"><b>(4) How do I tag a check-in?</b></p> <blockquote>There are several ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add a tag to that check-in using the "--tag <i>TAGNAME</i>" command-line option. You can repeat the --tag option to give a check-in multiple tags. Tags need not be unique. So, |
| ︙ | ︙ | |||
96 97 98 99 100 101 102 | [./webui.wiki | web interface]. First locate the check-in that you what to tag on the timeline, then click on the link to go the detailed information page for that check-in. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. There are controls on the edit page that allow new tags to be added and existing tags to be removed.</blockquote></li> | < | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | [./webui.wiki | web interface]. First locate the check-in that you what to tag on the timeline, then click on the link to go the detailed information page for that check-in. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. There are controls on the edit page that allow new tags to be added and existing tags to be removed.</blockquote></li> <p id="q5"><b>(5) How do I create a private branch that won't get pushed back to the main repository.</b></p> <blockquote>Use the <b>--private</b> command-line option on the <b>commit</b> command. The result will be a check-in which exists on your local repository only and is never pushed to other repositories. All descendants of a private check-in are also private. |
| ︙ | ︙ | |||
121 122 123 124 125 126 127 | as if all the changes that occurred on your private branch occurred in a single check-in. Of course, you can also keep your branch private forever simply by not merging the changes in the private branch back into the trunk. [./private.wiki | Additional information]</blockquote></li> | < | < | < | | 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 | as if all the changes that occurred on your private branch occurred in a single check-in. Of course, you can also keep your branch private forever simply by not merging the changes in the private branch back into the trunk. [./private.wiki | Additional information]</blockquote></li> <p id="q6"><b>(6) How can I delete inappropriate content from my fossil repository?</b></p> <blockquote>See the article on [./shunning.wiki | "shunning"] for details.</blockquote></li> <p id="q7"><b>(7) How do I make a clone of the fossil self-hosting repository?</b></p> <blockquote>Any of the following commands should work: <blockquote><pre> fossil [/help/clone|clone] http://fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] http://www2.fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] http://www3.fossil-scm.org/site.cgi fossil.fossil </pre></blockquote> Once you have the repository cloned, you can open a local check-out as follows: <blockquote><pre> mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil </pre></blockquote> Thereafter you should be able to keep your local check-out up to date with the latest code in the public repository by typing: <blockquote><pre> fossil [/help/update|update] </pre></blockquote></blockquote></li> <p id="q8"><b>(8) How do I import or export content from and to other version control systems?</b></p> <blockquote>Please see [./inout.wiki | Import And Export]</blockquote></li> </ol> |
Changes to www/fileformat.wiki.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | different in separate repositories. The local state is not versioned and is not synchronized with the global state. The local state is not composed of artifacts and is not intended to be enduring. This document is concerned with global state only. Local state is only mentioned here in order to distinguish it from global state. | < | < | | 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 | different in separate repositories. The local state is not versioned and is not synchronized with the global state. The local state is not composed of artifacts and is not intended to be enduring. This document is concerned with global state only. Local state is only mentioned here in order to distinguish it from global state. <h2 id="names">1.0 Artifact Names</h2> Each artifact in the repository is named by a hash of its content. No prefixes, suffixes, or other information is added to an artifact before the hash is computed. The artifact name is just the (lower-case hexadecimal) hash of the raw artifact. Fossil currently computes artifact names using either SHA1 or SHA3-256. It is relatively easy to add new algorithms in the future, but there are no plans to do so at this time. When referring to artifacts in using tty commands or webpage URLs, it is sufficient to specify a unique prefix for the artifact name. If the input prefix is not unique, Fossil will show an error. Within a structural artifact, however, all references to other artifacts must be the complete hash. Prior to Fossil version 2.0, all names were formed from the SHA1 hash of the artifact. The key innovation in Fossil 2.0 was adding support for alternative hash algorithms. <h2 id="structural">2.0 Structural Artifacts</h2> A structural artifact is an artifact with a particular format that is used to define the relationships between other artifacts in the repository. Fossil recognizes the following kinds of structural artifacts: |
| ︙ | ︙ | |||
98 99 100 101 102 103 104 | 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. | < | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 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. <h3 id="manifest">2.1 The Manifest</h3> A manifest defines a check-in. A manifest contains a list of artifacts for each file in the project and the corresponding filenames, as well as information such as parent check-ins, the username of the programmer who created the check-in, the date and time when the check-in was created, and any check-in comments associated |
| ︙ | ︙ | |||
254 255 256 257 258 259 260 | clear-signing prefix. The <b>Z</b> card is a sanity check to prove that the manifest is well-formed and consistent. A sample manifest from Fossil itself can be seen [/artifact/28987096ac | here]. | < | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | clear-signing prefix. The <b>Z</b> card is a sanity check to prove that the manifest is well-formed and consistent. A sample manifest from Fossil itself can be seen [/artifact/28987096ac | here]. <h3 id="cluster">2.2 Clusters</h3> A cluster is an artifact that declares the existence of other artifacts. Clusters are used during repository synchronization to help reduce network traffic. As such, clusters are an optimization and may be removed from a repository without loss or damage to the underlying project code. |
| ︙ | ︙ | |||
280 281 282 283 284 285 286 | the <b>Z</b> card of a manifest. The argument to the <b>Z</b> card is the lower-case hexadecimal representation of the MD5 checksum of all prior cards in the cluster. The <b>Z</b> card is required. An example cluster from Fossil can be seen [/artifact/d03dbdd73a2a8 | here]. | < | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | the <b>Z</b> card of a manifest. The argument to the <b>Z</b> card is the lower-case hexadecimal representation of the MD5 checksum of all prior cards in the cluster. The <b>Z</b> card is required. An example cluster from Fossil can be seen [/artifact/d03dbdd73a2a8 | here]. <h3 id="ctrl">2.3 Control Artifacts</h3> Control artifacts are used to assign properties to other artifacts within the repository. Allowed cards in a control artifact are as follows: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> |
| ︙ | ︙ | |||
334 335 336 337 338 339 340 | The <b>U</b> card is the name of the user that created the control artifact. The <b>Z</b> card is the usual required artifact checksum. An example control artifact can be seen [/info/9d302ccda8 | here]. | < | | 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | The <b>U</b> card is the name of the user that created the control artifact. The <b>Z</b> card is the usual required artifact checksum. An example control artifact can be seen [/info/9d302ccda8 | here]. <h3 id="wikichng">2.4 Wiki Pages</h3> A wiki artifact defines a single version of a single wiki page. Wiki artifacts accept the following card types: <blockquote> |
| ︙ | ︙ | |||
378 379 380 381 382 383 384 | of user George Krivov and is not currently used or generated by the implementation. Older versions of Fossil will reject a wiki-page artifact that includes a <b>C</b> card. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. | < | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | of user George Krivov and is not currently used or generated by the implementation. Older versions of Fossil will reject a wiki-page artifact that includes a <b>C</b> card. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. <h3 id="tktchng">2.5 Ticket Changes</h3> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br /> |
| ︙ | ︙ | |||
424 425 426 427 428 429 430 | on the <b>J</b> card replaces any previous value of the field. The field name and value are both encoded using the character escapes defined for the <b>C</b> card of a manifest. An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. | < | | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | on the <b>J</b> card replaces any previous value of the field. The field name and value are both encoded using the character escapes defined for the <b>C</b> card of a manifest. An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <h3 id="attachment">2.6 Attachments</h3> An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page or technical note to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: |
| ︙ | ︙ | |||
466 467 468 469 470 471 472 | A single <b>U</b> card gives the name of the user who added the attachment. If an attachment is added anonymously, then the <b>U</b> card may be omitted. The <b>Z</b> card is the usual checksum over the rest of the attachment artifact. The <b>Z</b> card is required. | < | | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | A single <b>U</b> card gives the name of the user who added the attachment. If an attachment is added anonymously, then the <b>U</b> card may be omitted. The <b>Z</b> card is the usual checksum over the rest of the attachment artifact. The <b>Z</b> card is required. <h3 id="event">2.7 Technical Notes</h3> A technical note or "technote" artifact (formerly known as an "event" artifact) associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Technotes can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on an technote artifact: |
| ︙ | ︙ | |||
534 535 536 537 538 539 540 | A single <b>W</b> card provides wiki text for the document associated with the technote. The format of the <b>W</b> card is exactly the same as for a [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. | < | | 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 | A single <b>W</b> card provides wiki text for the document associated with the technote. The format of the <b>W</b> card is exactly the same as for a [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. <h3 id="forum">2.8 Forum Posts</h3> Forum posts are intended as a mechanism for users and developers to discuss a project. Forum posts are like messages on a mailing list. The following cards are allowed on an forum post artifact: <blockquote> |
| ︙ | ︙ | |||
609 610 611 612 613 614 615 | A single <b>W</b> card provides wiki text for the forum post. The format of the <b>W</b> card is exactly the same as for a [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. | < | | 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 | A single <b>W</b> card provides wiki text for the forum post. The format of the <b>W</b> card is exactly the same as for a [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. <h2 id="summary">3.0 Card Summary</h2> The following table summarizes the various kinds of cards that appear on Fossil artifacts. A blank entry means that combination of card and artifact is not legal. A number or range of numbers indicates the number of times a card may (or must) appear in the corresponding artifact type. e.g. a value of 1 indicates a required unique card and 1+ indicates that one or more such cards are required. |
| ︙ | ︙ | |||
885 886 887 888 889 890 891 | 4) [#forum | Forum Posts] must have either one H-card or one I-card, not both. 5) [#forum | Forum Post] P-cards may have only a single parent hash. i.e. they may not have merge parents. | < | < | | 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 | 4) [#forum | Forum Posts] must have either one H-card or one I-card, not both. 5) [#forum | Forum Post] P-cards may have only a single parent hash. i.e. they may not have merge parents. <h2 id="addenda">4.0 Addenda</h2> This section contains additional information which may be useful when implementing algorithms described above. <h3 id="outofordercards">4.1 Relaxed Card Ordering Due To An Historical Bug</h3> All cards of a structural artifact should be in lexicographical order. The Fossil implementation verifies this and rejects any structural artifact which has out-of-order cards. Futhermore, when Fossil is generating new structural artifacts, it runs the generated artifact through the parser to confirm that all cards really are in the correct order before committing the transaction. In this way, Fossil prevents |
| ︙ | ︙ |
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
246 247 248 249 250 251 252 | [https://git-scm.com/book/en/v2/Git-Internals-Packfiles|pack-files], whereas Fossil stores its objects in a [https://www.sqlite.org/|SQLite] database file which provides ACID transactions and a high-level query language. This difference is more than an implementation detail. It has important practical consequences. | | | | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | [https://git-scm.com/book/en/v2/Git-Internals-Packfiles|pack-files], whereas Fossil stores its objects in a [https://www.sqlite.org/|SQLite] database file which provides ACID transactions and a high-level query language. This difference is more than an implementation detail. It has important practical consequences. One notable consequence is that it is difficult to find the descendants of check-ins in Git. One can easily locate the ancestors of a particular Git check-in by following the pointers embedded in the check-in object, but it is difficult to go the other direction and locate the descendants of a check-in. It is so difficult, in fact, that neither native Git nor GitHub provide this capability short of crawling the [https://www.git-scm.com/docs/git-log|commit log]. With Fossil, on the other hand, finding descendants is a simple SQL query. It is common in Fossil to ask to see [/timeline?df=release&y=ci|all check-ins since the last release]. Git lets you see "what came before". Fossil makes it just as easy to also see "what came after". Leaf check-ins in Git that lack a "ref" become "detached," making them difficult to locate and subject to garbage collection. This |
| ︙ | ︙ | |||
401 402 403 404 405 406 407 | allows an anonymous developer to rebase and push specific locally-named private branches, so that a Git repo clone often isn't really a clone at all: it may have an arbitrary number of differences relative to the repository it originally cloned from. Git encourages siloed development. Select work in a developer's local repository may remain private indefinitely. | | | | | > > > | | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | allows an anonymous developer to rebase and push specific locally-named private branches, so that a Git repo clone often isn't really a clone at all: it may have an arbitrary number of differences relative to the repository it originally cloned from. Git encourages siloed development. Select work in a developer's local repository may remain private indefinitely. The Git preference for siloed development has been strongly adopted by GitHub, who say "[https://guides.github.com/activities/forking|Forking is at the core of social coding at GitHub]". As of September 2021, [https://github.com/search?q=is:public|Github hosts 46 million distinct software projects], most of them created by forking a previously-existing project. Since this is [https://evansdata.com/reports/viewRelease.php?reportID=9 | roughly twice the number of developers in the world], it beggars belief that most of these forks are still under active development. We expect that the vast bulk of these are abandoned one-off efforts. All of this is exactly what one wants when doing bazaar-style development. Fossil's normal mode of operation differs on every one of these points, with the specific designed-in goal of promoting SQLite's cathedral development model: |
| ︙ | ︙ | |||
438 439 440 441 442 443 444 |
[https://en.wikipedia.org/wiki/Flat_organization|flat
organizations].</p></li>
<li><p><b>No easy drive-by contributions:</b> Git
[https://www.git-scm.com/docs/git-request-pull|pull requests] offer
a low-friction path to accepting
[https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by
| | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 |
[https://en.wikipedia.org/wiki/Flat_organization|flat
organizations].</p></li>
<li><p><b>No easy drive-by contributions:</b> Git
[https://www.git-scm.com/docs/git-request-pull|pull requests] offer
a low-friction path to accepting
[https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by
contributions]. Fossil's closest equivalents are its unique
[/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement
than firing off a PR.⁷ This difference comes directly from the
initial designed purpose for each tool: the SQLite project doesn't
accept outside contributions from previously-unknown developers, but
the Linux kernel does.</p></li>
<li><p><b>No rebasing:</b> When your local repo clone syncs changes
up to its parent, those changes are sent exactly as they were
|
| ︙ | ︙ | |||
476 477 478 479 480 481 482 |
<li><p><b>Branch names sync:</b> Unlike in Git, branch names in
Fossil are not purely local labels. They sync along with everything
else, so everyone sees the same set of branch names. Fossil's design
choice here is a direct reflection of the Linux vs. SQLite project
outlook: SQLite's developers collaborate closely on a single
coherent project, whereas Linux's developers go off on tangents and
| | | 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
<li><p><b>Branch names sync:</b> Unlike in Git, branch names in
Fossil are not purely local labels. They sync along with everything
else, so everyone sees the same set of branch names. Fossil's design
choice here is a direct reflection of the Linux vs. SQLite project
outlook: SQLite's developers collaborate closely on a single
coherent project, whereas Linux's developers go off on tangents and
occasionally send selected change sets to each other.</p></li>
<li><p><b>Private branches are rare:</b>
[/doc/trunk/www/private.wiki|Private branches exist in Fossil], but
they're normally used to handle rare exception cases, whereas in
many Git projects, they're part of the straight-line development
process.</p></li>
|
| ︙ | ︙ | |||
504 505 506 507 508 509 510 | [https://en.wikipedia.org/wiki/Feedback | feedback loop size], which we know from the mathematics of [https://en.wikipedia.org/wiki/Control_theory | control theory] to directly affect the speed at which any system can safely make changes. The larger the feedback loop, the slower the whole system must run in order to avoid loss of control. The same concept shows up in other contexts, such as in the [https://en.wikipedia.org/wiki/OODA_loop | OODA | | < < | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | [https://en.wikipedia.org/wiki/Feedback | feedback loop size], which we know from the mathematics of [https://en.wikipedia.org/wiki/Control_theory | control theory] to directly affect the speed at which any system can safely make changes. The larger the feedback loop, the slower the whole system must run in order to avoid loss of control. The same concept shows up in other contexts, such as in the [https://en.wikipedia.org/wiki/OODA_loop | OODA loop] concept. Committing your changes to private branches in order to delay a public push to the parent repo increases the size of your collaborators' control loops, either causing them to slow their work in order to safely react to your work, or to overcorrect in response to each change. Each DVCS can be used in the opposite style, but doing so works against their low-friction paths. |
| ︙ | ︙ | |||
922 923 924 925 926 927 928 |
distribution is 44.7 MiB but the current <tt>fossil.exe</tt>
zip file for Windows is 2.24 MiB. Fossil is much smaller
despite using a roughly similar amount of high-level scripting code
because its interpreters are compact and built into Fossil itself.
<li><p>Both Fossil and Git support
[https://en.wikipedia.org/wiki/Patch_(Unix)|<tt>patch(1)</tt>
| | | > > > > > | 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 |
distribution is 44.7 MiB but the current <tt>fossil.exe</tt>
zip file for Windows is 2.24 MiB. Fossil is much smaller
despite using a roughly similar amount of high-level scripting code
because its interpreters are compact and built into Fossil itself.
<li><p>Both Fossil and Git support
[https://en.wikipedia.org/wiki/Patch_(Unix)|<tt>patch(1)</tt>
files] — unified diff formatted output — for accepting drive-by contributions, but it's a
lossy contribution path for both systems. Unlike Git PRs and Fossil
bundles, patch files collapse multiple checkins together, they don't
include check-in comments, and they cannot encode changes made above
the individual file content layer: you lose branching decisions,
tag changes, file renames, and more when using patch files. Fossil
2.16 adds [./patchcmd.md | a <tt>fossil patch</tt> command] that
also solves these problems, but it is because it works like a Fossil
bundle, only for uncommitted changes; it doesn't use Larry Wall's
<tt>patch</tt> tool to apply unified diff output to the receiving
Fossil checkout.</p></li>
</ol></i></small>
|
Changes to www/gitusers.md.
| ︙ | ︙ | |||
111 112 113 114 115 116 117 | There is no implicit “and update the local working directory” step in Fossil’s push, pull, or sync commands, as there is with `git pull`. Someone coming from the Git perspective may perceive that `fossil up` has two purposes: * Without the optional `VERSION` argument, it updates the working | | | | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
There is no implicit “and update the local working directory” step in Fossil’s
push, pull, or sync commands, as there is with `git pull`.
Someone coming from the Git perspective may perceive that `fossil up`
has two purposes:
* Without the optional `VERSION` argument, it updates the working
checkout to the tip of the current branch, as `git pull` does.
* Given a `VERSION` argument, it updates to the named version. If that’s the
name of a branch, it updates to the *tip* of that branch, as
`git checkout BRANCH` does.
In fact, these are the same operation, so they’re the same command in
Fossil. The first form simply allows the `VERSION` to be implicit: the
tip of the current branch.
We think this is a more sensible command design than `git pull` vs
`git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan])
[fpull]: /help?cmd=pull
[gpull]: https://git-scm.com/docs/git-pull
[gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well
|
| ︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 | "`.fossil`" suffixes. That aside, you can follow any other convention that makes sense to you. This author uses a scheme like the following on mobile machines that shuttle between home and the office: ``` pikchr toggle indent box "~/museum/" fit move right 0.1 line right dotted move right 0.05 box invis "where one stores valuable fossils" ljust arrow down 50% from first box.s then right 50% | > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | "`.fossil`" suffixes. That aside, you can follow any other convention that makes sense to you. This author uses a scheme like the following on mobile machines that shuttle between home and the office: ``` pikchr toggle indent scale=0.8 box "~/museum/" fit move right 0.1 line right dotted move right 0.05 box invis "where one stores valuable fossils" ljust arrow down 50% from first box.s then right 50% |
| ︙ | ︙ | |||
174 175 176 177 178 179 180 | box invis "clones of Fossil itself, SQLite, etc." ljust ``` On a Windows box, you might instead choose "`C:\Fossils`" and do without the subdirectory scheme, for example. | | | > > | | | | 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 | box invis "clones of Fossil itself, SQLite, etc." ljust ``` On a Windows box, you might instead choose "`C:\Fossils`" and do without the subdirectory scheme, for example. #### <a id="close" name="dotfile"></a> Closing a Check-Out The [`fossil close`][close] command dissociates a check-out directory from the Fossil repository database, nondestructively inverting [`fossil open`][open]. (Contrast [its closest inverse](#worktree), `git worktree remove`, which *is* destructive in Git!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the `.fslckout` file at the root of a check-out directory. This is a SQLite database that keeps track of local state such as what version you have checked out, the contents of the [stash] for that working directory, the [undo] buffers, per-checkout [settings][set], and so forth. The stash and undo buffers are considered precious uncommitted changes, so you have to force Fossil to discard these as part of closing the check-out. |
| ︙ | ︙ | |||
224 225 226 227 228 229 230 |
ln -s ../actual-clone-dir/.git .
git checkout foo-branch
The symlink trick has a number of problems, the largest being that
symlinks weren’t available on Windows until Vista, and until the Windows
10 Creators Update was released in spring of 2017, you had to be an
Administrator to use the feature besides. ([Source][wsyml]) Git solved
| > | < > > > > > > > | 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 |
ln -s ../actual-clone-dir/.git .
git checkout foo-branch
The symlink trick has a number of problems, the largest being that
symlinks weren’t available on Windows until Vista, and until the Windows
10 Creators Update was released in spring of 2017, you had to be an
Administrator to use the feature besides. ([Source][wsyml]) Git solved
this problem back when Windows XP was Microsoft’s current offering
with the `git-worktree` command, added in Git 2.5:
git worktree add ../foo-branch foo-branch
cd ../foo-branch
That is approximately equivalent to this in Fossil:
mkdir ../foo-branch
cd ../foo-branch
fossil open /path/to/repo.fossil foo-branch
The Fossil alternative is wordier, but this tends to be one-time setup,
not something you do everyday. This author keeps a “scratch” checkout
for cases where is isn’t appropriate to reuse the “trunk” checkout. The
other peer checkouts therefore tend to track long-lived branches, so
they rarely change once a development machine is set up.
That then leads us to the closest equivalent in Git to [closing a Fossil
check-out](#close):
git worktree remove .
Note, however, that unlike `fossil close`, once the Git command
|
| ︙ | ︙ | |||
338 339 340 341 342 343 344 | generally run in O(log n) time, faster than O(n) for equal *n* when the constants are equal. Yet the constants are *not* equal because Fossil reads from a single disk file rather than visit potentially many files in sequence as Git must, so the OS’s buffer cache can result in [still better performance][35pct]. Unlike Git’s log, Fossil’s timeline shows info across branches by | | | | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | generally run in O(log n) time, faster than O(n) for equal *n* when the constants are equal. Yet the constants are *not* equal because Fossil reads from a single disk file rather than visit potentially many files in sequence as Git must, so the OS’s buffer cache can result in [still better performance][35pct]. Unlike Git’s log, Fossil’s timeline shows info across branches by default, a feature for maintaining better situational awareness. Although the `fossil timeline` command has no way to show a single branch’s commits, you can restrict your view like this using the web UI equivalent by clicking the name of a branch on the `/timeline` or `/brlist` page. (Or manually, by adding the `r=` query parameter.) Note that even in this case, the Fossil timeline still shows other branches where they interact with the one you’ve referenced in this way; again, better situational awareness. |
| ︙ | ︙ | |||
505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
automatically. There is no need for the "-a" option as with Git.
If you only want to commit _some_ of the changes, list the names
of the files or directories you want to commit as arguments, like this:
fossil commit src/feature.c doc/feature.md examples/feature
Although there are currently no
<a id="csplit"></a>[commit splitting][gcspl] features in Fossil like
`git add -p`, `git commit -p`, or `git rebase -i`, you can get the same
effect by converting an uncommitted change set to a patch and then
running it through [Patchouli].
Rather than use `fossil diff -i` to produce such a patch, a safer and
| > > > | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
automatically. There is no need for the "-a" option as with Git.
If you only want to commit _some_ of the changes, list the names
of the files or directories you want to commit as arguments, like this:
fossil commit src/feature.c doc/feature.md examples/feature
Note that the last element is a directory name, meaning “any changed
file under the `examples/feature` directory.”
Although there are currently no
<a id="csplit"></a>[commit splitting][gcspl] features in Fossil like
`git add -p`, `git commit -p`, or `git rebase -i`, you can get the same
effect by converting an uncommitted change set to a patch and then
running it through [Patchouli].
Rather than use `fossil diff -i` to produce such a patch, a safer and
|
| ︙ | ︙ | |||
548 549 550 551 552 553 554 | [ctrb]: https://fossil-scm.org/fossil/doc/trunk/www/contribute.wiki [gcspl]: https://git-scm.com/docs/git-rebase#_splitting_commits [Patchouli]: https://pypi.org/project/patchouli/ <a id="bneed"></a> | | | | > > | | | > | | > > | > > > > > > > > | 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 |
[ctrb]: https://fossil-scm.org/fossil/doc/trunk/www/contribute.wiki
[gcspl]: https://git-scm.com/docs/git-rebase#_splitting_commits
[Patchouli]: https://pypi.org/project/patchouli/
<a id="bneed"></a>
## Create Branches at Point of Need, Rather Than Ahead of Need
Fossil prefers that you create new branches as part of the first commit
on that branch:
fossil commit --branch my-branch
If that commit is successful, your local check-out directory is then
switched to the tip of that branch, so subsequent commits don’t need the
“`--branch`” option. You simply say `fossil commit` again to continue
adding commits to the tip of that branch.
To switch back to the parent branch, say something like:
fossil update trunk
(This is approximately equivalent to `git checkout master`.)
Fossil does also support the Git style, creating the branch ahead of
need:
fossil branch new my-branch
fossil up my-branch
...work on first commit...
fossil commit
This is more verbose, giving the same overall effect though the initial
actions are inverted: create a new branch for the first commit, switch
the check-out directory to that branch, and make that first commit. As
above, subsequent commits are descendants of that initial branch commit.
We think you’ll agree that creating a branch as part of the initial
commit is simpler.
Fossil also allows you to move a check-in to a different branch
*after* you commit it, using the "`fossil amend`" command.
For example:
fossil amend current --branch my-branch
This works by inserting a tag into the repository that causes the web UI
to relabel commits from that point forward with the new name. Like Git,
Fossil’s fundamental data structure is the interlinked DAG of commit
hashes; branch names are supplemental data for making it easier for the
humans to understand this DAG, so this command does not change the core
history of the project, only annotate it for better display to the
humans.
(The version string “current” is one of the [special check-in names][scin] in Fossil. See
that document for the many other names you can give to “`amend`”, or
indeed to any other Fossil command documented to accept a `VERSION` or
`NAME` string.)
[scin]: ./checkin_names.wiki
|
| ︙ | ︙ | |||
611 612 613 614 615 616 617 |
pulls all remote changes down to your local clone of the repository as
part of a "`fossil update`".
This provides most of the advantages of a centralized version control
system while retaining the advantages of distributed version control:
1. Your work stays synced up with your coworkers’ efforts as long as your
machine can connect to the remote repository. At need, you can go
| | | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
pulls all remote changes down to your local clone of the repository as
part of a "`fossil update`".
This provides most of the advantages of a centralized version control
system while retaining the advantages of distributed version control:
1. Your work stays synced up with your coworkers’ efforts as long as your
machine can connect to the remote repository. At need, you can go
off-network and continue work atop the last version you synced with
the remote.
2. It provides immediate off-machine backup of your commits. Unlike
centralized version control, though, you can still work while
disconnected; your changes will sync up with the remote once you get
back online.
|
| ︙ | ︙ | |||
637 638 639 640 641 642 643 | [bu]: ./backup.md [setup]: ./caps/admin-v-setup.md#apsu [wflow]: ./concepts.wiki#workflow <a id="syncall"></a> | | | 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | [bu]: ./backup.md [setup]: ./caps/admin-v-setup.md#apsu [wflow]: ./concepts.wiki#workflow <a id="syncall"></a> ## Sync Is All-or-Nothing Fossil does not support the concept of syncing, pushing, or pulling individual branches. When you sync/push/pull in Fossil, it processes all artifacts in its hash tree: branches, tags, wiki articles, tickets, forum posts, technotes… This is [not quite “everything,” full stop][bu], but it’s close. |
| ︙ | ︙ | |||
709 710 711 712 713 714 715 | draft was written, it’s been revised multiple times to address less common objections as well. Chances are not good that you are going to come up with a new objection that we haven’t already considered and addressed there. There is only one sub-feature of `git rebase` that is philosophically compatible with Fossil yet which currently has no functional equivalent. | | | | > | | | > > > > | | | 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 |
draft was written, it’s been revised multiple times to address less
common objections as well. Chances are not good that you are going to
come up with a new objection that we haven’t already considered and
addressed there.
There is only one sub-feature of `git rebase` that is philosophically
compatible with Fossil yet which currently has no functional equivalent.
We [covered this and the workaround for its lack](#csplit) above.
[3]: ./rebaseharm.md
## <a id="cdiff"></a> Colorized Diffs
The graphical diffs in the Fossil web UI and `fossil diff --tk` use
color to distinguish insertions, deletions, and replacements, but unlike
with `git diff` when the output is to an ANSI X3.64 capable terminal,
`fossil diff` does not.
There are a few easy ways to add this feature to Fossil, though.
One is to install
[`colordiff`][cdiff], which is included in [many package systems][cdpkg],
then say:
fossil set --global diff-command 'colordiff -wu'
Because this is unconditional, unlike `git diff --color=auto`, you will
then have to remember to add the `-i` option to `fossil diff` commands
when you want color disabled, such as when producing `patch(1)` files
or piping diff output to another
command that doesn’t understand ANSI escape sequences. There’s an
example of this [below](#dstat).
Another way, which avoids this problem, is to say instead:
fossil set --global diff-command 'git diff --no-index'
This delegates `fossil diff` to `git diff` by using the latter’s
ability to run on files not inside any repository.
[cdpkg]: https://repology.org/project/colordiff/versions
## <a id="show"></a> Showing Information About Commits
While there is no direct equivalent to Git’s “`show`” command, similar
functionality is present in Fossil under other commands:
#### <a id="patch"></a> Show a Patch for a Commit
git show -p COMMIT_ID
…gives much the same output as
fossil diff --checkin COMMIT_ID
…only without the patch email header. Git comes out of the [LKML] world,
where emailing a patch is a normal thing to do. Fossil is [designed for
cohesive teams][devorg] where such drive-by patches are rarer.
You can use any of [Fossil’s special check-in names][scin] in place of
the `COMMIT_ID` in this and later examples. Fossil docs usually say
“`VERSION`” or “`NAME`” where this is allowed, since the version string
or name might not refer to a commit ID, but instead to a forum post, a
wiki document, etc. For instance, the following command answers the question “What did
I just commit?”
fossil diff --checkin tip
…or equivalently using a different symbolic commit name:
fossil diff --from prev
[devorg]: ./fossil-v-git.wiki#devorg
[LKML]: https://lkml.org/
#### <a id="cmsg"></a> Show a Specific Commit Message
git show -s COMMIT_ID
…is
fossil time -n 1 COMMIT_ID
…or with a shorter, more obvious command, though with more verbose output:
fossil info COMMIT_ID
The `fossil info` command isn’t otherwise a good equivalent to
`git show`; it just overlaps its functionality in some areas. Much of
what’s missing is present in the corresponding [`/info` web
view][infow], though.
#### <a id="dstat"></a> Diff Statistics
Fossil’s closest internal equivalent to commands like
`git show --stat` is:
fossil diff -i --from 2020-04-01 --numstat
The `--numstat` output is a bit cryptic, so we recommend delegating
|
| ︙ | ︙ | |||
888 889 890 891 892 893 894 | [mv]: /help?cmd=mv [rm]: /help?cmd=rm ---- | | | | | | 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 |
[mv]: /help?cmd=mv
[rm]: /help?cmd=rm
----
## <a id="cvdate" name="cs1"></a> Case Study 1: Checking Out a Version by Date
Let’s get into something a bit more complicated: a case study showing
how the concepts lined out above cause Fossil to materially differ in
day-to-day operation from Git.
Why would you want to check out a version of a project by date? Perhaps
your customer gave you a vague bug report referencing only a
date rather than a version. Or, you may be poking semi-randomly through
history to find a “good” version to anchor the start point of a
[`fossil bisect`][fbis] operation.
My search engine’s first result for “git checkout by date” is [this
highly-upvoted accepted Stack Overflow answer][gcod]. The first command
it gives is based on Git’s [`rev-parse` feature][grp]:
git checkout master@{2020-03-17}
There are a number of weaknesses in this command. From least to most
critical:
1. It’s a bit cryptic. Leave off the refname or punctuation, and it
means something else. You cannot simplify the cryptic incantation in
the typical use case.
2. A date string in Git without a time will be interpreted as
“[at the local wall clock time on the given date][gapxd],” so the
command means something different from one second to the next! This
can be a serious problem if there are multiple commits on that date, because
the command will give different results depending on the time of
day you run it.
3. It gives misleading output if there is no close match for the date
in the local [reflog]. It starts out empty after a fresh clone, and
while it does build up as you use that clone, Git [automatically
prunes][gle] the reflog to 90 days of history by default. This means
|
| ︙ | ︙ | |||
938 939 940 941 942 943 944 |
useful result, and sometimes it doesn’t. If you’re on a fresh clone,
you are likely to get the “tip” commit’s revision ID no matter what
date value you give.
Git tries its best, but because it’s working from a purgeable and
possibly-stale local cache, you cannot trust its results.
| | | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
useful result, and sometimes it doesn’t. If you’re on a fresh clone,
you are likely to get the “tip” commit’s revision ID no matter what
date value you give.
Git tries its best, but because it’s working from a purgeable and
possibly-stale local cache, you cannot trust its results.
Consequently, we cannot recommend this command at all. It’s unreliable even in the
best case.
That same Stack Overflow answer therefore goes on to recommend an
entirely different command:
git checkout $(git rev-list -n 1 --first-parent --before="2020-03-17" master)
|
| ︙ | ︙ | |||
1018 1019 1020 1021 1022 1023 1024 | * Parse timestamps from all commits on clone into a local commit index, then maintain that index through subsequent commits and syncs. * Use an indexed SQL `ORDER BY` query to match timestamps to commit IDs for a fast and consistent result. | | | 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | * Parse timestamps from all commits on clone into a local commit index, then maintain that index through subsequent commits and syncs. * Use an indexed SQL `ORDER BY` query to match timestamps to commit IDs for a fast and consistent result. * Round incomplete timestamp strings up using [rules][frud] consistent across computers and local time of day. [frud]: https://fossil-scm.org/home/file/src/name.c?ci=d2a59b03727bc3&ln=122-141 [gbash]: https://appuals.com/what-is-git-bash/ [gapxd]: https://github.com/git/git/blob/7f7ebe054a/date.c#L1298-L1300 [gcod]: https://stackoverflow.com/a/6990682/142454 [gdh]: https://www.git-tower.com/learn/git/faq/detached-head-when-checkout-commit/ |
| ︙ | ︙ | |||
1193 1194 1195 1196 1197 1198 1199 |
[staging feature](#staging).
The “Friday afternoon sync-up” case is simpler, too:
fossil remote work
fossil sync
| | | > > > > > > | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 |
[staging feature](#staging).
The “Friday afternoon sync-up” case is simpler, too:
fossil remote work
fossil sync
Back at home, it’s simpler still: we may be able to do away with the second command,
saying just “`fossil remote home`” because the sync will happen as part
of the next commit, thanks once again to Fossil’s autosync feature. If
the working branch now has commits from other developers after syncing
with the central repository, though, you’ll want to say “`fossil up`” to
avoid creating an inadvertent fork in the branch.
(Which is easy to fix if it occurs: `fossil merge` without arguments
means “merge open tips on the current branch.”)
[grsync]: https://stackoverflow.com/q/1398018/142454
[qs]: ./quickstart.wiki
[shwmd]: ./fossil-v-git.wiki#checkouts
[sn]: https://en.wikipedia.org/wiki/Sneakernet
|
Changes to www/globs.md.
| ︙ | ︙ | |||
280 281 282 283 284 285 286 | settings` commands. That advice does not help you when you are giving one-off glob patterns in `fossil` commands. The remainder of this section gives remedies and workarounds for these problems. | | | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | settings` commands. That advice does not help you when you are giving one-off glob patterns in `fossil` commands. The remainder of this section gives remedies and workarounds for these problems. ### <a id="posix"></a>POSIX Systems If you are using Fossil on a system with a POSIX-compatible shell — Linux, macOS, the BSDs, Unix, Cygwin, WSL etc. — the shell may expand the glob patterns before passing the result to the `fossil` executable. Sometimes this is exactly what you want. Consider this command for |
| ︙ | ︙ | |||
367 368 369 370 371 372 373 | above to make sure the right set of files were scheduled for insertion into the repository before checking the changes in. You never want to accidentally check something like a password, an API key, or the private half of a public cryptographic key into Fossil repository that can be read by people who should not have such secrets. | | | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | above to make sure the right set of files were scheduled for insertion into the repository before checking the changes in. You never want to accidentally check something like a password, an API key, or the private half of a public cryptographic key into Fossil repository that can be read by people who should not have such secrets. ### <a id="windows"></a>Windows Before we get into Windows-specific details here, beware that this section does not apply to the several Microsoft Windows extensions that provide POSIX semantics to Windows, for which you want to use the advice in [the POSIX section above](#posix) instead: * the ancient and rarely-used [Microsoft POSIX subsystem][mps]; |
| ︙ | ︙ |
Changes to www/hashes.md.
1 2 3 4 5 6 7 | # Hashes: Fossil Artifact Identification All artifacts in Fossil are identified by a unique hash, currently using [the SHA3 algorithm by default][hpol], but historically using the SHA1 algorithm: <table border="1" cellspacing="0" cellpadding="10"> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Hashes: Fossil Artifact Identification All artifacts in Fossil are identified by a unique hash, currently using [the SHA3 algorithm by default][hpol], but historically using the SHA1 algorithm: <table border="1" cellspacing="0" cellpadding="10"> <tr><th>Algorithm</th><th>Raw Bits</th> <th>Hexadecimal digits</th></tr> <tr><td>SHA3-256</td> <td>256</td> <td>64</td></tr> <tr><td>SHA1</td> <td>160</td> <td>40</td></tr> </table> There are many types of artifacts in Fossil: commits (a.k.a. check-ins), tickets, ticket comments, wiki articles, forum postings, file data belonging to check-ins, etc. ([More info...](./concepts.wiki#artifacts)). |
| ︙ | ︙ |
Changes to www/index.wiki.
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
| | | | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
<h3>Latest Release: 2.17 ([/timeline?c=version-2.17|2021-10-09])</h3>
* [/uv/download.html|Download]
* [./changes.wiki#v2_17|Change Summary]
* [/timeline?p=version-2.17&bt=version-2.16&y=ci|Check-ins in version 2.17]
* [/timeline?df=version-2.17&y=ci|Check-ins derived from the 2.17 release]
* [/timeline?t=release|Timeline of all past releases]
<hr>
<h3>Quick Start</h3>
1. [/uv/download.html|Download] or install using a package manager or
[./build.wiki|compile from sources].
|
| ︙ | ︙ |
Changes to www/inout.wiki.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 | The --git option is not actually required. The git-fast-export file format is currently the only VCS interchange format that Fossil understands. But future versions of Fossil might be enhanced to understand other VCS interchange formats, and so for compatibility, use of the --git option is recommended. | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | The --git option is not actually required. The git-fast-export file format is currently the only VCS interchange format that Fossil understands. But future versions of Fossil might be enhanced to understand other VCS interchange formats, and so for compatibility, use of the --git option is recommended. <a id="fx_git"></a> Note that in new imports, Fossil defaults to using the email component of the Git <em>committer</em> (or <em>author</em> if <code>--use-author</code> is passed) to attribute check-ins in the imported repository. Alternatively, the [/help?cmd=import | <code>--attribute</code>] option can be passed to have all commits by a given committer attributed to a desired username. This will create and populate the new <code>fx_git</code> table in the repository database to maintain a record of correspondent usernames and email addresses that can be |
| ︙ | ︙ |
Changes to www/interwiki.md.
| ︙ | ︙ | |||
55 56 57 58 59 60 61 | 3. <b>Wiki Links</b> → An PageName that is not a Path or Hash. The Intermap defines a base URL for each Tag. Path links are appended directly to the URL contained in the Intermap. The Intermap can define additional text to put in between the base URL and the PageName for Hash and Wiki links, respectively. | | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 3. <b>Wiki Links</b> → An PageName that is not a Path or Hash. The Intermap defines a base URL for each Tag. Path links are appended directly to the URL contained in the Intermap. The Intermap can define additional text to put in between the base URL and the PageName for Hash and Wiki links, respectively. <a id="intermap"></a> ## Intermap The intermap defines a mapping from interwiki Tags to full URLs. The Intermap can be viewed and managed using the [fossil interwiki][iwiki] command or the [/intermap][imap] webpages. [iwiki]: /help?cmd=interwiki |
| ︙ | ︙ |
Changes to www/javascript.md.
| ︙ | ︙ | |||
461 462 463 464 465 466 467 468 469 470 471 472 473 474 | therefore scrolls both boxes when you drag the scroll bar on one because if you want to examine part of a line scrolled out of the HTML element in one box, you probably want to examine the same point on that line in the other box. _Graceful Fallback:_ Manually scroll both boxes to sync their views. ### <a id="sort"></a>Table Sorting On pages showing a data table, the column headers may be clickable to do a client-side sort of the data on that column. _Potential Workaround:_ This feature could be enhanced to do the sort on | > > > > > > > > > > > > | 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 | therefore scrolls both boxes when you drag the scroll bar on one because if you want to examine part of a line scrolled out of the HTML element in one box, you probably want to examine the same point on that line in the other box. _Graceful Fallback:_ Manually scroll both boxes to sync their views. ### <a id="diffcontext"></a>Diff Context Loading As of version 2.17, fossil adds the ability for the diff views to dynamically load more lines of context around changed blocks. The UI controls for this feature are injected using JavaScript when the page initializes and make use of XHR requests to fetch data from the fossil instance. _Graceful Fallback:_ The UI controls for this feature do not appear when JS is unavailable, leaving the user with the "legacy" static diff view. ### <a id="sort"></a>Table Sorting On pages showing a data table, the column headers may be clickable to do a client-side sort of the data on that column. _Potential Workaround:_ This feature could be enhanced to do the sort on |
| ︙ | ︙ | |||
514 515 516 517 518 519 520 | [abd]: ./antibot.wiki [caps]: ./caps/ ### <a id="hbm"></a>Hamburger Menu | | | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | [abd]: ./antibot.wiki [caps]: ./caps/ ### <a id="hbm"></a>Hamburger Menu Several of the stock skins (including the default) include a “hamburger menu” (☰) which uses JavaScript to show a simplified version of the Fossil UI site map using an animated-in dropdown. _Graceful Fallback:_ Clicking the hamburger menu button with JavaScript disabled will take you to the `/sitemap` page instead of showing a simplified version of that page’s content in a drop-down. |
| ︙ | ︙ | |||
583 584 585 586 587 588 589 | _Potential Workaround:_ A user can manually construct an appropriate regular expession and put it into the "Tag Filter" entry of the `/timeline` page (in its advanced mode). ### <a id="wcontent"></a>Wiki content listing | | | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 | _Potential Workaround:_ A user can manually construct an appropriate regular expession and put it into the "Tag Filter" entry of the `/timeline` page (in its advanced mode). ### <a id="wcontent"></a>Wiki content listing [Since](/timeline?r=wcontent-subsets) version 2.18 it is possible to add [configurable](/help?cmd=wiki-classes) checkbox controls to the submenu of [available wiki pages](/wcontent) for the interactive adjustment of a subset of wiki pages that are shown. Client-side script is used to toggle visibility of the corresponding rows according to the state of these checkboxes. ---- |
| ︙ | ︙ |
Changes to www/makefile.wiki.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | want to compile the code themselves can use one of the [./build.wiki | existing makefiles]. So must people do not need to be concerned with the build complexities of Fossil. But hard-core developers who desire a deep understanding of how Fossil is put together can benefit from reviewing this article. | < | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | want to compile the code themselves can use one of the [./build.wiki | existing makefiles]. So must people do not need to be concerned with the build complexities of Fossil. But hard-core developers who desire a deep understanding of how Fossil is put together can benefit from reviewing this article. <h1 id="srctour">2.0 Source Code Tour</h1> The source code for Fossil is found in the [/dir?ci=trunk&name=src | src/] subdirectory of the source tree. The src/ subdirectory contains all code, including the code for the separate preprocessor programs. Each preprocessor program is a separate C program implemented in |
| ︙ | ︙ | |||
169 170 171 172 173 174 175 | </pre></blockquote> At the time of this writing, the "diff.tcl" script (a Tcl/Tk script used to generate implement --tk option on the diff command) is the only resource file processed using mkbuiltin.exe. However, new resources will likely be added using this facility in future versions of Fossil. | < | | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | </pre></blockquote> At the time of this writing, the "diff.tcl" script (a Tcl/Tk script used to generate implement --tk option on the diff command) is the only resource file processed using mkbuiltin.exe. However, new resources will likely be added using this facility in future versions of Fossil. <h1 id="preprocessing">4.0 Preprocessing</h1> There are three preprocessors for the Fossil sources. The mkindex and translate preprocessors can be run in any order. The makeheaders preprocessor must be run after translate. <h2>4.1 The mkindex preprocessor</h2> |
| ︙ | ︙ |
Changes to www/mirrortogithub.md.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
abovementioned generic address be used.
[attr]: /help?cmd=import
[fxgit]: ./inout.wiki#fx_git
[ui]: /help?cmd=ui
[usercmd]: /help?cmd=user
| | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
abovementioned generic address be used.
[attr]: /help?cmd=import
[fxgit]: ./inout.wiki#fx_git
[ui]: /help?cmd=ui
[usercmd]: /help?cmd=user
## <a id='ex1'></a>Example GitHub Mirrors
As of this writing (2019-03-16) Fossil’s own repository is mirrored
on GitHub at:
>
<https://github.com/drhsqlite/fossil-mirror>
|
| ︙ | ︙ |
Changes to www/mkindex.tcl.
| ︙ | ︙ | |||
163 164 165 166 167 168 169 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> | | < < | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
<li> <a href='$ROOT/help'>Built-in help for commands and webpages</a>
<li> <a href='history.md'>Purpose and History of Fossil</a>
<li> <a href='build.wiki'>Compiling and installing Fossil</a>
<li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
<li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
<li> <a href='http://fossil-scm.org/fossil-book/home'>Fossil book</a>
</ul>
<h2 id="pindex">Other Documents:</h2>
<ul>}
foreach entry $permindex {
foreach {title file bold} $entry break
# if {$bold} {set title <b>$title</b>}
if {[string match /* $file]} {set file ../../..$file}
puts $out "<li><a href=\"$file\">$title</a></li>"
}
|
| ︙ | ︙ |
Changes to www/permutedindex.html.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> | | < < | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> <li> <a href='http://fossil-scm.org/fossil-book/home'>Fossil book</a> </ul> <h2 id="pindex">Other Documents:</h2> <ul> <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> <li><a href="serverext.wiki">Adding Extensions To A Fossil Server Using CGI Scripts</a></li> <li><a href="adding_code.wiki">Adding New Features To Fossil</a></li> <li><a href="caps/">Administering User Capabilities</a></li> <li><a href="backup.md">Backing Up a Remote Fossil Repository</a></li> <li><a href="whyusefossil.wiki">Benefits Of Version Control</a></li> |
| ︙ | ︙ |
Changes to www/private.wiki.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | <blockquote><pre> fossil update trunk fossil merge private fossil commit </pre></blockquote> | | > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <blockquote><pre> fossil update trunk fossil merge private fossil commit </pre></blockquote> The private branch remains private and is not recorded as a parent in the merge manifest's P-card, but all of the changes associated with the private branch are now folded into the public branch and are hence visible to other users of the project. A private branch created with Fossil version 1.30 or newer can also be converted into a public branch using the <code>fossil publish</code> command. However, there is no way to convert a private branch created with older versions of Fossil into a public branch. |
| ︙ | ︙ |
Changes to www/quickstart.wiki.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | <blockquote> <b>fossil init </b><i> repository-filename</i> </blockquote> You can name the database anything you like, and you can place it anywhere in the filesystem. The <tt>.fossil</tt> extension is traditional but only required if you are going to use the | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <blockquote> <b>fossil init </b><i> repository-filename</i> </blockquote> You can name the database anything you like, and you can place it anywhere in the filesystem. The <tt>.fossil</tt> extension is traditional but only required if you are going to use the <tt>[/help/server | fossil server DIRECTORY]</tt> feature.” <h2 id="clone">Cloning An Existing Repository</h2> <p>Most fossil operations interact with a repository that is on the local disk drive, not on a remote system. Hence, before accessing a remote repository it is necessary to make a local copy of that repository. Making a local copy of a remote repository is called |
| ︙ | ︙ |
Changes to www/rebaseharm.md.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 | Most people, even strident advocates of rebase, agree that rebase can cause problems when misused. The Git rebase documentation talks about the [golden rule of rebasing][golden]: never rebase on a public branch. Horror stories of misused rebase abound, and the rebase documentation devotes considerable space toward explaining how to recover from rebase errors and/or misuse. | | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Most people, even strident advocates of rebase, agree that rebase can cause problems when misused. The Git rebase documentation talks about the [golden rule of rebasing][golden]: never rebase on a public branch. Horror stories of misused rebase abound, and the rebase documentation devotes considerable space toward explaining how to recover from rebase errors and/or misuse. ## <a id="cap-loss"></a>2.0 Rebase provides no new capabilities Sometimes sharp and dangerous tools are justified, because they accomplish things that cannot be done otherwise, or at least cannot be done easily. Rebase does not fall into that category, because it provides no new capabilities. ### <a id="orphaning"></a>2.1 A rebase is just a merge with historical references omitted A rebase is really nothing more than a merge (or a series of merges) that deliberately forgets one of the parents of each merge step. To help illustrate this fact, consider the first rebase example from the [Git documentation][gitrebase]. The merge looks like this: |
| ︙ | ︙ | |||
86 87 88 89 90 91 92 | So, another way of thinking about rebase is that it is a kind of merge that intentionally forgets some details in order to not overwhelm the weak history display mechanisms available in Git. Wouldn't it be better, less error-prone, and easier on users to enhance the history display mechanisms in Git so that rebasing for a clean, linear history became unnecessary? | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | So, another way of thinking about rebase is that it is a kind of merge that intentionally forgets some details in order to not overwhelm the weak history display mechanisms available in Git. Wouldn't it be better, less error-prone, and easier on users to enhance the history display mechanisms in Git so that rebasing for a clean, linear history became unnecessary? ### <a id="clean-diffs"></a>2.2 Rebase does not actually provide better feature-branch diffs Another argument, often cited, is that rebasing a feature branch allows one to see just the changes in the feature branch without the concurrent changes in the main line of development. Consider a hypothetical case: ~~~ pikchr toggle |
| ︙ | ︙ | |||
210 211 212 213 214 215 216 | diff is not determined by whether you select C7 or C5\' as the target of the diff, but rather by your choice of the diff source, C2 or C6. So, to help with the problem of viewing changes associated with a feature branch, perhaps what is needed is not rebase but rather better tools to help users identify an appropriate baseline for their diffs. | | | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | diff is not determined by whether you select C7 or C5\' as the target of the diff, but rather by your choice of the diff source, C2 or C6. So, to help with the problem of viewing changes associated with a feature branch, perhaps what is needed is not rebase but rather better tools to help users identify an appropriate baseline for their diffs. ## <a id="siloing"></a>3.0 Rebase encourages siloed development The [golden rule of rebasing][golden] is that you should never do it on public branches, so if you are using rebase as intended, that means you are keeping private branches. Or, to put it another way, you are doing siloed development. You are not sharing your intermediate work with collaborators. This is not good for product quality. |
| ︙ | ︙ | |||
249 250 251 252 253 254 255 | Given that, is it better for those many eyeballs to find your problems while they're still isolated on a feature branch, or should that vetting wait until you finally push a collapsed version of a private working branch to the parent repo? Will the many eyeballs even see those errors when they’re intermingled with code implementing some compelling new feature? | | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | Given that, is it better for those many eyeballs to find your problems while they're still isolated on a feature branch, or should that vetting wait until you finally push a collapsed version of a private working branch to the parent repo? Will the many eyeballs even see those errors when they’re intermingled with code implementing some compelling new feature? ## <a id="timestamps"></a>4.0 Rebase causes timestamp confusion Consider the earlier example of rebasing a feature branch: ~~~ pikchr toggle # Copy of second diagram in section 2.2 above scale = 0.8 circle "C0" fit fill white |
| ︙ | ︙ | |||
291 292 293 294 295 296 297 | system clocks, so they are not unique to rebase, but they are very confusing and so best avoided. The other option is to provide new unique timestamps for C3' and C5' but then you lose the information about when those check-ins were originally created, which can make historical analysis of changes more difficult. It might also complicate the legal defense of prior art claims. | | | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | system clocks, so they are not unique to rebase, but they are very confusing and so best avoided. The other option is to provide new unique timestamps for C3' and C5' but then you lose the information about when those check-ins were originally created, which can make historical analysis of changes more difficult. It might also complicate the legal defense of prior art claims. ## <a id="lying"></a>5.0 Rebase misrepresents the project history By discarding parentage information, rebase attempts to deceive the reader about how the code actually came together. Git’s rebase feature is more than just an alternative to merging: it also provides mechanisms for changing the project history in order to make editorial changes. Fossil shows that |
| ︙ | ︙ | |||
327 328 329 330 331 332 333 | Git needs rebase because it lacks these annotation facilities. Rather than consider rebase a desirable feature missing in Fossil, ask instead why Git lacks support for making editorial changes to check-ins without modifying history? Wouldn't it be better to fix the version control tool rather than requiring users to fabricate a fictitious project history? | | | | 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 | Git needs rebase because it lacks these annotation facilities. Rather than consider rebase a desirable feature missing in Fossil, ask instead why Git lacks support for making editorial changes to check-ins without modifying history? Wouldn't it be better to fix the version control tool rather than requiring users to fabricate a fictitious project history? ## <a id="collapsing"></a>6.0 Collapsing check-ins throws away valuable information One of the oft-cited advantages of rebasing in Git is that it lets you collapse multiple check-ins down to a single check-in to make the development history “clean.” The intent is that development appear as though every feature were created in a single step: no multi-step evolution, no back-tracking, no false starts, no mistakes. This ignores actual developer psychology: ideas rarely spring forth from fingers to files in faultless finished form. A wish for collapsed, finalized check-ins is a wish for a counterfactual situation. The common counterargument is that collapsed check-ins represent a better world, the ideal we're striving for. What that argument overlooks is that we must throw away valuable information to get there. ### <a id="empathy"></a>6.1 Individual check-ins support mutual understanding Ideally, future developers of our software can understand every feature in it using only context available in the version of the code they start work with. Prior to widespread version control, developers had no choice but to work that way. Pre-existing codebases could only be understood as-is or not at all. Developers in that world had an incentive to develop software that was easy to understand retrospectively, even if |
| ︙ | ︙ | |||
387 388 389 390 391 392 393 | check-in it was a part of — and then to understand the surrounding check-ins as necessary — than it is to understand a 500-line check-in that collapses a whole branch's worth of changes down to a single finished feature. [sdm]: ./fossil-v-git.wiki#durable | | | | | 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 | check-in it was a part of — and then to understand the surrounding check-ins as necessary — than it is to understand a 500-line check-in that collapses a whole branch's worth of changes down to a single finished feature. [sdm]: ./fossil-v-git.wiki#durable ### <a id="bisecting"></a>6.2 Bisecting works better on small check-ins Git lets a developer write a feature in ten check-ins but collapse it down to an eleventh check-in and then deliberately push only that final collapsed check-in to the parent repo. Someone else may then do a bisect that blames the merged check-in as the source of the problem they’re chasing down; they then have to manually work out which of the 10 steps the original developer took to create it to find the source of the actual problem. An equivalent push in Fossil will send all 11 check-ins to the parent repository so that a later investigator doing the same sort of bisect sees the complete check-in history. That bisect will point the investigator at the single original check-in that caused the problem. ### <a id="comments"></a>6.3 Multiple check-ins require multiple check-in comments The more comments you have from a given developer on a given body of code, the more concise documentation you have of that developer's thought process. To resume the bisecting example, a developer trying to work out what the original developer was thinking with a given change will have more success given a check-in comment that explains what the one check-in out of ten blamed by the "bisect" command was trying to accomplish than if they must work that out from the eleventh check-in's comment, which only explains the "clean" version of the collapsed feature. ### <a id="cherrypicking"></a>6.4 Cherry-picks work better with small check-ins While working on a new feature in one branch, you may come across a bug in the pre-existing code that you need to fix in order for work on that feature to proceed. You could choose to switch briefly back to the parent branch, develop the fix there, check it in, then merge the parent back up to the feature branch in order to continue work, but that's distracting. If the fix isn't for a critical bug, fixing it on the |
| ︙ | ︙ | |||
456 457 458 459 460 461 462 | complete. If a support organization must manually disentangle a fix from a feature check-in, they are likely to introduce new bugs on the stable branch. Even if they manage to do their work without error, it takes them more time to do the cherry-pick that way. [rh]: https://en.wikipedia.org/wiki/Red_Hat | | | | 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | complete. If a support organization must manually disentangle a fix from a feature check-in, they are likely to introduce new bugs on the stable branch. Even if they manage to do their work without error, it takes them more time to do the cherry-pick that way. [rh]: https://en.wikipedia.org/wiki/Red_Hat ### <a id="backouts"></a>6.5 Back-outs also work better with small check-ins The inverse of the cherry-pick merge is the back-out merge. If you push only a collapsed version of a private working branch up to the parent repo, those working from that parent repo cannot automatically back out any of the individual check-ins that went into that private branch. Others must either manually disentangle the problematic part of your merge check-in or back out the entire feature. ## <a id="better-plan"></a>7.0 Cherry-pick merges work better than rebase Perhaps there are some cases where a rebase-like transformation is actually helpful, but those cases are rare, and when they do come up, running a series of cherry-pick merges achieves the same topology with several advantages: 1. In Fossil, cherry-pick merges preserve an honest and clear record |
| ︙ | ︙ | |||
499 500 501 502 503 504 505 |
`git reset --hard` to repair the damage.
3. Cherry-picks keep both the original and the revised check-ins,
so both timestamps are preserved.
[tbc]: ./fossil-v-git.wiki#testing
| | | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
`git reset --hard` to repair the damage.
3. Cherry-picks keep both the original and the revised check-ins,
so both timestamps are preserved.
[tbc]: ./fossil-v-git.wiki#testing
## <a id="conclusion"></a>8.0 Summary and conclusion
Rebasing is an anti-pattern. It is dishonest. It deliberately
omits historical information. It causes problems for collaboration.
And it has no offsetting benefits.
For these reasons, rebase is intentionally and deliberately omitted
from the design of Fossil.
[golden]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing
[gitrebase]: https://git-scm.com/book/en/v2/Git-Branching-Rebasing
[nagappan]: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-2008-11.pdf
[weinberg]: https://books.google.com/books?id=76dIAAAAMAAJ
|
Changes to www/selfhost.wiki.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 | 3. [https://www3.fossil-scm.org/site.cgi] The canonical repository is (1). Repositories (2) and (3) automatically stay in synchronization with (1) via a <a href="http://en.wikipedia.org/wiki/Cron">cron job</a> that invokes "fossil sync" at regular intervals. Repository (2) also publishes a | | > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 3. [https://www3.fossil-scm.org/site.cgi] The canonical repository is (1). Repositories (2) and (3) automatically stay in synchronization with (1) via a <a href="http://en.wikipedia.org/wiki/Cron">cron job</a> that invokes "fossil sync" at regular intervals. Repository (2) also publishes a [https://github.com/drhsqlite/fossil-mirror|GitHub mirror of Fossil] as a demonstration of [./mirrortogithub.md|how that can be done]. Note that the two secondary repositories are more than just read-only mirrors. All three servers support full read/write capabilities. Changes (such as new tickets or wiki or check-ins) can be implemented on any of the three servers and those changes automatically propagate to the other two servers. |
| ︙ | ︙ |
Added www/server/any/http-over-ssh.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
# Forcing Use of Fossil’s RBAC over SSH
Andy Bradford posted a [clever solution][sshfc] to the problem of
Fossil’s RBAC system [being ignored](../../caps/#webonly) over `ssh://`
URLs: use OpenSSH’s `ForceCommand` feature to route the sync transfer
protocol data over `fossil http` rather than `fossil test-http`.
The setup for this is complicated, but it’s a worthy option when you
need encrypted communications between the client and server, you already
have SSH set up, and [the HTTPS alternative](../../ssl.wiki) is
unworkable for some reason.
## 1. Force remote Fossil access through a wrapper script <a id="sshd"></a>
Put something like the following into the `sshd_config` file on the
Fossil repository server:
``` ssh-config
Match Group fossil
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
ForceCommand /home/fossil/bin/wrapper
```
This file is usually found in `/etc/ssh`, but some OSes put it
elsewhere.
The first line presumes that we will put all users who need to use our
Fossil repositories into the `fossil` group, as we will do
[below](#perms). You could instead say something like:
``` ssh-config
Match User alice,bob,carol,dave
```
You have to list the users allowed to use Fossil in this case because
your system likely has a system administrator that uses SSH for remote
shell access, so you want to *exclude* that user from the list. For the
same reason, you don’t want to put the `ForceCommand` directive outside
a `Match` block of some sort.
You could instead list the exceptions:
``` ssh-config
Match User !evi
```
This would permit only [Evi the System Administrator][evi] to bypass this
mechanism.
[evi]: https://en.wikipedia.org/wiki/Evi_Nemeth
If you have a user that needs both interactive SSH shell access *and*
Fossil access, exclude that user from the `Match` rule and use Fossil’s
normal `ssh://` URL scheme for those cases. This user will bypass the
Fossil RBAC, but they effectively have Setup capability on those
repositories anyway by having full read/write access to the DB files via
the shell.
## 2. Rewrite the sync command with that wrapper <a id="wrapper"></a>
When Fossil syncs over SSH, it attempts to launch a remote Fossil
instance with certain parameters in order to set up the HTTP-based sync
protocol over that SSH tunnel. We need to preserve some of this command
and rewrite other parts to make this work.
Here is a simpler variant of Andy’s original wrapper script:
``` sh
#!/bin/bash
set -- $SSH_ORIGINAL_COMMAND
while [ $# -gt 1 ] ; do shift ; done
export REMOTE_USER="$USER"
ROOT=/home/fossil
exec "$ROOT/bin/fossil" http "$ROOT/museum/$(/bin/basename "$1")"
```
The substantive changes are:
1. Move the command rewriting bits to the start.
2. Be explicit about executable paths. You might extend this idea by
using chroot, BSD jails, Linux containers, etc.
3. Restrict the Fossil repositories to a single flat subdirectory under
the `fossil` user’s home directory. This scheme is easier to secure
than one allowing subdirectories, since you’d need to take care of
`../` and such to prevent a sandbox escape.
4. Don’t take the user name via the SSH command; to this author’s mind,
the user should not get to override their Fossil user name on the
remote server, as that permits impersonation. The identity you
present to the SSH server must be the same identity that the Fossil
repository you’re working with knows you by. Since the users
selected by “`Match`” block above are dedicated to using only Fossil
in this setup, this restriction shouldn’t present a practical problem.
The script’s shebang line assumes `/bin/sh` is POSIX-compliant, but that
is not the case everywhere. If the script fails to run on your system,
try changing this line to point at `bash`, `dash`, `ksh`, or `zsh`. Also
check the absolute paths for local correctness: is `/bin/basename`
installed on your system, for example?
Under this scheme, you clone with a command like:
$ fossil clone ssh://HOST/repo.fossil
This will clone the remote `/home/fossil/museum/repo.fossil` repository
to your local machine under the same name and open it into a “`repo/`”
subdirectory. Notice that we didn’t have to give the `museum/` part of
the path: it’s implicit per point #3 above.
This presumes your local user name matches the remote user name. Unlike
with `http[s]://` URLs, you don’t have to provide the `USER@` part to
get authenticated access
since this scheme doesn’t permit anonymous cloning. Only
if these two user names are different do you need to add the `USER@` bit to the
URL.
## 3. Set permissions <a id="perms"></a>
This scheme assumes that the users covered by the `Match` rule can read
the wrapper script from where you placed it and execute it, and that
they have read/write access on the directory where the Fossil
repositories are stored.
You can achieve all of this on a Linux box with:
``` shell
sudo adduser fossil
for u in alice bob carol dave ; do
sudo adduser $u
sudo gpasswd -a fossil $u
done
sudo -i -u fossil
chmod 710 .
mkdir -m 750 bin
mkdir -m 770 museum
ln -s /usr/local/bin/fossil bin
```
You then need to copy the Fossil repositories into `~fossil/museum` and
make them readable and writable by group `fossil`. These repositories
presumably already have Fossil users configured, with the necessary
[user capabilities](../../caps/), the point of this article being to
show you how to make Fossil-over-SSH pay attention to those caps.
You must also permit use of `REMOTE_USER` on each shared repository.
Fossil only pays attention to this environment variable in certain
contexts, of which “`fossil http`” is not one. Run this command against
each repo to allow that:
``` shell
echo "INSERT OR REPLACE INTO config VALUES ('remote_user_ok',1,strftime('%s','now'));" |
fossil sql -R museum/repo.fossil
```
Now you can configure SSH authentication for each user. Since Fossil’s
password-saving feature doesn’t work in this case, I suggest setting up
SSH keys via `~USER/.ssh/authorized_keys` since the SSH authentication
occurs on each sync, which Fossil’s default-enabled autosync setting
makes frequent.
Equivalent commands for other OSes should be readily discerned from the
script above.
[sshfc]: forum:/forumpost/0d7d6c3df41fcdfd
<div style="height:50em" id="this-space-intentionally-left-blank"></div>
|
Changes to www/server/any/stunnel.md.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 | that made the request. You can run `stunnel` in one of two modes: socket listener — much like in our [`inetd` doc](./inetd.md) — and as an HTTP reverse proxy. We’ll cover both cases here, separately. | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
that made the request.
You can run `stunnel` in one of two modes: socket listener — much like
in our [`inetd` doc](./inetd.md) — and as an HTTP reverse proxy. We’ll
cover both cases here, separately.
## <a id="sa"></a>Socket Activation
The following `stunnel.conf` configuration configures it to run Fossil
in socket listener mode, launching Fossil only when an HTTPS hit comes
in, then shutting it back down as soon as the transaction is complete:
```dosini
[fossil]
|
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | It is important that the [`fossil http`](/help/http) command in that configuration include the `--https` option to let Fossil know to use “`https://`” instead of “`http://`” in generated hyperlinks. | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | It is important that the [`fossil http`](/help/http) command in that configuration include the `--https` option to let Fossil know to use “`https://`” instead of “`http://`” in generated hyperlinks. ## <a id="proxy"></a>Reverse Proxy You can instead have Fossil running in the background in [standalone HTTP server mode](./none.md), bound to a high random TCP port number on localhost via the `--localhost` and `--port` flags, then configure `stunnel` to reverse proxy public HTTPS connections down to it via HTTP. The configuration is the same as the above except that you drop the |
| ︙ | ︙ | |||
73 74 75 76 77 78 79 |
than in socket listener mode, where the Fossil binary has to be
loaded and re-initialized on each HTTPS hit.
2. The socket listener mode doesn’t work on all platforms that
`stunnel` runs on, particularly [on Windows](../windows/stunnel.md).
*[Return to the top-level Fossil server article.](../)*
| > > | 73 74 75 76 77 78 79 80 81 |
than in socket listener mode, where the Fossil binary has to be
loaded and re-initialized on each HTTPS hit.
2. The socket listener mode doesn’t work on all platforms that
`stunnel` runs on, particularly [on Windows](../windows/stunnel.md).
*[Return to the top-level Fossil server article.](../)*
<div style="height:50em" id="this-space-intentionally-left-blank"></div>
|
Changes to www/server/debian/nginx.md.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 | details depend on the host OS and web stack details. Besides, TLS is widely considered part of the baseline configuration these days. [scgii]: ../any/scgi.md [vps]: https://en.wikipedia.org/wiki/Virtual_private_server | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | details depend on the host OS and web stack details. Besides, TLS is widely considered part of the baseline configuration these days. [scgii]: ../any/scgi.md [vps]: https://en.wikipedia.org/wiki/Virtual_private_server ## <a id="benefits"></a>Benefits This scheme is considerably more complicated than the [standalone HTTP server](../any/none.md) and [CGI options](../any/cgi.md). Even with the benefit of this guide and pre-built binary packages, it requires quite a bit of work to set it up. Why should you put up with this complexity? Because it gives many benefits that are difficult or impossible to get with the less complicated options: |
| ︙ | ︙ | |||
57 58 59 60 61 62 63 | world and interior site services like Fossil. It allows Fossil to participate seamlessly as part of a larger web stack. * **Availability** — nginx is already in most operating system binary package repositories, so you don’t need to go out of your way to get it. | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
world and interior site services like Fossil. It allows Fossil to
participate seamlessly as part of a larger web stack.
* **Availability** — nginx is already in most operating system binary
package repositories, so you don’t need to go out of your way to get it.
## <a id="modes"></a>Fossil Service Modes
Fossil provides four major ways to access a repository it’s serving
remotely, three of which are straightforward to use with nginx:
* **HTTP** — Fossil has [a built-in HTTP server](../any/none.md).
While this method is efficient and it’s
possible to use nginx to proxy access to another HTTP server, we
|
| ︙ | ︙ | |||
92 93 94 95 96 97 98 |
without its performance problems.
SCGI it is, then.
[scgip]: https://en.wikipedia.org/wiki/Simple_Common_Gateway_Interface
| | | | | 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 |
without its performance problems.
SCGI it is, then.
[scgip]: https://en.wikipedia.org/wiki/Simple_Common_Gateway_Interface
## <a id="deps"></a>Installing the Dependencies
The first step is to install some non-default packages we’ll need. SSH into
your server, then say:
$ sudo apt install fossil nginx
You can leave “`fossil`” out of that if you’re building Fossil from
source to get a more up-to-date version than is shipped with the host
OS.
## <a id="scgi"></a>Running Fossil in SCGI Mode
For the following nginx configuration to work, it needs to contact a
Fossil instance speaking the SCGI protocol. There are [many ways](../)
to set that up. For Debian type systems, we recommend
following [our systemd system service guide](service.md).
There are other ways to arrange for Fossil to run as a service backing
nginx, but however you do it, you need to match up the TCP port numbers between it
and those in the nginx configuration below.
## <a id="config"></a>Configuration
On Debian and Ubuntu systems the primary user-level configuration file
for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
file contain only a list of include statements, one for each site that
server hosts:
include local/example.com
|
| ︙ | ︙ | |||
192 193 194 195 196 197 198 | on a single server. The configuration for `foo.net` is similar. See [the nginx docs](https://nginx.org/en/docs/) for more ideas. | | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | on a single server. The configuration for `foo.net` is similar. See [the nginx docs](https://nginx.org/en/docs/) for more ideas. ## <a id="http"></a>Proxying HTTP Anyway [Above](#modes), we argued that proxying SCGI is a better option than making nginx reinterpret Fossil’s own implementation of HTTP. If you want Fossil to speak HTTP, just [set Fossil up as a standalone server](../any/none.md). And if you want nginx to [provide TLS encryption for Fossil](#tls), proxying HTTP instead of SCGI provides no benefit. |
| ︙ | ︙ | |||
216 217 218 219 220 221 222 | The most common thing people get wrong when hand-rolling a configuration like this is to get the slashes wrong. Fossil is sensitive to this. For instance, Fossil will not collapse double slashes down to a single slash, as some other HTTP servers will. | | | | 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 |
The most common thing people get wrong when hand-rolling a configuration
like this is to get the slashes wrong. Fossil is sensitive to this. For
instance, Fossil will not collapse double slashes down to a single
slash, as some other HTTP servers will.
## <a id="large-uv"></a> Allowing Large Unversioned Files
By default, nginx only accepts HTTP messages [up to a
meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size)
in size. Fossil chunks its sync protocol such that this is not normally
a problem, but when sending [unversioned content][uv], it uses a single
message for the entire file. Therefore, if you will be storing files
larger than this limit as unversioned content, you need to raise the
limit. Within the `location` block:
# Allow large unversioned file uploads, such as PDFs
client_max_body_size 20M;
[uv]: ../../unvers.wiki
## <a id="fail2ban"></a> Integrating `fail2ban`
One of the nice things that falls out of proxying Fossil behind nginx is
that it makes it easier to configure `fail2ban` to recognize attacks on
Fossil and automatically block them. Fossil logs the sorts of errors we
want to detect, but it does so in places like the repository’s admin
log, a SQL table, which `fail2ban` doesn’t know how to query. By putting
Fossil behind an nginx proxy, we convert these failures to log file
|
| ︙ | ︙ | |||
277 278 279 280 281 282 283 | There’s a [lot more you can do][dof2b], but that gets us out of scope of this guide. [dof2b]: https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-14-04 | | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | There’s a [lot more you can do][dof2b], but that gets us out of scope of this guide. [dof2b]: https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-14-04 ## <a id="tls"></a> Adding TLS (HTTPS) Support One of the [many ways](../../ssl.wiki) to provide TLS-encrypted HTTP access (a.k.a. HTTPS) to Fossil is to run it behind a web proxy that supports TLS. One such option is nginx on Debian, so we show the details of that here. You can extend this guide to other operating systems by following the |
| ︙ | ︙ |
Changes to www/server/index.html.
| ︙ | ︙ | |||
104 105 106 107 108 109 110 | repository file to your server and proceed with server setup, below. Further configuration steps can wait until <a href="#postsetup">after the server is running</a>.</p> <h2 id="methods">Activation Methods</h2> | | | | | | > | | | | > > > > > > > > | 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 | repository file to your server and proceed with server setup, below. Further configuration steps can wait until <a href="#postsetup">after the server is running</a>.</p> <h2 id="methods">Activation Methods</h2> <p>There are basically five ways to run a Fossil server:</p> <ol> <li><a href="any/cgi.md">CGI</a> <li><a href="#slist">Socket listener</a> <li><a href="any/none.md">Stand-alone HTTP server</a> <li><a href="any/scgi.md">SCGI</a> <li><a href="#ssh">SSH</a> </ol> <p>All of these methods can serve either a single repository or a directory hierarchy containing mulitiple repositories.</p> <p>You are not restricted to a single server setup. The same Fossil repository can be served using two or more of the above techniques at the same time. These methods use clean, well-defined, standard interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from one method to another in response to changes in hosting providers or administrator preferences.</p> <h3 id="cgi">CGI</h3> <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a CGI script</a>. This method is known to work with Apache, <tt>lighttpd</tt>, and <a href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server administrator places a <a href="$ROOT/help?cmd=cgi">short CGI script</a> in the web server's document hierarchy and when a client requests the URL that corresponds to that script, Fossil runs and generates the response.</p> <p>CGI is a good choice for merging Fossil into an existing web site, particularly on hosts that have CGI set up and working. The Fossil <a href="../selfhost.wiki">self-hosting repositories</a> are implemented with CGI underneath <tt>althttpd</tt>.</p> <h3 id="slist">Socket Listener</h3> <p>Socket listener daemons such as <a id="inetd" href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd" href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel" href="any/stunnel.md"><tt>stunnel</tt></a>, <a href="macos/service.md"><tt>launchd</tt></a>, and <a href="debian/service.md"><tt>systemd</tt></a> can be configured to invoke the the <a href="$ROOT/help?cmd=http"><tt>fossil http</tt></a> command to handle each incoming HTTP request. The "<tt>fossil http</tt>" command reads the HTTP request off of standard input, computes an appropriate reply, and writes the reply on standard output. There is a separate invocation of the "<tt>fossil http</tt>" command for each HTTP request. The socket listener daemon takes care of relaying content to and from the client, and (in the case of <a href="any/stunnel.md">stunnel</a>) handling TLS decryption and encryption. <h3 id="standalone">Stand-alone HTTP Server</h3> <p>This is the <a href="any/none.md">easiest method</a>. A stand-alone server uses the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command to run a process that listens for incoming HTTP requests on a socket and then dispatches a copy of itself to deal with each incoming request. You can expose Fossil directly to the clients in this way or you can interpose a <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> layer between the clients and Fossil.</p> <h3 id="scgi"3>SCGI</h3> <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>. When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is run with the extra <tt>--scgi</tt> option, it listens for incoming SCGI requests rather than HTTP requests. This allows Fossil to respond to requests from web servers <a href="debian/nginx.md">such as nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy than HTTP, since the HTTP doesn't have to be re-interpreted in terms of the proxy's existing HTTP implementation, but it's more complex to set up because you also have to set up an SCGI-to-HTTP proxy for it. It is worth taking on this difficulty only when you need to integrate Fossil into an existing web site already being served by an SCGI-capable web server.</p> <h3 id="ssh">SSH</h3> <p>Fossil supports <tt>ssh://</tt> URLs, but there are <a href="../caps/#webonly">practical limitations</a> with the default behavior. You can get the full power of <a href="../caps/">Fossil's RBAC system</a> over SSH <a href="any/http-over-ssh.md">with a bit of clever configuration</a>.</p> <h2 id="matrix">Activation Tutorials</h2> <p>We've broken the configuration for each method out into a series of sub-articles. Some of these are generic, while others depend on particular operating systems or front-end software:</p> <div id="tutpick" class="show"></div> |
| ︙ | ︙ |
Changes to www/server/openbsd/fastcgi.md.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | a single directory within a chroot, and allow `ssh` access to create new repositories remotely. **NOTE:** The following instructions assume an OpenBSD 6.7 installation. [httpd]: https://www.openbsd.org/papers/httpd-asiabsdcon2015.pdf | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
a single directory within a chroot, and allow `ssh` access to create
new repositories remotely.
**NOTE:** The following instructions assume an OpenBSD 6.7 installation.
[httpd]: https://www.openbsd.org/papers/httpd-asiabsdcon2015.pdf
## <a id="fslinstall"></a>Install Fossil
Use the OpenBSD package manager `pkg_add` to install Fossil, making sure
to select the statically linked binary.
```console
$ doas pkg_add fossil
quirks-3.325 signed on 2020-06-12T06:24:53Z
|
| ︙ | ︙ | |||
60 61 62 63 64 65 66 |
$ doas mkdir /var/www/htdocs/fsl.domain.tld
$ doas touch /var/www/logs/fossil.log
$ doas chown www /var/www/logs/fossil.log
$ doas chmod 660 /var/www/logs/fossil.log
$ doas chmod 755 /var/www/cgi-bin/scm
```
| | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
$ doas mkdir /var/www/htdocs/fsl.domain.tld
$ doas touch /var/www/logs/fossil.log
$ doas chown www /var/www/logs/fossil.log
$ doas chmod 660 /var/www/logs/fossil.log
$ doas chmod 755 /var/www/cgi-bin/scm
```
## <a id="chroot"></a>Setup chroot
Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible
from within the chroot, so need to be constructed; `/var`, however, is
mounted with the `nodev` option. Rather than removing this default
setting, create a small memory filesystem and then mount it on to
`/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and
`null` device files can be created. In order to avoid necessitating a
|
| ︙ | ︙ | |||
106 107 108 109 110 111 112 | user who will push to, pull from, and create repositories. ```console $ doas chown -R user:www /var/www/htdocs/fsl.domain.tld $ doas chmod 770 /var/www/htdocs/fsl.domain.tld ``` | | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | user who will push to, pull from, and create repositories. ```console $ doas chown -R user:www /var/www/htdocs/fsl.domain.tld $ doas chmod 770 /var/www/htdocs/fsl.domain.tld ``` ## <a id="httpdconfig"></a>Configure httpd On OpenBSD, [httpd.conf(5)][httpd] is the configuration file for `httpd`. To setup the server to serve all Fossil repositores within the directory specified in the CGI script, and automatically redirect standard HTTP requests to HTTPS—apart from [Let's Encrypt][LE] challenges issued in response to [acme-client(1)][acme] certificate requests—create `/etc/httpd.conf` as a privileged user with the |
| ︙ | ︙ | |||
176 177 178 179 180 181 182 | **NOTE:** If not already in possession of a HTTPS certificate, comment out the `https` server block and proceed to securing a free [Let's Encrypt Certificate](#letsencrypt); otherwise skip to [Start `httpd`](#starthttpd). | | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | **NOTE:** If not already in possession of a HTTPS certificate, comment out the `https` server block and proceed to securing a free [Let's Encrypt Certificate](#letsencrypt); otherwise skip to [Start `httpd`](#starthttpd). ## <a id="letsencrypt"></a>Let's Encrypt Certificate In order for `httpd` to serve HTTPS, secure a free certificate from Let's Encrypt using `acme-client`. Before issuing the request, however, ensure you have a zone record for the subdomain with your registrar or nameserver. Then open `/etc/acme-client.conf` as a privileged user to configure the request. |
| ︙ | ︙ | |||
237 238 239 240 241 242 243 | /etc/ssl/private: -r-------- 1 root wheel 3.2K Mar 2 01:31:03 2018 domain.tld.key ``` Make sure to reopen `/etc/httpd.conf` to uncomment the second server block responsible for serving HTTPS requests before proceeding. | | | | 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 |
/etc/ssl/private:
-r-------- 1 root wheel 3.2K Mar 2 01:31:03 2018 domain.tld.key
```
Make sure to reopen `/etc/httpd.conf` to uncomment the second server
block responsible for serving HTTPS requests before proceeding.
## <a id="starthttpd"></a>Start `httpd`
With `httpd` configured to serve Fossil repositories out of
`/var/www/htdocs/fsl.domain.tld`, and the certificates and key in place,
enable and start `slowcgi`—OpenBSD's FastCGI wrapper server that will
execute the above Fossil CGI script—before checking that the syntax of
the `httpd.conf` configuration file is correct, and (re)starting the
server (if still running from requesting a Let's Encrypt certificate).
```console
$ doas rcctl enable slowcgi
$ doas rcctl start slowcgi
slowcgi(ok)
$ doas httpd -vnf /etc/httpd.conf
configuration OK
$ doas rcctl start httpd
httpd(ok)
```
## <a id="clientconfig"></a>Configure Client
To facilitate creating new repositories and pushing them to the server,
add the following function to your `~/.cshrc` or `~/.zprofile` or the
config file for whichever shell you are using on your development box.
```sh
finit() {
|
| ︙ | ︙ |
Changes to www/server/windows/iis.md.
| ︙ | ︙ | |||
41 42 43 44 45 46 47 | instructions](../any/none.md) for further details. For a more robust setup, we recommend that you [install Fossil as a Windows service](./service.md), which will allow Fossil to start at system boot, before anyone has logged in interactively. | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | instructions](../any/none.md) for further details. For a more robust setup, we recommend that you [install Fossil as a Windows service](./service.md), which will allow Fossil to start at system boot, before anyone has logged in interactively. ## <a id="install"></a>Install IIS IIS might not be installed in your system yet, so follow the path appropriate to your host OS. We’ve tested only the latest Microsoft OSes as of the time of this writing, but the basic process should be similar on older OSes. |
| ︙ | ︙ |
Changes to www/server/windows/service.md.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 | If you wish to server a directory of repositories, the `fossil winsrv` command requires a slightly different set of options vs. `fossil server`: ``` fossil winsrv create --repository D:/Path/to/Repos --repolist ``` | | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | If you wish to server a directory of repositories, the `fossil winsrv` command requires a slightly different set of options vs. `fossil server`: ``` fossil winsrv create --repository D:/Path/to/Repos --repolist ``` ### <a id='PowerShell'></a>Advanced service installation using PowerShell As great as `fossil winsrv` is, it does not have one to one reflection of all of the `fossil server` [options](/help?cmd=server). When you need to use some of the more advanced options, such as `--https`, `--skin`, or `--extroot`, you will need to use PowerShell to configure and install the Windows service. PowerShell provides the [New-Service](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-5.1) |
| ︙ | ︙ |
Changes to www/server/windows/stunnel.md.
| ︙ | ︙ | |||
105 106 107 108 109 110 111 |
Get-ChildItem Cert:\LocalMachine\My | Where{$_.FriendlyName -eq "FriendlyName"} |
Export-PfxCertificate -FilePath fossil-scm.pfx -Password $passwd
```
You will now have your certificate stored as a PFX file.
| | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
Get-ChildItem Cert:\LocalMachine\My | Where{$_.FriendlyName -eq "FriendlyName"} |
Export-PfxCertificate -FilePath fossil-scm.pfx -Password $passwd
```
You will now have your certificate stored as a PFX file.
<a id="convert"></a>
### Convert Certificate from PFX to PEM
For this step you will need the openssl tools that were installed with stunnel.
```PowerShell
# Add stunnel\bin directory to path for this session.
$env:PATH += ";${env:ProgramFiles(x86)}\stunnel\bin"
|
| ︙ | ︙ |
Changes to www/sync.wiki.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 | some local state is transferred during a [/help?cmd=clone|clone] in order to initialize the local state of the new repository. Also, an administrator can sync local state using the [/help?cmd=configuration|config push] and [/help?cmd=configuration|config pull] commands. | < | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | some local state is transferred during a [/help?cmd=clone|clone] in order to initialize the local state of the new repository. Also, an administrator can sync local state using the [/help?cmd=configuration|config push] and [/help?cmd=configuration|config pull] commands. <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3> <p>The "bag of artifacts" data model used by Fossil Fossil is apparently an implementation of a particular [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|Conflict-Free Replicated Datatype (CRDT)] called a "G-Set" or "Grow-only Set". The academic literature on CRDTs only began to appear in about 2011, and |
| ︙ | ︙ | |||
703 704 705 706 707 708 709 | the client because the client login lacks the "write-unversioned" permission.</p> <li><p><b>uv-push-ok</b></i> <p>A server sends the uv-push-ok pragma to the client in response to a uv-hash pragma with a mismatched content hash argument. This pragma indicates that there are differences in unversioned content | | | | 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 | the client because the client login lacks the "write-unversioned" permission.</p> <li><p><b>uv-push-ok</b></i> <p>A server sends the uv-push-ok pragma to the client in response to a uv-hash pragma with a mismatched content hash argument. This pragma indicates that there are differences in unversioned content between the client and server and that content can be transferred in either direction. The server is willing to accept content from the client because the client login has the "write-unversioned" permission.</p> <li><p><b>ci-lock</b> <i>CHECKIN-HASH CLIENT-ID</i></p> <p>A client sends the "ci-lock" pragma to the server to indicate that it is about to add a new check-in as a child of the CHECKIN-HASH check-in and on the same branch as CHECKIN-HASH. If some other client has already indicated that it was also trying to commit against CHECKIN-HASH, that indicates that a fork is about to occur, and the server will reply with a "ci-lock-fail" pragma (see below). Check-in locks automatically expire when the check-in actually occurs, or after a timeout (currently one minute but subject to change). <li><p><b>ci-lock-fail</b> <i>LOGIN MTIME</i></p> <p>When a server receives two or more "ci-lock" pragma messages for the same check-in but from different clients, the second a subsequent ci-lock will provoke a ci-lock-fail pragma in the reply to let the client know that it if continues with the check-in it will likely generate a fork. The LOGIN and MTIME |
| ︙ | ︙ |
Changes to www/tech_overview.wiki.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 | <li>The "[/help/stash | stash]" <li>Information needed to "[/help/undo|undo]" or "[/help/redo|redo]" </ul> </td> </tr> </table> | < | < | | 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 | <li>The "[/help/stash | stash]" <li>Information needed to "[/help/undo|undo]" or "[/help/redo|redo]" </ul> </td> </tr> </table> <h3 id="configdb">2.1 The Configuration Database</h3> The configuration database holds cross-repository preferences and a list of all repositories for a single user. The [/help/settings | fossil settings] command can be used to specify various operating parameters and preferences for Fossil repositories. Settings can apply to a single repository, or they can apply globally to all repositories for a user. If both a global and a repository value exists for a setting, then the repository-specific value takes precedence. All of the settings have reasonable defaults, and so many users will never need to change them. But if changes to settings are desired, the configuration database provides a way to change settings for all repositories with a single command, rather than having to change the setting individually on each repository. The configuration database also maintains a list of repositories. This list is used by the [/help/all | fossil all] command in order to run various operations such as "sync" or "rebuild" on all repositories managed by a user. <h4 id="configloc">2.1.1 Location Of The Configuration Database</h4> On Unix systems, the configuration database is named by the following algorithm: <blockquote><table border="0"> <tr><td>1. if environment variable FOSSIL_HOME exists <td> → <td>$FOSSIL_HOME/.fossil |
| ︙ | ︙ | |||
327 328 329 330 331 332 333 | The "shun" table in the repository database records the hash values for all shunned artifacts. The shun table can be pushed or pulled using the [/help/config | fossil config] command with the "shun" AREA argument. The shun table is also copied during a [/help/clone | clone]. | < | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | The "shun" table in the repository database records the hash values for all shunned artifacts. The shun table can be pushed or pulled using the [/help/config | fossil config] command with the "shun" AREA argument. The shun table is also copied during a [/help/clone | clone]. <h3 id="localdb">2.3 Checkout Databases</h3> Fossil allows a single repository to have multiple working checkouts. Each working checkout has a single database in its root directory that records the state of that checkout. The checkout database is named "_FOSSIL_" or ".fslckout". The checkout database records information such as the following: |
| ︙ | ︙ |
Changes to www/th1-hooks.md.
| ︙ | ︙ | |||
34 35 36 37 38 39 40 | TH1 Hook Related Variables for Web Pages ---------------------------------------- * web\_name -- _Name of web page being rendered._ * web\_args -- _Current web page arguments._ * web\_flags -- _Bitmask of CMDFLAG values for the web page being rendered._ | | | | | | | | | 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 |
TH1 Hook Related Variables for Web Pages
----------------------------------------
* web\_name -- _Name of web page being rendered._
* web\_args -- _Current web page arguments._
* web\_flags -- _Bitmask of CMDFLAG values for the web page being rendered._
<a id="cmdReturnCodes"></a>TH1 Hook Related Return Codes for Commands
-----------------------------------------------------------------------
* TH\_OK -- _Command will be executed, notification will be executed._
* TH\_ERROR -- _Command will be skipped, notification will be skipped,
error message will be emitted._
* TH\_BREAK -- _Command will be skipped, notification will be skipped._
* TH\_RETURN -- _Command will be executed, notification will be skipped._
* TH\_CONTINUE -- _Command will be skipped, notification will be executed._
For commands that are not included in the Fossil binary, allowing their
execution will cause the standard "unknown command" error message to be
generated, which will typically exit the process. Therefore, adding a
new command generally requires using the TH_CONTINUE return code.
<a id="webReturnCodes"></a>TH1 Hook Related Return Codes for Web Pages
------------------------------------------------------------------------
* TH\_OK -- _Web page will be rendered, notification will be executed._
* TH\_ERROR -- _Web page will be skipped, notification will be skipped,
error message will be emitted._
* TH\_BREAK -- _Web page will be skipped, notification will be skipped._
* TH\_RETURN -- _Web page will be rendered, notification will be skipped._
* TH\_CONTINUE -- _Web page will be skipped, notification will be executed._
For web pages that are not included in the Fossil binary, allowing their
rendering will cause the standard "Not Found" error message to be generated,
which will cause an HTTP 404 status code to be sent. Therefore, adding a
new web page generally requires using the TH_CONTINUE return code.
<a id="triggerReturnCodes"></a>Triggering TH1 Return Codes from a Script
--------------------------------------------------------------------------
* TH\_OK -- _This is the default return code, nothing special needed._
* TH\_ERROR -- _Use the **error** command._
* TH\_BREAK -- _Use the **break** command._
* TH\_RETURN -- _Use the **return -code 5** command._
* TH\_CONTINUE -- _Use the **continue** command._
<a id="command_hook"></a>TH1 command_hook Procedure
-----------------------------------------------------
* command\_hook
This user-defined procedure, if present, is called just before the
execution of a command. The name of the command being executed will
be stored in the "cmd\_name" global variable. The arguments to the
command being executed will be stored in the "cmd\_args" global variable.
The associated CMDFLAG value will be stored in the "cmd\_flags" global
variable. Before exiting, the procedure should trigger the return
<a href="#cmdReturnCodes">code</a> that corresponds to the desired action
to take next.
<a id="command_notify"></a>TH1 command_notify Procedure
---------------------------------------------------------
* command\_notify
This user-defined procedure, if present, is called just after the
execution of a command. The name of the command being executed will
be stored in the "cmd\_name" global variable. The arguments to the
command being executed will be stored in the "cmd\_args" global variable.
The associated CMDFLAG value will be stored in the "cmd\_flags" global
variable. Before exiting, the procedure should trigger the return
<a href="#cmdReturnCodes">code</a> that corresponds to the desired action
to take next.
<a id="webpage_hook"></a>TH1 webpage_hook Procedure
-----------------------------------------------------
* webpage\_hook
This user-defined procedure, if present, is called just before the
rendering of a web page. The name of the web page being rendered will
be stored in the "web\_name" global variable. The arguments to the
web page being rendered will be stored in the "web\_args" global variable.
The associated CMDFLAG value will be stored in the "web\_flags" global
variable. Before exiting, the procedure should trigger the return
<a href="#webReturnCodes">code</a> that corresponds to the desired action
to take next.
<a id="webpage_notify"></a>TH1 webpage_notify Procedure
---------------------------------------------------------
* webpage\_notify
This user-defined procedure, if present, is called just after the
rendering of a web page. The name of the web page being rendered will
be stored in the "web\_name" global variable. The arguments to the
web page being rendered will be stored in the "web\_args" global variable.
The associated CMDFLAG value will be stored in the "web\_flags" global
variable. Before exiting, the procedure should trigger the return
<a href="#webReturnCodes">code</a> that corresponds to the desired action
to take next.
|
Changes to www/whyusefossil.wiki.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
<li>Everyone always has the latest code
<li>Failed disk-drives cause no loss of work
<li>Avoid wasting time doing manual file copying
<li>Avoid human errors during manual backups
</ol>
</ol>
| < | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<li>Everyone always has the latest code
<li>Failed disk-drives cause no loss of work
<li>Avoid wasting time doing manual file copying
<li>Avoid human errors during manual backups
</ol>
</ol>
<p id="definitions"><b>II. Definitions</b></p>
<ul>
<li><p><b>Project</b> →
a collection of computer files that serve some common
purpose. Often the project is a software application and the
individual files are source code together with makefiles, scripts, and
"README.txt" files. Other examples of projects include books or
|
| ︙ | ︙ |
Changes to www/wikitheory.wiki.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 | <h2>Bug-reports and check-in comments and Forum messages</h2> The comments on check-ins and the text in the descriptions of bug reports both use wiki formatting. Exactly the same set of formatting rules apply. There is never a need to learn one formatting language for documentation and a different markup for bugs or for check-in comments. | < | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <h2>Bug-reports and check-in comments and Forum messages</h2> The comments on check-ins and the text in the descriptions of bug reports both use wiki formatting. Exactly the same set of formatting rules apply. There is never a need to learn one formatting language for documentation and a different markup for bugs or for check-in comments. <h2 id="assocwiki">Auxiliary notes attached to check-ins or branches</h2> Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" or "checkin/<i>HASH</i>" are associated with the corresponding branch or check-in. The wiki text appears in an "About" section of timelines and info screens. Examples: * [/timeline?r=graph-test-branch] shows the text of the |
| ︙ | ︙ |